diff options
44 files changed, 1540 insertions, 5559 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..8b7038aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Submit a new bug report to help us improve ADOdb +title: '' +labels: 'triage' +assignees: '' + +--- + +### Description +A clear and concise description of what the problem is, including relevant error messages and stack trace. + +### Environment +- ADOdb version: _Version number (or Git Commit/Ref)_ +- Driver or Module: +- Database type and version: +- PHP version: +- Platform: + +* [ ] I have tested that the problem is reproducible in the [latest release](https://github.com/ADOdb/ADOdb/releases/latest) / **master** branch / **hotfix** branch + ⚠️ Keep only the applicable option(s), i.e. where you actually tested and remove the others) + +### Steps to reproduce +Detailed, step-by-step instructions to reproduce the behavior, including: +- code snippet +- SQL to create and populate database objects used by the code snippet + +### Expected behavior +A clear and concise description of what you expected to happen. + +### Additional context +Add any other information relevant to the problem here. diff --git a/SECURITY.md b/SECURITY.md index 00670f24..a52a6c2c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -19,7 +19,7 @@ If you discover a vulnerability in ADOdb, please contact the [project's maintainer](https://github.com/dregad) - by e-mail (look for it in the Git history) -- via private chat on [Gitter](https://gitter.im/dregad) +- via private chat on [Matrix](https://matrix.to/#/@dregad:matrix.org) Kindly provide the following information in your report: diff --git a/adodb-lib.inc.php b/adodb-lib.inc.php index 90b8b9d6..72514f40 100644 --- a/adodb-lib.inc.php +++ b/adodb-lib.inc.php @@ -1175,104 +1175,108 @@ function _adodb_column_sql(&$zthis, $action, $type, $fname, $fnameq, $arrFields, * @param string|string[] $sql A string or array of SQL statements * @param string[]|null $inputarr An optional array of bind parameters * -* @return handle|void A handle to the executed query +* @return mixed A handle to the executed query (actual type is driver-dependent) */ function _adodb_debug_execute($zthis, $sql, $inputarr) { + // Execute the query, capturing any output + ob_start(); + $queryId = $zthis->_query($sql, $inputarr); + $queryOutput = ob_get_clean(); + + // Get last error number and message if query execution failed + if (!$queryId) { + if ($zthis->databaseType == 'mssql') { + // Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql + // because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion + // ErrorNo is a slow function call in mssql + $errMsg = $zthis->ErrorMsg(); + if ($errMsg && ($errNo = $zthis->ErrorNo())) { + $queryOutput .= $errNo . ': ' . $errMsg . "\n"; + } + } else { + $errNo = $zthis->ErrorNo(); + if ($errNo) { + $queryOutput .= $errNo . ': ' . $zthis->ErrorMsg() . "\n"; + } + } + } + + // Driver name + $driverName = $zthis->databaseType; + if (!isset($zthis->dsnType)) { + // Append the PDO driver name + $driverName .= '-' . $zthis->dsnType; + } + + // Prepare SQL statement for display (remove newlines and tabs, compress repeating spaces) + $sqlText = preg_replace('/\s+/', ' ', is_array($sql) ? $sql[0] : $sql); + // Unpack the bind parameters - $ss = ''; + $bindParams = ''; if ($inputarr) { + $MAXSTRLEN = 64; foreach ($inputarr as $kk => $vv) { - if (is_string($vv) && strlen($vv) > 64) { - $vv = substr($vv, 0, 64) . '...'; + if (is_string($vv) && strlen($vv) > $MAXSTRLEN) { + $vv = substr($vv, 0, $MAXSTRLEN) . '...'; } if (is_null($vv)) { - $ss .= "($kk=>null) "; + $bindParams .= "$kk=>null\n"; } else { if (is_array($vv)) { $vv = sprintf("Array Of Values: [%s]", implode(',', $vv)); } - $ss .= "($kk=>'$vv') "; + $bindParams .= "$kk=>'$vv'\n"; } } - $ss = "[ $ss ]"; } - $sqlTxt = is_array($sql) ? $sql[0] : $sql; - - // Remove newlines and tabs, compress repeating spaces - $sqlTxt = preg_replace('/\s+/', ' ', $sqlTxt); - // check if running from browser or command-line - $inBrowser = isset($_SERVER['HTTP_USER_AGENT']); - - $myDatabaseType = $zthis->databaseType; - if (!isset($zthis->dsnType)) { - // Append the PDO driver name - $myDatabaseType .= '-' . $zthis->dsnType; - } + $isHtml = isset($_SERVER['HTTP_USER_AGENT']); - if ($inBrowser) { - if ($ss) { - // Default formatting for passed parameter - $ss = sprintf('<code class="adodb-debug">%s</code>', htmlspecialchars($ss)); + // Output format - sprintf parameters: + // %1 = horizontal line, %2 = DB driver, %3 = SQL statement, %4 = Query params + if ($isHtml) { + $fmtSql = '<div class="adodb-debug">' . PHP_EOL + . '<div class="adodb-debug-sql">' . PHP_EOL + . '%1$s<table>' . PHP_EOL + . '<tr><th>%2$s</th><td><code>%3$s</code></td></tr>' . PHP_EOL + . '%4$s</table>%1$s' . PHP_EOL + . '</div>' . PHP_EOL; + $hr = $zthis->debug === -1 ? '' : '<hr>'; + $sqlText = htmlspecialchars($sqlText); + if ($bindParams) { + $bindParams = '<tr><th>Parameters</th><td><code>' + . nl2br(htmlspecialchars($bindParams)) + . '</code></td></tr>' . PHP_EOL; } - if ($zthis->debug === -1) { - $outString = "<br class='adodb-debug'>(%s): %s %s<br class='adodb-debug'>"; - ADOConnection::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false); - } elseif ($zthis->debug !== -99) { - $outString = "<hr class='adodb-debug'>(%s): %s %s<hr class='adodb-debug'>"; - ADOConnection::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false); + if ($queryOutput) { + $queryOutput = '<div class="adodb-debug-errmsg">' . $queryOutput . '</div>' . PHP_EOL; } } else { // CLI output - if ($zthis->debug !== -99) { - $outString = sprintf("%s\n%s\n %s %s \n%s\n", str_repeat('-', 78), $myDatabaseType, $sqlTxt, $ss, str_repeat('-', 78)); - ADOConnection::outp($outString, false); - } + $fmtSql = '%1$s%2$s: %3$s%4$s%1$s'; + $hr = $zthis->debug === -1 ? '' : str_repeat('-', 78) . "\n"; + $sqlText .= "\n"; } - // Now execute the query - $qID = $zthis->_query($sql, $inputarr); - - // Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql - // because ErrorNo() calls Execute('SELECT @ERROR'), causing recursion - if ($zthis->databaseType == 'mssql') { - // ErrorNo is a slow function call in mssql - if ($emsg = $zthis->ErrorMsg()) { - if ($err = $zthis->ErrorNo()) { - if ($zthis->debug === -99) { - ADOConnection::outp("<hr>\n($myDatabaseType): " . htmlspecialchars($sqlTxt) . " $ss\n<hr>\n", false); - } - - ADOConnection::outp($err . ': ' . $emsg); - } - } - } else { - if (!$qID) { - // Statement execution has failed - if ($zthis->debug === -99) { - if ($inBrowser) { - $outString = "<hr class='adodb-debug'>(%s): %s %s<hr class='adodb-debug'>"; - ADOConnection::outp(sprintf($outString, $myDatabaseType, htmlspecialchars($sqlTxt), $ss), false); - } else { - $outString = sprintf("%s\n%s\n %s %s \n%s\n",str_repeat('-',78),$myDatabaseType,$sqlTxt,$ss,str_repeat('-',78)); - ADOConnection::outp($outString, false); - } - } - - // Send last error to output - $errno = $zthis->ErrorNo(); - if ($errno) { - ADOConnection::outp($errno . ': ' . $zthis->ErrorMsg()); - } + // Always output debug info if statement execution failed + if (!$queryId || $zthis->debug !== -99) { + printf($fmtSql, $hr, $driverName, $sqlText, $bindParams); + if ($queryOutput) { + echo $queryOutput . ($isHtml ? '' : "\n"); } } - if ($qID === false || $zthis->debug === 99) { - _adodb_backtrace(); + // Print backtrace if query failed or forced + if ($queryId === false || $zthis->debug === 99) { + _adodb_backtrace(true, 0, 0, $isHtml); + } + if ($isHtml && $zthis->debug !== -99) { + echo '</div>' . PHP_EOL; } - return $qID; + + return $queryId; } /** @@ -1281,73 +1285,60 @@ function _adodb_debug_execute($zthis, $sql, $inputarr) * @param string[]|bool $printOrArr Whether to print the result directly or return the result * @param int $maximumDepth The maximum depth of the array to traverse * @param int $elementsToIgnore The backtrace array indexes to ignore - * @param null|bool $ishtml True if we are in a CGI environment, false for CLI, + * @param null|bool $isHtml True if we are in a CGI environment, false for CLI, * null to auto detect * * @return string Formatted backtrace */ -function _adodb_backtrace($printOrArr=true, $maximumDepth=9999, $elementsToIgnore=0, $ishtml=null) +function _adodb_backtrace($printOrArr=true, $maximumDepth=0, $elementsToIgnore=0, $isHtml=null) { - if (!function_exists('debug_backtrace')) { - return ''; + if ($isHtml === null) { + // Auto determine if we in a CGI environment + $isHtml = isset($_SERVER['HTTP_USER_AGENT']); } - if ($ishtml === null) { - // Auto determine if we in a CGI enviroment - $html = (isset($_SERVER['HTTP_USER_AGENT'])); + $s = "Call stack (most recent call first):\n"; + if ($isHtml) { + $s = '<div class="adodb-debug-trace">' . PHP_EOL + . "<h4>$s</h4>\n" + . '<table>' . PHP_EOL + . '<thead><tr><th>#</th><th>Function</th><th>Location</th></tr></thead>' . PHP_EOL; + $fmt = '<tr><td>%1$d</td><td>%2$s</td><td>%3$s line %4$d</td></tr>' . PHP_EOL; } else { - $html = $ishtml; + $fmt = '%1$2d. %2$s in %3$s line %4$d' . PHP_EOL; } - $cgiString = "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>"; - $cliString = "%% line %4d, file: %s"; - $fmt = ($html) ? $cgiString : $cliString; - + // Maximum length for string arguments display $MAXSTRLEN = 128; - $s = ($html) ? '<pre align=left>' : ''; - + // Get 2 extra elements if max depth is specified + if ($maximumDepth) { + $maximumDepth += 2; + } if (is_array($printOrArr)) { - $traceArr = $printOrArr; + $traceArr = array_slice($printOrArr, 0, $maximumDepth); } else { - $traceArr = debug_backtrace(); + $traceArr = debug_backtrace(0, $maximumDepth); } - // Remove first 2 elements that just show calls to adodb_backtrace - array_shift($traceArr); - array_shift($traceArr); - - // We want last element to have no indent - $tabs = sizeof($traceArr) - 1; + // Remove elements to ignore, plus the first 2 elements that just show + // calls to adodb_backtrace + for ($elementsToIgnore += 2; $elementsToIgnore > 0; $elementsToIgnore--) { + array_shift($traceArr); + } + $elements = sizeof($traceArr); - foreach ($traceArr as $arr) { - if ($elementsToIgnore) { - // Ignore array element at start of array - $elementsToIgnore--; - $tabs--; - continue; - } - $maximumDepth--; - if ($maximumDepth < 0) { - break; + foreach ($traceArr as $element) { + // Function name with class prefix + $functionName = $element['function']; + if (isset($element['class'])) { + $functionName = $element['class'] . '::' . $functionName; } + // Function arguments $args = array(); - - if ($tabs) { - $s .= str_repeat($html ? ' ' : "\t", $tabs); - $tabs--; - } - if ($html) { - $s .= '<font face="Courier New,Courier">'; - } - - if (isset($arr['class'])) { - $s .= $arr['class'] . '.'; - } - - if (isset($arr['args'])) { - foreach ($arr['args'] as $v) { + if (isset($element['args'])) { + foreach ($element['args'] as $v) { if (is_null($v)) { $args[] = 'null'; } elseif (is_array($v)) { @@ -1357,29 +1348,34 @@ function _adodb_backtrace($printOrArr=true, $maximumDepth=9999, $elementsToIgnor } elseif (is_bool($v)) { $args[] = $v ? 'true' : 'false'; } else { - $v = (string)@$v; - // Truncate - $v = substr($v, 0, $MAXSTRLEN); // Remove newlines and tabs, compress repeating spaces $v = preg_replace('/\s+/', ' ', $v); - // Convert htmlchars (not sure why we do this in CLI) - $str = htmlspecialchars($v); + // Truncate if needed if (strlen($v) > $MAXSTRLEN) { - $str .= '...'; + $v = substr($v, 0, $MAXSTRLEN) . '...'; } - $args[] = $str; + $args[] = $isHtml ? htmlspecialchars($v) : $v; } } } - $s .= $arr['function'] . '(' . implode(', ', $args) . ')'; - $s .= @sprintf($fmt, $arr['line'], $arr['file'], basename($arr['file'])); - $s .= "\n"; + + // Shorten ADOdb paths ('/path/to/adodb/XXX' printed as '.../XXX') + $file = str_replace(__DIR__, '...', $element['file']); + + $s .= sprintf($fmt, + $elements--, + $functionName . '(' . implode(', ', $args) . ')', + $file, + $element['line'] + ); } - if ($html) { - $s .= '</pre>'; + + if ($isHtml) { + $s .= '</table>' . PHP_EOL . '</div>' . PHP_EOL; } + if ($printOrArr) { print $s; } diff --git a/adodb-time.inc.php b/adodb-time.inc.php deleted file mode 100644 index 0c3dd11d..00000000 --- a/adodb-time.inc.php +++ /dev/null @@ -1,1482 +0,0 @@ -<?php -/** - * ADOdb Date Library. - * - * @deprecated 5.22.6 Use 64-bit PHP native functions instead. - * - * PHP native date functions use integer timestamps for computations. - * Because of this, dates are restricted to the years 1901-2038 on Unix - * and 1970-2038 on Windows due to integer overflow for dates beyond - * those years. This library overcomes these limitations by replacing the - * native function's signed integers (normally 32-bits) with PHP floating - * point numbers (normally 64-bits). - * - * Dates from 100 A.D. to 3000 A.D. and later have been tested. - * The minimum is 100 A.D. as <100 will invoke the 2 => 4 digit year - * conversion. The maximum is billions of years in the future, but this - * is a theoretical limit as the computation of that year would take too - * long with the current implementation of adodb_mktime(). - * - * Replaces native functions as follows: - * - getdate() with adodb_getdate() - * - date() with adodb_date() - * - gmdate() with adodb_gmdate() - * - mktime() with adodb_mktime() - * - gmmktime() with adodb_gmmktime() - * - strftime() with adodb_strftime() - * - strftime() with adodb_gmstrftime() - * - * The parameters are identical, except that adodb_date() accepts a subset - * of date()'s field formats. Mktime() will convert from local time to GMT, - * and date() will convert from GMT to local time, but daylight savings is - * not handled currently. - * - * To improve performance, the native date functions are used whenever - * possible, the library only switches to PHP code when the dates fall outside - * of the 32-bit signed integer range. - * - * This library is independent of the rest of ADOdb, and can be used - * as standalone code. - * - * GREGORIAN CORRECTION - * - * Pope Gregory shortened October of A.D. 1582 by ten days. Thursday, - * October 4, 1582 (Julian) was followed immediately by Friday, October 15, - * 1582 (Gregorian). We handle this correctly, so: - * adodb_mktime(0, 0, 0, 10, 15, 1582) - adodb_mktime(0, 0, 0, 10, 4, 1582) - * == 24 * 3600 (1 day) - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2003-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -/* -============================================================================= - -FUNCTION DESCRIPTIONS - -** FUNCTION adodb_time() - -Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) as an unsigned integer. - -** FUNCTION adodb_getdate($date=false) - -Returns an array containing date information, as getdate(), but supports -dates greater than 1901 to 2038. The local date/time format is derived from a -heuristic the first time adodb_getdate is called. - - -** FUNCTION adodb_date($fmt, $timestamp = false) - -Convert a timestamp to a formatted local date. If $timestamp is not defined, the -current timestamp is used. Unlike the function date(), it supports dates -outside the 1901 to 2038 range. - -The format fields that adodb_date supports: - -<pre> - a - "am" or "pm" - A - "AM" or "PM" - d - day of the month, 2 digits with leading zeros; i.e. "01" to "31" - D - day of the week, textual, 3 letters; e.g. "Fri" - F - month, textual, long; e.g. "January" - g - hour, 12-hour format without leading zeros; i.e. "1" to "12" - G - hour, 24-hour format without leading zeros; i.e. "0" to "23" - h - hour, 12-hour format; i.e. "01" to "12" - H - hour, 24-hour format; i.e. "00" to "23" - i - minutes; i.e. "00" to "59" - j - day of the month without leading zeros; i.e. "1" to "31" - l (lowercase 'L') - day of the week, textual, long; e.g. "Friday" - L - boolean for whether it is a leap year; i.e. "0" or "1" - m - month; i.e. "01" to "12" - M - month, textual, 3 letters; e.g. "Jan" - n - month without leading zeros; i.e. "1" to "12" - O - Difference to Greenwich time in hours; e.g. "+0200" - Q - Quarter, as in 1, 2, 3, 4 - r - RFC 2822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" - s - seconds; i.e. "00" to "59" - S - English ordinal suffix for the day of the month, 2 characters; - i.e. "st", "nd", "rd" or "th" - t - number of days in the given month; i.e. "28" to "31" - T - Timezone setting of this machine; e.g. "EST" or "MDT" - U - seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) - w - day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) - Y - year, 4 digits; e.g. "1999" - y - year, 2 digits; e.g. "99" - z - day of the year; i.e. "0" to "365" - Z - timezone offset in seconds (i.e. "-43200" to "43200"). - The offset for timezones west of UTC is always negative, - and for those east of UTC is always positive. -</pre> - -Unsupported: -<pre> - B - Swatch Internet time - I (capital i) - "1" if Daylight Savings Time, "0" otherwise. - W - ISO-8601 week number of year, weeks starting on Monday - -</pre> - - -** FUNCTION adodb_date2($fmt, $isoDateString = false) -Same as adodb_date, but 2nd parameter accepts iso date, eg. - - adodb_date2('d-M-Y H:i','2003-12-25 13:01:34'); - - -** FUNCTION adodb_gmdate($fmt, $timestamp = false) - -Convert a timestamp to a formatted GMT date. If $timestamp is not defined, the -current timestamp is used. Unlike the function date(), it supports dates -outside the 1901 to 2038 range. - - -** FUNCTION adodb_mktime($hr, $min, $sec[, $month, $day, $year]) - -Converts a local date to a unix timestamp. Unlike the function mktime(), it supports -dates outside the 1901 to 2038 range. All parameters are optional. - - -** FUNCTION adodb_gmmktime($hr, $min, $sec [, $month, $day, $year]) - -Converts a gmt date to a unix timestamp. Unlike the function gmmktime(), it supports -dates outside the 1901 to 2038 range. Differs from gmmktime() in that all parameters -are currently compulsory. - -** FUNCTION adodb_gmstrftime($fmt, $timestamp = false) -Convert a timestamp to a formatted GMT date. - -** FUNCTION adodb_strftime($fmt, $timestamp = false) - -Convert a timestamp to a formatted local date. Internally converts $fmt into -adodb_date format, then echo result. - -For best results, you can define the local date format yourself. Define a global -variable $ADODB_DATE_LOCALE which is an array, 1st element is date format using -adodb_date syntax, and 2nd element is the time format, also in adodb_date syntax. - - eg. $ADODB_DATE_LOCALE = array('d/m/Y','H:i:s'); - - Supported format codes: - -<pre> - %a - abbreviated weekday name according to the current locale - %A - full weekday name according to the current locale - %b - abbreviated month name according to the current locale - %B - full month name according to the current locale - %c - preferred date and time representation for the current locale - %d - day of the month as a decimal number (range 01 to 31) - %D - same as %m/%d/%y - %e - day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31') - %h - same as %b - %H - hour as a decimal number using a 24-hour clock (range 00 to 23) - %I - hour as a decimal number using a 12-hour clock (range 01 to 12) - %m - month as a decimal number (range 01 to 12) - %M - minute as a decimal number - %n - newline character - %p - either `am' or `pm' according to the given time value, or the corresponding strings for the current locale - %r - time in a.m. and p.m. notation - %R - time in 24 hour notation - %S - second as a decimal number - %t - tab character - %T - current time, equal to %H:%M:%S - %x - preferred date representation for the current locale without the time - %X - preferred time representation for the current locale without the date - %y - year as a decimal number without a century (range 00 to 99) - %Y - year as a decimal number including the century - %Z - time zone or name or abbreviation - %% - a literal `%' character -</pre> - - Unsupported codes: -<pre> - %C - century number (the year divided by 100 and truncated to an integer, range 00 to 99) - %g - like %G, but without the century. - %G - The 4-digit year corresponding to the ISO week number (see %V). - This has the same format and value as %Y, except that if the ISO week number belongs - to the previous or next year, that year is used instead. - %j - day of the year as a decimal number (range 001 to 366) - %u - weekday as a decimal number [1,7], with 1 representing Monday - %U - week number of the current year as a decimal number, starting - with the first Sunday as the first day of the first week - %V - The ISO 8601:1988 week number of the current year as a decimal number, - range 01 to 53, where week 1 is the first week that has at least 4 days in the - current year, and with Monday as the first day of the week. (Use %G or %g for - the year component that corresponds to the week number for the specified timestamp.) - %w - day of the week as a decimal, Sunday being 0 - %W - week number of the current year as a decimal number, starting with the - first Monday as the first day of the first week -</pre> - -============================================================================= - -NOTES - -Useful url for generating test timestamps: - http://www.4webhelp.net/us/timestamp.php - -Possible future optimizations include - -a. Using an algorithm similar to Plauger's in "The Standard C Library" -(page 428, xttotm.c _Ttotm() function). Plauger's algorithm will not -work outside 32-bit signed range, so i decided not to implement it. - -b. Implement daylight savings, which looks awfully complicated, see - http://webexhibits.org/daylightsaving/ - - -CHANGELOG -- 16 Jan 2011 0.36 -Added adodb_time() which returns current time. If > 2038, will return as float - -- 7 Feb 2011 0.35 -Changed adodb_date to be symmetric with adodb_mktime. See $jan1_71. fix for bc. - -- 13 July 2010 0.34 -Changed adodb_get_gm_diff to use DateTimeZone(). - -- 11 Feb 2008 0.33 -* Bug in 0.32 fix for hour handling. Fixed. - -- 1 Feb 2008 0.32 -* Now adodb_mktime(0,0,0,12+$m,20,2040) works properly. - -- 10 Jan 2008 0.31 -* Now adodb_mktime(0,0,0,24,1,2037) works correctly. - -- 15 July 2007 0.30 -Added PHP 5.2.0 compatibility fixes. - * gmtime behaviour for 1970 has changed. We use the actual date if it is between 1970 to 2038 to get the - * timezone, otherwise we use the current year as the baseline to retrieve the timezone. - * Also the timezone's in php 5.2.* support historical data better, eg. if timezone today was +8, but - in 1970 it was +7:30, then php 5.2 return +7:30, while this library will use +8. - * - -- 19 March 2006 0.24 -Changed strftime() locale detection, because some locales prepend the day of week to the date when %c is used. - -- 10 Feb 2006 0.23 -PHP5 compat: when we detect PHP5, the RFC2822 format for gmt 0000hrs is changed from -0000 to +0000. - In PHP4, we will still use -0000 for 100% compat with PHP4. - -- 08 Sept 2005 0.22 -In adodb_date2(), $is_gmt not supported properly. Fixed. - -- 18 July 2005 0.21 -In PHP 4.3.11, the 'r' format has changed. Leading 0 in day is added. Changed for compat. -Added support for negative months in adodb_mktime(). - -- 24 Feb 2005 0.20 -Added limited strftime/gmstrftime support. x10 improvement in performance of adodb_date(). - -- 21 Dec 2004 0.17 -In adodb_getdate(), the timestamp was accidentally converted to gmt when $is_gmt is false. -Also adodb_mktime(0,0,0) did not work properly. Both fixed thx Mauro. - -- 17 Nov 2004 0.16 -Removed intval typecast in adodb_mktime() for secs, allowing: - adodb_mktime(0,0,0 + 2236672153,1,1,1934); -Suggested by Ryan. - -- 18 July 2004 0.15 -All params in adodb_mktime were formerly compulsory. Now only the hour, min, secs is compulsory. -This brings it more in line with mktime (still not identical). - -- 23 June 2004 0.14 - -Allow you to define your own daylights savings function, adodb_daylight_sv. -If the function is defined (somewhere in an include), then you can correct for daylights savings. - -In this example, we apply daylights savings in June or July, adding one hour. This is extremely -unrealistic as it does not take into account time-zone, geographic location, current year. - -function adodb_daylight_sv(&$arr, $is_gmt) -{ - if ($is_gmt) return; - $m = $arr['mon']; - if ($m == 6 || $m == 7) $arr['hours'] += 1; -} - -This is only called by adodb_date() and not by adodb_mktime(). - -The format of $arr is -Array ( - [seconds] => 0 - [minutes] => 0 - [hours] => 0 - [mday] => 1 # day of month, eg 1st day of the month - [mon] => 2 # month (eg. Feb) - [year] => 2102 - [yday] => 31 # days in current year - [leap] => # true if leap year - [ndays] => 28 # no of days in current month - ) - - -- 28 Apr 2004 0.13 -Fixed adodb_date to properly support $is_gmt. Thx to Dimitar Angelov. - -- 20 Mar 2004 0.12 -Fixed month calculation error in adodb_date. 2102-June-01 appeared as 2102-May-32. - -- 26 Oct 2003 0.11 -Because of daylight savings problems (some systems apply daylight savings to -January!!!), changed adodb_get_gmt_diff() to ignore daylight savings. - -- 9 Aug 2003 0.10 -Fixed bug with dates after 2038. -See PHPLens Issue No: 6980 - -- 1 July 2003 0.09 -Added support for Q (Quarter). -Added adodb_date2(), which accepts ISO date in 2nd param - -- 3 March 2003 0.08 -Added support for 'S' adodb_date() format char. Added constant ADODB_ALLOW_NEGATIVE_TS -if you want PHP to handle negative timestamps between 1901 to 1969. - -- 27 Feb 2003 0.07 -All negative numbers handled by adodb now because of RH 7.3+ problems. -See http://bugs.php.net/bug.php?id=20048&edit=2 - -- 4 Feb 2003 0.06 -Fixed a typo, 1852 changed to 1582! This means that pre-1852 dates -are now correctly handled. - -- 29 Jan 2003 0.05 - -Leap year checking differs under Julian calendar (pre 1582). Also -leap year code optimized by checking for most common case first. - -We also handle month overflow correctly in mktime (eg month set to 13). - -Day overflow for less than one month's days is supported. - -- 28 Jan 2003 0.04 - -Gregorian correction handled. In PHP5, we might throw an error if -mktime uses invalid dates around 5-14 Oct 1582. Released with ADOdb 3.10. -Added limbo 5-14 Oct 1582 check, when we set to 15 Oct 1582. - -- 27 Jan 2003 0.03 - -Fixed some more month problems due to gmt issues. Added constant ADODB_DATE_VERSION. -Fixed calculation of days since start of year for <1970. - -- 27 Jan 2003 0.02 - -Changed _adodb_getdate() to inline leap year checking for better performance. -Fixed problem with time-zones west of GMT +0000. - -- 24 Jan 2003 0.01 - -First implementation. -*/ - - -/* Initialization */ - -/* - Version Number -*/ -define('ADODB_DATE_VERSION',0.35); - -/* - This code was originally for windows. But apparently this problem happens - also with Linux, RH 7.3 and later! - - glibc-2.2.5-34 and greater has been changed to return -1 for dates < - 1970. This used to work. The problem exists with RedHat 7.3 and 8.0 - echo (mktime(0, 0, 0, 1, 1, 1960)); // prints -1 - - References: - http://bugs.php.net/bug.php?id=20048&edit=2 - http://lists.debian.org/debian-glibc/2002/debian-glibc-200205/msg00010.html -*/ - -if (!defined('ADODB_ALLOW_NEGATIVE_TS')) define('ADODB_NO_NEGATIVE_TS',1); - -if (!DEFINED('ADODB_FUTURE_DATE_CUTOFF_YEARS')) - DEFINE('ADODB_FUTURE_DATE_CUTOFF_YEARS',200); - -function adodb_date_test_date($y1,$m,$d=13) -{ - $h = round(rand()% 24); - $t = adodb_mktime($h,0,0,$m,$d,$y1); - $rez = adodb_date('Y-n-j H:i:s',$t); - if ($h == 0) $h = '00'; - else if ($h < 10) $h = '0'.$h; - if ("$y1-$m-$d $h:00:00" != $rez) { - print "<b>$y1 error, expected=$y1-$m-$d $h:00:00, adodb=$rez</b><br>"; - return false; - } - return true; -} - -function adodb_date_test_strftime($fmt) -{ - $s1 = strftime($fmt); - $s2 = adodb_strftime($fmt); - - if ($s1 == $s2) return true; - - echo "error for $fmt, strftime=$s1, adodb=$s2<br>"; - return false; -} - -/** - Test Suite -*/ -function adodb_date_test() -{ - - for ($m=-24; $m<=24; $m++) - echo "$m :",adodb_date('d-m-Y',adodb_mktime(0,0,0,1+$m,20,2040)),"<br>"; - - error_reporting(E_ALL); - print "<h4>Testing adodb_date and adodb_mktime. version=".ADODB_DATE_VERSION.' PHP='.PHP_VERSION."</h4>"; - @set_time_limit(0); - $fail = false; - - // This flag disables calling of PHP native functions, so we can properly test the code - if (!defined('ADODB_TEST_DATES')) define('ADODB_TEST_DATES',1); - - $t = time(); - - - $fmt = 'Y-m-d H:i:s'; - echo '<pre>'; - echo 'adodb: ',adodb_date($fmt,$t),'<br>'; - echo 'php : ',date($fmt,$t),'<br>'; - echo '</pre>'; - - adodb_date_test_strftime('%Y %m %x %X'); - adodb_date_test_strftime("%A %d %B %Y"); - adodb_date_test_strftime("%H %M S"); - - $t = adodb_mktime(0,0,0); - if (!(adodb_date('Y-m-d') == date('Y-m-d'))) print 'Error in '.adodb_mktime(0,0,0).'<br>'; - - $t = adodb_mktime(0,0,0,6,1,2102); - if (!(adodb_date('Y-m-d',$t) == '2102-06-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>'; - - $t = adodb_mktime(0,0,0,2,1,2102); - if (!(adodb_date('Y-m-d',$t) == '2102-02-01')) print 'Error in '.adodb_date('Y-m-d',$t).'<br>'; - - - print "<p>Testing gregorian <=> julian conversion<p>"; - $t = adodb_mktime(0,0,0,10,11,1492); - //http://www.holidayorigins.com/html/columbus_day.html - Friday check - if (!(adodb_date('D Y-m-d',$t) == 'Fri 1492-10-11')) print 'Error in Columbus landing<br>'; - - $t = adodb_mktime(0,0,0,2,29,1500); - if (!(adodb_date('Y-m-d',$t) == '1500-02-29')) print 'Error in julian leap years<br>'; - - $t = adodb_mktime(0,0,0,2,29,1700); - if (!(adodb_date('Y-m-d',$t) == '1700-03-01')) print 'Error in gregorian leap years<br>'; - - print adodb_mktime(0,0,0,10,4,1582).' '; - print adodb_mktime(0,0,0,10,15,1582); - $diff = (adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)); - if ($diff != 3600*24) print " <b>Error in gregorian correction = ".($diff/3600/24)." days </b><br>"; - - print " 15 Oct 1582, Fri=".(adodb_dow(1582,10,15) == 5 ? 'Fri' : '<b>Error</b>')."<br>"; - print " 4 Oct 1582, Thu=".(adodb_dow(1582,10,4) == 4 ? 'Thu' : '<b>Error</b>')."<br>"; - - print "<p>Testing overflow<p>"; - - $t = adodb_mktime(0,0,0,3,33,1965); - if (!(adodb_date('Y-m-d',$t) == '1965-04-02')) print 'Error in day overflow 1 <br>'; - $t = adodb_mktime(0,0,0,4,33,1971); - if (!(adodb_date('Y-m-d',$t) == '1971-05-03')) print 'Error in day overflow 2 <br>'; - $t = adodb_mktime(0,0,0,1,60,1965); - if (!(adodb_date('Y-m-d',$t) == '1965-03-01')) print 'Error in day overflow 3 '.adodb_date('Y-m-d',$t).' <br>'; - $t = adodb_mktime(0,0,0,12,32,1965); - if (!(adodb_date('Y-m-d',$t) == '1966-01-01')) print 'Error in day overflow 4 '.adodb_date('Y-m-d',$t).' <br>'; - $t = adodb_mktime(0,0,0,12,63,1965); - if (!(adodb_date('Y-m-d',$t) == '1966-02-01')) print 'Error in day overflow 5 '.adodb_date('Y-m-d',$t).' <br>'; - $t = adodb_mktime(0,0,0,13,3,1965); - if (!(adodb_date('Y-m-d',$t) == '1966-01-03')) print 'Error in mth overflow 1 <br>'; - - print "Testing 2-digit => 4-digit year conversion<p>"; - if (adodb_year_digit_check(00) != 2000) print "Err 2-digit 2000<br>"; - if (adodb_year_digit_check(10) != 2010) print "Err 2-digit 2010<br>"; - if (adodb_year_digit_check(20) != 2020) print "Err 2-digit 2020<br>"; - if (adodb_year_digit_check(30) != 2030) print "Err 2-digit 2030<br>"; - if (adodb_year_digit_check(40) != 1940) print "Err 2-digit 1940<br>"; - if (adodb_year_digit_check(50) != 1950) print "Err 2-digit 1950<br>"; - if (adodb_year_digit_check(90) != 1990) print "Err 2-digit 1990<br>"; - - // Test string formatting - print "<p>Testing date formatting</p>"; - - $fmt = '\d\a\t\e T Y-m-d H:i:s a A d D F g G h H i j l L m M n O \R\F\C2822 r s t U w y Y z Z 2003'; - $s1 = date($fmt,0); - $s2 = adodb_date($fmt,0); - if ($s1 != $s2) { - print " date() 0 failed<br>$s1<br>$s2<br>"; - } - flush(); - for ($i=100; --$i > 0; ) { - - $ts = 3600.0*((rand()%60000)+(rand()%60000))+(rand()%60000); - $s1 = date($fmt,$ts); - $s2 = adodb_date($fmt,$ts); - //print "$s1 <br>$s2 <p>"; - $pos = strcmp($s1,$s2); - - if (($s1) != ($s2)) { - for ($j=0,$k=strlen($s1); $j < $k; $j++) { - if ($s1[$j] != $s2[$j]) { - print substr($s1,$j).' '; - break; - } - } - print "<b>Error date(): $ts<br><pre> - \"$s1\" (date len=".strlen($s1).") - \"$s2\" (adodb_date len=".strlen($s2).")</b></pre><br>"; - $fail = true; - } - - $a1 = getdate($ts); - $a2 = adodb_getdate($ts); - $rez = array_diff($a1,$a2); - if (sizeof($rez)>0) { - print "<b>Error getdate() $ts</b><br>"; - print_r($a1); - print "<br>"; - print_r($a2); - print "<p>"; - $fail = true; - } - } - - // Test generation of dates outside 1901-2038 - print "<p>Testing random dates between 100 and 4000</p>"; - adodb_date_test_date(100,1); - for ($i=100; --$i >= 0;) { - $y1 = 100+rand(0,1970-100); - $m = rand(1,12); - adodb_date_test_date($y1,$m); - - $y1 = 3000-rand(0,3000-1970); - adodb_date_test_date($y1,$m); - } - print '<p>'; - $start = 1960+rand(0,10); - $yrs = 12; - $i = 365.25*86400*($start-1970); - $offset = 36000+rand(10000,60000); - $max = 365*$yrs*86400; - $lastyear = 0; - - // we generate a timestamp, convert it to a date, and convert it back to a timestamp - // and check if the roundtrip broke the original timestamp value. - print "Testing $start to ".($start+$yrs).", or $max seconds, offset=$offset: "; - $cnt = 0; - for ($max += $i; $i < $max; $i += $offset) { - $ret = adodb_date('m,d,Y,H,i,s',$i); - $arr = explode(',',$ret); - if ($lastyear != $arr[2]) { - $lastyear = $arr[2]; - print " $lastyear "; - flush(); - } - $newi = adodb_mktime($arr[3],$arr[4],$arr[5],$arr[0],$arr[1],$arr[2]); - if ($i != $newi) { - print "Error at $i, adodb_mktime returned $newi ($ret)"; - $fail = true; - break; - } - $cnt += 1; - } - echo "Tested $cnt dates<br>"; - if (!$fail) print "<p>Passed !</p>"; - else print "<p><b>Failed</b> :-(</p>"; -} - -function adodb_time() -{ - $d = new DateTime(); - return $d->format('U'); -} - -/** - Returns day of week, 0 = Sunday,... 6=Saturday. - Algorithm from PEAR::Date_Calc -*/ -function adodb_dow($year, $month, $day) -{ -/* -Pope Gregory removed 10 days - October 5 to October 14 - from the year 1582 and -proclaimed that from that time onwards 3 days would be dropped from the calendar -every 400 years. - -Thursday, October 4, 1582 (Julian) was followed immediately by Friday, October 15, 1582 (Gregorian). -*/ - if ($year <= 1582) { - if ($year < 1582 || - ($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3; - else - $greg_correction = 0; - } else - $greg_correction = 0; - - if($month > 2) - $month -= 2; - else { - $month += 10; - $year--; - } - - $day = floor((13 * $month - 1) / 5) + - $day + ($year % 100) + - floor(($year % 100) / 4) + - floor(($year / 100) / 4) - 2 * - floor($year / 100) + 77 + $greg_correction; - - return $day - 7 * floor($day / 7); -} - - -/** - Checks for leap year, returns true if it is. No 2-digit year check. Also - handles julian calendar correctly. -*/ -function _adodb_is_leap_year($year) -{ - if ($year % 4 != 0) return false; - - if ($year % 400 == 0) { - return true; - // if gregorian calendar (>1582), century not-divisible by 400 is not leap - } else if ($year > 1582 && $year % 100 == 0 ) { - return false; - } - - return true; -} - - -/** - checks for leap year, returns true if it is. Has 2-digit year check -*/ -function adodb_is_leap_year($year) -{ - return _adodb_is_leap_year(adodb_year_digit_check($year)); -} - -/** - Fix 2-digit years. Works for any century. - Assumes that if 2-digit is more than 30 years in future, then previous century. -*/ -function adodb_year_digit_check($y) -{ - if ($y < 100) { - - $yr = (integer) date("Y"); - $century = (integer) ($yr /100); - - if ($yr%100 > 50) { - $c1 = $century + 1; - $c0 = $century; - } else { - $c1 = $century; - $c0 = $century - 1; - } - $c1 *= 100; - // if 2-digit year is less than 30 years in future, set it to this century - // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. - if (($y + $c1) < $yr+30) $y = $y + $c1; - else $y = $y + $c0*100; - } - return $y; -} - -function adodb_get_gmt_diff_ts($ts) -{ - if (0 <= $ts && $ts <= 0x7FFFFFFF) { // check if number in 32-bit signed range) { - $arr = getdate($ts); - $y = $arr['year']; - $m = $arr['mon']; - $d = $arr['mday']; - return adodb_get_gmt_diff($y,$m,$d); - } else { - return adodb_get_gmt_diff(false,false,false); - } - -} - -/** - get local time zone offset from GMT. Does not handle historical timezones before 1970. -*/ -function adodb_get_gmt_diff($y,$m,$d) -{ - static $TZ,$tzo; - - if (!defined('ADODB_TEST_DATES')) $y = false; - else if ($y < 1970 || $y >= 2038) $y = false; - - if ($y !== false) { - $dt = new DateTime(); - $dt->setISODate($y,$m,$d); - if (empty($tzo)) { - $tzo = new DateTimeZone(date_default_timezone_get()); - # $tzt = timezone_transitions_get( $tzo ); - } - return -$tzo->getOffset($dt); - } else { - if (isset($TZ)) return $TZ; - $y = date('Y'); - /* - if (function_exists('date_default_timezone_get') && function_exists('timezone_offset_get')) { - $tzonename = date_default_timezone_get(); - if ($tzonename) { - $tobj = new DateTimeZone($tzonename); - $TZ = -timezone_offset_get($tobj,new DateTime("now",$tzo)); - } - } - */ - if (empty($TZ)) $TZ = mktime(0,0,0,12,2,$y) - gmmktime(0,0,0,12,2,$y); - } - return $TZ; -} - -/** - Returns an array with date info. -*/ -function adodb_getdate($d=false,$fast=false) -{ - if ($d === false) return getdate(); - if (!defined('ADODB_TEST_DATES')) { - if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer - return @getdate($d); - } - } - return _adodb_getdate($d); -} - -/* -// generate $YRS table for _adodb_getdate() -function adodb_date_gentable($out=true) -{ - - for ($i=1970; $i >= 1600; $i-=10) { - $s = adodb_gmmktime(0,0,0,1,1,$i); - echo "$i => $s,<br>"; - } -} -adodb_date_gentable(); - -for ($i=1970; $i > 1500; $i--) { - -echo "<hr />$i "; - adodb_date_test_date($i,1,1); -} - -*/ - - -$_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); -$_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); - -function adodb_validdate($y,$m,$d) -{ -global $_month_table_normal,$_month_table_leaf; - - if (_adodb_is_leap_year($y)) $marr = $_month_table_leaf; - else $marr = $_month_table_normal; - - if ($m > 12 || $m < 1) return false; - - if ($d > 31 || $d < 1) return false; - - if ($marr[$m] < $d) return false; - - if ($y < 1000 || $y > 3000) return false; - - return true; -} - -/** - Low-level function that returns the getdate() array. We have a special - $fast flag, which if set to true, will return fewer array values, - and is much faster as it does not calculate dow, etc. -*/ -function _adodb_getdate($origd=false,$fast=false,$is_gmt=false) -{ -static $YRS; -global $_month_table_normal,$_month_table_leaf, $_adodb_last_date_call_failed; - - $_adodb_last_date_call_failed = false; - - $d = $origd - ($is_gmt ? 0 : adodb_get_gmt_diff_ts($origd)); - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - $cutoffDate = time() + (60 * 60 * 24 * 365 * ADODB_FUTURE_DATE_CUTOFF_YEARS); - - if ($d > $cutoffDate) - { - $d = $cutoffDate; - $_adodb_last_date_call_failed = true; - } - - if ($d < -12219321600) $d -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction - - $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); - $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); - - $d366 = $_day_power * 366; - $d365 = $_day_power * 365; - - if ($d < 0) { - - if (empty($YRS)) $YRS = array( - 1970 => 0, - 1960 => -315619200, - 1950 => -631152000, - 1940 => -946771200, - 1930 => -1262304000, - 1920 => -1577923200, - 1910 => -1893456000, - 1900 => -2208988800, - 1890 => -2524521600, - 1880 => -2840140800, - 1870 => -3155673600, - 1860 => -3471292800, - 1850 => -3786825600, - 1840 => -4102444800, - 1830 => -4417977600, - 1820 => -4733596800, - 1810 => -5049129600, - 1800 => -5364662400, - 1790 => -5680195200, - 1780 => -5995814400, - 1770 => -6311347200, - 1760 => -6626966400, - 1750 => -6942499200, - 1740 => -7258118400, - 1730 => -7573651200, - 1720 => -7889270400, - 1710 => -8204803200, - 1700 => -8520336000, - 1690 => -8835868800, - 1680 => -9151488000, - 1670 => -9467020800, - 1660 => -9782640000, - 1650 => -10098172800, - 1640 => -10413792000, - 1630 => -10729324800, - 1620 => -11044944000, - 1610 => -11360476800, - 1600 => -11676096000); - - if ($is_gmt) $origd = $d; - // The valid range of a 32bit signed timestamp is typically from - // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT - // - - # old algorithm iterates through all years. new algorithm does it in - # 10 year blocks - - /* - # old algo - for ($a = 1970 ; --$a >= 0;) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) $d += $d366; - else $d += $d365; - - if ($d >= 0) { - $year = $a; - break; - } - } - */ - - $lastsecs = 0; - $lastyear = 1970; - foreach($YRS as $year => $secs) { - if ($d >= $secs) { - $a = $lastyear; - break; - } - $lastsecs = $secs; - $lastyear = $year; - } - - $d -= $lastsecs; - if (!isset($a)) $a = $lastyear; - - //echo ' yr=',$a,' ', $d,'.'; - - for (; --$a >= 0;) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) $d += $d366; - else $d += $d365; - - if ($d >= 0) { - $year = $a; - break; - } - } - /**/ - - $secsInYear = 86400 * ($leaf ? 366 : 365) + $lastd; - - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 13 ; --$a > 0;) { - $lastd = $d; - $d += $mtab[$a] * $_day_power; - if ($d >= 0) { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - - $d = $lastd; - $day = $ndays + ceil(($d+1) / ($_day_power)); - - $d += ($ndays - $day+1)* $_day_power; - $hour = floor($d/$_hour_power); - - } else { - for ($a = 1970 ;; $a++) { - $lastd = $d; - - if ($leaf = _adodb_is_leap_year($a)) $d -= $d366; - else $d -= $d365; - if ($d < 0) { - $year = $a; - break; - } - } - $secsInYear = $lastd; - $d = $lastd; - $mtab = ($leaf) ? $_month_table_leaf : $_month_table_normal; - for ($a = 1 ; $a <= 12; $a++) { - $lastd = $d; - $d -= $mtab[$a] * $_day_power; - if ($d < 0) { - $month = $a; - $ndays = $mtab[$a]; - break; - } - } - $d = $lastd; - $day = ceil(($d+1) / $_day_power); - $d = $d - ($day-1) * $_day_power; - $hour = floor($d /$_hour_power); - } - - $d -= $hour * $_hour_power; - $min = floor($d/$_min_power); - $secs = $d - $min * $_min_power; - if ($fast) { - return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'leap' => $leaf, - 'ndays' => $ndays - ); - } - - - $dow = adodb_dow($year,$month,$day); - - return array( - 'seconds' => $secs, - 'minutes' => $min, - 'hours' => $hour, - 'mday' => $day, - 'wday' => $dow, - 'mon' => $month, - 'year' => $year, - 'yday' => floor($secsInYear/$_day_power), - 'weekday' => gmdate('l',$_day_power*(3+$dow)), - 'month' => gmdate('F',mktime(0,0,0,$month,2,1971)), - 0 => $origd - ); -} - -/** - * Compute timezone offset. - * - * @param int $gmt Time offset from GMT, in seconds - * @param bool $ignored Param leftover from removed PHP4-compatibility code - * kept to avoid altering function signature. - * @return string - */ -function adodb_tz_offset($gmt, $ignored=true) -{ - $zhrs = abs($gmt) / 3600; - $hrs = floor($zhrs); - return sprintf('%s%02d%02d', ($gmt <= 0) ? '+' : '-', $hrs, ($zhrs - $hrs) * 60); -} - - -function adodb_gmdate($fmt,$d=false) -{ - return adodb_date($fmt,$d,true); -} - -// accepts unix timestamp and iso date format in $d -function adodb_date2($fmt, $d=false, $is_gmt=false) -{ - if ($d !== false) { - if (!preg_match( - "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|", - ($d), $rr)) return adodb_date($fmt,false,$is_gmt); - - if ($rr[1] <= 100 && $rr[2]<= 1) return adodb_date($fmt,false,$is_gmt); - - // h-m-s-MM-DD-YY - if (!isset($rr[5])) $d = adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1],false,$is_gmt); - else $d = @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1],false,$is_gmt); - } - - return adodb_date($fmt,$d,$is_gmt); -} - - -/** - Return formatted date based on timestamp $d -*/ -function adodb_date($fmt,$d=false,$is_gmt=false) -{ - static $daylight; - static $jan1_1971; - - if (!isset($daylight)) { - $daylight = function_exists('adodb_daylight_sv'); - if (empty($jan1_1971)) $jan1_1971 = mktime(0,0,0,1,1,1971); // we only use date() when > 1970 as adodb_mktime() only uses mktime() when > 1970 - } - - if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt); - if (!defined('ADODB_TEST_DATES')) { - - /* - * Format 'Q' is an ADOdb custom format, not supported in PHP - * so if there is a 'Q' in the format, we force it to use our - * function. There is a trivial overhead in this - */ - - if ((abs($d) <= 0x7FFFFFFF) && strpos($fmt,'Q') === false) - { // check if number in 32-bit signed range - - if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= $jan1_1971) // if windows, must be +ve integer - return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d); - - } - } - $_day_power = 86400; - - $arr = _adodb_getdate($d,true,$is_gmt); - - if ($daylight) adodb_daylight_sv($arr, $is_gmt); - - $year = $arr['year']; - $month = $arr['mon']; - $day = $arr['mday']; - $hour = $arr['hours']; - $min = $arr['minutes']; - $secs = $arr['seconds']; - - $max = strlen($fmt); - $dates = ''; - - /* - at this point, we have the following integer vars to manipulate: - $year, $month, $day, $hour, $min, $secs - */ - for ($i=0; $i < $max; $i++) { - switch($fmt[$i]) { - case 'e': - $dates .= date('e'); - break; - case 'T': - $dt = new DateTime(); - $dt->SetDate($year,$month,$day); - $dates .= $dt->Format('T'); - break; - // YEAR - case 'L': $dates .= $arr['leap'] ? '1' : '0'; break; - case 'r': // Thu, 21 Dec 2000 16:01:07 +0200 - - // 4.3.11 uses '04 Jun 2004' - // 4.3.8 uses ' 4 Jun 2004' - $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))).', ' - . ($day<10?'0'.$day:$day) . ' '.date('M',mktime(0,0,0,$month,2,1971)).' '.$year.' '; - - if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour; - - if ($min < 10) $dates .= ':0'.$min; else $dates .= ':'.$min; - - if ($secs < 10) $dates .= ':0'.$secs; else $dates .= ':'.$secs; - - $gmt = adodb_get_gmt_diff($year,$month,$day); - - $dates .= ' '.adodb_tz_offset($gmt); - break; - - case 'Y': $dates .= $year; break; - case 'y': $dates .= substr($year,strlen($year)-2,2); break; - // MONTH - case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break; - case 'Q': - $dates .= ceil($month / 3); - break; - case 'n': $dates .= $month; break; - case 'M': $dates .= date('M',mktime(0,0,0,$month,2,1971)); break; - case 'F': $dates .= date('F',mktime(0,0,0,$month,2,1971)); break; - // DAY - case 't': $dates .= $arr['ndays']; break; - case 'z': $dates .= $arr['yday']; break; - case 'w': $dates .= adodb_dow($year,$month,$day); break; - case 'W': - $dates .= sprintf('%02d',ceil( $arr['yday'] / 7) - 1); - break; - case 'l': $dates .= gmdate('l',$_day_power*(3+adodb_dow($year,$month,$day))); break; - case 'D': $dates .= gmdate('D',$_day_power*(3+adodb_dow($year,$month,$day))); break; - case 'j': $dates .= $day; break; - case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break; - case 'S': - $d10 = $day % 10; - if ($d10 == 1) $dates .= 'st'; - else if ($d10 == 2 && $day != 12) $dates .= 'nd'; - else if ($d10 == 3) $dates .= 'rd'; - else $dates .= 'th'; - break; - - // HOUR - case 'Z': - $dates .= ($is_gmt) ? 0 : -adodb_get_gmt_diff($year,$month,$day); break; - case 'O': - $gmt = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$month,$day); - - $dates .= adodb_tz_offset($gmt); - break; - - case 'H': - if ($hour < 10) $dates .= '0'.$hour; - else $dates .= $hour; - break; - case 'h': - if ($hour > 12) $hh = $hour - 12; - else { - if ($hour == 0) $hh = '12'; - else $hh = $hour; - } - - if ($hh < 10) $dates .= '0'.$hh; - else $dates .= $hh; - break; - - case 'G': - $dates .= $hour; - break; - - case 'g': - if ($hour > 12) $hh = $hour - 12; - else { - if ($hour == 0) $hh = '12'; - else $hh = $hour; - } - $dates .= $hh; - break; - // MINUTES - case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break; - // SECONDS - case 'U': $dates .= $d; break; - case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break; - // AM/PM - // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM - case 'a': - if ($hour>=12) $dates .= 'pm'; - else $dates .= 'am'; - break; - case 'A': - if ($hour>=12) $dates .= 'PM'; - else $dates .= 'AM'; - break; - default: - $dates .= $fmt[$i]; break; - // ESCAPE - case "\\": - $i++; - if ($i < $max) $dates .= $fmt[$i]; - break; - } - } - return $dates; -} - -/** - Returns a timestamp given a GMT/UTC time. - Note that $is_dst is not implemented and is ignored. -*/ -function adodb_gmmktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false) -{ - return adodb_mktime($hr,$min,$sec,$mon,$day,$year,$is_dst,true); -} - -/** - Return a timestamp given a local time. Originally by jackbbs. - Note that $is_dst is not implemented and is ignored. - - Not a very fast algorithm - O(n) operation. Could be optimized to O(1). -*/ -function adodb_mktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=false,$is_gmt=false) -{ - if (!defined('ADODB_TEST_DATES')) { - - if ($mon === false) { - return $is_gmt? @gmmktime($hr,$min,$sec): @mktime($hr,$min,$sec); - } - - // for windows, we don't check 1970 because with timezone differences, - // 1 Jan 1970 could generate negative timestamp, which is illegal - $usephpfns = (1970 < $year && $year < 2038 - || !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038) - ); - - - if ($usephpfns && ($year + $mon/12+$day/365.25+$hr/(24*365.25) >= 2038)) $usephpfns = false; - - if ($usephpfns) { - return $is_gmt ? - @gmmktime($hr,$min,$sec,$mon,$day,$year): - @mktime($hr,$min,$sec,$mon,$day,$year); - } - } - - $gmt_different = ($is_gmt) ? 0 : adodb_get_gmt_diff($year,$mon,$day); - - /* - # disabled because some people place large values in $sec. - # however we need it for $mon because we use an array... - $hr = intval($hr); - $min = intval($min); - $sec = intval($sec); - */ - $mon = intval($mon); - $day = intval($day); - $year = intval($year); - - - $year = adodb_year_digit_check($year); - - if ($mon > 12) { - $y = floor(($mon-1)/ 12); - $year += $y; - $mon -= $y*12; - } else if ($mon < 1) { - $y = ceil((1-$mon) / 12); - $year -= $y; - $mon += $y*12; - } - - $_day_power = 86400; - $_hour_power = 3600; - $_min_power = 60; - - $_month_table_normal = array("",31,28,31,30,31,30,31,31,30,31,30,31); - $_month_table_leaf = array("",31,29,31,30,31,30,31,31,30,31,30,31); - - $_total_date = 0; - if ($year >= 1970) { - for ($a = 1970 ; $a <= $year; $a++) { - $leaf = _adodb_is_leap_year($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_add_date = 366; - } else { - $loop_table = $_month_table_normal; - $_add_date = 365; - } - if ($a < $year) { - $_total_date += $_add_date; - } else { - for($b=1;$b<$mon;$b++) { - $_total_date += $loop_table[$b]; - } - } - } - $_total_date +=$day-1; - $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different; - - } else { - for ($a = 1969 ; $a >= $year; $a--) { - $leaf = _adodb_is_leap_year($a); - if ($leaf == true) { - $loop_table = $_month_table_leaf; - $_add_date = 366; - } else { - $loop_table = $_month_table_normal; - $_add_date = 365; - } - if ($a > $year) { $_total_date += $_add_date; - } else { - for($b=12;$b>$mon;$b--) { - $_total_date += $loop_table[$b]; - } - } - } - $_total_date += $loop_table[$mon] - $day; - - $_day_time = $hr * $_hour_power + $min * $_min_power + $sec; - $_day_time = $_day_power - $_day_time; - $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different); - if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction - else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582. - } - //print " dmy=$day/$mon/$year $hr:$min:$sec => " .$ret; - return $ret; -} - -function adodb_gmstrftime($fmt, $ts=false) -{ - return adodb_strftime($fmt,$ts,true); -} - -// hack - convert to adodb_date -function adodb_strftime($fmt, $ts=false,$is_gmt=false) -{ -global $ADODB_DATE_LOCALE; - - if (!defined('ADODB_TEST_DATES')) { - if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range - if (!defined('ADODB_NO_NEGATIVE_TS') || $ts >= 0) // if windows, must be +ve integer - return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts); - - } - } - - if (empty($ADODB_DATE_LOCALE)) { - /* - $tstr = strtoupper(gmstrftime('%c',31366800)); // 30 Dec 1970, 1 am - $sep = substr($tstr,2,1); - $hasAM = strrpos($tstr,'M') !== false; - */ - # see PHPLens Issue No: 14865 for reasoning, and changelog for version 0.24 - $dstr = gmstrftime('%x',31366800); // 30 Dec 1970, 1 am - $sep = substr($dstr,2,1); - $tstr = strtoupper(gmstrftime('%X',31366800)); // 30 Dec 1970, 1 am - $hasAM = strrpos($tstr,'M') !== false; - - $ADODB_DATE_LOCALE = array(); - $ADODB_DATE_LOCALE[] = strncmp($tstr,'30',2) == 0 ? 'd'.$sep.'m'.$sep.'y' : 'm'.$sep.'d'.$sep.'y'; - $ADODB_DATE_LOCALE[] = ($hasAM) ? 'h:i:s a' : 'H:i:s'; - - } - $inpct = false; - $fmtdate = ''; - for ($i=0,$max = strlen($fmt); $i < $max; $i++) { - $ch = $fmt[$i]; - if ($ch == '%') { - if ($inpct) { - $fmtdate .= '%'; - $inpct = false; - } else - $inpct = true; - } else if ($inpct) { - - $inpct = false; - switch($ch) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'E': - case 'O': - /* ignore format modifiers */ - $inpct = true; - break; - - case 'a': $fmtdate .= 'D'; break; - case 'A': $fmtdate .= 'l'; break; - case 'h': - case 'b': $fmtdate .= 'M'; break; - case 'B': $fmtdate .= 'F'; break; - case 'c': $fmtdate .= $ADODB_DATE_LOCALE[0].$ADODB_DATE_LOCALE[1]; break; - case 'C': $fmtdate .= '\C?'; break; // century - case 'd': $fmtdate .= 'd'; break; - case 'D': $fmtdate .= 'm/d/y'; break; - case 'e': $fmtdate .= 'j'; break; - case 'g': $fmtdate .= '\g?'; break; //? - case 'G': $fmtdate .= '\G?'; break; //? - case 'H': $fmtdate .= 'H'; break; - case 'I': $fmtdate .= 'h'; break; - case 'j': $fmtdate .= '?z'; $parsej = true; break; // wrong as j=1-based, z=0-basd - case 'm': $fmtdate .= 'm'; break; - case 'M': $fmtdate .= 'i'; break; - case 'n': $fmtdate .= "\n"; break; - case 'p': $fmtdate .= 'a'; break; - case 'r': $fmtdate .= 'h:i:s a'; break; - case 'R': $fmtdate .= 'H:i:s'; break; - case 'S': $fmtdate .= 's'; break; - case 't': $fmtdate .= "\t"; break; - case 'T': $fmtdate .= 'H:i:s'; break; - case 'u': $fmtdate .= '?u'; $parseu = true; break; // wrong strftime=1-based, date=0-based - case 'U': $fmtdate .= '?U'; $parseU = true; break;// wrong strftime=1-based, date=0-based - case 'x': $fmtdate .= $ADODB_DATE_LOCALE[0]; break; - case 'X': $fmtdate .= $ADODB_DATE_LOCALE[1]; break; - case 'w': $fmtdate .= '?w'; $parseu = true; break; // wrong strftime=1-based, date=0-based - case 'W': $fmtdate .= '?W'; $parseU = true; break;// wrong strftime=1-based, date=0-based - case 'y': $fmtdate .= 'y'; break; - case 'Y': $fmtdate .= 'Y'; break; - case 'Z': $fmtdate .= 'T'; break; - } - } else if (('A' <= ($ch) && ($ch) <= 'Z' ) || ('a' <= ($ch) && ($ch) <= 'z' )) - $fmtdate .= "\\".$ch; - else - $fmtdate .= $ch; - } - //echo "fmt=",$fmtdate,"<br>"; - if ($ts === false) $ts = time(); - $ret = adodb_date($fmtdate, $ts, $is_gmt); - return $ret; -} - -/** -* Returns the status of the last date calculation and whether it exceeds -* the limit of ADODB_FUTURE_DATE_CUTOFF_YEARS -* -* @return boolean -*/ -function adodb_last_date_status() -{ - global $_adodb_last_date_call_failed; - - return $_adodb_last_date_call_failed; -} diff --git a/adodb.inc.php b/adodb.inc.php index 73bef1ef..e521d5db 100644 --- a/adodb.inc.php +++ b/adodb.inc.php @@ -198,7 +198,7 @@ if (!defined('_ADODB_LAYER')) { /** * ADODB version as a string. */ - $ADODB_vers = 'v5.22.6-dev Unreleased'; + $ADODB_vers = 'v5.23.0-dev Unreleased'; /** * Determines whether recordset->RecordCount() is used. @@ -228,19 +228,55 @@ if (!defined('_ADODB_LAYER')) { */ #[\AllowDynamicProperties] class ADOFieldObject { - var $name = ''; - var $max_length=0; - var $type=""; -/* - // additional fields by dannym... (danny_milo@yahoo.com) - var $not_null = false; - // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ - // so we can as well make not_null standard (leaving it at "false" does not harm anyways) + /** + * @var string Field name + */ + public $name = ''; - var $has_default = false; // this one I have done only in mysql and postgres for now ... - // others to come (dannym) - var $default_value; // default, if any, and supported. Check has_default first. -*/ + /** + * @var int Field size + */ + public $max_length = 0; + + /** + * @var string Field type. + */ + public $type = ''; + + /** + * @var int|null Numeric field scale. + */ + public $scale; + + /** + * @var bool True if field can be NULL + */ + public $not_null = false; + + /** + * @var bool True if field is a primary key + */ + public $primary_key = false; + + /** + * @var bool True if field is unique key + */ + public $unique = false; + + /** + * @var bool True if field is automatically incremented + */ + public $auto_increment = false; + + /** + * @var bool True if field has a default value + */ + public $has_default = false; + + /** + * @var mixed Default value, if any and supported; check {@see $has_default} first. + */ + public $default_value; } @@ -477,7 +513,29 @@ if (!defined('_ADODB_LAYER')) { var $port = ''; /// The port of the database server var $user = ''; /// The username which is used to connect to the database server. var $password = ''; /// Password for the username. For security, we no longer store it. - var $debug = false; /// if set to true will output sql statements + + /** + * Debug Mode. + * + * Enables printing of SQL queries execution and additional debugging + * information. Can be enabled/disabled at any time after the database + * Connection has been initialized {@see ADONewConnection()}. + * + * Possible values are: + * - False: Disabled + * - True: Standard mode, prints executed SQL statements and error + * information including a Backtrace if the query failed. + * - -1: Same as standard mode, but without line separators. + * - 99: Prints a Backtrace after every query execution, even if + * it was successful. + * - -99: Debug information is only printed if query execution failed. + * + * @see https://adodb.org/dokuwiki/doku.php?id=v5:userguide:debug + * + * @var bool|int + */ + public $debug = false; + var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase var $substr = 'substr'; /// substring operator @@ -854,21 +912,15 @@ if (!defined('_ADODB_LAYER')) { return; } - if ($newline) { - $msg .= "<br>\n"; - } - - if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) { - echo $msg; + if (isset($_SERVER['HTTP_USER_AGENT'])) { + echo $msg . ($newline ? '<br>' :''); } else { - echo strip_tags($msg); + echo strip_tags($msg) . ($newline ? PHP_EOL : ''); } - if (!empty($ADODB_FLUSH) && ob_get_length() !== false) { flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan } - } /** @@ -3884,13 +3936,6 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } } - //============================================================================================== - // DATE AND TIME FUNCTIONS - //============================================================================================== - if (!defined('ADODB_DATE_VERSION')) { - include_once(ADODB_DIR.'/adodb-time.inc.php'); - } - /** * Class ADODB_Iterator */ @@ -3975,7 +4020,7 @@ class ADORecordSet implements IteratorAggregate { var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets var $bind = false; /// used by Fields() to hold array - should be private? - var $fetchMode; /// default fetch mode + /** @var ADOConnection The parent connection */ var $connection = false; /** @@ -4024,6 +4069,21 @@ class ADORecordSet implements IteratorAggregate { protected $fieldObjectsCache; /** + * @var bool True if we have retrieved the fields metadata + */ + protected $fieldObjectsRetrieved = false; + + /** + * @var array Cross-reference the objects by name for easy access + */ + protected $fieldObjectsIndex = array(); + + /** + * @var bool|int Driver-specific fetch mode + */ + var $fetchMode; + + /** * @var int Defines the Fetch Mode for a recordset * See the ADODB_FETCH_* constants */ @@ -4035,7 +4095,13 @@ class ADORecordSet implements IteratorAggregate { * @param resource|int $queryID Query ID returned by ADOConnection->_query() * @param int|bool $mode The ADODB_FETCH_MODE value */ - function __construct($queryID,$mode=false) { + function __construct($queryID, $mode=false) { + if ($mode === false) { + global $ADODB_FETCH_MODE; + $mode = $ADODB_FETCH_MODE; + } + $this->adodbFetchMode = $this->fetchMode = $mode; + $this->_queryID = $queryID; } @@ -5255,15 +5321,13 @@ class ADORecordSet implements IteratorAggregate { * @param int|bool $mode The ADODB_FETCH_MODE value */ function __construct($queryID, $mode=false) { - global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH; + parent::__construct(self::DUMMY_QUERY_ID); // fetch() on EOF does not delete $this->fields + global $ADODB_COMPAT_FETCH; $this->compat = !empty($ADODB_COMPAT_FETCH); - parent::__construct($queryID); // fake queryID - $this->fetchMode = $ADODB_FETCH_MODE; } - /** * Setup the array. * diff --git a/composer.json b/composer.json index cd97e9d7..eea5e002 100644 --- a/composer.json +++ b/composer.json @@ -27,11 +27,11 @@ }, "require" : { - "php" : "^7.0 || ^8.0" + "php" : "^7.4 || ^8.0" }, "require-dev" : { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^9.5" }, "autoload" : { diff --git a/docs/changelog.md b/docs/changelog.md index b41f81c2..69b1e804 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,50 @@ Older changelogs: -------------------------------------------------------------------------------- +## [5.23.0] - Unreleased + +### Added + +- oci8: support session_mode parameter + [#801](https://github.com/ADOdb/ADOdb/issues/801) +- oci8: support setting the client identifier + [#805](https://github.com/ADOdb/ADOdb/issues/805) +- pdo: bind support both '?'-style and named parameters + [#880](https://github.com/ADOdb/ADOdb/issues/880) + +### Changed + +- Refactor debug functions + [#863](https://github.com/ADOdb/ADOdb/issues/863) +- mssql: Simplify serverInfo() + [#830](https://github.com/ADOdb/ADOdb/pull/830#issuecomment-1119655907) +- Code cleanup: PHPDoc, code style, whitespace, etc. + +### Removed + +- Compatibility with PHP < 7.4 + [#868](https://github.com/ADOdb/ADOdb/issues/868) +- Database Replication add-on + [#780](https://github.com/ADOdb/ADOdb/issues/780) +- Date/Time Library + [#970](https://github.com/ADOdb/ADOdb/issues/970) +- mysqli: legacy non-functional $optionFlags property + [#814](https://github.com/ADOdb/ADOdb/issues/814) +- mysqli: remove obsolete, dead code + [#877](https://github.com/ADOdb/ADOdb/issues/877) +- pgsql: Remove legacy workarounds for old PostgreSQL versions + [#950](https://github.com/ADOdb/ADOdb/issues/950) +- session: remove legacy PHP 4 scripts + [#962](https://github.com/ADOdb/ADOdb/issues/962) + +### Fixed + +- oci8: silence deprecation warnings on PHP 8 + [#883](https://github.com/ADOdb/ADOdb/issues/883) +- pgsql: affected_rows() always returns false on PHP 8.1 + [#833](https://github.com/ADOdb/ADOdb/issues/833) + + ## [5.22.6] - Unreleased ### Deprecated @@ -1409,6 +1453,8 @@ Released together with [v4.95](changelog_v4.x.md#495---17-may-2007) - Adodb5 version,more error checking code now will use exceptions if available. +[5.23.0]: https://github.com/adodb/adodb/compare/v5.22.5...master + [5.22.6]: https://github.com/adodb/adodb/compare/v5.22.5...hotfix/5.22 [5.22.5]: https://github.com/adodb/adodb/compare/v5.22.4...v5.22.5 [5.22.4]: https://github.com/adodb/adodb/compare/v5.22.3...v5.22.4 diff --git a/drivers/adodb-ado.inc.php b/drivers/adodb-ado.inc.php index df95c690..30edf28b 100644 --- a/drivers/adodb-ado.inc.php +++ b/drivers/adodb-ado.inc.php @@ -345,16 +345,6 @@ class ADORecordSet_ado extends ADORecordSet { var $canSeek = true; var $hideErrors = true; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - // returns the field object function FetchField($fieldOffset = -1) { diff --git a/drivers/adodb-ado5.inc.php b/drivers/adodb-ado5.inc.php index 04b45abf..53bdc6a1 100644 --- a/drivers/adodb-ado5.inc.php +++ b/drivers/adodb-ado5.inc.php @@ -382,16 +382,6 @@ class ADORecordSet_ado extends ADORecordSet { var $canSeek = true; var $hideErrors = true; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - // returns the field object function FetchField($fieldOffset = -1) { diff --git a/drivers/adodb-ads.inc.php b/drivers/adodb-ads.inc.php index 16eec976..e6cd4f3b 100644 --- a/drivers/adodb-ads.inc.php +++ b/drivers/adodb-ads.inc.php @@ -287,7 +287,7 @@ class ADODB_ads extends ADOConnection // Returns tables,Views or both on successful execution. Returns // tables by default on successful execution. - function &MetaTables($ttype = false, $showSchema = false, $mask = false) + function MetaTables($ttype = false, $showSchema = false, $mask = false) { $recordSet1 = $this->Execute("select * from system.tables"); if (!$recordSet1) { @@ -325,7 +325,7 @@ class ADODB_ads extends ADOConnection } - function &MetaPrimaryKeys($table, $owner = false) + function MetaPrimaryKeys($table, $owner = false) { $recordSet = $this->Execute("select table_primary_key from system.tables where name='$table'"); if (!$recordSet) { @@ -533,7 +533,7 @@ class ADODB_ads extends ADOConnection } // Returns an array of columns names for a given table - function &MetaColumnNames($table, $numIndexes = false, $useattnum = false) + function MetaColumnNames($table, $numIndexes = false, $useattnum = false) { $recordSet = $this->Execute("select name from system.columns where parent='$table'"); if (!$recordSet) { @@ -684,20 +684,13 @@ class ADORecordSet_ads extends ADORecordSet var $dataProvider = "ads"; var $useFetchArray; - function __construct($id, $mode = false) + function __construct($queryID, $mode = false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; + parent::__construct($queryID, $mode); // the following is required for mysql odbc driver in 4.3.1 -- why? $this->EOF = false; $this->_currentRow = -1; - //parent::__construct($id); } @@ -760,7 +753,7 @@ class ADORecordSet_ads extends ADORecordSet function &GetArrayLimit($nrows, $offset = -1) { if ($offset <= 0) { - $rs =& $this->GetArray($nrows); + $rs = $this->GetArray($nrows); return $rs; } $savem = $this->fetchMode; @@ -769,7 +762,7 @@ class ADORecordSet_ads extends ADORecordSet $this->fetchMode = $savem; if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); + $this->fields = $this->GetRowAssoc(); } $results = array(); @@ -802,7 +795,7 @@ class ADORecordSet_ads extends ADORecordSet $rez = @ads_fetch_into($this->_queryID, $this->fields); if ($rez) { if ($this->fetchMode & ADODB_FETCH_ASSOC) { - $this->fields =& $this->GetRowAssoc(); + $this->fields = $this->GetRowAssoc(); } return true; } diff --git a/drivers/adodb-db2.inc.php b/drivers/adodb-db2.inc.php index e214d2d7..9a3a90c5 100644 --- a/drivers/adodb-db2.inc.php +++ b/drivers/adodb-db2.inc.php @@ -1832,17 +1832,6 @@ class ADORecordSet_db2 extends ADORecordSet { var $dataProvider = "db2"; var $useFetchArray; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; - } - // returns the field object function fetchField($offset = 0) diff --git a/drivers/adodb-fbsql.inc.php b/drivers/adodb-fbsql.inc.php index 64913bc2..ca2df2da 100644 --- a/drivers/adodb-fbsql.inc.php +++ b/drivers/adodb-fbsql.inc.php @@ -169,20 +169,22 @@ class ADORecordSet_fbsql extends ADORecordSet{ var $databaseType = "fbsql"; var $canSeek = true; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if (!$mode) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } + parent::__construct($queryID, $mode); + switch ($mode) { - case ADODB_FETCH_NUM: $this->fetchMode = FBSQL_NUM; break; - case ADODB_FETCH_ASSOC: $this->fetchMode = FBSQL_ASSOC; break; - case ADODB_FETCH_BOTH: - default: - $this->fetchMode = FBSQL_BOTH; break; + case ADODB_FETCH_NUM: + $this->fetchMode = FBSQL_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = FBSQL_ASSOC; + break; + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = FBSQL_BOTH; + break; } - parent::__construct($queryID); } function _initrs() diff --git a/drivers/adodb-firebird.inc.php b/drivers/adodb-firebird.inc.php index db3cca5d..848b3c4e 100644 --- a/drivers/adodb-firebird.inc.php +++ b/drivers/adodb-firebird.inc.php @@ -1020,28 +1020,10 @@ class ADORecordset_firebird extends ADORecordSet private $fieldObjects = false; /** - * @var bool Flags if we have retrieved the metadata - */ - private $fieldObjectsRetrieved = false; - - /** - * @var array Cross-reference the objects by name for easy access - */ - private $fieldObjectsIndex = array(); - - /** * @var bool Flag to indicate if the result has a blob */ private $fieldObjectsHaveBlob = false; - function __construct($id, $mode = false) - { - global $ADODB_FETCH_MODE; - - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - parent::__construct($id); - } - /** * Returns: an object containing field information. diff --git a/drivers/adodb-ibase.inc.php b/drivers/adodb-ibase.inc.php index 8159c512..8d48f17d 100644 --- a/drivers/adodb-ibase.inc.php +++ b/drivers/adodb-ibase.inc.php @@ -741,13 +741,6 @@ class ADORecordSet_ibase extends ADORecordSet var $bind=false; var $_cacheType; - function __construct($id,$mode=false) - { - global $ADODB_FETCH_MODE; - - $this->fetchMode = ($mode === false) ? $ADODB_FETCH_MODE : $mode; - parent::__construct($id); - } /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about diff --git a/drivers/adodb-informix72.inc.php b/drivers/adodb-informix72.inc.php index 6fddde3d..d4083d5e 100644 --- a/drivers/adodb-informix72.inc.php +++ b/drivers/adodb-informix72.inc.php @@ -399,17 +399,6 @@ class ADORecordset_informix72 extends ADORecordSet { var $canSeek = true; var $_fieldprops = false; - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($id); - } - - /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about diff --git a/drivers/adodb-ldap.inc.php b/drivers/adodb-ldap.inc.php index 305f6a7f..e81cb818 100644 --- a/drivers/adodb-ldap.inc.php +++ b/drivers/adodb-ldap.inc.php @@ -295,28 +295,23 @@ class ADORecordSet_ldap extends ADORecordSet{ var $canSeek = false; var $_entryID; /* keeps track of the entry resource identifier */ - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: - $this->fetchMode = LDAP_NUM; - break; - case ADODB_FETCH_ASSOC: - $this->fetchMode = LDAP_ASSOC; - break; - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: - $this->fetchMode = LDAP_BOTH; - break; - } + parent::__construct($queryID, $mode); - parent::__construct($queryID); + switch ($mode) { + case ADODB_FETCH_NUM: + $this->fetchMode = LDAP_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = LDAP_ASSOC; + break; + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = LDAP_BOTH; + break; + } } function _initrs() diff --git a/drivers/adodb-mssql.inc.php b/drivers/adodb-mssql.inc.php index c0b714ed..c0a2eabd 100644 --- a/drivers/adodb-mssql.inc.php +++ b/drivers/adodb-mssql.inc.php @@ -857,18 +857,12 @@ class ADORecordset_mssql extends ADORecordSet { var $hasFetchAssoc; // see PHPLens Issue No: 6083 // _mths works only in non-localised system - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { + parent::__construct($queryID, $mode); + // freedts check... $this->hasFetchAssoc = function_exists('mssql_fetch_assoc'); - - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - - } - $this->fetchMode = $mode; - return parent::__construct($id); } diff --git a/drivers/adodb-mssqlnative.inc.php b/drivers/adodb-mssqlnative.inc.php index f7e1bcc1..a7640625 100644 --- a/drivers/adodb-mssqlnative.inc.php +++ b/drivers/adodb-mssqlnative.inc.php @@ -136,26 +136,12 @@ class ADODB_mssqlnative extends ADOConnection { $this->mssql_version = $version; } - function ServerInfo() { - global $ADODB_FETCH_MODE; - static $arr = false; - if (is_array($arr)) - return $arr; - if ($this->fetchMode === false) { - $savem = $ADODB_FETCH_MODE; - $ADODB_FETCH_MODE = ADODB_FETCH_NUM; - } elseif ($this->fetchMode >=0 && $this->fetchMode <=2) { - $savem = $this->fetchMode; - } else - $savem = $this->SetFetchMode(ADODB_FETCH_NUM); - - $arrServerInfo = sqlsrv_server_info($this->_connectionID); - $ADODB_FETCH_MODE = $savem; - - $arr = array(); - $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase']; - $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']); - return $arr; + function serverInfo() { + $info = sqlsrv_server_info($this->_connectionID); + return array( + 'description' => $info['SQLServerName'] . ' connected to ' . $info['CurrentDatabase'], + 'version' => $info['SQLServerVersion'], + ); } function IfNull( $field, $ifNull ) @@ -1053,15 +1039,6 @@ class ADORecordset_mssqlnative extends ADORecordSet { var $fieldOffset = 0; // _mths works only in non-localised system - /** - * @var bool True if we have retrieved the fields metadata - */ - private $fieldObjectsRetrieved = false; - - /* - * Cross-reference the objects by name for easy access - */ - private $fieldObjectsIndex = array(); /* * Cross references the dateTime objects for faster decoding @@ -1107,20 +1084,6 @@ class ADORecordset_mssqlnative extends ADORecordSet { ); - - - function __construct($id,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - - } - $this->fetchMode = $mode; - parent::__construct($id); - } - - function _initrs() { $this->_numOfRows = -1;//not supported diff --git a/drivers/adodb-mysqli.inc.php b/drivers/adodb-mysqli.inc.php index 07b294ca..62c28949 100644 --- a/drivers/adodb-mysqli.inc.php +++ b/drivers/adodb-mysqli.inc.php @@ -66,7 +66,6 @@ class ADODB_mysqli extends ADOConnection { var $socket = ''; //Default to empty string to fix HHVM bug var $_bindInputArray = false; var $nameQuote = '`'; /// string to use to quote identifiers and names - var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0)); var $arrayClass = 'ADORecordSet_array_mysqli'; var $multiQuery = false; var $ssl_key = null; @@ -176,15 +175,6 @@ class ADODB_mysqli extends ADOConnection { } return false; } - /* - I suggest a simple fix which would enable adodb and mysqli driver to - read connection options from the standard mysql configuration file - /etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com> - */ - $this->optionFlags = array(); - foreach($this->optionFlags as $arr) { - mysqli_options($this->_connectionID,$arr[0],$arr[1]); - } // Now merge in the standard connection parameters setting foreach ($this->connectionParameters as $options) { @@ -211,7 +201,6 @@ class ADODB_mysqli extends ADOConnection { $this->clientFlags = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; } - #if (!empty($this->port)) $argHostname .= ":".$this->port; /** @noinspection PhpCastIsUnnecessaryInspection */ $ok = @mysqli_real_connect($this->_connectionID, $argHostname, @@ -747,8 +736,6 @@ class ADODB_mysqli extends ADOConnection { $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; - -// return "from_unixtime(unix_timestamp($date)+$fraction)"; } /** @@ -1015,7 +1002,6 @@ class ADODB_mysqli extends ADOConnection { */ function SelectDB($dbName) { -// $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); $this->database = $dbName; if ($this->_connectionID) { @@ -1074,21 +1060,11 @@ class ADODB_mysqli extends ADOConnection { */ function Prepare($sql) { - /* - * Flag the insert_id method to use the correct retrieval method - */ + // Flag the insert_id method to use the correct retrieval method $this->usePreparedStatement = true; - /* - * Prepared statements are not yet handled correctly - */ + // Prepared statements are not yet handled correctly return $sql; - $stmt = $this->_connectionID->prepare($sql); - if (!$stmt) { - echo $this->errorMsg(); - return $sql; - } - return array($sql,$stmt); } public function execute($sql, $inputarr = false) @@ -1189,12 +1165,6 @@ class ADODB_mysqli extends ADOConnection { function _query($sql, $inputarr = false) { global $ADODB_COUNTRECS; - // Move to the next recordset, or return false if there is none. In a stored proc - // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result - // returns false. I think this is because the last "recordset" is actually just the - // return value of the stored proc (ie the number of rows affected). - // Commented out for reasons of performance. You should retrieve every recordset yourself. - // if (!mysqli_next_result($this->connection->_connectionID)) return false; // When SQL is empty, mysqli_query() throws exception on PHP 8 (#945) if (!$sql) { @@ -1204,80 +1174,39 @@ class ADODB_mysqli extends ADOConnection { return false; } - if (is_array($sql)) { - - // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but - // returns as bound variables. - - $stmt = $sql[1]; - $a = ''; - foreach($inputarr as $v) { - if (is_string($v)) $a .= 's'; - else if (is_integer($v)) $a .= 'i'; - else $a .= 'd'; - } - - /* - * set prepared statement flags - */ - if ($this->usePreparedStatement) - $this->useLastInsertStatement = true; - - $fnarr = array_merge( array($stmt,$a) , $inputarr); - call_user_func_array('mysqli_stmt_bind_param',$fnarr); - return mysqli_stmt_execute($stmt); - } - else if (is_string($sql) && is_array($inputarr)) - { - - /* - * This is support for true prepared queries - * with bound parameters - * - * set prepared statement flags - */ + if (is_string($sql) && is_array($inputarr)) { + // This is support for true prepared queries with bound parameters + // set prepared statement flags $this->usePreparedStatement = true; $this->usingBoundVariables = true; $bulkBindArray = array(); - if (is_array($inputarr[0])) - { + if (is_array($inputarr[0])) { $bulkBindArray = $inputarr; $inputArrayCount = count($inputarr[0]) - 1; - } - else - { + } else { $bulkBindArray[] = $inputarr; $inputArrayCount = count($inputarr) - 1; } - - /* - * Prepare the statement with the placeholders, - * prepare will fail if the statement is invalid - * so we trap and error if necessary. Note that we - * are calling MySQL prepare here, not ADOdb - */ + // Prepare the statement with the placeholders + // prepare will fail if the statement is invalid, so we trap and error if necessary. + // Note that we are calling MySQL prepare here, not ADOdb $stmt = $this->_connectionID->prepare($sql); - if ($stmt === false) - { + if ($stmt === false) { $this->outp_throw( "SQL Statement failed on preparation: " . htmlspecialchars($sql) . "'", 'Execute' ); return false; } - /* - * Make sure the number of parameters provided in the input - * array matches what the query expects. We must discount - * the first parameter which contains the data types in - * our inbound parameters - */ - $nparams = $stmt->param_count; - if ($nparams != $inputArrayCount) - { + // Make sure the number of parameters provided in the input array + // matches what the query expects. We must discount the first + // parameter which contains the data types in our inbound parameters + $nparams = $stmt->param_count; + if ($nparams != $inputArrayCount) { $this->outp_throw( "Input array has " . $inputArrayCount . " params, does not match query: '" . htmlspecialchars($sql) . "'", @@ -1286,37 +1215,28 @@ class ADODB_mysqli extends ADOConnection { return false; } - foreach ($bulkBindArray as $inputarr) - { - /* - * Must pass references into call_user_func_array - */ + foreach ($bulkBindArray as $inputarr) { + // Must pass references into call_user_func_array $paramsByReference = array(); - foreach($inputarr as $key => $value) { + foreach ($inputarr as $key => $value) { /** @noinspection PhpArrayAccessCanBeReplacedWithForeachValueInspection */ $paramsByReference[$key] = &$inputarr[$key]; } - /* - * Bind the params - */ + // Bind the params call_user_func_array(array($stmt, 'bind_param'), $paramsByReference); - /* - * Execute - */ - + // Execute $ret = mysqli_stmt_execute($stmt); // Store error code and message $this->_errorCode = $stmt->errno; $this->_errorMsg = $stmt->error; - /* - * Did we throw an error? - */ - if ($ret == false) + // Did we throw an error? + if (!$ret) { return false; + } } // Tells affected_rows to be compliant @@ -1328,14 +1248,9 @@ class ADODB_mysqli extends ADOConnection { // Turn the statement into a result set and return it return $stmt->get_result(); - } - else - { - /* - * reset prepared statement flags, in case we set them - * previously and didn't use them - */ - $this->usePreparedStatement = false; + } else { + // Reset prepared statement flags, in case we set them previously and didn't use them + $this->usePreparedStatement = false; $this->useLastInsertStatement = false; // Reset error code and message @@ -1343,19 +1258,12 @@ class ADODB_mysqli extends ADOConnection { $this->_errorMsg = ''; } - /* - if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) { - if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); - return false; - } - - return $mysql_res; - */ - if ($this->multiQuery) { - $rs = mysqli_multi_query($this->_connectionID, $sql.';'); + $rs = mysqli_multi_query($this->_connectionID, $sql . ';'); if ($rs) { - $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID ); + $rs = ($ADODB_COUNTRECS) + ? @mysqli_store_result($this->_connectionID) + : @mysqli_use_result($this->_connectionID); return $rs ?: true; // mysqli_more_results( $this->_connectionID ) } } else { @@ -1366,11 +1274,10 @@ class ADODB_mysqli extends ADOConnection { } } - if($this->debug) + if ($this->debug) { ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); - + } return false; - } /** @@ -1492,10 +1399,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ function __construct($queryID, $mode = false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } + parent::__construct($queryID, $mode); switch ($mode) { case ADODB_FETCH_NUM: @@ -1510,8 +1414,6 @@ class ADORecordSet_mysqli extends ADORecordSet{ $this->fetchMode = MYSQLI_BOTH; break; } - $this->adodbFetchMode = $mode; - parent::__construct($queryID); } function _initrs() diff --git a/drivers/adodb-oci8.inc.php b/drivers/adodb-oci8.inc.php index f361d683..2c2c891a 100644 --- a/drivers/adodb-oci8.inc.php +++ b/drivers/adodb-oci8.inc.php @@ -105,8 +105,6 @@ END; var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true) var $_refLOBs = array(); - // var $ansiOuter = true; // if oracle9 - /* * Legacy compatibility for sequence names for emulated auto-increments */ @@ -193,21 +191,33 @@ END; } /** + * Connect to database. + * * Multiple modes of connection are supported: * - * a. Local Database - * $conn->Connect(false,'scott','tiger'); + * 1. Local Database + * $conn->connect(false, 'scott', 'tiger'); * - * b. From tnsnames.ora - * $conn->Connect($tnsname,'scott','tiger'); - * $conn->Connect(false,'scott','tiger',$tnsname); + * 2. From tnsnames.ora + * $conn->connect($tnsname, 'scott', 'tiger'); + * OR + * $conn->connect(false, 'scott', 'tiger', $tnsname); * - * c. Server + service name - * $conn->Connect($serveraddress,'scott,'tiger',$service_name); + * 3. Server + service name + * $conn->connect($serveraddress, 'scott, 'tiger', $service_name); * - * d. Server + SID + * 4. Server + SID * $conn->connectSID = true; * $conn->Connect($serveraddress,'scott,'tiger',$SID); + * OR + * $conn->Connect($serveraddress,'scott,'tiger',"SID=$SID"); + * + * By default, the Session Mode will be set to OCI_DEFAULT, but it is + * possible to use one of other modes referenced in the + * {@link https://www.php.net/manual/en/function.oci-connect oci8_connect()} + * function's documentation, like this: + * $conn->setConnectionParameter('session_mode', OCI_SYSDBA); + * $conn->connect(...); * * @param string|false $argHostname DB server hostname or TNS name * @param string $argUsername @@ -245,45 +255,60 @@ END; $argDatabasename = substr($argDatabasename,4); $this->connectSID = true; } - - if ($this->connectSID) { - $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname - .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))"; - } else - $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname - .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))"; + $sidOrService = $this->connectSID ? 'SID' : 'SERVICE_NAME'; + $argDatabasename = "(DESCRIPTION=" + . "(ADDRESS=(PROTOCOL=TCP)(HOST=$argHostname)(PORT=$argHostport))" + . "(CONNECT_DATA=($sidOrService=$argDatabasename)))"; } } - //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>"; - if ($mode==1) { - $this->_connectionID = ($this->charSet) - ? oci_pconnect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_pconnect($argUsername,$argPassword, $argDatabasename); - if ($this->_connectionID && $this->autoRollback) { - oci_rollback($this->_connectionID); + // Determine the connect function to use based on connection mode + switch ($mode) { + case 1: $ociConnectFunction = 'oci_pconnect'; break; + case 2: $ociConnectFunction = 'oci_new_connect'; break; + default: $ociConnectFunction = 'oci_connect'; + } + + // Process the connection parameters + $sessionMode = OCI_DEFAULT; + $clientIdentifier = ''; + foreach ($this->connectionParameters as $options) { + foreach($options as $parameter => $value) { + switch ($parameter) { + case 'session_mode': + $sessionMode = $value; + break; + case 'client_identifier': + $clientIdentifier = $value; + break; + } } - } else if ($mode==2) { - $this->_connectionID = ($this->charSet) - ? oci_new_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_new_connect($argUsername,$argPassword, $argDatabasename); - } else { - $this->_connectionID = ($this->charSet) - ? oci_connect($argUsername,$argPassword, $argDatabasename,$this->charSet) - : oci_connect($argUsername,$argPassword, $argDatabasename); } + + $this->_connectionID = $ociConnectFunction( + $argUsername, + $argPassword, + $argDatabasename, + $this->charSet ?: '', + $sessionMode + ); if (!$this->_connectionID) { return false; } + // Set client identifier, but see documentation for limitations + if ($clientIdentifier) { + oci_set_client_identifier($this->_connectionID, $clientIdentifier); + } + + if ($mode == 1 && $this->autoRollback ) { + oci_rollback($this->_connectionID); + } + if ($this->_initdate) { $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'"); } - // looks like: - // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production - // $vers = oci_server_version($this->_connectionID); - // if (strpos($vers,'8i') !== false) $this->ansiOuter = true; return true; } @@ -547,7 +572,7 @@ END; $ok = true; } - return $ok ? true : false; + return (bool)$ok; } function CommitTrans($ok=true) @@ -1252,9 +1277,9 @@ END; * $db->Parameter($stmt,$group,'group'); * $db->Execute($stmt); * - * @param $stmt Statement returned by {@see Prepare()} or {@see PrepareSP()}. - * @param $var PHP variable to bind to - * @param $name Name of stored procedure variable name to bind to. + * @param array $stmt Statement returned by {@see Prepare()} or {@see PrepareSP()}. + * @param mixed $var PHP variable to bind to + * @param string $name Name of stored procedure variable name to bind to. * @param bool $isOutput Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8. * @param int $maxLen Holds an maximum length of the variable. * @param mixed $type The data type of $var. Legal values depend on driver. @@ -1398,15 +1423,13 @@ END; $ok = oci_execute($cursor); return $cursor; } - return $stmt; } else { if (is_resource($stmt)) { oci_free_statement($stmt); return true; } - return $stmt; } - break; + return $stmt; default : return true; @@ -1571,8 +1594,6 @@ SELECT /*+ RULE */ distinct b.column_name * It remains for backwards compatibility. * * @return string Quoted string to be sent back to database - * - * @noinspection PhpUnusedParameterInspection */ function qStr($s, $magic_quotes=false) { @@ -1600,12 +1621,10 @@ class ADORecordset_oci8 extends ADORecordSet { /** @var resource Cursor reference */ var $_refcursor; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } + parent::__construct($queryID, $mode); + switch ($mode) { case ADODB_FETCH_ASSOC: $this->fetchMode = OCI_ASSOC; @@ -1620,8 +1639,6 @@ class ADORecordset_oci8 extends ADORecordSet { break; } $this->fetchMode += OCI_RETURN_NULLS + OCI_RETURN_LOBS; - $this->adodbFetchMode = $mode; - $this->_queryID = $queryID; } /** @@ -1821,7 +1838,7 @@ class ADORecordset_oci8 extends ADORecordSet { * @param mixed $t * @param int $len [optional] Length of blobsize * @param bool $fieldobj [optional][discarded] - * @return str The metatype of the field + * @return string The metatype of the field */ function MetaType($t, $len=-1, $fieldobj=false) { diff --git a/drivers/adodb-odbc.inc.php b/drivers/adodb-odbc.inc.php index 56db8b0e..59909646 100644 --- a/drivers/adodb-odbc.inc.php +++ b/drivers/adodb-odbc.inc.php @@ -613,20 +613,13 @@ class ADORecordSet_odbc extends ADORecordSet { var $dataProvider = "odbc"; var $useFetchArray; - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $id; + parent::__construct($queryID, $mode); // the following is required for mysql odbc driver in 4.3.1 -- why? $this->EOF = false; $this->_currentRow = -1; - //parent::__construct($id); } diff --git a/drivers/adodb-odbtp.inc.php b/drivers/adodb-odbtp.inc.php index e3f81481..b4e12601 100644 --- a/drivers/adodb-odbtp.inc.php +++ b/drivers/adodb-odbtp.inc.php @@ -684,15 +684,6 @@ class ADORecordSet_odbtp extends ADORecordSet { var $databaseType = 'odbtp'; var $canSeek = true; - function __construct($queryID,$mode=false) - { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - parent::__construct($queryID); - } function _initrs() { diff --git a/drivers/adodb-oracle.inc.php b/drivers/adodb-oracle.inc.php index dc0462fe..5cd4bedf 100644 --- a/drivers/adodb-oracle.inc.php +++ b/drivers/adodb-oracle.inc.php @@ -223,16 +223,9 @@ class ADORecordset_oracle extends ADORecordSet { var $databaseType = "oracle"; var $bind = false; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->fetchMode = $mode; - - $this->_queryID = $queryID; + parent::__construct($queryID, $mode); $this->_inited = true; $this->fields = array(); @@ -249,9 +242,7 @@ class ADORecordset_oracle extends ADORecordSet { return $this->_queryID; } - - - /* Returns: an object containing field information. + /* Returns: an object containing field information. Get column information in the Recordset object. fetchField() can be used in order to obtain information about fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by fetchField() is retrieved. */ diff --git a/drivers/adodb-pdo.inc.php b/drivers/adodb-pdo.inc.php index d3ce12d0..bdc466dd 100644 --- a/drivers/adodb-pdo.inc.php +++ b/drivers/adodb-pdo.inc.php @@ -66,6 +66,10 @@ function adodb_pdo_type($t) class ADODB_pdo extends ADOConnection { + const BIND_USE_QUESTION_MARKS = 0; + const BIND_USE_NAMED_PARAMETERS = 1; + const BIND_USE_BOTH = 2; + var $databaseType = "pdo"; var $dataProvider = "pdo"; var $fmtDate = "'Y-m-d'"; @@ -100,6 +104,15 @@ class ADODB_pdo extends ADOConnection { */ public $pdoParameters = array(); + /* + * Set which style is used to bind parameters + * + * BIND_USE_QUESTION_MARKS = Use only question marks + * BIND_USE_NAMED_PARAMETERS = Use only named parameters + * BIND_USE_BOTH = Use both question marks and named parameters (Default) + */ + public $bindParameterStyle = self::BIND_USE_BOTH; + function _UpdatePDO() { $d = $this->_driver; @@ -592,11 +605,7 @@ class ADODB_pdo extends ADOConnection { $this->_driver->debug = $this->debug; } if ($inputarr) { - - /* - * inputarr must be numeric - */ - $inputarr = array_values($inputarr); + $inputarr = $this->conformToBindParameterStyle($stmt->queryString, $inputarr); $ok = $stmt->execute($inputarr); } else { @@ -666,6 +675,58 @@ class ADODB_pdo extends ADOConnection { return "'" . str_replace("'", $this->replaceQuote, $s) . "'"; } + /** + * Make bind parameters conform to settings. + * + * @param string $sql + * @param array $inputarr + * @return array + */ + private function conformToBindParameterStyle($sql, $inputarr) + { + switch ($this->bindParameterStyle) + { + case self::BIND_USE_QUESTION_MARKS: + default: + $inputarr = array_values($inputarr); + break; + + case self::BIND_USE_NAMED_PARAMETERS: + break; + + case self::BIND_USE_BOTH: + // inputarr must be numeric if SQL contains a question mark + if ($this->containsQuestionMarkPlaceholder($sql)) { + $inputarr = array_values($inputarr); + + if ($this->debug) { + ADOconnection::outp('improve the performance of this query by setting the bindParameterStyle to BIND_USE_QUESTION_MARKS'); + } + } + break; + } + + return $inputarr; + } + + /** + * Checks for the inclusion of a question mark placeholder. + * + * @param string $sql SQL string + * @return boolean Returns true if a question mark placeholder is included + */ + private function containsQuestionMarkPlaceholder($sql) + { + $pattern = '/(.\?(:?.|$))/'; + if (preg_match_all($pattern, $sql, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + if ($match[1] !== '`?`' && strpos($match[1], '??') === false) { + return true; + } + } + } + return false; + } } /** @@ -792,27 +853,25 @@ class ADORecordSet_pdo extends ADORecordSet { /** @var PDOStatement */ var $_queryID; - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - $this->adodbFetchMode = $mode; - switch($mode) { - case ADODB_FETCH_NUM: $mode = PDO::FETCH_NUM; break; - case ADODB_FETCH_ASSOC: $mode = PDO::FETCH_ASSOC; break; + parent::__construct($queryID, $mode); - case ADODB_FETCH_BOTH: - default: $mode = PDO::FETCH_BOTH; break; + switch($mode) { + case ADODB_FETCH_NUM: + $mode = PDO::FETCH_NUM; + break; + case ADODB_FETCH_ASSOC: + $mode = PDO::FETCH_ASSOC; + break; + case ADODB_FETCH_BOTH: + default: + $mode = PDO::FETCH_BOTH; + break; } $this->fetchMode = $mode; - - $this->_queryID = $id; - parent::__construct($id); } - function Init() { if ($this->_inited) { diff --git a/drivers/adodb-postgres64.inc.php b/drivers/adodb-postgres64.inc.php index 9bf9e713..9f02c5cd 100644 --- a/drivers/adodb-postgres64.inc.php +++ b/drivers/adodb-postgres64.inc.php @@ -26,6 +26,10 @@ class ADODB_postgres64 extends ADOConnection{ var $databaseType = 'postgres64'; var $dataProvider = 'postgres'; var $hasInsertID = true; + /** @var PgSql\Connection|resource|false Identifier for the native database connection */ + var $_connectionID = false; + /** @var PgSql\Connection|resource|false */ + var $_queryID; /** @var PgSql\Connection|resource|false */ var $_resultid = false; var $concat_operator='||'; @@ -85,7 +89,7 @@ class ADODB_postgres64 extends ADOConnection{ var $_pnum = 0; var $version; - var $_nestedSQL = false; + var $_nestedSQL = true; // The last (fmtTimeStamp is not entirely correct: // PostgreSQL also has support for time zones, @@ -117,9 +121,7 @@ class ADODB_postgres64 extends ADOConnection{ // If PHP has been compiled with PostgreSQL 7.3 or lower, then // server version is not set so we use pg_parameter_status() // which includes logic to obtain values server_version - 'version' => isset($version['server']) - ? $version['server'] - : pg_parameter_status($this->_connectionID, 'server_version'), + 'version' => $version['server'] ?? pg_parameter_status($this->_connectionID, 'server_version'), 'client' => $version['client'], 'description' => null, ); @@ -136,7 +138,15 @@ class ADODB_postgres64 extends ADOConnection{ return " coalesce($field, $ifNull) "; } - // get the last id - never tested + /** + * Get the last id - never tested. + * + * @param string $tablename + * @param string $fieldname + * @return false|mixed + * + * @noinspection PhpUnused + */ function pg_insert_id($tablename,$fieldname) { $result=pg_query($this->_connectionID, 'SELECT last_value FROM '. $tablename .'_'. $fieldname .'_seq'); @@ -158,7 +168,9 @@ class ADODB_postgres64 extends ADOConnection{ */ protected function _insertID($table = '', $column = '') { - if ($this->_resultid === false) return false; + if ($this->_resultid === false) { + return false; + } $oid = pg_last_oid($this->_resultid); // to really return the id, we need the table and column-name, else we can only return the oid != id return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid); @@ -166,7 +178,9 @@ class ADODB_postgres64 extends ADOConnection{ function _affectedrows() { - if ($this->_resultid === false) return false; + if ($this->_resultid === false) { + return false; + } return pg_affected_rows($this->_resultid); } @@ -181,13 +195,14 @@ class ADODB_postgres64 extends ADOConnection{ return pg_query($this->_connectionID, 'begin '.$this->_transmode); } - function RowLock($tables,$where,$col='1 as adodbignore') + function RowLock($table, $where, $col='1 as adodbignore') { - if (!$this->transCnt) $this->BeginTrans(); - return $this->GetOne("select $col from $tables where $where for update"); + if (!$this->transCnt) { + $this->BeginTrans(); + } + return $this->GetOne("select $col from $table where $where for update"); } - // returns true/false. function CommitTrans($ok=true) { if ($this->transOff) return true; @@ -197,7 +212,6 @@ class ADODB_postgres64 extends ADOConnection{ return pg_query($this->_connectionID, 'commit'); } - // returns true/false function RollbackTrans() { if ($this->transOff) return true; @@ -264,8 +278,6 @@ class ADODB_postgres64 extends ADOConnection{ } } - - // Format date column in sql string given an input format that understands Y M D function SQLDate($fmt, $col=false) { if (!$col) $col = $this->sysTimeStamp; @@ -345,15 +357,24 @@ class ADODB_postgres64 extends ADOConnection{ - /* - * Load a Large Object from a file - * - the procedure stores the object id in the table and imports the object using - * postgres proprietary blob handling routines - * - * contributed by Mattia Rossi mattia@technologist.com - * modified for safe mode by juraj chlebec - */ - function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') + /** + * Update a BLOB from a file. + * + * The procedure stores the object id in the table and imports the object using + * postgres proprietary blob handling routines. + * + * Usage example: + * $conn->updateBlobFile('table', 'blob_col', '/path/to/file', 'id=1'); + * + * @param string $table + * @param string $column + * @param string $path Filename containing blob data + * @param mixed $where {@see updateBlob()} + * @param string $blobtype supports 'BLOB' and 'CLOB' + * + * @return bool success + */ + function updateBlobFile($table,$column,$path,$where,$blobtype='BLOB') { pg_query($this->_connectionID, 'begin'); @@ -369,24 +390,25 @@ class ADODB_postgres64 extends ADOConnection{ // $oid = pg_lo_import ($path); pg_query($this->_connectionID, 'commit'); $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); - $rez = !empty($rs); - return $rez; + return !empty($rs); } - /* - * Deletes/Unlinks a Blob from the database, otherwise it - * will be left behind - * - * Returns TRUE on success or FALSE on failure. - * - * contributed by Todd Rogers todd#windfox.net - */ - function BlobDelete( $blob ) + /** + * Deletes/Unlinks a Blob from the database, otherwise it will be left behind. + * + * contributed by Todd Rogers todd#windfox.net + * + * @param mixed $blob + * @return bool True on success, false on failure. + * + * @noinspection PhpUnused + */ + function BlobDelete($blob) { pg_query($this->_connectionID, 'begin'); $result = @pg_lo_unlink($this->_connectionID, $blob); pg_query($this->_connectionID, 'commit'); - return( $result ); + return $result; } /* @@ -398,19 +420,21 @@ class ADODB_postgres64 extends ADOConnection{ return is_numeric($oid); } - /* - * If an OID is detected, then we use pg_lo_* to open the oid file and read the - * real blob from the db using the oid supplied as a parameter. If you are storing - * blobs using bytea, we autodetect and process it so this function is not needed. - * - * contributed by Mattia Rossi mattia@technologist.com - * - * see http://www.postgresql.org/idocs/index.php?largeobjects.html - * - * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also - * added maxsize parameter, which defaults to $db->maxblobsize if not defined. - */ - function BlobDecode($blob,$maxsize=false,$hastrans=true) + /** + * If an OID is detected, then we use pg_lo_* to open the oid file and read the + * real blob from the db using the oid supplied as a parameter. If you are storing + * blobs using bytea, we autodetect and process it so this function is not needed. + * + * Contributed by Mattia Rossi mattia@technologist.com + * + * @link https://www.postgresql.org/docs/current/largeobjects.html + * + * @param mixed $blob + * @param int|false $maxsize Defaults to $db->maxblobsize if false + * @param bool $hastrans + * @return string|false The blob + */ + function BlobDecode($blob, $maxsize=false, $hastrans=true) { if (!$this->GuessOID($blob)) return $blob; @@ -430,7 +454,7 @@ class ADODB_postgres64 extends ADOConnection{ /** * Encode binary value prior to DB storage. * - * See https://www.postgresql.org/docs/current/static/datatype-binary.html + * @link https://www.postgresql.org/docs/current/static/datatype-binary.html * * NOTE: SQL string literals (input strings) must be preceded with two * backslashes due to the fact that they must pass through two parsers in @@ -491,7 +515,6 @@ class ADODB_postgres64 extends ADOConnection{ global $ADODB_FETCH_MODE; $schema = false; - $false = false; $this->_findschema($table,$schema); if ($normalize) $table = strtolower($table); @@ -505,7 +528,7 @@ class ADODB_postgres64 extends ADOConnection{ $ADODB_FETCH_MODE = $save; if ($rs === false) { - return $false; + return false; } if (!empty($this->metaKeySQL)) { // If we want the primary keys, we have to issue a separate query @@ -523,6 +546,8 @@ class ADODB_postgres64 extends ADOConnection{ $rskey->Close(); unset($rskey); + } else { + $keys = []; } $rsdefa = array(); @@ -575,28 +600,29 @@ class ADODB_postgres64 extends ADOConnection{ //Freek $fld->not_null = $rs->fields[4] == 't'; - // Freek if (is_array($keys)) { foreach($keys as $key) { - if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') + if ($fld->name == $key['column_name'] && $key['primary_key'] == 't') { $fld->primary_key = true; - if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') + } + if ($fld->name == $key['column_name'] && $key['unique_key'] == 't') { $fld->unique = true; // What name is more compatible? + } } } - if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; - else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } + else { + $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; + } $rs->MoveNext(); } $rs->Close(); - if (empty($retarr)) - return $false; - else - return $retarr; - + return $retarr ?: false; } function param($name, $type='C') @@ -640,7 +666,7 @@ class ADODB_postgres64 extends ADOConnection{ WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; } - if ($primary == FALSE) { + if (!$primary) { $sql .= ' AND i.indisprimary=false;'; } @@ -657,8 +683,7 @@ class ADODB_postgres64 extends ADOConnection{ $ADODB_FETCH_MODE = $save; if (!is_object($rs)) { - $false = false; - return $false; + return false; } // Get column names indexed by attnum so we can lookup the index key @@ -749,22 +774,6 @@ class ADODB_postgres64 extends ADOConnection{ if ($this->_connectionID === false) return false; $this->Execute("set datestyle='ISO'"); - $info = $this->ServerInfo(false); - - if (version_compare($info['version'], '7.1', '>=')) { - $this->_nestedSQL = true; - } - - # PostgreSQL 9.0 changed the default output for bytea from 'escape' to 'hex' - # PHP does not handle 'hex' properly ('x74657374' is returned as 't657374') - # https://bugs.php.net/bug.php?id=59831 states this is in fact not a bug, - # so we manually set bytea_output - if (version_compare($info['version'], '9.0', '>=') - && version_compare($info['client'], '9.2', '<') - ) { - $this->Execute('set bytea_output=escape'); - } - return true; } @@ -911,17 +920,17 @@ class ADODB_postgres64 extends ADOConnection{ } - /* - * Maximum size of C field - */ + /** + * @return int Maximum size of C field + */ function CharMax() { return 1000000000; // should be 1 Gb? } - /* - * Maximum size of X field - */ + /** + * @return int Maximum size of X field + */ function TextMax() { return 1000000000; // should be 1 Gb? @@ -941,23 +950,21 @@ class ADORecordSet_postgres64 extends ADORecordSet{ function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch ($mode) - { - case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; - case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; + parent::__construct($queryID, $mode); - case ADODB_FETCH_DEFAULT: - case ADODB_FETCH_BOTH: - default: $this->fetchMode = PGSQL_BOTH; break; + switch ($mode) { + case ADODB_FETCH_NUM: + $this->fetchMode = PGSQL_NUM; + break; + case ADODB_FETCH_ASSOC: + $this->fetchMode = PGSQL_ASSOC; + break; + case ADODB_FETCH_DEFAULT: + case ADODB_FETCH_BOTH: + default: + $this->fetchMode = PGSQL_BOTH; + break; } - $this->adodbFetchMode = $mode; - - // Parent's constructor - parent::__construct($queryID); } function GetRowAssoc($upper = ADODB_ASSOC_CASE) @@ -965,8 +972,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ if ($this->fetchMode == PGSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) { return $this->fields; } - $row = ADORecordSet::GetRowAssoc($upper); - return $row; + return ADORecordSet::GetRowAssoc($upper); } function _initRS() @@ -978,7 +984,6 @@ class ADORecordSet_postgres64 extends ADORecordSet{ // cache types for blob decode check // apparently pg_field_type actually performs an sql query on the database to get the type. - if (empty($this->connection->noBlobs)) for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { if (pg_field_type($qid,$i) == 'bytea') { $this->_blobArr[$i] = pg_field_name($qid,$i); @@ -1045,7 +1050,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ } } if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { - foreach($this->_blobArr as $k => $v) { + foreach($this->_blobArr as $v) { $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); } } @@ -1086,7 +1091,7 @@ class ADORecordSet_postgres64 extends ADORecordSet{ return pg_free_result($this->_queryID); } - function MetaType($t,$len=-1,$fieldobj=false) + function MetaType($t,$len=-1, $fieldObj=false) { if (is_object($t)) { $fieldobj = $t; @@ -1096,71 +1101,81 @@ class ADORecordSet_postgres64 extends ADORecordSet{ $t = strtoupper($t); - if (array_key_exists($t,$this->connection->customActualTypes)) - return $this->connection->customActualTypes[$t]; + if (array_key_exists($t, $this->connection->customActualTypes)) { + return $this->connection->customActualTypes[$t]; + } switch ($t) { - case 'MONEY': // stupid, postgres expects money to be a string - case 'INTERVAL': - case 'CHAR': - case 'CHARACTER': - case 'VARCHAR': - case 'NAME': - case 'BPCHAR': - case '_VARCHAR': - case 'CIDR': - case 'INET': - case 'MACADDR': - case 'UUID': - if ($len <= $this->blobSize) return 'C'; - - case 'TEXT': - return 'X'; + case 'MONEY': // stupid, postgres expects money to be a string + case 'INTERVAL': + case 'CHAR': + case 'CHARACTER': + case 'VARCHAR': + case 'NAME': + case 'BPCHAR': + case '_VARCHAR': + case 'CIDR': + case 'INET': + case 'MACADDR': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'UUID': + if ($len <= $this->blobSize) { + return 'C'; + } + // Fall-through - case 'IMAGE': // user defined type - case 'BLOB': // user defined type - case 'BIT': // This is a bit string, not a single bit, so don't return 'L' - case 'VARBIT': - case 'BYTEA': - return 'B'; + case 'TEXT': + return 'X'; - case 'BOOL': - case 'BOOLEAN': - return 'L'; + case 'IMAGE': // user defined type + case 'BLOB': // user defined type + case 'BIT': // This is a bit string, not a single bit, so don't return 'L' + case 'VARBIT': + case 'BYTEA': + return 'B'; - case 'DATE': - return 'D'; + case 'BOOL': + case 'BOOLEAN': + return 'L'; + case 'DATE': + return 'D'; - case 'TIMESTAMP WITHOUT TIME ZONE': - case 'TIME': - case 'DATETIME': - case 'TIMESTAMP': - case 'TIMESTAMPTZ': - return 'T'; + case 'TIMESTAMP WITHOUT TIME ZONE': + case 'TIME': + case 'DATETIME': + case 'TIMESTAMP': + case 'TIMESTAMPTZ': + return 'T'; - case 'SMALLINT': - case 'BIGINT': - case 'INTEGER': - case 'INT8': - case 'INT4': - case 'INT2': - if (isset($fieldobj) && - empty($fieldobj->primary_key) && (!$this->connection->uniqueIisR || empty($fieldobj->unique))) return 'I'; + case 'SMALLINT': + case 'BIGINT': + case 'INTEGER': + case 'INT8': + case 'INT4': + /** @noinspection PhpMissingBreakStatementInspection */ + case 'INT2': + if (isset($fieldobj) + && empty($fieldobj->primary_key) + && (!$this->connection->uniqueIisR || empty($fieldobj->unique)) + ) { + return 'I'; + } + // Fall-through - case 'OID': - case 'SERIAL': - return 'R'; + case 'OID': + case 'SERIAL': + return 'R'; - case 'NUMERIC': - case 'DECIMAL': - case 'FLOAT4': - case 'FLOAT8': - return 'N'; + case 'NUMERIC': + case 'DECIMAL': + case 'FLOAT4': + case 'FLOAT8': + return 'N'; - default: - return ADODB_DEFAULT_METATYPE; - } + default: + return ADODB_DEFAULT_METATYPE; + } } } diff --git a/drivers/adodb-sqlite.inc.php b/drivers/adodb-sqlite.inc.php index d41829c0..d4cf19be 100644 --- a/drivers/adodb-sqlite.inc.php +++ b/drivers/adodb-sqlite.inc.php @@ -425,14 +425,11 @@ class ADORecordset_sqlite extends ADORecordSet { var $databaseType = "sqlite"; var $bind = false; - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { + parent::__construct($queryID, $mode); - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } - switch($mode) { + switch ($mode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE_NUM; break; @@ -443,9 +440,6 @@ class ADORecordset_sqlite extends ADORecordSet { $this->fetchMode = SQLITE_BOTH; break; } - $this->adodbFetchMode = $mode; - - $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); @@ -462,7 +456,6 @@ class ADORecordset_sqlite extends ADORecordSet { return $this->_queryID; } - function FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; diff --git a/drivers/adodb-sqlite3.inc.php b/drivers/adodb-sqlite3.inc.php index 7623a3c8..822a649d 100644 --- a/drivers/adodb-sqlite3.inc.php +++ b/drivers/adodb-sqlite3.inc.php @@ -719,12 +719,9 @@ class ADORecordset_sqlite3 extends ADORecordSet { var $_queryID; /** @noinspection PhpMissingParentConstructorInspection */ - function __construct($queryID,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; - } + parent::__construct($queryID, $mode); switch($mode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE3_NUM; @@ -736,9 +733,6 @@ class ADORecordset_sqlite3 extends ADORecordSet { $this->fetchMode = SQLITE3_BOTH; break; } - $this->adodbFetchMode = $mode; - - $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); @@ -755,7 +749,6 @@ class ADORecordset_sqlite3 extends ADORecordSet { return $this->_queryID; } - function FetchField($fieldOffset = -1) { $fld = new ADOFieldObject; diff --git a/drivers/adodb-sybase.inc.php b/drivers/adodb-sybase.inc.php index 79fb82e8..f6c8f58c 100644 --- a/drivers/adodb-sybase.inc.php +++ b/drivers/adodb-sybase.inc.php @@ -315,15 +315,15 @@ class ADORecordset_sybase extends ADORecordSet { // _mths works only in non-localised system var $_mths = array('JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12); - function __construct($id,$mode=false) + function __construct($queryID, $mode=false) { - if ($mode === false) { - global $ADODB_FETCH_MODE; - $mode = $ADODB_FETCH_MODE; + parent::__construct($queryID, $mode); + + if (!$mode) { + $this->fetchMode = ADODB_FETCH_ASSOC; + } else { + $this->fetchMode = $mode; } - if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; - else $this->fetchMode = $mode; - parent::__construct($id); } /* Returns: an object containing field information. diff --git a/replicate/adodb-replicate.inc.php b/replicate/adodb-replicate.inc.php deleted file mode 100644 index ee5c539f..00000000 --- a/replicate/adodb-replicate.inc.php +++ /dev/null @@ -1,1179 +0,0 @@ -<?php -/** - * Replication engine - * - * @deprecated 5.22.0 Will be removed in 5.23.0 {@link https://github.com/ADOdb/ADOdb/issues/780) - * - * - Copy table structures and data from different databases - * (e.g. mysql to oracle) - * - Generate CREATE TABLE, CREATE INDEX, INSERT for installation scripts - * - * Note: this code assumes that comments such as / * * / are allowed, - * which works with: mssql, postgresql, oracle, mssql - * - * Table Structure copying includes - * - fields and limited subset of types - * - optional default values - * - indexes - * - but not constraints - * - * Two modes of data copy: - * 1. ReplicateData - Copy from src to dest, with update of status of copy - * back to src, with configurable src SELECT where clause - * 2. MergeData - Copy from src to dest based on last mod date field and/or - * copied flag field - * - * Default settings are - * - do not execute, generate sql ($rep->execute = false) - * - do not delete records in dest table first ($rep->deleteFirst = false). - * if $rep->deleteFirst is true and primary keys are defined, - * then no deletion will occur unless *INSERTONLY* is defined in pkey array - * - only commit once at the end of every ReplicateData ($rep->commitReplicate = true) - * - do not autocommit every x records processed ($rep->commitRecs = -1) - * - even if error occurs on one record, continue copying remaining records ($rep->neverAbort = true) - * - debugging turned off ($rep->debug = false) - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -define('ADODB_REPLICATE',1.2); - -include_once(ADODB_DIR.'/adodb-datadict.inc.php'); - -class ADODB_Replicate { - var $connSrc; - var $connDest; - - var $connSrc2 = false; - var $connDest2 = false; - var $ddSrc; - var $ddDest; - - var $execute = false; - var $debug = false; - var $deleteFirst = false; - var $commitReplicate = true; // commit at end of replicatedata - var $commitRecs = -1; // only commit at end of ReplicateData() - - var $selFilter = false; - var $fieldFilter = false; - var $indexFilter = false; - var $updateFilter = false; - var $insertFilter = false; - var $updateSrcFn = false; - - var $limitRecs = false; - - var $neverAbort = true; - var $copyTableDefaults = false; // turn off because functions defined as defaults will not work when copied - var $errHandler = false; // name of error handler function, if used. - var $htmlSpecialChars = true; // if execute false, then output with htmlspecialchars enabled. - // Will autoconfigure itself. No need to modify - - var $trgSuffix = '_mrgTr'; - var $idxSuffix = '_mrgidx'; - var $trLogic = '1 = 1'; - var $datesAreTimeStamps = false; - - var $oracleSequence = false; - var $readUncommitted = false; // read without obeying shared locks for fast select (mssql) - - var $compat = false; - // connSrc2 and connDest2 are only required if the db driver - // does not allow updates back to src db in first connection (the select connection), - // so we need 2nd connection - function __construct($connSrc, $connDest, $connSrc2=false, $connDest2=false) - { - - if (strpos($connSrc->databaseType,'odbtp') !== false) { - $connSrc->_bindInputArray = false; # bug in odbtp, binding fails - } - - if (strpos($connDest->databaseType,'odbtp') !== false) { - $connDest->_bindInputArray = false; # bug in odbtp, binding fails - } - - $this->connSrc = $connSrc; - $this->connDest = $connDest; - - $this->connSrc2 = ($connSrc2) ? $connSrc2 : $connSrc; - $this->connDest2 = ($connDest2) ? $connDest2 : $connDest; - - $this->ddSrc = newDataDictionary($connSrc); - $this->ddDest = newDataDictionary($connDest); - $this->htmlSpecialChars = isset($_SERVER['HTTP_HOST']); - } - - function ExecSQL($sql) - { - if (!is_array($sql)) $sql[] = $sql; - - $ret = true; - foreach($sql as $s) - if (!$this->execute) echo "<pre>",$s.";\n</pre>"; - else { - $ok = $this->connDest->Execute($s); - if (!$ok) - if ($this->neverAbort) $ret = false; - else return false; - } - - return $ret; - } - - /* - We assume replication between $table and $desttable only works if the field names and types match for both tables. - - Also $table and desttable can have different names. - */ - - function CopyTableStruct($table,$desttable='') - { - $sql = $this->CopyTableStructSQL($table,$desttable); - if (empty($sql)) return false; - return $this->ExecSQL($sql); - } - - function RunFieldFilter(&$fld, $mode = '') - { - if ($this->fieldFilter) { - $fn = $this->fieldFilter; - return $fn($fld, $mode); - } else - return $fld; - } - - function RunUpdateFilter($table, $fld, $val) - { - if ($this->updateFilter) { - $fn = $this->updateFilter; - return $fn($table, $fld, $val); - } else - return $val; - } - - function RunInsertFilter($table, $fld, &$val) - { - if ($this->insertFilter) { - $fn = $this->insertFilter; - return $fn($table, $fld, $val); - } else - return $fld; - } - - /* - $mode = INS or UPD - - The lastUpdateFld holds the field that counts the number of updates or the date of last mod. This ensures that - if the rec was modified after replicatedata retrieves the data but before we update back the src record, - we don't set the copiedflag='Y' yet. - */ - function RunUpdateSrcFn($srcdb, $table, $fldoffsets, $row, $where, $mode, $dest_insertid=null, $lastUpdateFld='') - { - if (!$this->updateSrcFn) return; - - $bindarr = array(); - foreach($fldoffsets as $k) { - $bindarr[$k] = $row[$k]; - } - $last = sizeof($row); - - if ($lastUpdateFld && $row[$last-1]) { - $ds = $row[$last-1]; - if (strpos($ds,':') !== false) $s = $srcdb->DBTimeStamp($ds); - else $s = $srcdb->qstr($ds); - $where = "WHERE $lastUpdateFld = $s and $where"; - } else - $where = "WHERE $where"; - $fn = $this->updateSrcFn; - if (is_array($fn)) { - if (sizeof($fn) == 1) $set = reset($fn); - else $set = @$fn[$mode]; - if ($set) { - - if (strlen($dest_insertid) == 0) $dest_insertid = 'null'; - $set = str_replace('$INSERT_ID',$dest_insertid,$set); - - $sql = "UPDATE $table SET $set $where "; - $ok = $srcdb->Execute($sql,$bindarr); - if (!$ok) { - echo $srcdb->ErrorMsg(),"<br>\n"; - die(); - } - } - } else $fn($srcdb, $table, $row, $where, $bindarr, $mode, $dest_insertid); - - } - - function CopyTableStructSQL($table, $desttable='',$dropdest =false) - { - if (!$desttable) { - $desttable = $table; - $prefixidx = ''; - } else - $prefixidx = $desttable; - - $conn = $this->connSrc; - $types = $conn->MetaColumns($table); - if (!$types) { - echo "$table does not exist in source db<br>\n"; - return array(); - } - if (!$dropdest && $this->connDest->MetaColumns($desttable)) { - echo "$desttable already exists in dest db<br>\n"; - return array(); - } - if ($this->debug) var_dump($types); - $sa = array(); - $idxcols = array(); - - foreach($types as $name => $t) { - $s = ''; - $mt = $this->ddSrc->MetaType($t->type); - $len = $t->max_length; - $fldname = $this->RunFieldFilter($t->name,'TABLE'); - if (!$fldname) continue; - - $s .= $fldname . ' '.$mt; - if (isset($t->scale)) $precision = '.'.$t->scale; - else $precision = ''; - if ($mt == 'C' or $mt == 'X') $s .= "($len)"; - else if ($mt == 'N' && $precision) $s .= "($len$precision)"; - - if ($mt == 'R') $idxcols[] = $fldname; - - if ($this->copyTableDefaults) { - if (isset($t->default_value)) { - $v = $t->default_value; - if ($mt == 'C' or $mt == 'X') $v = $this->connDest->qstr($v); // might not work as this could be function - $s .= ' DEFAULT '.$v; - } - } - - $sa[] = $s; - } - - $s = implode(",\n",$sa); - - // dump adodb intermediate data dictionary format - if ($this->debug) echo '<pre>'.$s.'</pre>'; - - $sqla = $this->ddDest->CreateTableSQL($desttable,$s); - - /* - if ($idxcols) { - $idxoptions = array('UNIQUE'=>1); - $sqla2 = $this->ddDest->_IndexSQL($table.'_'.$fldname.'_SERIAL', $desttable, $idxcols,$idxoptions); - $sqla = array_merge($sqla,$sqla2); - }*/ - - $idxs = $conn->MetaIndexes($table); - if ($idxs) - foreach($idxs as $name => $iarr) { - $idxoptions = array(); - $fldnames = array(); - - if(!empty($iarr['unique'])) { - $idxoptions['UNIQUE'] = 1; - } - - foreach($iarr['columns'] as $fld) { - $fldnames[] = $this->RunFieldFilter($fld,'TABLE'); - } - - $idxname = $prefixidx.str_replace($table,$desttable,$name); - - if (!empty($this->indexFilter)) { - $fn = $this->indexFilter; - $idxname = $fn($desttable,$idxname,$fldnames,$idxoptions); - } - $sqla2 = $this->ddDest->_IndexSQL($idxname, $desttable, $fldnames,$idxoptions); - $sqla = array_merge($sqla,$sqla2); - } - - return $sqla; - } - - function _clearcache() - { - - } - - function _concat($v) - { - return $this->connDest->concat("' ","chr(".ord($v).")","'"); - } - - function fixupbinary($v) - { - return str_replace( - array("\r","\n"), - array($this->_concat("\r"),$this->_concat("\n")), - $v ); - } - - function SwapDBs() - { - $o = $this->connSrc; - $this->connSrc = $this->connDest; - $this->connDest = $o; - - - $o = $this->connSrc2; - $this->connSrc2 = $this->connDest2; - $this->connDest2 = $o; - - $o = $this->ddSrc; - $this->ddSrc = $this->ddDest; - $this->ddDest = $o; - } - - /* - // if no uniqflds defined, then all desttable recs will be deleted before insert - // $where clause must include the WHERE word if used - // if $this->commitRecs is set to a +ve value, then it will autocommit every $this->commitRecs records - // -- this should never be done with 7x24 db's - - Returns an array: - $arr[0] = true if no error, false if error - $arr[1] = number of recs processed - $arr[2] = number of successful inserts - $arr[3] = number of successful updates - - ReplicateData() params: - - $table = src table name - $desttable = dest table name, leave blank to use src table name - $uniqflds = array() = an array. If set, then inserts and updates will occur. eg. array('PK1', 'PK2'); - To prevent updates to desttable (allow only to src table), add '*INSERTONLY*' or '*ONLYINSERT*' to array. - - Sometimes you are replicating a src table with an autoinc primary key. - You sometimes create recs in the dest table. The dest table has to retrieve the - src table's autoinc key (stored in a 2nd field) so you can match the two tables. - - To define this, and the uniqflds contains nested arrays. Copying from autoinc table to other table: - array(array($destpkey), array($destfld_holds_src_autoinc_pkey)) - - Copying from normal table to autoinc table: - array(array($destpkey), array(), array($srcfld_holds_dest_autoinc_pkey)) - - $where = where clause for SELECT from $table $where. Include the WHERE reserved word in beginning. - You can put ORDER BY at the end also - $ignoreflds = array(), list of fields to ignore. e.g. array('FLD1',FLD2'); - $dstCopyDateFld = date field on $desttable to update with current date - $extraflds allows you to add additional flds to insert/update. Format - array(fldname => $fldval) - $fldval itself can be an array or a string. If an array, then - $extraflds = array($fldname => array($insertval, $updateval)) - - Thus we have the following behaviours: - - a. Delete all data in $desttable then insert from src $table - - $rep->execute = true; - $rep->ReplicateData($table, $desttable) - - b. Update $desttable if record exists (based on $uniqflds), otherwise insert. - - $rep->execute = true; - $rep->ReplicateData($table, $desttable, $array($pkey1, $pkey2)) - - c. Select from src $table all data modified since a date. Then update $desttable - if record exists (based on $uniqflds), otherwise insert - - $rep->execute = true; - $rep->ReplicateData($table, $desttable, array($pkey1, $pkey2), "WHERE update_datetime_fld > $LAST_REFRESH") - - d. Insert all records into $desttable modified after a certain id (or time) in src $table: - - $rep->execute = true; - $rep->ReplicateData($table, $desttable, false, "WHERE id_fld > $LAST_ID_SAVED", true); - - - For (a) to (d), returns array: array($boolean_ok_fail, $no_recs_selected_from_src_db, $no_recs_inserted, $no_recs_updated); - - e. Generate sample SQL: - - $rep->execute = false; - $rep->ReplicateData(....); - - This returns $array, which contains: - - $array['SEL'] = select stmt from src db - $array['UPD'] = update stmt to dest db - $array['INS'] = insert stmt to dest db - - - Error-handling - ============== - Default is never abort if error occurs. You can set $rep->neverAbort = false; to force replication to abort if an error occurs. - - - Value Filtering - ======== - Sometimes you might need to modify/massage the data before the code works. Assume that the value used for True and False is - 'T' and 'F' in src DB, but is 'Y' and 'N' in dest DB for field[2] in select stmt. You can do this by - - $rep->filterSelect = 'filter'; - $rep->ReplicateData(...); - - function filter($table,& $fields, $deleteFirst) - { - if ($table == 'SOMETABLE') { - if ($fields[2] == 'T') $fields[2] = 'Y'; - else if ($fields[2] == 'F') $fields[2] = 'N'; - } - } - - We pass in $deleteFirst as that determines the order of the fields (which are numeric-based): - TRUE: the order of fields matches the src table order - FALSE: the order of fields is all non-primary key fields first, followed by primary key fields. This is because it needs - to match the UPDATE statement, which is UPDATE $table SET f2 = ?, f3 = ? ... WHERE f1 = ? - - Name Filtering - ========= - Sometimes field names that are legal in one RDBMS can be illegal in another. - We allow you to handle this using a field filter. - Also if you don't want to replicate certain fields, just return false. - - $rep->fieldFilter = 'ffilter'; - - function ffilter(&$fld,$mode) - { - $uf = strtoupper($fld); - switch($uf) { - case 'GROUP': - if ($mode == 'SELECT') $fld = '"Group"'; - return 'GroupFld'; - - case 'PRIVATEFLD': # do not replicate - return false; - } - return $fld; - } - - - UPDATE FILTERING - ================ - Sometimes, when we want to update - UPDATE table SET fld = val WHERE .... - - we want to modify val. To do so, define - - $rep->updateFilter = 'ufilter'; - - function ufilter($table, $fld, $val) - { - return "nvl($fld, $val)"; - } - - - Sending back audit info back to src Table - ========================================= - - Use $rep->updateSrcFn. This can be an array of strings, or the name of a php function to call. - - If an array of strings is defined, then it will perform an update statement... - - UPDATE srctable SET $string WHERE .... - - With $string set to the array you define. If a new record was inserted into desttable, then the - 'INS' string is used ($INSERT_ID will be replaced with the real INSERT_ID, if any), - and if an update then use the 'UPD' string. - - array( - 'INS' => 'insertid = $INSERT_ID, copieddate=getdate(), copied = 1', - 'UPD' => 'copieddate=getdate(), copied = 1' - ) - - If a single string array is defined, then it will be used for both insert and update. - array('copieddate=getdate(), copied = 1') - - Note that the where clause is automatically defined by the system. - - If $rep->updateSrcFn is a PHP function name, then it will be called with the following params: - - $fn($srcConnection, $tableName, $row, $where, $bindarr, $mode, $dest_insertid) - - $srcConnection - source db connection - $tableName - source tablename - $row - array holding records updated into dest - $where - where clause to be used (uses bind vars) - $bindarr - array holding bind variables for where clause - $mode - INS or UPD - $dest_insertid - when mode=INS, then the insert_id is stored here. - - - oracle mssql - ---> insert - mssqlid <--- insert_id - ----> update with mssqlid - <---- update with mssqlid - - - TODO: add src pkey and dest pkey for updates. Also sql stmt needs to be tuned, so dest pkey, src pkey - */ - - - function ReplicateData($table, $desttable = '', $uniqflds = array(), $where = '',$ignore_flds = array(), - $dstCopyDateFld='', $extraflds = array(), $lastUpdateFld = '') - { - if (is_array($where)) { - $wheresrc = $where[0]; - $wheredest = $where[1]; - } else { - $wheresrc = $wheredest = $where; - } - $dstCopyDateName = $dstCopyDateFld; - $dstCopyDateFld = strtoupper($dstCopyDateFld); - - $this->_clearcache(); - if (is_string($uniqflds) && strlen($uniqflds)) $uniqflds = array($uniqflds); - if (!$desttable) $desttable = $table; - - $uniq = array(); - if ($uniqflds) { - if (is_array(reset($uniqflds))) { - /* - primary key of src and dest tables differ. This means when we perform the select stmts - we retrieve both keys. Then any insert statement will have to ignore one array element. - Any update statement will need to use a different where clause - */ - $destuniqflds = $uniqflds[0]; - if (sizeof($uniqflds)>1 && $uniqflds[1]) // srckey field name in dest table - $srcuniqflds = $uniqflds[1]; - else - $srcuniqflds = array(); - - if (sizeof($uniqflds)>2) - $srcPKDest = reset($uniqflds[2]); - - } else { - $destuniqflds = $uniqflds; - $srcuniqflds = array(); - } - $onlyInsert = false; - foreach($destuniqflds as $k => $u) { - if ($u == '*INSERTONLY*' || $u == '*ONLYINSERT*') { - $onlyInsert = true; - continue; - } - $uniq[strtoupper($u)] = $k; - } - $deleteFirst = $this->deleteFirst; - } else { - $deleteFirst = true; - } - - if ($deleteFirst) $onlyInsert = true; - - if ($ignore_flds) { - foreach($ignore_flds as $u) { - $ignoreflds[strtoupper($u)] = 1; - } - } else - $ignoreflds = array(); - - $src = $this->connSrc; - $dest = $this->connDest; - $src2 = $this->connSrc2; - - $dest->noNullStrings = false; - $src->noNullStrings = false; - $src2->noNullStrings = false; - - if ($src === $dest) $this->execute = false; - - $types = $src->MetaColumns($table); - if (!$types) { - echo "Source $table does not exist<br>\n"; - return array(); - } - $dtypes = $dest->MetaColumns($desttable); - if (!$dtypes) { - echo "Destination $desttable does not exist<br>\n"; - return array(); - } - $sa = array(); - $selflds = array(); - $wheref = array(); - $wheres = array(); - $srcwheref = array(); - $fldoffsets = array(); - $k = 0; - foreach($types as $name => $t) { - $name2 = strtoupper($this->RunFieldFilter($name,'SELECT')); - // handle quotes - if ($name2 && $name2[0] == '"' && $name2[strlen($name2)-1] == '"') $name22 = substr($name2,1,strlen($name2)-2); - elseif ($name2 && $name2[0] == '`' && $name2[strlen($name2)-1] == '`') $name22 = substr($name2,1,strlen($name2)-2); - else $name22 = $name2; - - //else $name22 = $name2; // this causes problem for quotes strip above - - if (!isset($dtypes[($name22)]) || !$name2) { - if ($this->debug) echo " Skipping $name ==> $name2 as not in destination $desttable<br>"; - continue; - } - - if ($name2 == $dstCopyDateFld) { - $dstCopyDateName = $t->name; - continue; - } - - $fld = $t->name; - $fldval = $t->name; - $mt = $src->MetaType($t->type); - if ($this->datesAreTimeStamps && $mt == 'D') $mt = 'T'; - if ($mt == 'D') $fldval = $dest->DBDate($fldval); - elseif ($mt == 'T') $fldval = $dest->DBTimeStamp($fldval); - $ufld = strtoupper($fld); - - if (isset($ignoreflds[($name2)]) && !isset($uniq[$ufld])) { - continue; - } - - if ($this->debug) echo " field=$fld type=$mt fldval=$fldval<br>"; - - if (!isset($uniq[$ufld])) { - - $selfld = $fld; - $fld = $this->RunFieldFilter($selfld,'SELECT'); - $selflds[] = $selfld; - - $p = $dest->Param($k); - - if ($mt == 'D') $p = $dest->DBDate($p, true); - else if ($mt == 'T') $p = $dest->DBTimeStamp($p, true); - - # UPDATES - $sets[] = "$fld = ".$this->RunUpdateFilter($desttable, $fld, $p); - - # INSERTS - $insflds[] = $this->RunInsertFilter($desttable,$fld, $p); $params[] = $p; - $k++; - } else { - $fld = $this->RunFieldFilter($fld); - $wheref[] = $fld; - if (!empty($srcuniqflds)) $srcwheref[] = $srcuniqflds[$uniq[$ufld]]; - if ($mt == 'C') { # normally we don't include the primary key in the insert if it is numeric, but ok if varchar - $insertpkey = true; - } - } - } - - - foreach($extraflds as $fld => $evals) { - if (!is_array($evals)) $evals = array($evals, $evals); - $insflds[] = $this->RunInsertFilter($desttable,$fld, $p); $params[] = $evals[0]; - $sets[] = "$fld = ".$evals[1]; - } - - if ($dstCopyDateFld) { - $sets[] = "$dstCopyDateName = ".$dest->sysTimeStamp; - $insflds[] = $this->RunInsertFilter($desttable,$dstCopyDateName, $p); $params[] = $dest->sysTimeStamp; - } - - - if (!empty($srcPKDest)) { - $selflds[] = $srcPKDest; - $fldoffsets = array($k+1); - } - - foreach($wheref as $uu => $fld) { - - $p = $dest->Param($k); - $sp = $src->Param($k); - if (!empty($srcuniqflds)) { - if ($uu > 1) die("Only one primary key for srcuniqflds allowed currently"); - $destsrckey = reset($srcuniqflds); - $wheres[] = reset($srcuniqflds).' = '.$p; - - $insflds[] = $this->RunInsertFilter($desttable,$destsrckey, $p); - $params[] = $p; - } else { - $wheres[] = $fld.' = '.$p; - if (!isset($ignoreflds[strtoupper($fld)]) || !empty($insertpkey)) { - $insflds[] = $this->RunInsertFilter($desttable,$fld, $p); - $params[] = $p; - } - } - - $selflds[] = $fld; - $srcwheres[] = $fld.' = '.$sp; - $fldoffsets[] = $k; - - $k++; - } - - if (!empty($srcPKDest)) { - $fldoffsets = array($k); - $srcwheres = array($fld.'='.$src->Param($k)); - $k++; - } - - if ($lastUpdateFld) { - $selflds[] = $lastUpdateFld; - } else - $selflds[] = 'null as Z55_DUMMY_LA5TUPD'; - - $insfldss = implode(', ', $insflds); - $fldss = implode(', ', $selflds); - $setss = implode(', ', $sets); - $paramss = implode(', ', $params); - $wheress = implode(' AND ', $wheres); - if (isset($srcwheres)) - $srcwheress = implode(' AND ',$srcwheres); - - - $seltable = $table; - if ($this->readUncommitted && strpos($src->databaseType,'mssql')) $seltable .= ' with (NOLOCK)'; - - $sa['SEL'] = "SELECT $fldss FROM $seltable $wheresrc"; - $sa['INS'] = "INSERT INTO $desttable ($insfldss) VALUES ($paramss) /**INS**/"; - $sa['UPD'] = "UPDATE $desttable SET $setss WHERE $wheress /**UPD**/"; - - - - $DB1 = "/* <font color=green> Source DB - sample sql in case you need to adapt code\n\n"; - $DB2 = "/* <font color=green> Dest DB - sample sql in case you need to adapt code\n\n"; - - if (!$this->execute) echo '/*<style> -pre { -white-space: pre-wrap; /* css-3 */ -white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ -white-space: -pre-wrap; /* Opera 4-6 */ -white-space: -o-pre-wrap; /* Opera 7 */ -word-wrap: break-word; /* Internet Explorer 5.5+ */ -} -</style><pre>*/ -'; - if ($deleteFirst && $this->deleteFirst) { - $where = preg_replace('/[ \n\r\t]+order[ \n\r\t]+by.*$/i', '', $where); - $sql = "DELETE FROM $desttable $wheredest\n"; - if (!$this->execute) echo $DB2,'</font>*/',$sql,"\n"; - else $dest->Execute($sql); - } - - global $ADODB_COUNTRECS; - $err = false; - $savemode = $src->setFetchMode(ADODB_FETCH_NUM); - $ADODB_COUNTRECS = false; - - if (!$this->execute) { - echo $DB1,$sa['SEL'],"</font>\n*/\n\n"; - echo $DB2,$sa['INS'],"</font>\n*/\n\n"; - $suffix = ($onlyInsert) ? ' PRIMKEY=?' : ''; - echo $DB2,$sa['UPD'],"$suffix</font>\n*/\n\n"; - - $rs = $src->Execute($sa['SEL']); - $cnt = 1; - $upd = 0; - $ins = 0; - - $sqlarr = explode('?',$sa['INS']); - $nparams = sizeof($sqlarr)-1; - - $useQmark = $dest && ($dest->dataProvider != 'oci8'); - - while ($rs && !$rs->EOF) { - if ($useQmark) { - $sql = ''; $i = 0; - $arr = array_reverse($rs->fields); - foreach ($arr as $v) { - $sql .= $sqlarr[$i]; - // from Ron Baldwin <ron.baldwin#sourceprose.com> - // Only quote string types - $typ = gettype($v); - if ($typ == 'string') - //New memory copy of input created here -mikefedyk - $sql .= $dest->qstr($v); - else if ($typ == 'double') - $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1 - else if ($typ == 'boolean') - $sql .= $v ? $dest->true : $dest->false; - else if ($typ == 'object') { - if (method_exists($v, '__toString')) $sql .= $dest->qstr($v->__toString()); - else $sql .= $dest->qstr((string) $v); - } else if ($v === null) - $sql .= 'NULL'; - else - $sql .= $v; - $i += 1; - - if ($i == $nparams) break; - } // while - - if (isset($sqlarr[$i])) { - $sql .= $sqlarr[$i]; - } - $INS = $sql; - } else { - $INS = $sa['INS']; - $arr = array_reverse($rs->fields); - foreach($arr as $k => $v) { // only works on oracle currently - $k = sizeof($arr)-$k-1; - $v = str_replace(":","%~%COLON%!%",$v); - $INS = str_replace(':'.$k,$this->fixupbinary($dest->qstr($v)),$INS); - } - $INS = str_replace("%~%COLON%!%",":",$INS); - if ($this->htmlSpecialChars) $INS = htmlspecialchars($INS); - } - echo "-- $cnt\n",$INS,";\n\n"; - $cnt += 1; - $ins += 1; - $rs->MoveNext(); - } - $src->setFetchMode($savemode); - return $sa; - } else { - $saved = $src->debug; - #$src->debug=1; - if ($this->limitRecs>100) - $rs = $src->SelectLimit($sa['SEL'],$this->limitRecs); - else - $rs = $src->Execute($sa['SEL']); - $src->debug = $saved; - if (!$rs) { - if ($this->errHandler) $this->_doerr('SEL',array()); - return array(0,0,0,0); - } - - - if ($this->commitReplicate || $commitRecs > 0) { - $dest->BeginTrans(); - if ($this->updateSrcFn) $src2->BeginTrans(); - } - - if ($this->updateSrcFn && strpos($src2->databaseType,'mssql') !== false) { - # problem is writers interfere with readers in mssql - $rs = $src->_rs2rs($rs); - } - $cnt = 0; - $upd = 0; - $ins = 0; - - $sizeofrow = sizeof($selflds); - - $fn = $this->selFilter; - $commitRecs = $this->commitRecs; - - $saved = $dest->debug; - - if ($this->deleteFirst) $onlyInsert = true; - while ($origrow = $rs->FetchRow()) { - - if ($dest->debug) {flush(); @ob_flush();} - - if ($fn) { - if (!$fn($desttable, $origrow, $deleteFirst, $this, $selflds)) continue; - } - $doinsert = true; - $row = array_slice($origrow,0,$sizeofrow-1); - - if (!$onlyInsert) { - $doinsert = false; - $upderr = false; - - if (isset($srcPKDest)) { - if (is_null($origrow[$sizeofrow-3])) { - $doinsert = true; - $upderr = true; - } - } - if (!$upderr && !$dest->Execute($sa['UPD'],$row)) { - $err = true; - $upderr = true; - if ($this->errHandler) $this->_doerr('UPD',$row); - if (!$this->neverAbort) break; - } - - if ($upderr || $dest->Affected_Rows() == 0) { - $doinsert = true; - } else { - if (!empty($uniqflds)) $this->RunUpdateSrcFn($src2, $table, $fldoffsets, $origrow, $srcwheress, 'UPD', null, $lastUpdateFld); - $upd += 1; - } - } - - if ($doinsert) { - $inserr = false; - if (isset($srcPKDest)) { - $row = array_slice($origrow,0,$sizeofrow-2); - } - - if (! $dest->Execute($sa['INS'],$row)) { - $err = true; - $inserr = true; - if ($this->errHandler) $this->_doerr('INS',$row); - if ($this->neverAbort) continue; - else break; - } else { - if ($dest->dataProvider == 'oci8') { - if ($this->oracleSequence) $lastid = $dest->GetOne("select ".$this->oracleSequence.".currVal from dual"); - else $lastid = 'null'; - } else { - $lastid = $dest->Insert_ID(); - } - - if (!$inserr && !empty($uniqflds)) { - $this->RunUpdateSrcFn($src2, $table, $fldoffsets, $origrow, $srcwheress, 'INS', $lastid,$lastUpdateFld); - } - $ins += 1; - } - } - $cnt += 1; - - if ($commitRecs > 0 && ($cnt % $commitRecs) == 0) { - $dest->CommitTrans(); - $dest->BeginTrans(); - - if ($this->updateSrcFn) { - $src2->CommitTrans(); - $src2->BeginTrans(); - } - } - - } // while - - - if ($this->commitReplicate || $commitRecs > 0) { - if (!$this->neverAbort && $err) { - $dest->RollbackTrans(); - if ($this->updateSrcFn) $src2->RollbackTrans(); - } else { - $dest->CommitTrans(); - if ($this->updateSrcFn) $src2->CommitTrans(); - } - } - } - if ($cnt != $ins + $upd) echo "<p>ERROR: $cnt != INS $ins + UPD $upd</p>"; - $src->setFetchMode($savemode); - return array(!$err, $cnt, $ins, $upd); - } - // trigger support only for sql server and oracle - // need to add - function MergeSrcSetup($srcTable, $pkeys, $srcUpdateDateFld, $srcCopyDateFld, $srcCopyFlagFld, - $srcCopyFlagType='C(1)', $srcCopyFlagVals = array('Y','N','P','=')) - { - $sqla = array(); - $src = $this->connSrc; - $idx = $srcTable.'_mrgIdx'; - $cols = $src->MetaColumns($srcTable); - #adodb_pr($cols); - if (!isset($cols[strtoupper($srcUpdateDateFld)])) { - $sqla = $this->ddSrc->AddColumnSQL($srcTable, "$srcUpdateDateFld TS DEFTIMESTAMP"); - foreach($sqla as $sql) $src->Execute($sql); - } - - if ($srcCopyDateFld && !isset($cols[strtoupper($srcCopyDateFld)])) { - $sqla = $this->ddSrc->AddColumnSQL($srcTable, "$srcCopyDateFld TS DEFTIMESTAMP"); - foreach($sqla as $sql) $src->Execute($sql); - } - - $sysdate = $src->sysTimeStamp; - $arrv0 = $src->qstr($srcCopyFlagVals[0]); - $arrv1 = $src->qstr($srcCopyFlagVals[1]); - $arrv2 = $src->qstr($srcCopyFlagVals[2]); - $arrv3 = $src->qstr($srcCopyFlagVals[3]); - - if ($srcCopyFlagFld && !isset($cols[strtoupper($srcCopyFlagFld)])) { - $sqla = $this->ddSrc->AddColumnSQL($srcTable, "$srcCopyFlagFld $srcCopyFlagType DEFAULT $arrv1"); - foreach($sqla as $sql) $src->Execute($sql); - } - - $sqla = array(); - - - $name = "{$srcTable}_mrgTr"; - if (is_array($pkeys) && strpos($src->databaseType,'mssql') !== false) { - $pk = reset($pkeys); - - #$sqla[] = "DROP TRIGGER $name"; - $sqltr = " - TRIGGER $name - ON $srcTable /* for data replication and merge */ - AFTER UPDATE - AS - UPDATE $srcTable - SET - $srcUpdateDateFld = case when I.$srcCopyFlagFld = $arrv2 or I.$srcCopyFlagFld = $arrv3 then I.$srcUpdateDateFld - else $sysdate end, - $srcCopyFlagFld = case - when I.$srcCopyFlagFld = $arrv2 then $arrv0 - when I.$srcCopyFlagFld = $arrv3 then D.$srcCopyFlagFld - else $arrv1 end - FROM $srcTable S Join Inserted AS I on I.$pk = S.$pk - JOIN Deleted as D ON I.$pk = D.$pk - WHERE I.$srcCopyFlagFld = D.$srcCopyFlagFld or I.$srcCopyFlagFld = $arrv2 - or I.$srcCopyFlagFld = $arrv3 or I.$srcCopyFlagFld is null - "; - $sqla[] = 'CREATE '.$sqltr; // first if does not exists - $sqla[] = 'ALTER '.$sqltr; // second if it already exists - } else if (strpos($src->databaseType,'oci') !== false) { - - if (strlen($srcTable)>22) $tableidx = substr($srcTable,0,16).substr(crc32($srcTable),6); - else $tableidx = $srcTable; - - $name = $tableidx.$this->trgSuffix; - $idx = $tableidx.$this->idxSuffix; - $sqla[] = " -CREATE OR REPLACE TRIGGER $name /* for data replication and merge */ -BEFORE UPDATE ON $srcTable REFERENCING NEW AS NEW OLD AS OLD -FOR EACH ROW -BEGIN - if :new.$srcCopyFlagFld = $arrv2 then - :new.$srcCopyFlagFld := $arrv0; - elsif :new.$srcCopyFlagFld = $arrv3 then - :new.$srcCopyFlagFld := :old.$srcCopyFlagFld; - elsif :old.$srcCopyFlagFld = :new.$srcCopyFlagFld or :new.$srcCopyFlagFld is null then - if $this->trLogic then - :new.$srcUpdateDateFld := $sysdate; - :new.$srcCopyFlagFld := $arrv1; - end if; - end if; -END; -"; - } - foreach($sqla as $sql) $src->Execute($sql); - - if ($srcCopyFlagFld) $srcCopyFlagFld .= ', '; - $src->Execute("CREATE INDEX {$idx} on $srcTable ($srcCopyFlagFld$srcUpdateDateFld)"); - } - - - /* - Perform Merge by copying all data modified from src to dest - then update src copied flag if present. - - Returns array taken from ReplicateData: - - Returns an array: - $arr[0] = true if no error, false if error - $arr[1] = number of recs processed - $arr[2] = number of successful inserts - $arr[3] = number of successful updates - - $srcTable = src table - $dstTable = dest table - $pkeys = primary keys array. if empty, then only inserts will occur - $srcignoreflds = ignore these flds (must be upper cased) - $setsrc = updateSrcFn string - $srcUpdateDateFld = field in src with the last update date - $srcCopyFlagFld = false = optional field that holds the copied indicator - $flagvals=array('Y','N','P','=') = array of values indicating array(copied, not copied). - Null is assumed to mean not copied. The 3rd value 'P' indicates that we want to force 'Y', bypassing - default trigger behaviour to reset the COPIED='N' when the record is replicated from other side. - The last value '=' is don't change copyflag. - $srcCopyDateFld = field that holds last copy date in src table, which will be updated on Merge() - $dstCopyDateFld = field that holds last copy date in dst table, which will be updated on Merge() - $defaultDestRaiseErrorFn = The adodb raiseErrorFn handler. Default is to not raise an error. - Just output error message to stdout - - */ - - - function Merge($srcTable, $dstTable, $pkeys, $srcignoreflds, $setsrc, - $srcUpdateDateFld, - $srcCopyFlagFld, $flagvals=array('Y','N','P','='), - $srcCopyDateFld = false, - $dstCopyDateFld = false, - $whereClauses = '', - $orderBy = '', # MUST INCLUDE THE "ORDER BY" suffix - $copyDoneFlagIdx = 3, - $defaultDestRaiseErrorFn = '') - { - $src = $this->connSrc; - $dest = $this->connDest; - - $time = $src->Time(); - - $delfirst = $this->deleteFirst; - $upd = $this->updateSrcFn; - - $this->deleteFirst = false; - //$this->updateFirst = true; - - $srcignoreflds[] = $srcUpdateDateFld; - $srcignoreflds[] = $srcCopyFlagFld; - $srcignoreflds[] = $srcCopyDateFld; - - if (empty($whereClauses)) $whereClauses = '1=1'; - $where = " WHERE ($whereClauses) and ($srcCopyFlagFld = ".$src->qstr($flagvals[1]).')'; - if ($orderBy) $where .= ' '.$orderBy; - else $where .= ' ORDER BY '.$srcUpdateDateFld; - - if ($setsrc) $set[] = $setsrc; - else $set = array(); - - if ($srcCopyFlagFld) $set[] = "$srcCopyFlagFld = ".$src->qstr($flagvals[2]); - if ($srcCopyDateFld) $set[]= "$srcCopyDateFld = ".$src->sysTimeStamp; - if ($set) $this->updateSrcFn = array(implode(', ',$set)); - else $this->updateSrcFn = ''; - - - $extra[$srcCopyFlagFld] = array($dest->qstr($flagvals[0]),$dest->qstr($flagvals[$copyDoneFlagIdx])); - - $saveraise = $dest->raiseErrorFn; - $dest->raiseErrorFn = ''; - - if ($this->compat && $this->compat == 1.0) $srcUpdateDateFld = ''; - $arr = $this->ReplicateData($srcTable, $dstTable, $pkeys, $where, $srcignoreflds, - $dstCopyDateFld,$extra,$srcUpdateDateFld); - - $dest->raiseErrorFn = $saveraise; - - $this->updateSrcFn = $upd; - $this->deleteFirst = $delfirst; - - return $arr; - } - /* - If doing a 2 way merge, then call - $rep->Merge() - to save without modifying the COPIEDFLAG ('='). - - Then can the following to set the COPIEDFLAG to 'P' which forces the COPIEDFLAG = 'Y' - $rep->MergeDone() - */ - - function MergeDone($srcTable, $dstTable, $pkeys, $srcignoreflds, $setsrc, - $srcUpdateDateFld, - $srcCopyFlagFld, $flagvals=array('Y','N','P','='), - $srcCopyDateFld = false, - $dstCopyDateFld = false, - $whereClauses = '', - $orderBy = '', # MUST INCLUDE THE "ORDER BY" suffix - $copyDoneFlagIdx = 2, - $defaultDestRaiseErrorFn = '') - { - return $this->Merge($srcTable, $dstTable, $pkeys, $srcignoreflds, $setsrc, - $srcUpdateDateFld, - $srcCopyFlagFld, $flagvals, - $srcCopyDateFld, - $dstCopyDateFld, - $whereClauses, - $orderBy, # MUST INCLUDE THE "ORDER BY" suffix - $copyDoneFlagIdx, - $defaultDestRaiseErrorFn); - } - - function _doerr($reason, $selflds) - { - $fn = $this->errHandler; - if ($fn) $fn($this, $reason, $selflds); // set $this->neverAbort to true or false as required inside $fn - } -} diff --git a/replicate/replicate-steps.php b/replicate/replicate-steps.php deleted file mode 100644 index fca719d2..00000000 --- a/replicate/replicate-steps.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php -/** - * Replication engine - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -# CONFIG - -if (empty($USER)) { - $BA = "LOAN"; ## -- leave $BA as empty string to copy all BA. Otherwise enter 1 BA (no need to quote BA) - $STAGES = ""; ## $STAGES = "STGCAT1,STGCAT2" -- leave $STAGES as empty string to run all stages. No need to quote stgcats. - - $HOST='192.168.0.2'; - $USER='JCOLLECT_BKRM'; - $PWD='natsoft'; - $DBASE='RAPTOR'; -} -# =================================== INCLUDES - -include_once('../adodb.inc.php'); -include_once('adodb-replicate.inc.php'); - -# ==================================== CONNECTION -$DB = ADONewConnection('oci8'); -$ok = $DB->Connect($HOST,$USER,$PWD,$DBASE); -if (!$ok) return; - - -#$DB->debug=1; - -$bkup = 'tmp'.date('ymd_His'); - - -if ($BA) { - $QTY_BA = " and qu_bacode='$BA'"; - if (1) $STP_BA = " and s_stagecat in (select stg_stagecat from kbstage where stg_bacode='$BA')"; # OLDER KBSTEP - else $STP_BA = " and s_bacode='$BA'"; # LATEST KBSTEP format - $STG_BA = " and stg_bacode='$BA'"; -} else { - $QTY_BA = ""; - $STP_BA = ""; - $STG_BA = ""; -} - -if ($STAGES) { - - $STAGES = explode(',',$STAGES); - $STAGES = "'".implode("','",$STAGES)."'"; - $QTY_STG = " and qu_stagecat in ($STAGES)"; - $STP_STG = " and s_stagecat in ($STAGES)"; - $STG_STG = " and stg_stagecat in ($STAGES)"; -} else { - $QTY_STG = ""; - $STP_STG = ""; - $STG_STG = ""; -} - -echo "<pre> - -/****************************************************************************** -<font color=green> - Migrate stages, steps and qtypes for the following - - business area: $BA - and stages: $STAGES - - - WARNING: DO NOT 'Ignore All Errors'. - If any error occurs, make sure you stop and check the reason and fix it. - Otherwise you could corrupt everything!!! - - Connected to $USER@$DBASE $HOST; -</font> -*******************************************************************************/ - --- BACKUP -create table kbstage_$bkup as select * from kbstage; -create table kbstep_$bkup as select * from kbstep; -create table kbqtype_$bkup as select * from kbqtype; - - --- IF CODE FAILS, REMEMBER TO RENABLE ALL TRIGGERS and following CONSTRAINT -ALTER TABLE kbstage DISABLE all triggers; -ALTER TABLE kbstep DISABLE all triggers; -ALTER TABLE kbqtype DISABLE all triggers; -ALTER TABLE jqueue DISABLE CONSTRAINT QUEUE_MUST_HAVE_TYPE; - - --- NOW DELETE OLD STEPS/STAGES/QUEUES -delete from kbqtype where qu_mode in ('STAGE','STEP') $QTY_BA $QTY_STG; -delete from kbstep where (1=1) $STP_BA$STP_STG; -delete from kbstage where (1=1)$STG_BA$STG_STG; - - - -SET DEFINE OFF; -- disable variable handling by sqlplus -/ -/* Assume kbstrategy and business areas are compatible for steps and stages to be copied */ -</pre> - -"; - - -$rep = new ADODB_Replicate($DB,$DB); -$rep->execute = false; -$rep->deleteFirst = false; - - // src table name, dst table name, primary key, where condition -$rep->ReplicateData('KBSTAGE', 'KBSTAGE', array(), " where (1=1)$STG_BA$STG_STG"); -$rep->ReplicateData('KBSTEP', 'KBSTEP', array(), " where (1=1)$STP_BA$STP_STG"); -$rep->ReplicateData('KBQTYPE','KBQTYPE',array()," where qu_mode in ('STAGE','STEP')$QTY_BA$QTY_STG"); - -echo " - --- Check for QUEUES not in KBQTYPE and FIX by copying from kbqtype_$bkup -begin -for rec in (select distinct q_type from jqueue where q_type not in (select qu_code from kbqtype)) loop - insert into kbqtype select * from kbqtype_$bkup where qu_code = rec.q_type; - update kbqtype set qu_name=substr('MISSING.'||qu_name,1,64) where qu_code=rec.q_type; -end loop; -end; -/ - -commit; - - -ALTER TABLE kbstage ENABLE all triggers; -ALTER TABLE kbstep ENABLE all triggers; -ALTER TABLE kbqtype ENABLE all triggers; -ALTER TABLE jqueue ENABLE CONSTRAINT QUEUE_MUST_HAVE_TYPE; - -/* --- REMEMBER TO COMMIT - commit; - begin Juris.UpdateQCounts; end; - --- To check for bad queues after conversion, run this - select * from kbqtype where qu_name like 'MISSING%' -*/ -/ -"; diff --git a/replicate/test-tnb.php b/replicate/test-tnb.php deleted file mode 100644 index 6be11072..00000000 --- a/replicate/test-tnb.php +++ /dev/null @@ -1,440 +0,0 @@ -<?php -/** - * Replication engine - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ -include_once('../adodb.inc.php'); -include_once('adodb-replicate.inc.php'); - -set_time_limit(0); - -function IndexFilter($dtable, $idxname,$flds,$options) -{ - if (strlen($idxname) > 28) $idxname = substr($idxname,0,24).rand(1000,9999); - return $idxname; -} - -function SelFilter($table, &$arr, $delfirst) -{ - return true; -} - -function updatefilter($table, $fld, $val) -{ - return "nvl($fld, $val)"; -} - - -function FieldFilter(&$fld,$mode) -{ - $uf = strtoupper($fld); - switch($uf) { - case 'SIZEFLD': - return 'Size'; - - case 'GROUPFLD': - return 'Group'; - - case 'GROUP': - if ($mode == 'SELECT') $fld = '"Group"'; - return 'GroupFld'; - case 'SIZE': - if ($mode == 'SELECT') $fld = '"Size"'; - return 'SizeFld'; - } - return $fld; -} - -function ParseTable(&$table, &$pkey) -{ - $table = trim($table); - if (strlen($table) == 0) return false; - if (strpos($table, '#') !== false) { - $at = strpos($table, '#'); - $table = trim(substr($table,0,$at)); - if (strlen($table) == 0) return false; - } - - $tabarr = explode(',',$table); - if (sizeof($tabarr) == 1) { - $table = $tabarr[0]; - $pkey = ''; - echo "No primary key for $table **** **** <br>"; - } else { - $table = trim($tabarr[0]); - $pkey = trim($tabarr[1]); - if (strpos($pkey,' ') !== false) echo "Bad PKEY for $table $pkey<br>"; - } - - return true; -} - -global $TARR; - -function TableStats($rep, $table, $pkey) -{ -global $TARR; - - if (empty($TARR)) $TARR = array(); - $cnt = $rep->connSrc->GetOne("select count(*) from $table"); - if (isset($TARR[$table])) echo "<h1>Table $table repeated twice</h1>"; - $TARR[$table] = $cnt; - - if ($pkey) { - $ok = $rep->connSrc->SelectLimit("select $pkey from $table",1); - if (!$ok) echo "<h1>$table: $pkey does not exist</h1>"; - } else - echo "<h1>$table: no primary key</h1>"; -} - -function CreateTable($rep, $table) -{ -## CREATE TABLE - #$DB2->Execute("drop table $table"); - - $rep->execute = true; - $ok = $rep->CopyTableStruct($table); - if ($ok) echo "Table Created<br>\n"; - else { - echo "<hr>Error: Cannot Create Table<hr>\n"; - } - flush();@ob_flush(); -} - -function CopyData($rep, $table, $pkey) -{ - $dtable = $table; - - $rep->execute = true; - $rep->deleteFirst = true; - - $secs = time(); - $rows = $rep->ReplicateData($table,$dtable,array($pkey)); - $secs = time() - $secs; - if (!$rows || !$rows[0] || !$rows[1] || $rows[1] != $rows[2]+$rows[3]) { - echo "<hr>Error: "; var_dump($rows); echo " (secs=$secs) <hr>\n"; - } else - echo date('H:i:s'),': ',$rows[1]," record(s) copied, ",$rows[2]," inserted, ",$rows[3]," updated (secs=$secs)<br>\n"; - flush();@ob_flush(); -} - -function MergeDataJohnTest($rep, $table, $pkey) -{ - $rep->SwapDBs(); - - $dtable = $table; - $rep->oracleSequence = 'LGBSEQUENCE'; - -# $rep->MergeSrcSetup($table, array($pkey),'UpdatedOn','CopiedFlag'); - if (strpos($rep->connDest->databaseType,'mssql') !== false) { # oracle ==> mssql - $ignoreflds = array($pkey); - $ignoreflds[] = 'MSSQL_ID'; - $set = 'MSSQL_ID=nvl($INSERT_ID,MSSQL_ID)'; - $pkeyarr = array(array($pkey),false,array('MSSQL_ID'));# array('MSSQL_ID', 'ORA_ID')); - } else { # mssql ==> oracle - $ignoreflds = array($pkey); - $ignoreflds[] = 'ORA_ID'; - $set = ''; - #$set = 'ORA_ID=isnull($INSERT_ID,ORA_ID)'; - $pkeyarr = array(array($pkey),array('MSSQL_ID')); - } - $rep->execute = true; - #$rep->updateFirst = false; - $ok = $rep->Merge($table, $dtable, $pkeyarr, $ignoreflds, $set, 'UpdatedOn','CopiedFlag',array('Y','N','P','='), 'CopyDate'); - var_dump($ok); - - #$rep->connSrc->Execute("update JohnTest set name='Apple' where id=4"); -} - -$DB = ADONewConnection('odbtp'); -#$ok = $DB->Connect('localhost','root','','northwind'); -$ok = $DB->Connect('192.168.0.1','DRIVER={SQL Server};SERVER=(local);UID=sa;PWD=natsoft;DATABASE=OIR;','',''); -$DB->_bindInputArray = false; - -$DB2 = ADONewConnection('oci8'); -$ok2 = $DB2->Connect('192.168.0.2','tnb','natsoft','RAPTOR',''); - -if (!$ok || !$ok2) die("Failed connection DB=$ok DB2=$ok2<br>"); - -$tables = -" -JohnTest,id -"; - -# net* are ERMS, need last updated field from LGBnet -# tblRep* are tables insert or update from Juris, need last updated field also -# The rest are lookup tables, can copy all from LGBnet - -$tablesOrig = -" -SysVoltSubLevel,id -# Lookup table for Restoration Details screen -sysefi,ID # (not identity) -sysgenkva,ID #(not identity) -sysrestoredby,ID #(not identity) -# Sel* table added on 24 Oct -SELSGManufacturer,ID -SelABCCondSizeLV,ID -SelABCCondSizeMV,ID -SelArchingHornSize,ID -SelBallastSize,ID -SelBallastType,ID -SelBatteryType,ID #(not identity) -SelBreakerCapacity,ID -SelBreakerType,ID #(not identity) -SelCBreakerManuf,ID -SelCTRatio,ID #(not identity) -SelCableBrand,ID -SelCableSize,ID -SelCableSizeLV,ID # (not identity) -SelCapacitorSize,ID -SelCapacitorType,ID -SelColourCode,ID -SelCombineSealingChamberSize,ID -SelConductorBrand,ID -SelConductorSize4,ID -SelConductorSizeLV,ID -SelConductorSizeMV,ID -SelContactorSize,ID -SelContractor,ID -SelCoverType,ID -SelCraddleSize,ID -SelDeadEndClampBrand,ID -SelDeadEndClampSize,ID -SelDevTermination,ID -SelFPManuf,ID -SelFPillarRating,ID -SelFalseTrue,ID -SelFuseManuf,ID -SelFuseType,ID -SelIPCBrand,ID -SelIPCSize,ID -SelIgnitorSize,ID -SelIgnitorType,ID -SelInsulatorBrand,ID -SelJoint,ID -SelJointBrand,ID -SelJunctionBoxBrand,ID -SelLVBoardBrand,ID -SelLVBoardSize,ID -SelLVOHManuf,ID -SelLVVoltage,ID -SelLightningArresterBrand,ID -SelLightningShieldwireSize,ID -SelLineTapSize,ID -SelLocation,ID -SelMVVoltage,ID -SelMidSpanConnectorsSize,ID -SelMidSpanJointSize,ID -SelNERManuf,ID -SelNERType,ID -SelNLinkSize,ID -SelPVCCondSizeLV,ID -SelPoleBrand,ID -SelPoleConcreteSize,ID -SelPoleSize,ID -SelPoleSpunConcreteSize,ID -SelPoleSteelSize,ID -SelPoleType,ID -SelPoleWoodSize,ID -SelPorcelainFuseSize,ID -SelRatedFaultCurrentBreaker,ID -SelRatedVoltageSG,ID #(not identity) -SelRelayType,ID # (not identity) -SelResistanceValue,ID -SelSGEquipmentType,ID # (not identity) -SelSGInsulationType,ID # (not identity) -SelSGManufacturer,ID -SelStayInsulatorSize,ID -SelSuspensionClampBrand,ID -SelSuspensionClampSize,ID -SelTSwitchType,ID -SelTowerType,ID -SelTransformerCapacity,ID -SelTransformerManuf,ID -SelTransformerType,ID #(not identity) -SelTypeOfArchingHorn,ID -SelTypeOfCable,ID #(not identity) -SelTypeOfConductor,ID # (not identity) -SelTypeOfInsulationCB,ID # (not identity) -SelTypeOfMidSpanJoint,ID -SelTypeOfSTJoint,ID -SelTypeSTCable,ID -SelUGVoltage,ID # (not identity) -SelVoltageInOut,ID -SelWireSize,ID -SelWireType,ID -SelWonpieceBrand,ID -# -# Net* tables added on 24 Oct -NetArchingHorn,Idx -NetBatteryBank,Idx # identity, FunctLocation Pri -NetBiMetal,Idx -NetBoxFuse,Idx -NetCable,Idx # identity, FunctLocation Pri -NetCapacitorBank,Idx # identity, FunctLocation Pri -NetCircuitBreaker,Idx # identity, FunctLocation Pri -NetCombineSealingChamber,Idx -NetCommunication,Idx -NetCompInfras,Idx -NetControl,Idx -NetCraddle,Idx -NetDeadEndClamp,Idx -NetEarthing,Idx -NetFaultIndicator,Idx -NetFeederPillar,Idx # identity, FunctLocation Pri -NetGenCable,Idx # identity , FunctLocation Not Null -NetGenerator,Idx -NetGrid,Idx -NetHVOverhead,Idx #identity, FunctLocation Pri -NetHVUnderground,Idx #identity, FunctLocation Pri -NetIPC,Idx -NetInductorBank,Idx -NetInsulator,Idx -NetJoint,Idx -NetJunctionBox,Idx -NetLVDB,Idx #identity, FunctLocation Pri -NetLVOverhead,Idx -NetLVUnderground,Idx # identity, FunctLocation Not Null -NetLightningArrester,Idx -NetLineTap,Idx -NetMidSpanConnectors,Idx -NetMidSpanJoint,Idx -NetNER,Idx # identity , FunctLocation Pri -NetOilPump,Idx -NetOtherComponent,Idx -NetPole,Idx -NetRMU,Idx # identity, FunctLocation Pri -NetStreetLight,Idx -NetStrucSupp,Idx -NetSuspensionClamp,Idx -NetSwitchGear,Idx # identity, FunctLocation Pri -NetTermination,Idx -NetTransition,Idx -NetWonpiece,Idx -# -# comment1 -SelMVFuseType,ID -selFuseSize,ID -netRelay,Idx # identity, FunctLocation Pri -SysListVolt,ID -sysVoltLevel,ID_SVL -sysRestoration,ID_SRE -sysRepairMethod,ID_SRM # (not identity) - -sysInterruptionType,ID_SIN -netTransformer,Idx # identity, FunctLocation Pri -# -# -sysComponent,ID_SC -sysCodecibs #-- no idea, UpdatedOn(the only column is unique),Ermscode,Cibscode is unique but got null value -sysCodeno,id -sysProtection,ID_SP -sysEquipment,ID_SEQ -sysAddress #-- no idea, ID_SAD(might be auto gen No) -sysWeather,ID_SW -sysEnvironment,ID_SE -sysPhase,ID_SPH -sysFailureCause,ID_SFC -sysFailureMode,ID_SFM -SysSchOutageMode,ID_SSM -SysOutageType,ID_SOT -SysInstallation,ID_SI -SysInstallationCat,ID_SIC -SysInstallationType,ID_SIT -SysFaultCategory,ID_SF #(not identity) -SysResponsible,ID_SR -SysProtectionOperation,ID_SPO #(not identity) -netCodename,CodeNo #(not identity) -netSubstation,Idx #identity, FunctLocation Pri -netLvFeeder,Idx # identity, FunctLocation Pri -# -# -tblReport,ReportNo -tblRepRestoration,ID_RR -tblRepResdetail,ID_RRD -tblRepFailureMode,ID_RFM -tblRepFailureCause,ID_RFC -tblRepRepairMethod,ReportNo # (not identity) -tblInterruptionType,ID_TIN -tblProtType,ID_PT #--capital letter -tblRepProtection,ID_RP -tblRepComponent,ID_RC -tblRepWeather,ID_RW -tblRepEnvironment,ID_RE -tblRepSubstation,ID_RSS -tblInstallationType,ID_TIT -tblInstallationCat,ID_TIC -tblFailureCause,ID_TFC -tblFailureMode,ID_TFM -tblProtection,ID_TP -tblComponent,ID_TC -tblProtdetail,Id # (Id)--capital letter for I -tblInstallation,ID_TI -# -"; - - -$tables = explode("\n",$tables); - -$rep = new ADODB_Replicate($DB,$DB2); -$rep->fieldFilter = 'FieldFilter'; -$rep->selFilter = 'SELFILTER'; -$rep->indexFilter = 'IndexFilter'; - -if (1) { - $rep->debug = 1; - $DB->debug=1; - $DB2->debug=1; -} - -# $rep->SwapDBs(); - -$cnt = sizeof($tables); -foreach($tables as $k => $table) { - $pkey = ''; - if (!ParseTable($table, $pkey)) continue; - - ####################### - - $kcnt = $k+1; - echo "<h1>($kcnt/$cnt) $table -- $pkey</h1>\n"; - flush();@ob_flush(); - - CreateTable($rep,$table); - - - # COPY DATA - - - TableStats($rep, $table, $pkey); - - if ($table == 'JohnTest') MergeDataJohnTest($rep, $table, $pkey); - else CopyData($rep, $table, $pkey); - -} - - -if (!empty($TARR)) { - ksort($TARR); - adodb_pr($TARR); - asort($TARR); - adodb_pr($TARR); -} - -echo "<hr>",date('H:i:s'),": Done</hr>"; diff --git a/scripts/adodbutil.py b/scripts/adodbutil.py new file mode 100644 index 00000000..c0bcfb4d --- /dev/null +++ b/scripts/adodbutil.py @@ -0,0 +1,182 @@ +""" +ADOdb release management scripts utilities and helper classes. + +- Environment class + Reads configuration variables from the environment file, and makes them + available in the 'env' global variable.. +- Gitter class + Use Gitter REST API to post announcements + +This file is part of ADOdb, a Database Abstraction Layer library for PHP. + +@package ADOdb +@link https://adodb.org Project's web site and documentation +@link https://github.com/ADOdb/ADOdb Source code and issue tracker + +The ADOdb Library is dual-licensed, released under both the BSD 3-Clause +and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, +any later version. This means you can use it in proprietary products. +See the LICENSE.md file distributed with this source code for details. +@license BSD-3-Clause +@license LGPL-2.1-or-later + +@copyright 2022 Damien Regad, Mark Newnham and the ADOdb community +@author Damien Regad +""" +import re +import urllib.parse +from os import path + +import requests +import yaml +from markdown import markdown + + +class Environment: + # See env.yml.sample for details about these config variables + sf_api_key = None + + github_token = None + github_repo = 'ADOdb/ADOdb' + + gitter_token = None + gitter_room = 'ADOdb/ADOdb' + + matrix_token = None + matrix_domain = 'gitter.im' + matrix_room = '#ADOdb_ADOdb:' + matrix_domain + + twitter_account = 'ADOdb_announce' + twitter_api_key = None + twitter_api_secret = None + twitter_bearer_token = None # Currently unused + twitter_access_token = None + twitter_access_secret = None + + def __init__(self, filename='env.yml'): + """ + Constructor - load the config file and initialize properties. + + :param filename: Name of YAML config file to load + """ + env_file = path.join(path.dirname(path.abspath(__file__)), filename) + + # Read the config file + try: + with open(env_file, 'r') as stream: + config = yaml.safe_load(stream) + except yaml.parser.ParserError as e: + raise Exception("Invalid Environment file") from e + + # Assign class properties from config + for key, value in config.items(): + setattr(self, key, value) + + +class Matrix: + """ + Posting messages to a Matrix room via REST API + """ + api_root = '_matrix/client/v3/' + domain = '' + base_url = '' + room_alias = '' + room_id = '' + + _headers = '' + + def __init__(self, domain, token, room_alias): + """ + Class Constructor. + + :param domain: Matrix Server's domain name + :param token: Matrix REST API token + :param room_alias: Matrix Room alias, e.g. `#room:server.id` + """ + self._headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer ' + token.strip() + } + + self.domain = domain + self._set_base_url() + self._set_room(room_alias) + + + def url(self, endpoint): + """ + Get Matrix REST API URL for the given endpoint. + + :param endpoint: REST API endpoint + :return: URL + """ + return self.base_url + endpoint + + def _set_base_url(self): + """ + Retrieve the Matrix API base URL for the given Domain and initialize + the self.base_url property. + """ + r = requests.get(f'https://{self.domain}/.well-known/matrix/client') + + if r.status_code != requests.codes.ok: + raise Exception(r.text) + + self.base_url = r.json()['m.homeserver']['base_url'] + \ + '/' + self.api_root + + def _set_room(self, alias): + """ + Retrieve the Matrix Room ID from the given alias (after adding the + leading '#' and the Server name if not provided) and initialize the + room_alias and room_id properties. + + :param alias: + """ + if not alias: + raise Exception("Matrix Room Alias not defined") + + # Add leading '#' if needed + if alias[0] != '#': + alias = '#' + alias + # If the alias does not include the Server, add the domain + if ':' not in alias: + alias += ':' + self.domain + self.room_alias = alias + + # Retrieve Room ID + url = self.url('directory/room/') + urllib.parse.quote(alias) + r = requests.get(url, headers=self._headers) + if r.status_code != requests.codes.ok: + raise Exception(r.json()['error']) + + self.room_id = r.json()['room_id'] + + def post(self, message): + """ + Post a message to a Matrix room. + + :param message: Message text in Markdown format + + :return: Posted message's ID + """ + html = markdown(message) + plain_text = re.sub(r'(<!--.*?-->|<[^>]*>)', '', html) + payload = { + 'msgtype': 'm.text', + 'body': plain_text, + 'format': 'org.matrix.custom.html', + 'formatted_body': html, + } + + url = self.url(f'rooms/{self.room_id}/send/m.room.message') + r = requests.post(url, headers=self._headers, json=payload) + if r.status_code != requests.codes.ok: + raise Exception(r.text) + + return r.json()['event_id'] + + +# Initialize environment +env = Environment() diff --git a/scripts/announce.py b/scripts/announce.py new file mode 100755 index 00000000..51b2dd3a --- /dev/null +++ b/scripts/announce.py @@ -0,0 +1,253 @@ +#!/usr/bin/env -S python3 -u +""" +ADOdb announcements script. + +Posts release announcements to +- Gitter +- Twitter + +This file is part of ADOdb, a Database Abstraction Layer library for PHP. + +@package ADOdb +@link https://adodb.org Project's web site and documentation +@link https://github.com/ADOdb/ADOdb Source code and issue tracker + +The ADOdb Library is dual-licensed, released under both the BSD 3-Clause +and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, +any later version. This means you can use it in proprietary products. +See the LICENSE.md file distributed with this source code for details. +@license BSD-3-Clause +@license LGPL-2.1-or-later + +@copyright 2022 Damien Regad, Mark Newnham and the ADOdb community +@author Damien Regad +""" + +import argparse +from datetime import date +import json +import re +from pathlib import Path + +import tweepy # https://www.tweepy.org/ +from git import Repo # https://gitpython.readthedocs.io +# https://github.com/PyGithub/PyGithub +from github import Github, GithubException, Milestone + +from adodbutil import env, Matrix + + +def process_command_line(): + """ + Parse command-line options + :return: Namespace + """ + # Get most recent Git tag + repo = Repo(path=Path(__file__).parents[1]) + tags = sorted(repo.tags, key=lambda t: t.tag.tagged_date) + latest_tag = str(tags[-1]) + + parser = argparse.ArgumentParser( + description="Post ADOdb release announcement messages to Gitter." + ) + parser.add_argument('version', + nargs='?', + default=latest_tag, + help="Version number to announce; if not specified, " + "the latest tag will be used.") + parser.add_argument('-m', '--message', + help="Additional text to add to announcement message") + parser.add_argument('-b', '--batch', + action="store_true", + help="Batch mode - do not ask for confirmation " + "before posting") + + only = parser.add_mutually_exclusive_group() + only.add_argument('-g', '--gitter-only', + action="store_true", + help="Only post the announcement to Gitter") + only.add_argument('-t', '--twitter-only', + action="store_true", + help="Only post the announcement to Twitter") + only.add_argument('-u', '--github-only', + action="store_true", + help="Only post the announcement to GitHub") + + return parser.parse_args() + + +def github_close_milestone(repo, version): + print(f"Closing Milestone '{version}'") + + # Search Milestone for version + milestone_found = False + milestone: Milestone.Milestone + for milestone in repo.get_milestones(): + if milestone.title == version: + milestone_found = True + break + + # Milestone not found, check if already closed + if not milestone_found: + # Process closed Milestones in reverse order of due_on, to minimize + # number of iterations + for milestone in repo.get_milestones(state='closed', + sort='due_on', + direction='desc'): + if milestone.title == version: + print(f"Already closed {milestone.raw_data['html_url']}") + return + raise Exception(f"Milestone '{version}' not found") + + # Close the milestone + # noinspection PyUnboundLocalVariable + milestone.edit(title=milestone.title, + state='closed', + due_on=date.today()) + + +def post_github(version, message, changelog_link): + print(f"GitHub Release for repository '{env.github_repo}'") + + gh = Github(env.github_token) + repo = gh.get_repo(env.github_repo) + + # Check if Release already exists + version = 'v' + version + try: + rel = repo.get_release(version) + print(f"Existing release '{version}' found", rel.html_url) + + # Discard the message provided on command-line, and use the one from + # the Release's description, inform user to update it on GitHub. + if message: + print(f"Your message will be discarded; " + "the Release's description will be used instead.\n" + "Edit it on GitHub if needed") + else: + print("Retrieving the Release's description for the " + "announcement message") + + # Remove the changelog link to keep only the release's message + message = re.sub(r"[,.]?\s*(Please )?See .*$", + "", + rel.body, + flags=re.IGNORECASE).strip() + if message: + message += ".\n" + except GithubException as err: + if err.status != 404: + raise err + print(f"Release '{version}' does not exist yet") + + # Make sure the version has been tagged + try: + repo.get_git_ref('tags/' + version) + print(f"Tag '{version}' found") + except GithubException: + print(f"ERROR: Tag '{version}' does not exist on GitHub, " + "please push it first") + exit(1) + + # Create the release + rel = repo.create_git_release(version, + version, + message + changelog_link) + print("Release created successfully", rel.html_url) + + print() + + # Closing the Milestone + try: + github_close_milestone(repo, version) + except Exception as e: + print("WARNING: " + str(e) + "\n") + + # Return message to be used for remaining announcements + return message + + +def post_gitter(message): + print(f"Posting to Gitter ({env.matrix_room})... ") + matrix = Matrix(env.matrix_domain, env.matrix_token, env.matrix_room) + message_id = matrix.post('# ' + message) + print("Message posted successfully\n" + f"https://matrix.to/#/{matrix.room_id}/{message_id}") + print() + + +def post_twitter(message): + print(f"Posting to Twitter ({env.twitter_account})... ",) + twitter = tweepy.Client( + consumer_key=env.twitter_api_key, + consumer_secret=env.twitter_api_secret, + access_token=env.twitter_access_token, + access_token_secret=env.twitter_access_secret + ) + try: + r = twitter.create_tweet(text=message) + except tweepy.errors.HTTPException as e: + print("ERROR:", e) + return + print("Tweeted successfully\n" + f"https://twitter.com/{env.twitter_account}/status/{r.data['id']}") + print() + + +def main(): + args = process_command_line() + + post_everywhere = not args.gitter_only \ + and not args.github_only \ + and not args.twitter_only + version = args.version.lstrip('v') + changelog_url = f"https://github.com/ADOdb/ADOdb/blob/v{version}" \ + "/docs/changelog.md" + message = args.message.rstrip(".") + ".\n" if args.message else "" + + # Tell user where the release will be announced + print(f"Posting to: ", end='') + if post_everywhere or args.github_only: + print(f"GitHub ({env.github_repo})", + end=', ' if post_everywhere else '\n') + if post_everywhere or args.gitter_only: + print(f"Gitter ({env.matrix_room})", + end=', ' if post_everywhere else '\n') + if post_everywhere or args.twitter_only: + print(f"Twitter ({env.twitter_account})") + print() + + # Create GitHub release, retrieve message from it if it already exists + if post_everywhere or args.github_only: + message = post_github(version, + message, + f"See [Changelog]({changelog_url}) for details") + if args.github_only: + return + + # Build announcement message + msg_announce = f"ADOdb Version {version} released\n{message}" \ + "See Changelog " + changelog_url + + # Get confirmation + if not args.batch: + print("Review ", end='') + print("Announcement message") + print("-" * 27) + print(msg_announce) + print("-" * 27) + if not args.batch: + reply = input("Proceed with posting ? ") + if not reply.casefold() == 'y': + print("Aborting") + exit(1) + print() + + if post_everywhere or args.gitter_only: + post_gitter(msg_announce) + if post_everywhere or args.twitter_only: + post_twitter(msg_announce) + + +if __name__ == "__main__": + main() diff --git a/scripts/env.yml.sample b/scripts/env.yml.sample index fce3840d..3b257b3e 100644 --- a/scripts/env.yml.sample +++ b/scripts/env.yml.sample @@ -4,4 +4,29 @@ # SourceForge API key # Get it from https://sourceforge.net/auth/preferences/ # under "Releases API Key" -api_key: +sf_api_key: + +# Matrix +# Get API Token from Element: click profile icon (top left), All Settings, +# Help & About, scroll down to Advanced section (bottom), expand Access Token +matrix_token: +# Home server of the user ID owning the Token +matrix_domain: gitter.im +# Room alias. If ':server' is not specified, matrix_domain will be appended +matrix_room: "#ADOdb_ADOdb:gitter.im" + +# Twitter +# Manage API keys from https://developer.twitter.com/en/portal/dashboard +twitter_account: ADOdb_announce +# Consumer Keys +twitter_api_key: +twitter_api_secret: +# Authentication Token (must have write permission to post tweets) +twitter_access_token: +twitter_access_secret: + +# GitHub +# Get Personal access token from https://github.com/settings/tokens +# (must have public_repo scope) +github_token: +github_repo: ADOdb/ADOdb diff --git a/scripts/fix-static-docs.php b/scripts/fix-static-docs.php index c4159dd5..07da8812 100644 --- a/scripts/fix-static-docs.php +++ b/scripts/fix-static-docs.php @@ -1,4 +1,4 @@ -<?php +<?php /** * Post-processing of the DokuWiki documentation export. * @@ -22,209 +22,240 @@ */ /** + * + */ +$source_dir = 'documentation-base'; +$target_dir = 'documentation'; + +/** * Recurses a directory and deletes files inside * * Copied from php.net * -* @param string $dir The driectory name -* $return null +* @param string $dir The directory name +* $return bool */ -function delTree($dir) { - $files = array_diff(scandir($dir), array('.','..')); - foreach ($files as $file) { - (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); - } - return rmdir($dir); -} +function delTree($dir) +{ + $files = @scandir($dir); + if ($files === false) { + return false; + } + $files = array_diff($files, array('.', '..')); + + foreach ($files as $file) { + $result = (is_dir("$dir/$file") ? delTree("$dir/$file") : unlink("$dir/$file")); + if ($result === false) { + return false; + } + } + return rmdir($dir); +} /** * Initializes the listdiraux method with a starting directory point * * copied from php.net * -* @param string $dir Starting directory -* @return array FQ list of files -*/ -function listdir($dir='.') { - if (!is_dir($dir)) { - return false; - } - $files = array(); - listdiraux($dir, $files); +* @param string $dir Starting directory +* @return array|false FQ list of files +*/ +function listdir($dir='.') +{ + if (!is_dir($dir)) { + return false; + } + $files = array(); + listdiraux($dir, $files); - return $files; -} + return $files; +} /** * Recurses a directory structure and creates a list of files * * @param string $dir Starting directory * @param string[] $files By reference, the current file list -* @return null -*/ -function listdiraux($dir, &$files) { - $handle = opendir($dir); - while (($file = readdir($handle)) !== false) { - if ($file == '.' || $file == '..') { - continue; - } - - if (preg_match('/v6$/',$file)) - /* - * This is only v5 documentation - */ +* @return void +*/ +function listdiraux($dir, &$files) +{ + $handle = opendir($dir); + while (($file = readdir($handle)) !== false) { + if ($file == '.' || $file == '..') { continue; - - $filepath = $dir == '.' ? $file : $dir . '/' . $file; - if (is_link($filepath)) - continue; - if (is_file($filepath)) - $files[] = $filepath; - else if (is_dir($filepath)) - listdiraux($filepath, $files); - } - closedir($handle); -} + } + + // Exclude internal use namespaces + if (preg_match('/^(?:admin|playground|wiki)/', $file)) { + continue; + } + + // This is only v5 documentation + if (preg_match('/v6$/', $file)) { + continue; + } + + $filepath = $dir == '.' ? $file : $dir . '/' . $file; + if (is_link($filepath)) { + continue; + } + if (is_file($filepath)) { + $files[] = $filepath; + } else { + if (is_dir($filepath)) { + listdiraux($filepath, $files); + } + } + } + closedir($handle); +} + /* * Clean up the documentation directory from prior use */ -if (is_dir('documentation')) - deltree('documentation'); -mkdir('documentation'); +if (is_dir($target_dir)) { + if(!deltree($target_dir)) { + echo "ERROR: unable to remove target directory '$target_dir'\n"; + die(1); + } +} +mkdir($target_dir); -$files = listdir('documentation-base'); -sort($files, SORT_LOCALE_STRING); +$files = listdir($source_dir); +if (!$files) { + echo "ERROR: Source directory '$source_dir' not found, not accessible or empty\n"; + die(1); +} +sort($files, SORT_LOCALE_STRING); /* -* Loop through files in documentation-base directory, creating a mirror -* structure in documentation, and applying the post-process rules defined -* below +* Loop through files in source directory, creating a mirror structure +* in target directory, and applying the post-process rules defined below */ -foreach ($files as $f) { +foreach ($files as $f) { - $r = str_replace('documentation-base','documentation',$f); - $dList = explode ('/',$r); + $r = str_replace($source_dir, $target_dir, $f); + $dList = explode('/', $r); $titleList = $dList; /* * Get rid of the initial directory */ array_shift($titleList); - - $depth = count($dList) -2; + + $depth = count($dList) - 2; $dSlash = ''; - while(count($dList) > 1) - { + while (count($dList) > 1) { $dSlash .= array_shift($dList) . '/'; - if (!is_dir($dSlash)) + if (!is_dir($dSlash)) { mkdir($dSlash); + } } - if (!is_file($f)) + if (!is_file($f)) { continue; - if (substr($f,-4) <> 'html') - { + } + if (substr($f, -4) <> 'html') { /* * An image or something else, copy unmodified */ - copy ($f,$r); + copy($f, $r); continue; } - - $prepend = str_repeat('../',$depth); - + + $prepend = str_repeat('../', $depth); + $doc = new DOMDocument(); @$doc->loadHTMLFile($f); - + /* * Remove Page Tools Group */ $xpath = new DOMXPath($doc); - + /* * Remove Top Menu Tools Group, and add a link to the ADOdb site */ $nodes = $xpath->query("//div[@class='tools group']"); - foreach($nodes as $node) { + foreach ($nodes as $node) { $pn = $node->parentNode; $pn->removeChild($node); - $newChild = $doc->createElement('div',''); + $newChild = $doc->createElement('div'); $newDiv = $pn->appendChild($newChild); - $newDiv->setAttribute('style','text-align:right'); - $newChild = $doc->createElement('a','ADOdb Web Site'); + $newDiv->setAttribute('style', 'text-align:right'); + $newChild = $doc->createElement('a', 'ADOdb Web Site'); $newA = $newDiv->appendChild($newChild); - $newA->setAttribute('href','https://adodb.org'); - } - + $newA->setAttribute('href', 'https://adodb.org'); + } + /* * Remove Trace */ $nodes = $xpath->query("//div[@class='breadcrumbs']"); - foreach($nodes as $node) { + foreach ($nodes as $node) { $node->parentNode->removeChild($node); - } - + } + /* * Remove Side Menu Tools Group */ $nodes = $xpath->query("//div[@id='dokuwiki__pagetools']"); - foreach($nodes as $node) { + foreach ($nodes as $node) { $node->parentNode->removeChild($node); - } - + } + /* * Fix main links */ $nodes = $xpath->query("//a[@class='wikilink1']"); - foreach($nodes as $node) { - $n = $node->getAttribute('title'); - $p = $prepend . str_replace(':','/',$n) . '.html'; + foreach ($nodes as $node) { + $n = $node->getAttribute('title'); + $p = $prepend . str_replace(':', '/', $n) . '.html'; $node->setAttribute('href', $p); - } - + } + /* * Fix In Page links */ $nodes = $xpath->query("//a[@class='wikilink2']"); - foreach($nodes as $node) { - $n = $node->getAttribute('title'); - $p = $prepend . str_replace(':','/',$n) . '.html'; + foreach ($nodes as $node) { + $n = $node->getAttribute('title'); + $p = $prepend . str_replace(':', '/', $n) . '.html'; $node->setAttribute('href', $p); - } - + } + /* * Make Graphic point to first page. This will break if the image size * ever changes. */ $corePage = $prepend . '/index.html'; $nodes = $xpath->query("//img[@width='176']"); - foreach($nodes as $node) { + foreach ($nodes as $node) { $node->parentNode->setAttribute('href', $corePage); - } - + } + /* * Change title of page */ $nodes = $xpath->query("//title"); - foreach($nodes as $node) { - - - $docTitle = implode(':',$titleList); - $docTitle = str_replace('.html','',$docTitle); - $pn = $node->parentNode; + foreach ($nodes as $node) { + $docTitle = implode(':', $titleList); + $docTitle = str_replace('.html', '', $docTitle); + $pn = $node->parentNode; $pn->removeChild($node); - $newChild = $doc->createElement('title',$docTitle); + $newChild = $doc->createElement('title', $docTitle); $pn->appendChild($newChild); - - } - + } + $doc->saveHTMLFile($r); - - echo $r, "\n"; + + echo $r, "\n"; } + /* * Now remove the original index and replace it with the hardcopy documentation one */ -unlink ('documentation/index.html'); -rename('documentation/adodb_index.html','documentation/index.html'); +unlink ($target_dir . '/index.html'); +rename($target_dir . '/adodb_index.html',$target_dir . '/index.html'); /* * We could add in an auto zip and upload here, but this is a good place to diff --git a/scripts/uploadrelease.py b/scripts/uploadrelease.py index 7d801eed..812a479b 100755 --- a/scripts/uploadrelease.py +++ b/scripts/uploadrelease.py @@ -21,18 +21,19 @@ See the LICENSE.md file distributed with this source code for details. @author Damien Regad """ -from distutils.version import LooseVersion import getopt import getpass import glob import json import os -from os import path import re -import requests import subprocess import sys -import yaml +from os import path + +import requests + +from adodbutil import env # Directories and files to exclude from release tarballs @@ -53,7 +54,6 @@ long_options = ["help", "user=", "dry-run", "skip-upload"] # Global flags dry_run = False -api_key = '' username = getpass.getuser() release_path = '' skip_upload = False @@ -129,7 +129,7 @@ def get_release_version(): try: version = re.search( - r"^adodb-([\d]+\.[\d]+\.[\d]+)(-(alpha|beta|rc)\.[\d]+)?\.zip$", + r"^adodb-(\d+\.\d+\.\d+)(-(alpha|beta|rc)\.\d+)?\.zip$", zipfile ).group(1) except AttributeError: @@ -165,36 +165,11 @@ def sourceforge_target_dir(version): # Keep only X.Y (discard patch number and pre-release suffix) short_version = version.split('-')[0].rsplit('.', 1)[0] - if LooseVersion(version) >= LooseVersion('5.21'): - directory += "adodb-" + short_version - else: - directory += "adodb-{}-for-php5".format(short_version.replace('.', '')) + directory += "adodb-" + short_version return directory -def load_env(): - """ - Load environment from env.yml config file. - """ - global api_key - - # Load the config file - env_file = path.join(path.dirname(path.abspath(__file__)), 'env.yml') - try: - stream = open(env_file, 'r') - y = yaml.safe_load(stream) - except IOError: - print("ERROR: Environment file {} not found".format(env_file)) - sys.exit(3) - except yaml.parser.ParserError as e: - print("ERROR: Invalid Environment file") - print(e) - sys.exit(3) - - api_key = y['api_key'] - - def process_command_line(): """ Retrieve command-line options and set global variables accordingly. @@ -261,7 +236,7 @@ def upload_release_files(): def set_sourceforge_file_info(): - global api_key, dry_run + global dry_run print("Updating uploaded files information") @@ -288,7 +263,7 @@ def set_sourceforge_file_info(): url = path.join(base_url, file) payload = { 'default': defaults, - 'api_key': api_key + 'api_key': env.sf_api_key } if dry_run: req = requests.Request('PUT', url, headers=headers, params=payload) @@ -314,7 +289,6 @@ def main(): # Start upload process print("ADOdb release upload script") - load_env() process_command_line() global skip_upload diff --git a/session/adodb-session2.php b/session/adodb-session2.php index 19793960..3a03f989 100644 --- a/session/adodb-session2.php +++ b/session/adodb-session2.php @@ -315,8 +315,9 @@ class ADODB_Session { /** * Get/Set garbage collection function. * - * @param callable $expire_notify Function name - * @return callable|false + * @param array $expire_notify [Expired Session ref, Callback function] + * + * @return array|false */ static function expireNotify($expire_notify = null) { @@ -560,6 +561,22 @@ class ADODB_Session { } /** + * Returns a MySQL "CAST(... AS BINARY)" function for the given value. + * + * For other DB types, the value is returned as-is. + * + * @param string $value + * @return string + */ + static protected function castBinary(string $value): string + { + if (self::isConnectionMysql()) { + return "CAST($value AS BINARY)"; + } + return $value; + } + + /** * Check if Session Connection's DB type is PostgreSQL. * * @return bool @@ -711,17 +728,17 @@ class ADODB_Session { return ''; } - $binary = ADODB_Session::isConnectionMysql() ? '/*! BINARY */' : ''; - global $ADODB_SESSION_SELECT_FIELDS; if (!isset($ADODB_SESSION_SELECT_FIELDS)) $ADODB_SESSION_SELECT_FIELDS = 'sessdata'; - $sql = "SELECT $ADODB_SESSION_SELECT_FIELDS FROM $table WHERE sesskey = $binary ".$conn->Param(0)." AND expiry >= " . $conn->sysTimeStamp; + $sql = "SELECT $ADODB_SESSION_SELECT_FIELDS FROM $table " + . "WHERE sesskey = " . self::castBinary($conn->Param(0)) + . " AND expiry >= " . $conn->sysTimeStamp; /* Lock code does not work as it needs to hold transaction within whole page, and we don't know if developer has committed elsewhere... :( */ #if (ADODB_Session::Lock()) - # $rs = $conn->RowLock($table, "$binary sesskey = $qkey AND expiry >= " . time(), sessdata); + # $rs = $conn->RowLock($table, "sesskey = " . self::castBinary($qkey). " AND expiry >= " . time(), sessdata); #else $rs = $conn->Execute($sql, array($key)); //ADODB_Session::_dumprs($rs); @@ -782,8 +799,7 @@ class ADODB_Session { $sysTimeStamp = $conn->sysTimeStamp; $expiry = $conn->OffsetDate($lifetime/(24*3600),$sysTimeStamp); - - $binary = ADODB_Session::isConnectionMysql() ? '/*! BINARY */' : ''; + $expireref = $expire_notify ? $GLOBALS[$expire_notify[0]] ?? '' : ''; // crc32 optimization since adodb 2.1 // now we only update expiry date, thx to sebastian thom in adodb 2.32 @@ -792,19 +808,10 @@ class ADODB_Session { echo '<p>Session: Only updating date - crc32 not changed</p>'; } - $expirevar = ''; - if ($expire_notify) { - $var = reset($expire_notify); - global $$var; - if (isset($$var)) { - $expirevar = $$var; - } - } - $sql = "UPDATE $table SET expiry = $expiry, expireref=" . $conn->Param('0') - . ", modified = $sysTimeStamp WHERE sesskey = $binary " . $conn->Param('1') + . ", modified = $sysTimeStamp WHERE sesskey = " . self::castBinary($conn->Param('1')) . " AND expiry >= $sysTimeStamp"; - $rs = $conn->Execute($sql,array($expirevar,$key)); + $rs = $conn->execute($sql,array($expireref, $key)); return true; } $val = rawurlencode($oval); @@ -814,18 +821,12 @@ class ADODB_Session { } } - $expireref = ''; - if ($expire_notify) { - $var = reset($expire_notify); - global $$var; - if (isset($$var)) { - $expireref = $$var; - } - } - if (!$clob) { // no lobs, simply use replace() - $rs = $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = ".$conn->Param(0),array($key)); + $rs = $conn->execute( + "SELECT COUNT(*) AS cnt FROM $table WHERE sesskey = " . self::castBinary($conn->Param(0)), + array($key) + ); if ($rs) $rs->Close(); if ($rs && reset($rs->fields) > 0) { @@ -845,7 +846,10 @@ class ADODB_Session { $conn->StartTrans(); - $rs = $conn->Execute("SELECT COUNT(*) AS cnt FROM $table WHERE $binary sesskey = ".$conn->Param(0),array($key)); + $rs = $conn->execute( + "SELECT COUNT(*) AS cnt FROM $table WHERE sesskey = " . self::castBinary($conn->Param(0)), + array($key) + ); if ($rs && reset($rs->fields) > 0) { $sql = "UPDATE $table SET expiry=$expiry, sessdata=$lob_value, expireref= ".$conn->Param(0).",modified=$sysTimeStamp WHERE sesskey = ".$conn->Param('1'); @@ -870,7 +874,7 @@ class ADODB_Session { // bug in access driver (could be odbc?) means that info is not committed // properly unless select statement executed in Win2000 if ($conn->databaseType == 'access') { - $sql = "SELECT sesskey FROM $table WHERE $binary sesskey = $qkey"; + $sql = "SELECT sesskey FROM $table WHERE sesskey = " . self::castBinary($qkey); $rs = $conn->Execute($sql); ADODB_Session::_dumprs($rs); if ($rs) { @@ -902,13 +906,11 @@ class ADODB_Session { if ($debug) $conn->debug = 1; $qkey = $conn->quote($key); - $binary = ADODB_Session::isConnectionMysql() ? '/*! BINARY */' : ''; if ($expire_notify) { - reset($expire_notify); - $fn = next($expire_notify); + $fn = $expire_notify[1]; $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); - $sql = "SELECT expireref, sesskey FROM $table WHERE sesskey = $binary $qkey"; + $sql = "SELECT expireref, sesskey FROM $table WHERE sesskey = " . self::castBinary($qkey); $rs = $conn->Execute($sql); ADODB_Session::_dumprs($rs); $conn->SetFetchMode($savem); @@ -923,7 +925,7 @@ class ADODB_Session { $rs->Close(); } - $sql = "DELETE FROM $table WHERE sesskey = $binary $qkey"; + $sql = "DELETE FROM $table WHERE sesskey = " . self::castBinary($qkey); $rs = $conn->Execute($sql); if ($rs) { $rs->Close(); @@ -958,14 +960,8 @@ class ADODB_Session { } $time = $conn->OffsetDate(-$maxlifetime/24/3600,$conn->sysTimeStamp); - $binary = ADODB_Session::isConnectionMysql() ? '/*! BINARY */' : ''; - if ($expire_notify) { - reset($expire_notify); - $fn = next($expire_notify); - } else { - $fn = false; - } + $fn = $expire_notify[1] ?? false; $savem = $conn->SetFetchMode(ADODB_FETCH_NUM); $sql = "SELECT expireref, sesskey FROM $table WHERE expiry < $time ORDER BY 2"; # add order by to prevent deadlock @@ -980,7 +976,10 @@ class ADODB_Session { $ref = $rs->fields[0]; $key = $rs->fields[1]; if ($fn) $fn($ref, $key); - $conn->Execute("DELETE FROM $table WHERE sesskey = $binary " . $conn->Param('0'), array($key)); + $conn->execute( + "DELETE FROM $table WHERE sesskey = " . self::castBinary($conn->Param('0') ), + array($key) + ); $rs->MoveNext(); $ccnt += 1; if ($tr && $ccnt % $COMMITNUM == 0) { diff --git a/session/old/adodb-cryptsession.php b/session/old/adodb-cryptsession.php deleted file mode 100644 index 6616de3d..00000000 --- a/session/old/adodb-cryptsession.php +++ /dev/null @@ -1,331 +0,0 @@ -<?php -/** - * ADOdb Session Management - * - * This file provides PHP4 session management using the ADODB database - * wrapper library. - * - * @deprecated - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ -/* - Example - ======= - - include('adodb.inc.php'); - #---------------------------------# - include('adodb-cryptsession.php'); - #---------------------------------# - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; - - - Installation - ============ - 1. Create a new database in MySQL or Access "sessions" like -so: - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA CLOB, - primary key (sesskey) - ); - - 2. Then define the following parameters. You can either modify - this file, or define them before this file is included: - - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - - 3. Recommended is PHP 4.0.2 or later. There are documented -session bugs in earlier versions of PHP. - -*/ - - -include_once('crypt.inc.php'); - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESS_INSERT, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_TBL; - - //$ADODB_SESS_DEBUG = true; - - /* SET THE FOLLOWING PARAMETERS */ -if (empty($ADODB_SESSION_DRIVER)) { - $ADODB_SESSION_DRIVER='mysql'; - $ADODB_SESSION_CONNECT='localhost'; - $ADODB_SESSION_USER ='root'; - $ADODB_SESSION_PWD =''; - $ADODB_SESSION_DB ='xphplens_2'; -} - -if (empty($ADODB_SESSION_TBL)){ - $ADODB_SESSION_TBL = 'sessions'; -} - -if (empty($ADODB_SESSION_EXPIRE_NOTIFY)) { - $ADODB_SESSION_EXPIRE_NOTIFY = false; -} - -function ADODB_Session_Key() -{ -$ADODB_CRYPT_KEY = 'CRYPTED ADODB SESSIONS ROCK!'; - - /* USE THIS FUNCTION TO CREATE THE ENCRYPTION KEY FOR CRYPTED SESSIONS */ - /* Crypt the used key, $ADODB_CRYPT_KEY as key and session_ID as SALT */ - return crypt($ADODB_CRYPT_KEY, session_ID()); -} - -$ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); -if ($ADODB_SESS_LIFE <= 1) { - // bug in PHP 4.0.3 pl 1 -- how about other versions? - //print "<h3>Session Error: PHP.INI setting <i>session.gc_maxlifetime</i>not set: $ADODB_SESS_LIFE</h3>"; - $ADODB_SESS_LIFE=1440; -} - -function adodb_sess_open($save_path, $session_name) -{ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_DEBUG; - - $ADODB_SESS_INSERT = false; - - if (isset($ADODB_SESS_CONN)) return true; - - $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER); - if (!empty($ADODB_SESS_DEBUG)) { - $ADODB_SESS_CONN->debug = true; - print" conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB "; - } - return $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT, - $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); - -} - -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -function adodb_sess_read($key) -{ -$Crypt = new MD5Crypt; -global $ADODB_SESS_CONN,$ADODB_SESS_INSERT,$ADODB_SESSION_TBL; - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $ADODB_SESS_INSERT = true; - $v = ''; - } else { - // Decrypt session data - $v = rawurldecode($Crypt->Decrypt(reset($rs->fields), ADODB_Session_Key())); - } - $rs->Close(); - return $v; - } - else $ADODB_SESS_INSERT = true; - - return ''; -} - -function adodb_sess_write($key, $val) -{ -$Crypt = new MD5Crypt; - global $ADODB_SESS_INSERT,$ADODB_SESS_CONN, $ADODB_SESS_LIFE, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - $expiry = time() + $ADODB_SESS_LIFE; - - // encrypt session data.. - $val = $Crypt->Encrypt(rawurlencode($val), ADODB_Session_Key()); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL, - $arr, - 'sesskey',$autoQuote = true); - - if (!$rs) { - ADOConnection::outp( ' --- Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'</p>',false); - } else { - // bug in access driver (could be odbc?) means that info is not committed - // properly unless select statement executed in Win2000 - - if ($ADODB_SESS_CONN->databaseType == 'access') $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return isset($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - - -function adodb_sess_gc($maxlifetime) { - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY,$ADODB_SESS_DEBUG; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $t = time(); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < $t"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - //$del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $rs->Close(); - - $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE expiry < $t"); - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); - $ADODB_SESS_CONN->Execute($qry); - } - - // suggested by Cameron, "GaM3R" <gamr@outworld.cx> - if (defined('ADODB_SESSION_OPTIMIZE')) - { - global $ADODB_SESSION_DRIVER; - - switch( $ADODB_SESSION_DRIVER ) { - case 'mysql': - case 'mysqlt': - $opt_qry = 'OPTIMIZE TABLE '.$ADODB_SESSION_TBL; - break; - case 'postgresql': - case 'postgresql7': - $opt_qry = 'VACUUM '.$ADODB_SESSION_TBL; - break; - } - } - - if ($ADODB_SESS_CONN->dataProvider === 'oci8') $sql = 'select TO_CHAR('.($ADODB_SESS_CONN->sysTimeStamp).', \'RRRR-MM-DD HH24:MI:SS\') from '. $ADODB_SESSION_TBL; - else $sql = 'select '.$ADODB_SESS_CONN->sysTimeStamp.' from '. $ADODB_SESSION_TBL; - - $rs = $ADODB_SESS_CONN->SelectLimit($sql,1); - if ($rs && !$rs->EOF) { - - $dbts = reset($rs->fields); - $rs->Close(); - $dbt = $ADODB_SESS_CONN->UnixTimeStamp($dbts); - $t = time(); - if (abs($dbt - $t) >= ADODB_SESSION_SYNCH_SECS) { - $msg = - __FILE__.": Server time for webserver {$_SERVER['HTTP_HOST']} not in synch with database: database=$dbt ($dbts), webserver=$t (diff=".(abs($dbt-$t)/3600)." hrs)"; - error_log($msg); - if ($ADODB_SESS_DEBUG) ADOConnection::outp(" --- $msg</p>"); - } - } - - return true; -} - -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ -/* -if (0) { - - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; -} -*/ diff --git a/session/old/adodb-session-clob.php b/session/old/adodb-session-clob.php deleted file mode 100644 index 864fdfd7..00000000 --- a/session/old/adodb-session-clob.php +++ /dev/null @@ -1,457 +0,0 @@ -<?php -/** - * ADOdb Session Management - * - * This file provides PHP4 session management using the ADODB database - * wrapper library. - * - * @deprecated - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ -/* - Example - ======= - - include('adodb.inc.php'); - include('adodb-session.php'); - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; - -To force non-persistent connections, call adodb_session_open first before session_start(): - - include('adodb.inc.php'); - include('adodb-session.php'); - adodb_session_open(false,false,false); - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; - - - Installation - ============ - 1. Create this table in your database (syntax might vary depending on your db): - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA CLOB, - primary key (sesskey) - ); - - - 2. Then define the following parameters in this file: - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - $ADODB_SESSION_USE_LOBS = false; (or, if you wanna use CLOBS (= 'CLOB') or ( = 'BLOB') - - 3. Recommended is PHP 4.1.0 or later. There are documented - session bugs in earlier versions of PHP. - - 4. If you want to receive notifications when a session expires, then - you can tag a session with an EXPIREREF, and before the session - record is deleted, we can call a function that will pass the EXPIREREF - as the first parameter, and the session key as the second parameter. - - To do this, define a notification function, say NotifyFn: - - function NotifyFn($expireref, $sesskey) - { - } - - Then you need to define a global variable $ADODB_SESSION_EXPIRE_NOTIFY. - This is an array with 2 elements, the first being the name of the variable - you would like to store in the EXPIREREF field, and the 2nd is the - notification function's name. - - In this example, we want to be notified when a user's session - has expired, so we store the user id in the global variable $USERID, - store this value in the EXPIREREF field: - - $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); - - Then when the NotifyFn is called, we are passed the $USERID as the first - parameter, eg. NotifyFn($userid, $sesskey). -*/ - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - -/****************************************************************************************\ - Global definitions -\****************************************************************************************/ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_CRC, - $ADODB_SESSION_USE_LOBS, - $ADODB_SESSION_TBL; - - if (!isset($ADODB_SESSION_USE_LOBS)) $ADODB_SESSION_USE_LOBS = 'CLOB'; - - $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); - if ($ADODB_SESS_LIFE <= 1) { - // bug in PHP 4.0.3 pl 1 -- how about other versions? - //print "<h3>Session Error: PHP.INI setting <i>session.gc_maxlifetime</i>not set: $ADODB_SESS_LIFE</h3>"; - $ADODB_SESS_LIFE=1440; - } - $ADODB_SESSION_CRC = false; - //$ADODB_SESS_DEBUG = true; - - ////////////////////////////////// - /* SET THE FOLLOWING PARAMETERS */ - ////////////////////////////////// - - if (empty($ADODB_SESSION_DRIVER)) { - $ADODB_SESSION_DRIVER='mysql'; - $ADODB_SESSION_CONNECT='localhost'; - $ADODB_SESSION_USER ='root'; - $ADODB_SESSION_PWD =''; - $ADODB_SESSION_DB ='xphplens_2'; - } - - if (empty($ADODB_SESSION_EXPIRE_NOTIFY)) { - $ADODB_SESSION_EXPIRE_NOTIFY = false; - } - // Made table name configurable - by David Johnson djohnson@inpro.net - if (empty($ADODB_SESSION_TBL)){ - $ADODB_SESSION_TBL = 'sessions'; - } - - - // defaulting $ADODB_SESSION_USE_LOBS - if (!isset($ADODB_SESSION_USE_LOBS) || empty($ADODB_SESSION_USE_LOBS)) { - $ADODB_SESSION_USE_LOBS = false; - } - - /* - $ADODB_SESS['driver'] = $ADODB_SESSION_DRIVER; - $ADODB_SESS['connect'] = $ADODB_SESSION_CONNECT; - $ADODB_SESS['user'] = $ADODB_SESSION_USER; - $ADODB_SESS['pwd'] = $ADODB_SESSION_PWD; - $ADODB_SESS['db'] = $ADODB_SESSION_DB; - $ADODB_SESS['life'] = $ADODB_SESS_LIFE; - $ADODB_SESS['debug'] = $ADODB_SESS_DEBUG; - - $ADODB_SESS['debug'] = $ADODB_SESS_DEBUG; - $ADODB_SESS['table'] = $ADODB_SESS_TBL; - */ - -/****************************************************************************************\ - Create the connection to the database. - - If $ADODB_SESS_CONN already exists, reuse that connection -\****************************************************************************************/ -function adodb_sess_open($save_path, $session_name,$persist=true) -{ -GLOBAL $ADODB_SESS_CONN; - if (isset($ADODB_SESS_CONN)) return true; - -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_DEBUG; - - // cannot use & below - do not know why... - $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER); - if (!empty($ADODB_SESS_DEBUG)) { - $ADODB_SESS_CONN->debug = true; - ADOConnection::outp( " conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB "); - } - if ($persist) $ok = $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT, - $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); - else $ok = $ADODB_SESS_CONN->Connect($ADODB_SESSION_CONNECT, - $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); - - if (!$ok) ADOConnection::outp( " --- Session: connection failed</p>",false); -} - -/****************************************************************************************\ - Close the connection -\****************************************************************************************/ -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -/****************************************************************************************\ - Slurp in the session variables and return the serialized string -\****************************************************************************************/ -function adodb_sess_read($key) -{ -global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; - - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $v = ''; - } else - $v = rawurldecode(reset($rs->fields)); - - $rs->Close(); - - // new optimization adodb 2.1 - $ADODB_SESSION_CRC = strlen($v).crc32($v); - - return $v; - } - - return ''; // thx to Jorma Tuomainen, webmaster#wizactive.com -} - -/****************************************************************************************\ - Write the serialized data to a database. - - If the data has not been modified since adodb_sess_read(), we do not write. -\****************************************************************************************/ -function adodb_sess_write($key, $val) -{ - global - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESSION_TBL, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_CRC, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_DRIVER, // added - $ADODB_SESSION_USE_LOBS; // added - - $expiry = time() + $ADODB_SESS_LIFE; - - // crc32 optimization since adodb 2.1 - // now we only update expiry date, thx to sebastian thom in adodb 2.32 - if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { - if ($ADODB_SESS_DEBUG) echo " --- Session: Only updating date - crc32 not changed</p>"; - $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); - $rs = $ADODB_SESS_CONN->Execute($qry); - return true; - } - $val = rawurlencode($val); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - - - if ($ADODB_SESSION_USE_LOBS === false) { // no lobs, simply use replace() - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, 'sesskey',$autoQuote = true); - if (!$rs) { - $err = $ADODB_SESS_CONN->ErrorMsg(); - } - } else { - // what value shall we insert/update for lob row? - switch ($ADODB_SESSION_DRIVER) { - // empty_clob or empty_lob for oracle dbs - case "oracle": - case "oci8": - case "oci8po": - case "oci805": - $lob_value = sprintf("empty_%s()", strtolower($ADODB_SESSION_USE_LOBS)); - break; - - // null for all other - default: - $lob_value = "null"; - break; - } - - // do we insert or update? => as for sesskey - $res = $ADODB_SESS_CONN->Execute("select count(*) as cnt from $ADODB_SESSION_TBL where sesskey = '$key'"); - if ($res && reset($res->fields) > 0) { - $qry = sprintf("update %s set expiry = %d, data = %s where sesskey = '%s'", $ADODB_SESSION_TBL, $expiry, $lob_value, $key); - } else { - // insert - $qry = sprintf("insert into %s (sesskey, expiry, data) values ('%s', %d, %s)", $ADODB_SESSION_TBL, $key, $expiry, $lob_value); - } - - $err = ""; - $rs1 = $ADODB_SESS_CONN->Execute($qry); - if (!$rs1) { - $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; - } - $rs2 = $ADODB_SESS_CONN->UpdateBlob($ADODB_SESSION_TBL, 'data', $val, "sesskey='$key'", strtoupper($ADODB_SESSION_USE_LOBS)); - if (!$rs2) { - $err .= $ADODB_SESS_CONN->ErrorMsg()."\n"; - } - $rs = ($rs1 && $rs2) ? true : false; - } - - if (!$rs) { - ADOConnection::outp( ' --- Session Replace: '.nl2br($err).'</p>',false); - } else { - // bug in access driver (could be odbc?) means that info is not committed - // properly unless select statement executed in Win2000 - if ($ADODB_SESS_CONN->databaseType == 'access') - $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return !empty($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - -function adodb_sess_gc($maxlifetime) -{ - global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $t = time(); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < $t"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $rs->Close(); - - //$ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE expiry < $t"); - $ADODB_SESS_CONN->CommitTrans(); - - } - } else { - $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time()); - - if ($ADODB_SESS_DEBUG) ADOConnection::outp(" --- <b>Garbage Collection</b>: $qry</p>"); - } - // suggested by Cameron, "GaM3R" <gamr@outworld.cx> - if (defined('ADODB_SESSION_OPTIMIZE')) { - global $ADODB_SESSION_DRIVER; - - switch( $ADODB_SESSION_DRIVER ) { - case 'mysql': - case 'mysqlt': - $opt_qry = 'OPTIMIZE TABLE '.$ADODB_SESSION_TBL; - break; - case 'postgresql': - case 'postgresql7': - $opt_qry = 'VACUUM '.$ADODB_SESSION_TBL; - break; - } - if (!empty($opt_qry)) { - $ADODB_SESS_CONN->Execute($opt_qry); - } - } - if ($ADODB_SESS_CONN->dataProvider === 'oci8') $sql = 'select TO_CHAR('.($ADODB_SESS_CONN->sysTimeStamp).', \'RRRR-MM-DD HH24:MI:SS\') from '. $ADODB_SESSION_TBL; - else $sql = 'select '.$ADODB_SESS_CONN->sysTimeStamp.' from '. $ADODB_SESSION_TBL; - - $rs = $ADODB_SESS_CONN->SelectLimit($sql,1); - if ($rs && !$rs->EOF) { - - $dbts = reset($rs->fields); - $rs->Close(); - $dbt = $ADODB_SESS_CONN->UnixTimeStamp($dbts); - $t = time(); - if (abs($dbt - $t) >= ADODB_SESSION_SYNCH_SECS) { - $msg = - __FILE__.": Server time for webserver {$_SERVER['HTTP_HOST']} not in synch with database: database=$dbt ($dbts), webserver=$t (diff=".(abs($dbt-$t)/3600)." hrs)"; - error_log($msg); - if ($ADODB_SESS_DEBUG) ADOConnection::outp(" --- $msg</p>"); - } - } - - return true; -} - -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ - -if (0) { - - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - ADOConnection::outp( " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>",false); -} diff --git a/session/old/adodb-session.php b/session/old/adodb-session.php deleted file mode 100644 index 5fd43abc..00000000 --- a/session/old/adodb-session.php +++ /dev/null @@ -1,449 +0,0 @@ -<?php -/** - * ADOdb Session Management - * - * This file provides PHP4 session management using the ADODB database - * wrapper library. - * - * @deprecated - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -/* - Example - ======= - - include('adodb.inc.php'); - include('adodb-session.php'); - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; - -To force non-persistent connections, call adodb_session_open first before session_start(): - - include('adodb.inc.php'); - include('adodb-session.php'); - adodb_sess_open(false,false,false); - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - print " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>"; - - - Installation - ============ - 1. Create this table in your database (syntax might vary depending on your db): - - create table sessions ( - SESSKEY char(32) not null, - EXPIRY int(11) unsigned not null, - EXPIREREF varchar(64), - DATA text not null, - primary key (sesskey) - ); - - For oracle: - create table sessions ( - SESSKEY char(32) not null, - EXPIRY DECIMAL(16) not null, - EXPIREREF varchar(64), - DATA varchar(4000) not null, - primary key (sesskey) - ); - - - 2. Then define the following parameters. You can either modify - this file, or define them before this file is included: - - $ADODB_SESSION_DRIVER='database driver, eg. mysql or ibase'; - $ADODB_SESSION_CONNECT='server to connect to'; - $ADODB_SESSION_USER ='user'; - $ADODB_SESSION_PWD ='password'; - $ADODB_SESSION_DB ='database'; - $ADODB_SESSION_TBL = 'sessions' - - 3. Recommended is PHP 4.1.0 or later. There are documented - session bugs in earlier versions of PHP. - - 4. If you want to receive notifications when a session expires, then - you can tag a session with an EXPIREREF, and before the session - record is deleted, we can call a function that will pass the EXPIREREF - as the first parameter, and the session key as the second parameter. - - To do this, define a notification function, say NotifyFn: - - function NotifyFn($expireref, $sesskey) - { - } - - Then you need to define a global variable $ADODB_SESSION_EXPIRE_NOTIFY. - This is an array with 2 elements, the first being the name of the variable - you would like to store in the EXPIREREF field, and the 2nd is the - notification function's name. - - In this example, we want to be notified when a user's session - has expired, so we store the user id in the global variable $USERID, - store this value in the EXPIREREF field: - - $ADODB_SESSION_EXPIRE_NOTIFY = array('USERID','NotifyFn'); - - Then when the NotifyFn is called, we are passed the $USERID as the first - parameter, eg. NotifyFn($userid, $sesskey). -*/ - -if (!defined('_ADODB_LAYER')) { - include (dirname(__FILE__).'/adodb.inc.php'); -} - -if (!defined('ADODB_SESSION')) { - - define('ADODB_SESSION',1); - - /* if database time and system time is difference is greater than this, then give warning */ - define('ADODB_SESSION_SYNCH_SECS',60); - - /* - Thanks Joe Li. See PHPLens Issue No: 11487&x=1 -*/ -function adodb_session_regenerate_id() -{ - $conn = ADODB_Session::_conn(); - if (!$conn) return false; - - $old_id = session_id(); - if (function_exists('session_regenerate_id')) { - session_regenerate_id(); - } else { - session_id(md5(uniqid(rand(), true))); - $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); - //@session_start(); - } - $new_id = session_id(); - $ok = $conn->Execute('UPDATE '. ADODB_Session::table(). ' SET sesskey='. $conn->qstr($new_id). ' WHERE sesskey='.$conn->qstr($old_id)); - - /* it is possible that the update statement fails due to a collision */ - if (!$ok) { - session_id($old_id); - if (empty($ck)) $ck = session_get_cookie_params(); - setcookie(session_name(), session_id(), false, $ck['path'], $ck['domain'], $ck['secure']); - return false; - } - - return true; -} - -/****************************************************************************************\ - Global definitions -\****************************************************************************************/ -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_EXPIRE_NOTIFY, - $ADODB_SESSION_CRC, - $ADODB_SESSION_TBL; - - - $ADODB_SESS_LIFE = ini_get('session.gc_maxlifetime'); - if ($ADODB_SESS_LIFE <= 1) { - // bug in PHP 4.0.3 pl 1 -- how about other versions? - //print "<h3>Session Error: PHP.INI setting <i>session.gc_maxlifetime</i>not set: $ADODB_SESS_LIFE</h3>"; - $ADODB_SESS_LIFE=1440; - } - $ADODB_SESSION_CRC = false; - //$ADODB_SESS_DEBUG = true; - - ////////////////////////////////// - /* SET THE FOLLOWING PARAMETERS */ - ////////////////////////////////// - - if (empty($ADODB_SESSION_DRIVER)) { - $ADODB_SESSION_DRIVER='mysql'; - $ADODB_SESSION_CONNECT='localhost'; - $ADODB_SESSION_USER ='root'; - $ADODB_SESSION_PWD =''; - $ADODB_SESSION_DB ='xphplens_2'; - } - - if (empty($ADODB_SESSION_EXPIRE_NOTIFY)) { - $ADODB_SESSION_EXPIRE_NOTIFY = false; - } - // Made table name configurable - by David Johnson djohnson@inpro.net - if (empty($ADODB_SESSION_TBL)){ - $ADODB_SESSION_TBL = 'sessions'; - } - - /* - $ADODB_SESS['driver'] = $ADODB_SESSION_DRIVER; - $ADODB_SESS['connect'] = $ADODB_SESSION_CONNECT; - $ADODB_SESS['user'] = $ADODB_SESSION_USER; - $ADODB_SESS['pwd'] = $ADODB_SESSION_PWD; - $ADODB_SESS['db'] = $ADODB_SESSION_DB; - $ADODB_SESS['life'] = $ADODB_SESS_LIFE; - $ADODB_SESS['debug'] = $ADODB_SESS_DEBUG; - - $ADODB_SESS['debug'] = $ADODB_SESS_DEBUG; - $ADODB_SESS['table'] = $ADODB_SESS_TBL; - */ - -/****************************************************************************************\ - Create the connection to the database. - - If $ADODB_SESS_CONN already exists, reuse that connection -\****************************************************************************************/ -function adodb_sess_open($save_path, $session_name,$persist=true) -{ -GLOBAL $ADODB_SESS_CONN; - if (isset($ADODB_SESS_CONN)) return true; - -GLOBAL $ADODB_SESSION_CONNECT, - $ADODB_SESSION_DRIVER, - $ADODB_SESSION_USER, - $ADODB_SESSION_PWD, - $ADODB_SESSION_DB, - $ADODB_SESS_DEBUG; - - // cannot use & below - do not know why... - $ADODB_SESS_CONN = ADONewConnection($ADODB_SESSION_DRIVER); - if (!empty($ADODB_SESS_DEBUG)) { - $ADODB_SESS_CONN->debug = true; - ADOConnection::outp( " conn=$ADODB_SESSION_CONNECT user=$ADODB_SESSION_USER pwd=$ADODB_SESSION_PWD db=$ADODB_SESSION_DB "); - } - if ($persist) $ok = $ADODB_SESS_CONN->PConnect($ADODB_SESSION_CONNECT, - $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); - else $ok = $ADODB_SESS_CONN->Connect($ADODB_SESSION_CONNECT, - $ADODB_SESSION_USER,$ADODB_SESSION_PWD,$ADODB_SESSION_DB); - - if (!$ok) ADOConnection::outp( " --- Session: connection failed</p>",false); -} - -/****************************************************************************************\ - Close the connection -\****************************************************************************************/ -function adodb_sess_close() -{ -global $ADODB_SESS_CONN; - - if ($ADODB_SESS_CONN) $ADODB_SESS_CONN->Close(); - return true; -} - -/****************************************************************************************\ - Slurp in the session variables and return the serialized string -\****************************************************************************************/ -function adodb_sess_read($key) -{ -global $ADODB_SESS_CONN,$ADODB_SESSION_TBL,$ADODB_SESSION_CRC; - - $rs = $ADODB_SESS_CONN->Execute("SELECT data FROM $ADODB_SESSION_TBL WHERE sesskey = '$key' AND expiry >= " . time()); - if ($rs) { - if ($rs->EOF) { - $v = ''; - } else - $v = rawurldecode(reset($rs->fields)); - - $rs->Close(); - - // new optimization adodb 2.1 - $ADODB_SESSION_CRC = strlen($v).crc32($v); - - return $v; - } - - return ''; // thx to Jorma Tuomainen, webmaster#wizactive.com -} - -/****************************************************************************************\ - Write the serialized data to a database. - - If the data has not been modified since adodb_sess_read(), we do not write. -\****************************************************************************************/ -function adodb_sess_write($key, $val) -{ - global - $ADODB_SESS_CONN, - $ADODB_SESS_LIFE, - $ADODB_SESSION_TBL, - $ADODB_SESS_DEBUG, - $ADODB_SESSION_CRC, - $ADODB_SESSION_EXPIRE_NOTIFY; - - $expiry = time() + $ADODB_SESS_LIFE; - - // crc32 optimization since adodb 2.1 - // now we only update expiry date, thx to sebastian thom in adodb 2.32 - if ($ADODB_SESSION_CRC !== false && $ADODB_SESSION_CRC == strlen($val).crc32($val)) { - if ($ADODB_SESS_DEBUG) echo " --- Session: Only updating date - crc32 not changed</p>"; - $qry = "UPDATE $ADODB_SESSION_TBL SET expiry=$expiry WHERE sesskey='$key' AND expiry >= " . time(); - $rs = $ADODB_SESS_CONN->Execute($qry); - return true; - } - $val = rawurlencode($val); - - $arr = array('sesskey' => $key, 'expiry' => $expiry, 'data' => $val); - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - $var = reset($ADODB_SESSION_EXPIRE_NOTIFY); - global $$var; - $arr['expireref'] = $$var; - } - $rs = $ADODB_SESS_CONN->Replace($ADODB_SESSION_TBL,$arr, - 'sesskey',$autoQuote = true); - - if (!$rs) { - ADOConnection::outp( ' --- Session Replace: '.$ADODB_SESS_CONN->ErrorMsg().'</p>',false); - } else { - // bug in access driver (could be odbc?) means that info is not committed - // properly unless select statement executed in Win2000 - if ($ADODB_SESS_CONN->databaseType == 'access') - $rs = $ADODB_SESS_CONN->Execute("select sesskey from $ADODB_SESSION_TBL WHERE sesskey='$key'"); - } - return !empty($rs); -} - -function adodb_sess_destroy($key) -{ - global $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $ADODB_SESS_CONN->CommitTrans(); - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE sesskey = '$key'"; - $rs = $ADODB_SESS_CONN->Execute($qry); - } - return $rs ? true : false; -} - -function adodb_sess_gc($maxlifetime) -{ - global $ADODB_SESS_DEBUG, $ADODB_SESS_CONN, $ADODB_SESSION_TBL,$ADODB_SESSION_EXPIRE_NOTIFY; - - if ($ADODB_SESSION_EXPIRE_NOTIFY) { - reset($ADODB_SESSION_EXPIRE_NOTIFY); - $fn = next($ADODB_SESSION_EXPIRE_NOTIFY); - $savem = $ADODB_SESS_CONN->SetFetchMode(ADODB_FETCH_NUM); - $t = time(); - $rs = $ADODB_SESS_CONN->Execute("SELECT expireref,sesskey FROM $ADODB_SESSION_TBL WHERE expiry < $t"); - $ADODB_SESS_CONN->SetFetchMode($savem); - if ($rs) { - $ADODB_SESS_CONN->BeginTrans(); - while (!$rs->EOF) { - $ref = $rs->fields[0]; - $key = $rs->fields[1]; - $fn($ref,$key); - $del = $ADODB_SESS_CONN->Execute("DELETE FROM $ADODB_SESSION_TBL WHERE sesskey='$key'"); - $rs->MoveNext(); - } - $rs->Close(); - - $ADODB_SESS_CONN->CommitTrans(); - - } - } else { - $qry = "DELETE FROM $ADODB_SESSION_TBL WHERE expiry < " . time(); - $ADODB_SESS_CONN->Execute($qry); - - if ($ADODB_SESS_DEBUG) ADOConnection::outp(" --- <b>Garbage Collection</b>: $qry</p>"); - } - // suggested by Cameron, "GaM3R" <gamr@outworld.cx> - if (defined('ADODB_SESSION_OPTIMIZE')) { - global $ADODB_SESSION_DRIVER; - - switch( $ADODB_SESSION_DRIVER ) { - case 'mysql': - case 'mysqlt': - $opt_qry = 'OPTIMIZE TABLE '.$ADODB_SESSION_TBL; - break; - case 'postgresql': - case 'postgresql7': - $opt_qry = 'VACUUM '.$ADODB_SESSION_TBL; - break; - } - if (!empty($opt_qry)) { - $ADODB_SESS_CONN->Execute($opt_qry); - } - } - if ($ADODB_SESS_CONN->dataProvider === 'oci8') $sql = 'select TO_CHAR('.($ADODB_SESS_CONN->sysTimeStamp).', \'RRRR-MM-DD HH24:MI:SS\') from '. $ADODB_SESSION_TBL; - else $sql = 'select '.$ADODB_SESS_CONN->sysTimeStamp.' from '. $ADODB_SESSION_TBL; - - $rs = $ADODB_SESS_CONN->SelectLimit($sql,1); - if ($rs && !$rs->EOF) { - - $dbts = reset($rs->fields); - $rs->Close(); - $dbt = $ADODB_SESS_CONN->UnixTimeStamp($dbts); - $t = time(); - - if (abs($dbt - $t) >= ADODB_SESSION_SYNCH_SECS) { - - $msg = - __FILE__.": Server time for webserver {$_SERVER['HTTP_HOST']} not in synch with database: database=$dbt ($dbts), webserver=$t (diff=".(abs($dbt-$t)/3600)." hrs)"; - error_log($msg); - if ($ADODB_SESS_DEBUG) ADOConnection::outp(" --- $msg</p>"); - } - } - - return true; -} - -session_set_save_handler( - "adodb_sess_open", - "adodb_sess_close", - "adodb_sess_read", - "adodb_sess_write", - "adodb_sess_destroy", - "adodb_sess_gc"); -} - -/* TEST SCRIPT -- UNCOMMENT */ - -if (0) { - - session_start(); - session_register('AVAR'); - $_SESSION['AVAR'] += 1; - ADOConnection::outp( " --- \$_SESSION['AVAR']={$_SESSION['AVAR']}</p>",false); -} diff --git a/session/old/crypt.inc.php b/session/old/crypt.inc.php deleted file mode 100644 index 089e24a0..00000000 --- a/session/old/crypt.inc.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * ADOdb Session Management - * - * @deprecated - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - * @author Ari Kuorikoski <ari.kuorikoski@finebyte.com> - */ - -class MD5Crypt{ - function keyED($txt,$encrypt_key) - { - $encrypt_key = md5($encrypt_key); - $ctr=0; - $tmp = ""; - for ($i=0;$i<strlen($txt);$i++){ - if ($ctr==strlen($encrypt_key)) $ctr=0; - $tmp.= substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1); - $ctr++; - } - return $tmp; - } - - function Encrypt($txt,$key) - { - $encrypt_key = md5(rand(0,32000)); - $ctr=0; - $tmp = ""; - for ($i=0;$i<strlen($txt);$i++) - { - if ($ctr==strlen($encrypt_key)) $ctr=0; - $tmp.= substr($encrypt_key,$ctr,1) . - (substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1)); - $ctr++; - } - return base64_encode($this->keyED($tmp,$key)); - } - - function Decrypt($txt,$key) - { - $txt = $this->keyED(base64_decode($txt),$key); - $tmp = ""; - for ($i=0;$i<strlen($txt);$i++){ - $md5 = substr($txt,$i,1); - $i++; - $tmp.= (substr($txt,$i,1) ^ $md5); - } - return $tmp; - } - - function RandPass() - { - $randomPassword = ""; - for($i=0;$i<8;$i++) - { - $randnumber = rand(48,120); - - while (($randnumber >= 58 && $randnumber <= 64) || ($randnumber >= 91 && $randnumber <= 96)) - { - $randnumber = rand(48,120); - } - - $randomPassword .= chr($randnumber); - } - return $randomPassword; - } - -} diff --git a/tests/PdoDriverTest.php b/tests/PdoDriverTest.php new file mode 100644 index 00000000..c22b291e --- /dev/null +++ b/tests/PdoDriverTest.php @@ -0,0 +1,160 @@ +<?php +/** + * Tests cases for adodb-lib.inc.php + * + * This file is part of ADOdb, a Database Abstraction Layer library for PHP. + * + * @package ADOdb + * @link https://adodb.org Project's web site and documentation + * @link https://github.com/ADOdb/ADOdb Source code and issue tracker + * + * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause + * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, + * any later version. This means you can use it in proprietary products. + * See the LICENSE.md file distributed with this source code for details. + * @license BSD-3-Clause + * @license LGPL-2.1-or-later + * + * @copyright 2000-2013 John Lim + * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + */ + +use PHPUnit\Framework\TestCase; + +require_once dirname(__FILE__) . '/../drivers/adodb-pdo.inc.php'; + +/** + * Class PdoDriverTest. + * + * Test cases for drivers/adodb-pdo.inc.php + */ +class PdoDriverTest extends TestCase +{ + /** + * Test for {@see ADODB_pdo#containsQuestionMarkPlaceholder) + * + * @dataProvider providerContainsQuestionMarkPlaceholder + */ + public function testContainsQuestionMarkPlaceholder($result, $sql): void + { + $method = new ReflectionMethod('ADODB_pdo', 'containsQuestionMarkPlaceholder'); + $method->setAccessible(true); + + $pdoDriver = new ADODB_pdo(); + $this->assertSame($result, $method->invoke($pdoDriver, $sql)); + } + + /** + * Data provider for {@see testContainsQuestionMarkPlaceholder()} + * + * @return array [result, SQL statement] + */ + public function providerContainsQuestionMarkPlaceholder(): array + { + return [ + [true, 'SELECT * FROM employees WHERE emp_no = ?;'], + [true, 'SELECT * FROM employees WHERE emp_no = ?'], + [true, 'SELECT * FROM employees WHERE emp_no=?'], + [true, 'SELECT * FROM employees WHERE emp_no>?'], + [true, 'SELECT * FROM employees WHERE emp_no<?'], + [true, 'SELECT * FROM employees WHERE emp_no>=?'], + [true, 'SELECT * FROM employees WHERE emp_no<=?'], + [true, 'SELECT * FROM employees WHERE emp_no<>?'], + [true, 'SELECT * FROM employees WHERE emp_no!=?'], + [true, 'SELECT * FROM employees WHERE emp_no IN (?)'], + [true, 'SELECT * FROM employees WHERE emp_no=`?` OR emp_no=?'], + [true, 'UPDATE employees SET emp_name=? WHERE emp_no=?'], + + [false, 'SELECT * FROM employees'], + [false, 'SELECT * FROM employees WHERE emp_no=`?`'], + [false, 'SELECT * FROM employees WHERE emp_no=??'], + [false, 'SELECT * FROM employees WHERE emp_no=:emp_no'], + ]; + } + + /** + * Test for {@see ADODB_pdo#conformToBindParameterStyle) + * + * @dataProvider providerConformToBindParameterStyle + */ + public function testConformToBindParameterStyle($expected, $inputarr, $bindParameterStyle, $sql): void + { + $method = new ReflectionMethod('ADODB_pdo', 'conformToBindParameterStyle'); + $method->setAccessible(true); + + $pdoDriver = new ADODB_pdo(); + $pdoDriver->bindParameterStyle = $bindParameterStyle; + $this->assertSame($expected, $method->invoke($pdoDriver, $sql, $inputarr)); + } + + /** + * Data provider for {@see testConformToBindParameterStyle()} + * + * @return array [expected, inputarr, bindParameterStyle, SQL statement] + */ + public function providerConformToBindParameterStyle(): array + { + return [ + [ + [1, 2, 3], + [1, 2, 3], + ADODB_pdo::BIND_USE_QUESTION_MARKS, + null + ], + [ + [1, 2, 3], + ['a' => 1, 'b' => 2, 'c' => 3], + ADODB_pdo::BIND_USE_QUESTION_MARKS, + null + ], + [ + [1, 2, 3], + [1, 2, 3], + ADODB_pdo::BIND_USE_NAMED_PARAMETERS, + null + ], + [ + ['a' => 1, 'b' => 2, 'c' => 3], + ['a' => 1, 'b' => 2, 'c' => 3], + ADODB_pdo::BIND_USE_NAMED_PARAMETERS, + null + ], + [ + [1, 2, 3], + [1, 2, 3], + ADODB_pdo::BIND_USE_BOTH, + 'SELECT * FROM employees WHERE emp_no = ?' + ], + [ + [1, 2, 3], + ['a' => 1, 'b' => 2, 'c' => 3], + ADODB_pdo::BIND_USE_BOTH, + 'SELECT * FROM employees WHERE emp_no = ?' + ], + [ + [1, 2, 3], + [1, 2, 3], + ADODB_pdo::BIND_USE_BOTH, + 'SELECT * FROM employees WHERE emp_no = :id' + ], + [ + ['a' => 1, 'b' => 2, 'c' => 3], + ['a' => 1, 'b' => 2, 'c' => 3], + ADODB_pdo::BIND_USE_BOTH, + 'SELECT * FROM employees WHERE emp_no = :id' + ], + [ + [1, 2, 3], + [1, 2, 3], + 9999, // Incorrect values result in default behavior. + null + ], + [ + [1, 2, 3], + ['a' => 1, 'b' => 2, 'c' => 3], + 9999, // Incorrect values result in default behavior. + null + ], + ]; + } +} diff --git a/tests/test.php b/tests/test.php index 08a2e301..37709004 100644 --- a/tests/test.php +++ b/tests/test.php @@ -1764,9 +1764,6 @@ Test <a href=test4.php>GetInsertSQL/GetUpdateSQL</a> <a href=test-perf.php>Perf Monitor</a><p> <?php - -include_once('../adodb-time.inc.php'); -if (isset($_GET['time'])) adodb_date_test(); flush(); include_once('./testdatabases.inc.php'); diff --git a/tests/time.php b/tests/time.php deleted file mode 100644 index 8760ccd4..00000000 --- a/tests/time.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * ADOdb tests - Time. - * - * This file is part of ADOdb, a Database Abstraction Layer library for PHP. - * - * @package ADOdb - * @link https://adodb.org Project's web site and documentation - * @link https://github.com/ADOdb/ADOdb Source code and issue tracker - * - * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause - * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option, - * any later version. This means you can use it in proprietary products. - * See the LICENSE.md file distributed with this source code for details. - * @license BSD-3-Clause - * @license LGPL-2.1-or-later - * - * @copyright 2000-2013 John Lim - * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community - */ - -include_once('../adodb-time.inc.php'); -adodb_date_test(); -?> -<?php -//require("adodb-time.inc.php"); - -$datestring = "2063-12-24"; // string normally from mySQL -$stringArray = explode("-", $datestring); -$date = adodb_mktime(0,0,0,$stringArray[1],$stringArray[2],$stringArray[0]); - -$convertedDate = adodb_date("d-M-Y", $date); // converted string to UK style date - -echo( "Original: $datestring<br>" ); -echo( "Converted: $convertedDate" ); //why is string returned as one day (3 not 4) less for this example?? |
