diff options
70 files changed, 3042 insertions, 6633 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..e62cc13c --- /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](https://github.com/ADOdb/ADOdb/tree/master) branch / latest [hotfix](https://github.com/ADOdb/ADOdb/branches/all?query=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-active-record.inc.php b/adodb-active-record.inc.php index 558e3ce3..0ab8056b 100644 --- a/adodb-active-record.inc.php +++ b/adodb-active-record.inc.php @@ -21,23 +21,46 @@ include_once(ADODB_DIR.'/adodb-lib.inc.php'); -global $_ADODB_ACTIVE_DBS; -global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info -global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks -global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record. - -// array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat +/** + * Array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat. + * @see ADODB_Active_Record + * + * @global ADODB_Active_DB[] $_ADODB_ACTIVE_DBS + */ $_ADODB_ACTIVE_DBS = array(); + +/** + * Set to true to enable caching of metadata such as field info. + * @global bool $ADODB_ACTIVE_CACHESECS + */ +$ADODB_ACTIVE_CACHESECS = 0; + +/** + * Set to false to disable safety checks + * @global bool $ACTIVE_RECORD_SAFETY + */ $ACTIVE_RECORD_SAFETY = true; + +/** + * Use default values of table definition when creating new active record. + * @global bool $ADODB_ACTIVE_DEFVALS + */ $ADODB_ACTIVE_DEFVALS = false; -$ADODB_ACTIVE_CACHESECS = 0; + class ADODB_Active_DB { - var $db; // ADOConnection - var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename + /** @var ADOConnection */ + var $db; + + /** + * assoc array of ADODB_Active_Table objects, indexed by tablename + * @var ADODB_Active_Table[] + */ + var $tables; } -class ADODB_Active_Table { +class ADODB_Active_Table +{ var $name; // table name var $flds; // assoc array of adofieldobjs, indexed by fieldname var $keys; // assoc array of primary keys, indexed by fieldname @@ -46,16 +69,19 @@ class ADODB_Active_Table { var $_hasMany = array(); } -// $db = database connection -// $index = name of index - can be associative, for an example see -// PHPLens Issue No: 17790 -// returns index into $_ADODB_ACTIVE_DBS -function ADODB_SetDatabaseAdapter(&$db, $index=false) +/** + * @param ADOConnection $db Database connection + * @param string|int $index Name of index - can be associative + * for an example see PHPLens Issue No: 17790 + * + * @return int|string + */ +function ADODB_SetDatabaseAdapter($db, $index=false) { global $_ADODB_ACTIVE_DBS; - foreach($_ADODB_ACTIVE_DBS as $k => $d) { - if($d->db === $db) { + foreach ($_ADODB_ACTIVE_DBS as $k => $d) { + if ($d->db === $db) { return $k; } } @@ -64,17 +90,18 @@ function ADODB_SetDatabaseAdapter(&$db, $index=false) $obj->db = $db; $obj->tables = array(); - if ($index == false) { + if (!$index) { $index = sizeof($_ADODB_ACTIVE_DBS); } $_ADODB_ACTIVE_DBS[$index] = $obj; - return sizeof($_ADODB_ACTIVE_DBS)-1; + return sizeof($_ADODB_ACTIVE_DBS) - 1; } -class ADODB_Active_Record { +class ADODB_Active_Record +{ static $_changeNames = true; // dynamically pluralize table names /** @var bool|string Allows override of global $ADODB_QUOTE_FIELDNAMES */ @@ -93,17 +120,16 @@ class ADODB_Active_Record { var $lockMode = ' for update '; // you might want to change to - static function UseDefaultValues($bool=null) + static function UseDefaultValues($bool = null) { - global $ADODB_ACTIVE_DEFVALS; + global $ADODB_ACTIVE_DEFVALS; if (isset($bool)) { $ADODB_ACTIVE_DEFVALS = $bool; } return $ADODB_ACTIVE_DEFVALS; } - // should be static - static function SetDatabaseAdapter(&$db, $index=false) + static function SetDatabaseAdapter($db, $index=false) { return ADODB_SetDatabaseAdapter($db, $index); } @@ -115,7 +141,6 @@ class ADODB_Active_Record { $this->$name = $value; } - // php5 constructor function __construct($table = false, $pkeyarr=false, $db=false) { global $_ADODB_ACTIVE_DBS, $ADODB_QUOTE_FIELDNAMES; @@ -125,7 +150,7 @@ class ADODB_Active_Record { $this->_quoteNames = $ADODB_QUOTE_FIELDNAMES; } - if ($db == false && is_object($pkeyarr)) { + if (!$db && is_object($pkeyarr)) { $db = $pkeyarr; $pkeyarr = false; } @@ -133,8 +158,7 @@ class ADODB_Active_Record { if (!$table) { if (!empty($this->_table)) { $table = $this->_table; - } - else $table = $this->_pluralize(get_class($this)); + } else $table = $this->_pluralize(get_class($this)); } $this->foreignName = strtolower(get_class($this)); // CFR: default foreign name if ($db) { @@ -158,8 +182,8 @@ class ADODB_Active_Record { function __wakeup() { - $class = get_class($this); - new $class; + $class = get_class($this); + new $class; } function _pluralize($table) @@ -170,15 +194,15 @@ class ADODB_Active_Record { $ut = strtoupper($table); $len = strlen($table); - $lastc = $ut[$len-1]; - $lastc2 = substr($ut,$len-2); + $lastc = $ut[$len - 1]; + $lastc2 = substr($ut, $len - 2); switch ($lastc) { case 'S': + case 'X': return $table.'es'; case 'Y': return substr($table,0,$len-1).'ies'; - case 'X': - return $table.'es'; + /** @noinspection PhpMissingBreakStatementInspection */ case 'H': if ($lastc2 == 'CH' || $lastc2 == 'SH') { return $table.'es'; @@ -192,31 +216,31 @@ class ADODB_Active_Record { // Note: There is an assumption here...and it is that the argument's length >= 4 function _singularize($tables) { - if (!ADODB_Active_Record::$_changeNames) { - return $table; + return $tables; } $ut = strtoupper($tables); $len = strlen($tables); - if($ut[$len-1] != 'S') { + if ($ut[$len - 1] != 'S') { return $tables; // I know...forget oxen } - if($ut[$len-2] != 'E') { - return substr($tables, 0, $len-1); + if ($ut[$len - 2] != 'E') { + return substr($tables, 0, $len - 1); } - switch($ut[$len-3]) { + switch ($ut[$len - 3]) { case 'S': case 'X': - return substr($tables, 0, $len-2); + return substr($tables, 0, $len - 2); case 'I': return substr($tables, 0, $len-3) . 'y'; + /** @noinspection PhpMissingBreakStatementInspection */ case 'H'; - if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') { - return substr($tables, 0, $len-2); + if ($ut[$len - 4] == 'C' || $ut[$len - 4] == 'S') { + return substr($tables, 0, $len - 2); } default: - return substr($tables, 0, $len-1); // ? + return substr($tables, 0, $len - 1); // ? } } @@ -225,10 +249,10 @@ class ADODB_Active_Record { $ar = new $foreignClass($foreignRef); $ar->foreignName = $foreignRef; $ar->UpdateActiveTable(); - $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix; + $ar->foreignKey = $foreignKey ?: $foreignRef.ADODB_Active_Record::$_foreignSuffix; $table =& $this->TableInfo(); $table->_hasMany[$foreignRef] = $ar; - # $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get() + # $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get() } // use when you don't want ADOdb to auto-pluralize tablename @@ -244,7 +268,7 @@ class ADODB_Active_Record { if (!is_array($tablePKey)) { $tablePKey = array($tablePKey); } - $ar = new ADODB_Active_Record($table,$tablePKey); + $ar = new ADODB_Active_Record($table, $tablePKey); $ar->hasMany($foreignRef, $foreignKey, $foreignClass); } @@ -258,34 +282,32 @@ class ADODB_Active_Record { } - function belongsTo($foreignRef,$foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record') + function belongsTo($foreignRef, $foreignKey = false, $parentKey = '', $parentClass = 'ADODB_Active_Record') { - global $inflector; - $ar = new $parentClass($this->_pluralize($foreignRef)); $ar->foreignName = $foreignRef; $ar->parentKey = $parentKey; $ar->UpdateActiveTable(); - $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix; + $ar->foreignKey = $foreignKey ?: $foreignRef . ADODB_Active_Record::$_foreignSuffix; $table =& $this->TableInfo(); $table->_belongsTo[$foreignRef] = $ar; - # $this->$foreignRef = $this->_belongsTo[$foreignRef]; + # $this->$foreignRef = $this->_belongsTo[$foreignRef]; } - static function ClassBelongsTo($class, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record') + static function ClassBelongsTo($class, $foreignRef, $foreignKey = false, $parentKey = '', $parentClass = 'ADODB_Active_Record') { $ar = new $class(); $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass); } - static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record') + static function TableBelongsTo($table, $foreignRef, $foreignKey = false, $parentKey = '', $parentClass = 'ADODB_Active_Record') { $ar = new ADODB_Active_Record($table); $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass); } - static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record') + static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey = false, $parentKey = '', $parentClass = 'ADODB_Active_Record') { if (!is_array($tablePKey)) { $tablePKey = array($tablePKey); @@ -302,19 +324,20 @@ class ADODB_Active_Record { * @access protected * @return mixed */ - function __get($name) + function __get($name) { return $this->LoadRelations($name, '', -1, -1); } /** * @param string $name - * @param string $whereOrderBy : eg. ' AND field1 = value ORDER BY field2' - * @param offset - * @param limit + * @param string $whereOrderBy : e.g. ' AND field1 = value ORDER BY field2' + * @param int $offset + * @param int $limit + * * @return mixed */ - function LoadRelations($name, $whereOrderBy='', $offset=-1,$limit=-1) + function LoadRelations($name, $whereOrderBy = '', $offset = -1, $limit = -1) { $extras = array(); $table = $this->TableInfo(); @@ -333,28 +356,26 @@ class ADODB_Active_Record { } } - if(!empty($table->_belongsTo[$name])) { + if (!empty($table->_belongsTo[$name])) { $obj = $table->_belongsTo[$name]; $columnName = $obj->foreignKey; - if(empty($this->$columnName)) { + if (empty($this->$columnName)) { $this->$name = null; - } - else { + } else { if ($obj->parentKey) { $key = $obj->parentKey; - } - else { + } else { $key = reset($table->keys); } - $arrayOfOne = $obj->Find($key.'='.$this->$columnName.' '.$whereOrderBy,false,false,$extras); + $arrayOfOne = $obj->Find($key . '=' . $this->$columnName . ' ' . $whereOrderBy, false, false, $extras); if ($arrayOfOne) { $this->$name = $arrayOfOne[0]; return $arrayOfOne[0]; } } } - if(!empty($table->_hasMany[$name])) { + if (!empty($table->_hasMany[$name])) { $obj = $table->_hasMany[$name]; $key = reset($table->keys); $id = @$this->$key; @@ -362,7 +383,7 @@ class ADODB_Active_Record { $db = $this->DB(); $id = $db->qstr($id); } - $objs = $obj->Find($obj->foreignKey.'='.$id. ' '.$whereOrderBy,false,false,$extras); + $objs = $obj->Find($obj->foreignKey . '=' . $id . ' ' . $whereOrderBy, false, false, $extras); if (!$objs) { $objs = array(); } @@ -372,13 +393,18 @@ class ADODB_Active_Record { return array(); } - ////////////////////////////////// - // update metadata - function UpdateActiveTable($pkeys=false,$forceUpdate=false) + /** + * Update metadata. + * + * @param $pkeys + * @param $forceUpdate + * @return false|void + */ + function UpdateActiveTable($pkeys=false, $forceUpdate=false) { - global $_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; - global $ADODB_ACTIVE_DEFVALS,$ADODB_FETCH_MODE; + global $_ADODB_ACTIVE_DBS, $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; + global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE; $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; @@ -388,32 +414,30 @@ class ADODB_Active_Record { if (!$forceUpdate && !empty($tables[$tableat])) { $acttab = $tables[$tableat]; - foreach($acttab->flds as $name => $fld) { + foreach ($acttab->flds as $name => $fld) { if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) { $this->$name = $fld->default_value; - } - else { + } else { $this->$name = null; } } return; } $db = $activedb->db; - $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; + $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_' . $table . '.cache'; if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { - $fp = fopen($fname,'r'); + $fp = fopen($fname, 'r'); @flock($fp, LOCK_SH); - $acttab = unserialize(fread($fp,100000)); + $acttab = unserialize(fread($fp, 100000)); fclose($fp); if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { // abs(rand()) randomizes deletion, reducing contention to delete/refresh file // ideally, you should cache at least 32 secs - foreach($acttab->flds as $name => $fld) { + foreach ($acttab->flds as $name => $fld) { if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) { $this->$name = $fld->default_value; - } - else { + } else { $this->$name = null; } } @@ -421,7 +445,7 @@ class ADODB_Active_Record { $activedb->tables[$table] = $acttab; //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); - return; + return; } else if ($db->debug) { ADOConnection::outp("Refreshing cached active record file: $fname"); } @@ -443,14 +467,14 @@ class ADODB_Active_Record { $ADODB_FETCH_MODE = $save; if (!$cols) { - $this->Error("Invalid table name: $table",'UpdateActiveTable'); + $this->Error("Invalid table name: $table", 'UpdateActiveTable'); return false; } $fld = reset($cols); if (!$pkeys) { if (isset($fld->primary_key)) { $pkeys = array(); - foreach($cols as $name => $fld) { + foreach ($cols as $name => $fld) { if (!empty($fld->primary_key)) { $pkeys[] = $name; } @@ -459,7 +483,7 @@ class ADODB_Active_Record { $pkeys = $this->GetPrimaryKeys($db, $table); } if (empty($pkeys)) { - $this->Error("No primary key found for table $table",'UpdateActiveTable'); + $this->Error("No primary key found for table $table", 'UpdateActiveTable'); return false; } @@ -467,55 +491,52 @@ class ADODB_Active_Record { $keys = array(); switch (ADODB_ASSOC_CASE) { - case ADODB_ASSOC_CASE_LOWER: - foreach($cols as $name => $fldobj) { - $name = strtolower($name); - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; + case ADODB_ASSOC_CASE_LOWER: + foreach ($cols as $name => $fldobj) { + $name = strtolower($name); + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - else { - $this->$name = null; + foreach ($pkeys as $k => $name) { + $keys[strtolower($name)] = strtolower($name); } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[strtolower($name)] = strtolower($name); - } - break; + break; - case ADODB_ASSOC_CASE_UPPER: - foreach($cols as $name => $fldobj) { - $name = strtoupper($name); + case ADODB_ASSOC_CASE_UPPER: + foreach ($cols as $name => $fldobj) { + $name = strtoupper($name); - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; - } - else { - $this->$name = null; + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[strtoupper($name)] = strtoupper($name); - } - break; - default: - foreach($cols as $fldobj) { - $name = $fldobj->name; + foreach ($pkeys as $k => $name) { + $keys[strtoupper($name)] = strtoupper($name); + } + break; + default: + foreach ($cols as $fldobj) { + $name = $fldobj->name; - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - else { - $this->$name = null; + foreach ($pkeys as $k => $name) { + $keys[$name] = $cols[strtoupper($name)]->name; } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[$name] = $cols[strtoupper($name)]->name; - } - break; + break; } $activetab->keys = $keys; @@ -525,9 +546,9 @@ class ADODB_Active_Record { $activetab->_created = time(); $s = serialize($activetab); if (!function_exists('adodb_write_file')) { - include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); + include_once(ADODB_DIR . '/adodb-csvlib.inc.php'); } - adodb_write_file($fname,$s); + adodb_write_file($fname, $s); } if (isset($activedb->tables[$table])) { $oldtab = $activedb->tables[$table]; @@ -540,23 +561,22 @@ class ADODB_Active_Record { $activedb->tables[$table] = $activetab; } - function GetPrimaryKeys(&$db, $table) + function GetPrimaryKeys($db, $table) { return $db->MetaPrimaryKeys($table); } // error handler for both PHP4+5. - function Error($err,$fn) + function Error($err, $fn) { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; - $fn = get_class($this).'::'.$fn; - $this->_lasterr = $fn.': '.$err; + $fn = get_class($this) . '::' . $fn; + $this->_lasterr = $fn . ': ' . $err; if ($this->_dbat < 0) { $db = false; - } - else { + } else { $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $db = $activedb->db; } @@ -564,8 +584,7 @@ class ADODB_Active_Record { if (function_exists('adodb_throw')) { if (!$db) { adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); - } - else { + } else { adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); } } else { @@ -582,8 +601,7 @@ class ADODB_Active_Record { if (!function_exists('adodb_throw')) { if ($this->_dbat < 0) { $db = false; - } - else { + } else { $db = $this->DB(); } @@ -602,32 +620,37 @@ class ADODB_Active_Record { } $db = $this->DB(); - return (int) $db->ErrorNo(); + return $db->ErrorNo(); } - // retrieve ADOConnection from _ADODB_Active_DBs + /** + * Retrieve ADOConnection from _ADODB_Active_DBs + * + * @return ADOConnection|false + */ function DB() { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; if ($this->_dbat < 0) { - $false = false; $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB"); - return $false; + return false; } $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; - $db = $activedb->db; - return $db; + return $activedb->db; } - // retrieve ADODB_Active_Table + /** + * Retrieve ADODB_Active_Table. + * + * @return ADODB_Active_Table + */ function &TableInfo() { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; - $table = $activedb->tables[$this->_tableat]; - return $table; + return $activedb->tables[$this->_tableat]; } @@ -641,16 +664,14 @@ class ADODB_Active_Record { } $table = $this->TableInfo(); $where = $this->GenWhere($db, $table); - return($this->Load($where)); + return ($this->Load($where)); } // set a numeric array (using natural table field ordering) as object properties - function Set(&$row) + function Set($row) { - global $ACTIVE_RECORD_SAFETY; - - $db = $this->DB(); + global $ACTIVE_RECORD_SAFETY; if (!$row) { $this->_saved = false; @@ -671,18 +692,17 @@ class ADODB_Active_Record { } } if ($bad_size) { - $this->Error("Table structure of $this->_table has changed","Load"); - return false; - } + $this->Error("Table structure of $this->_table has changed", "Load"); + return false; + } # </AP> - } - else + } else $keys = array_keys($row); # <AP> reset($keys); $this->_original = array(); - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $value = $row[current($keys)]; $this->$name = $value; $this->_original[] = $value; @@ -694,18 +714,17 @@ class ADODB_Active_Record { } // get last inserted id for INSERT - function LastInsertID(&$db,$fieldname) + function LastInsertID($db, $fieldname) { if ($db->hasInsertID) { - $val = $db->Insert_ID($this->_table,$fieldname); - } - else { + $val = $db->Insert_ID($this->_table, $fieldname); + } else { $val = false; } if (is_null($val) || $val === false) { - $SQL = sprintf("SELECT MAX(%s) FROM %s", + $SQL = sprintf(/** @lang text */ "SELECT MAX(%s) FROM %s", $this->nameQuoter($db,$fieldname), $this->nameQuoter($db,$this->_table) ); @@ -716,14 +735,16 @@ class ADODB_Active_Record { } // quote data in where clause - function doquote(&$db, $val,$t) + function doquote($db, $val, $t) { switch($t) { + /** @noinspection PhpMissingBreakStatementInspection */ case 'L': if (strpos($db->databaseType,'postgres') !== false) { return $db->qstr($val); } - case 'D': + case 'D': + /** @noinspection PhpMissingBreakStatementInspection */ case 'T': if (empty($val)) { return 'null'; @@ -731,43 +752,49 @@ class ADODB_Active_Record { case 'B': case 'N': case 'C': + /** @noinspection PhpMissingBreakStatementInspection */ case 'X': if (is_null($val)) { return 'null'; } - if (strlen($val)>0 && - (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'") + if (strlen($val) > 0 && + (strncmp($val, "'", 1) != 0 || substr($val, strlen($val) - 1, 1) != "'") ) { return $db->qstr($val); - break; } default: return $val; - break; } } - // generate where clause for an UPDATE/SELECT - function GenWhere(&$db, &$table) + /** + * Generate where clause for an UPDATE/SELECT + * + * @param ADOConnection $db + * @param ADODB_Active_Table $table + * + * @return string + */ + function GenWhere($db, $table) { $keys = $table->keys; $parr = array(); - foreach($keys as $k) { + foreach ($keys as $k) { $f = $table->flds[$k]; if ($f) { - $columnName = $this->nameQuoter($db,$k); - $parr[] = $columnName.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); + $columnName = $this->nameQuoter($db, $k); + $parr[] = $columnName . ' = ' . $this->doquote($db, $this->$k, $db->MetaType($f->type)); } } return implode(' AND ', $parr); } - function _QName($n,$db=false) + function _QName($n, $db = false) { - if (!ADODB_Active_Record::$_quoteNames) { + if (!$this->_quoteNames) { return $n; } if (!$db) { @@ -776,12 +803,12 @@ class ADODB_Active_Record { return false; } } - return $db->nameQuote.$n.$db->nameQuote; + return $db->nameQuote . $n . $db->nameQuote; } //------------------------------------------------------------ Public functions below - function Load($where=null,$bindarr=false, $lock = false) + function Load($where = null, $bindarr = false, $lock = false) { global $ADODB_FETCH_MODE; @@ -797,18 +824,18 @@ class ADODB_Active_Record { $savem = $db->SetFetchMode(false); } - $qry = sprintf("SELECT * FROM %s", + $qry = sprintf(/** @lang text */ "SELECT * FROM %s", $this->nameQuoter($db,$this->_table) ); - if($where) { - $qry .= ' WHERE '.$where; + if ($where) { + $qry .= ' WHERE ' . $where; } if ($lock) { $qry .= $this->lockMode; } - $row = $db->GetRow($qry,$bindarr); + $row = $db->GetRow($qry, $bindarr); if (isset($savem)) { $db->SetFetchMode($savem); @@ -818,26 +845,26 @@ class ADODB_Active_Record { return $this->Set($row); } - function LoadLocked($where=null, $bindarr=false) + function LoadLocked($where = null, $bindarr = false) { - $this->Load($where,$bindarr,true); + $this->Load($where, $bindarr, true); } # useful for multiple record inserts # see PHPLens Issue No: 17795 function Reset() { - $this->_where=null; + $this->_where = null; $this->_saved = false; $this->_lasterr = false; $this->_original = false; - $vars=get_object_vars($this); - foreach($vars as $k=>$v){ - if(substr($k,0,1)!=='_'){ - $this->{$k}=null; + $vars = get_object_vars($this); + foreach ($vars as $k => $v) { + if (substr($k, 0, 1) !== '_') { + $this->{$k} = null; } } - $this->foreignName=strtolower(get_class($this)); + $this->foreignName = strtolower(get_class($this)); return true; } @@ -846,8 +873,7 @@ class ADODB_Active_Record { { if ($this->_saved) { $ok = $this->Update(); - } - else { + } else { $ok = $this->Insert(); } @@ -869,27 +895,27 @@ class ADODB_Active_Record { $names = array(); $valstr = array(); - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $val = $this->$name; - if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) { + if(!is_array($val) || !array_key_exists($name, $table->keys)) { $valarr[] = $val; - $names[] = $this->nameQuoter($db,$name); + $names[] = $this->nameQuoter($db, $name); $valstr[] = $db->Param($cnt); $cnt += 1; } } - if (empty($names)){ - foreach($table->flds as $name=>$fld) { + if (empty($names)) { + foreach ($table->flds as $name => $fld) { $valarr[] = null; - $names[] = $this->nameQuoter($db,$name); + $names[] = $this->nameQuoter($db, $name); $valstr[] = $db->Param($cnt); $cnt += 1; } } $tableName = $this->nameQuoter($db,$this->_table); - $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)', + $sql = sprintf(/** @lang text */ 'INSERT INTO %s (%s) VALUES (%s)', $tableName, implode(',',$names), implode(',',$valstr) @@ -899,7 +925,7 @@ class ADODB_Active_Record { if ($ok) { $this->_saved = true; $autoinc = false; - foreach($table->keys as $k) { + foreach ($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; @@ -907,7 +933,7 @@ class ADODB_Active_Record { } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); - $this->$k = $this->LastInsertID($db,$k); + $this->$k = $this->LastInsertID($db, $k); } } @@ -923,32 +949,37 @@ class ADODB_Active_Record { } $table = $this->TableInfo(); + $where = $this->GenWhere($db, $table); + + $tableName = $this->nameQuoter($db, $this->_table); + $where = $this->GenWhere($db,$table); $tableName = $this->nameQuoter($db,$this->_table); - $sql = sprintf('DELETE FROM %s WHERE %s', + $sql = sprintf(/** @lang text */ 'DELETE FROM %s WHERE %s', $tableName, $where ); $ok = $db->Execute($sql); - return $ok ? true : false; + return (bool)$ok; } // returns an array of active record objects - function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) + function Find($whereOrderBy, $bindarr = false, $pkeysArr = false, $extra = array()) { $db = $this->DB(); if (!$db || empty($this->_table)) { return false; } - $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra); - return $arr; + return $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra); } - // returns 0 on error, 1 on update, 2 on insert + /** + * @return false|int 0 on error, 1 on update, 2 on insert + */ function Replace() { $db = $this->DB(); @@ -959,7 +990,7 @@ class ADODB_Active_Record { $pkey = $table->keys; - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $val = $this->$name; /* if (is_null($val)) { @@ -982,7 +1013,7 @@ class ADODB_Active_Record { } $t = $db->MetaType($fld->type); - $arr[$name] = $this->doquote($db,$val,$t); + $arr[$name] = $this->doquote($db, $val, $t); $valarr[] = $val; } @@ -1004,23 +1035,23 @@ class ADODB_Active_Record { } $newArr = array(); - foreach($arr as $k=>$v) - $newArr[$this->nameQuoter($db,$k)] = $v; + foreach ($arr as $k => $v) + $newArr[$this->nameQuoter($db, $k)] = $v; $arr = $newArr; $newPkey = array(); - foreach($pkey as $k=>$v) - $newPkey[$k] = $this->nameQuoter($db,$v); + foreach ($pkey as $k => $v) + $newPkey[$k] = $this->nameQuoter($db, $v); $pkey = $newPkey; - $tableName = $this->nameQuoter($db,$this->_table); + $tableName = $this->nameQuoter($db, $this->_table); - $ok = $db->Replace($tableName,$arr,$pkey); + $ok = $db->Replace($tableName, $arr, $pkey); if ($ok) { $this->_saved = true; // 1= update 2=insert if ($ok == 2) { $autoinc = false; - foreach($table->keys as $k) { + foreach ($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; @@ -1028,7 +1059,7 @@ class ADODB_Active_Record { } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); - $this->$k = $this->LastInsertID($db,$k); + $this->$k = $this->LastInsertID($db, $k); } } @@ -1037,7 +1068,9 @@ class ADODB_Active_Record { return $ok; } - // returns 0 on error, 1 on update, -1 if no change in data (no update) + /** + * @return false|int 0 on error, 1 on update, -1 if no change in data (no update) + */ function Update() { $db = $this->DB(); @@ -1049,7 +1082,7 @@ class ADODB_Active_Record { $where = $this->GenWhere($db, $table); if (!$where) { - $this->error("Where missing for table $table", "Update"); + $this->error("Where missing for table $table->name", "Update"); return false; } $valarr = array(); @@ -1057,7 +1090,7 @@ class ADODB_Active_Record { $pairs = array(); $i = 0; $cnt = 0; - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $orig = $this->_original[$i++] ?? null; $val = $this->$name; $neworig[] = $val; @@ -1070,9 +1103,8 @@ class ADODB_Active_Record { if (isset($fld->not_null) && $fld->not_null) { if (isset($fld->default_value) && strlen($fld->default_value)) { continue; - } - else { - $this->Error("Cannot set field $name to NULL","Update"); + } else { + $this->Error("Cannot set field $name to NULL", "Update"); return false; } } @@ -1083,23 +1115,21 @@ class ADODB_Active_Record { } $valarr[] = $val; - $pairs[] = $this->nameQuoter($db,$name).'='.$db->Param($cnt); + $pairs[] = $this->nameQuoter($db, $name) . '=' . $db->Param($cnt); $cnt += 1; } - if (!$cnt) { return -1; } - $tableName = $this->nameQuoter($db,$this->_table); - - $sql = sprintf('UPDATE %s SET %s WHERE %s', - $tableName, - implode(',',$pairs), - $where); + $sql = sprintf(/** @lang text */ 'UPDATE %s SET %s WHERE %s', + $this->nameQuoter($db, $this->_table), + implode(',', $pairs), + $where + ); - $ok = $db->Execute($sql,$valarr); + $ok = $db->Execute($sql, $valarr); if ($ok) { $this->_original = $neworig; return 1; @@ -1122,8 +1152,8 @@ class ADODB_Active_Record { * This honours the internal {@see $_quoteNames} property, which overrides * the global $ADODB_QUOTE_FIELDNAMES directive. * - * @param ADOConnection $db The database connection - * @param string $name The table or column name to quote + * @param ADOConnection $db The database connection + * @param string $name The table or column name to quote * * @return string The quoted name */ @@ -1141,17 +1171,24 @@ class ADODB_Active_Record { return $string; } -}; +} -function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr, - $extra) +/** + * @param ADOConnection $db + * @param string $class + * @param string $table + * @param string $whereOrderBy + * @param array $bindarr + * @param array $primkeyArr + * @param array $extra + * + * @return array|false + */ +function adodb_GetActiveRecordsClass($db, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra) { -global $_ADODB_ACTIVE_DBS; - - $save = $db->SetFetchMode(ADODB_FETCH_NUM); - $qry = "select * from ".$table; + $qry = /** @lang text */ "select * from " . $table; if (!empty($whereOrderBy)) { $qry .= ' WHERE '.$whereOrderBy; @@ -1174,34 +1211,28 @@ global $_ADODB_ACTIVE_DBS; $db->SetFetchMode($save); - $false = false; - if ($rows === false) { - return $false; + return false; } - if (!class_exists($class)) { $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass'); - return $false; + return false; } $arr = array(); // arrRef will be the structure that knows about our objects. // It is an associative array. // We will, however, return arr, preserving regular 0.. order so that // obj[0] can be used by app developers. - $arrRef = array(); - $bTos = array(); // Will store belongTo's indices if any foreach($rows as $row) { - $obj = new $class($table,$primkeyArr,$db); if ($obj->ErrorNo()){ $db->_errorMsg = $obj->ErrorMsg(); - return $false; + return false; } $obj->Set($row); $arr[] = $obj; - } // foreach($rows as $row) + } return $arr; } diff --git a/adodb-active-recordx.inc.php b/adodb-active-recordx.inc.php index cee95c66..37c2938f 100644 --- a/adodb-active-recordx.inc.php +++ b/adodb-active-recordx.inc.php @@ -37,12 +37,14 @@ $_ADODB_ACTIVE_DBS = array(); $ACTIVE_RECORD_SAFETY = true; // CFR: disabled while playing with relations $ADODB_ACTIVE_DEFVALS = false; -class ADODB_Active_DB { +class ADODB_Active_DB +{ var $db; // ADOConnection var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename } -class ADODB_Active_Table { +class ADODB_Active_Table +{ var $name; // table name var $flds; // assoc array of adofieldobjs, indexed by fieldname var $keys; // assoc array of primary keys, indexed by fieldname @@ -54,9 +56,9 @@ class ADODB_Active_Table { function updateColsCount() { $this->_colsCount = sizeof($this->flds); - foreach($this->_belongsTo as $foreignTable) + foreach ($this->_belongsTo as $foreignTable) $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); - foreach($this->_hasMany as $foreignTable) + foreach ($this->_hasMany as $foreignTable) $this->_colsCount += sizeof($foreignTable->TableInfo()->flds); } } @@ -66,7 +68,7 @@ function ADODB_SetDatabaseAdapter(&$db) { global $_ADODB_ACTIVE_DBS; - foreach($_ADODB_ACTIVE_DBS as $k => $d) { + foreach ($_ADODB_ACTIVE_DBS as $k => $d) { if ($d->db === $db) { return $k; } @@ -78,11 +80,12 @@ function ADODB_SetDatabaseAdapter(&$db) $_ADODB_ACTIVE_DBS[] = $obj; - return sizeof($_ADODB_ACTIVE_DBS)-1; + return sizeof($_ADODB_ACTIVE_DBS) - 1; } -class ADODB_Active_Record { +class ADODB_Active_Record +{ static $_changeNames = true; // dynamically pluralize table names static $_foreignSuffix = '_id'; // var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat] @@ -97,9 +100,9 @@ class ADODB_Active_Record { var $foreignName; // CFR: class name when in a relationship - static function UseDefaultValues($bool=null) + static function UseDefaultValues($bool = null) { - global $ADODB_ACTIVE_DEFVALS; + global $ADODB_ACTIVE_DEFVALS; if (isset($bool)) { $ADODB_ACTIVE_DEFVALS = $bool; } @@ -129,22 +132,21 @@ class ADODB_Active_Record { // $options is an array that allows us to tweak the constructor's behaviour // if $options['refresh'] is true, we re-scan our metadata information // if $options['new'] is true, we forget all relations - function __construct($table = false, $pkeyarr=false, $db=false, $options=array()) + function __construct($table = false, $pkeyarr = false, $db = false, $options = array()) { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; if ($db == false && is_object($pkeyarr)) { $db = $pkeyarr; $pkeyarr = false; } - if($table) { + if ($table) { // table argument exists. It is expected to be // already plural form. $this->_pTable = $table; $this->_sTable = $this->_singularize($this->_pTable); - } - else { + } else { // We will use current classname as table name. // We need to pluralize it for the real table name. $this->_sTable = strtolower(get_class($this)); @@ -157,7 +159,7 @@ class ADODB_Active_Record { if ($db) { $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db); } else - $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1; + $this->_dbat = sizeof($_ADODB_ACTIVE_DBS) - 1; if ($this->_dbat < 0) { @@ -173,7 +175,7 @@ class ADODB_Active_Record { // but there was no way to ask it to do that. $forceUpdate = (isset($options['refresh']) && true === $options['refresh']); $this->UpdateActiveTable($pkeyarr, $forceUpdate); - if(isset($options['new']) && true === $options['new']) { + if (isset($options['new']) && true === $options['new']) { $table =& $this->TableInfo(); unset($table->_hasMany); unset($table->_belongsTo); @@ -190,30 +192,30 @@ class ADODB_Active_Record { // CFR: Constants found in Rails static $IrregularP = array( - 'PERSON' => 'people', - 'MAN' => 'men', - 'WOMAN' => 'women', - 'CHILD' => 'children', - 'COW' => 'kine', + 'PERSON' => 'people', + 'MAN' => 'men', + 'WOMAN' => 'women', + 'CHILD' => 'children', + 'COW' => 'kine', ); static $IrregularS = array( - 'PEOPLE' => 'PERSON', - 'MEN' => 'man', - 'WOMEN' => 'woman', - 'CHILDREN' => 'child', - 'KINE' => 'cow', + 'PEOPLE' => 'PERSON', + 'MEN' => 'man', + 'WOMEN' => 'woman', + 'CHILDREN' => 'child', + 'KINE' => 'cow', ); static $WeIsI = array( 'EQUIPMENT' => true, - 'INFORMATION' => true, - 'RICE' => true, - 'MONEY' => true, - 'SPECIES' => true, - 'SERIES' => true, - 'FISH' => true, - 'SHEEP' => true, + 'INFORMATION' => true, + 'RICE' => true, + 'MONEY' => true, + 'SPECIES' => true, + 'SERIES' => true, + 'FISH' => true, + 'SHEEP' => true, ); function _pluralize($table) @@ -222,28 +224,28 @@ class ADODB_Active_Record { return $table; } $ut = strtoupper($table); - if(isset(self::$WeIsI[$ut])) { + if (isset(self::$WeIsI[$ut])) { return $table; } - if(isset(self::$IrregularP[$ut])) { + if (isset(self::$IrregularP[$ut])) { return self::$IrregularP[$ut]; } $len = strlen($table); - $lastc = $ut[$len-1]; - $lastc2 = substr($ut,$len-2); + $lastc = $ut[$len - 1]; + $lastc2 = substr($ut, $len - 2); switch ($lastc) { case 'S': - return $table.'es'; + return $table . 'es'; case 'Y': - return substr($table,0,$len-1).'ies'; + return substr($table, 0, $len - 1) . 'ies'; case 'X': - return $table.'es'; + return $table . 'es'; case 'H': if ($lastc2 == 'CH' || $lastc2 == 'SH') { - return $table.'es'; + return $table . 'es'; } default: - return $table.'s'; + return $table . 's'; } } @@ -251,36 +253,36 @@ class ADODB_Active_Record { // Note: There is an assumption here...and it is that the argument's length >= 4 function _singularize($table) { - if (!ADODB_Active_Record::$_changeNames) { - return $table; - } + return $table; + } + $ut = strtoupper($table); - if(isset(self::$WeIsI[$ut])) { + if (isset(self::$WeIsI[$ut])) { return $table; } - if(isset(self::$IrregularS[$ut])) { + if (isset(self::$IrregularS[$ut])) { return self::$IrregularS[$ut]; } $len = strlen($table); - if($ut[$len-1] != 'S') { + if ($ut[$len - 1] != 'S') { return $table; // I know...forget oxen } - if($ut[$len-2] != 'E') { - return substr($table, 0, $len-1); + if ($ut[$len - 2] != 'E') { + return substr($table, 0, $len - 1); } - switch($ut[$len-3]) { + switch ($ut[$len - 3]) { case 'S': case 'X': - return substr($table, 0, $len-2); + return substr($table, 0, $len - 2); case 'I': - return substr($table, 0, $len-3) . 'y'; + return substr($table, 0, $len - 3) . 'y'; case 'H'; - if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') { - return substr($table, 0, $len-2); + if ($ut[$len - 4] == 'C' || $ut[$len - 4] == 'S') { + return substr($table, 0, $len - 2); } default: - return substr($table, 0, $len-1); // ? + return substr($table, 0, $len - 1); // ? } } @@ -292,7 +294,7 @@ class ADODB_Active_Record { * this-table.id = other-table-#1.this-table_id * = other-table-#2.this-table_id */ - function hasMany($foreignRef,$foreignKey=false) + function hasMany($foreignRef, $foreignKey = false) { $ar = new ADODB_Active_Record($foreignRef); $ar->foreignName = $foreignRef; @@ -300,7 +302,7 @@ class ADODB_Active_Record { $ar->foreignKey = ($foreignKey) ? $foreignKey : strtolower(get_class($this)) . self::$_foreignSuffix; $table =& $this->TableInfo(); - if(!isset($table->_hasMany[$foreignRef])) { + if (!isset($table->_hasMany[$foreignRef])) { $table->_hasMany[$foreignRef] = $ar; $table->updateColsCount(); } @@ -315,7 +317,7 @@ class ADODB_Active_Record { * * this-table.other-table_id = other-table.id */ - function belongsTo($foreignRef,$foreignKey=false) + function belongsTo($foreignRef, $foreignKey = false) { global $inflector; @@ -325,7 +327,7 @@ class ADODB_Active_Record { $ar->foreignKey = ($foreignKey) ? $foreignKey : $ar->foreignName . self::$_foreignSuffix; $table =& $this->TableInfo(); - if(!isset($table->_belongsTo[$foreignRef])) { + if (!isset($table->_belongsTo[$foreignRef])) { $table->_belongsTo[$foreignRef] = $ar; $table->updateColsCount(); } @@ -341,71 +343,68 @@ class ADODB_Active_Record { */ function __get($name) { - return $this->LoadRelations($name, '', -1. -1); + return $this->LoadRelations($name, '', -1. - 1); } - function LoadRelations($name, $whereOrderBy, $offset=-1, $limit=-1) + function LoadRelations($name, $whereOrderBy, $offset = -1, $limit = -1) { $extras = array(); - if($offset >= 0) { + if ($offset >= 0) { $extras['offset'] = $offset; } - if($limit >= 0) { + if ($limit >= 0) { $extras['limit'] = $limit; } $table =& $this->TableInfo(); if (strlen($whereOrderBy)) { - if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy)) { - if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy)) { - $whereOrderBy = 'AND '.$whereOrderBy; + if (!preg_match('/^[ \n\r]*AND/i', $whereOrderBy)) { + if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i', $whereOrderBy)) { + $whereOrderBy = 'AND ' . $whereOrderBy; } } } - if(!empty($table->_belongsTo[$name])) { + if (!empty($table->_belongsTo[$name])) { $obj = $table->_belongsTo[$name]; $columnName = $obj->foreignKey; - if(empty($this->$columnName)) { + if (empty($this->$columnName)) { $this->$name = null; - } - else { - if(($k = reset($obj->TableInfo()->keys))) { + } else { + if (($k = reset($obj->TableInfo()->keys))) { $belongsToId = $k; - } - else { + } else { $belongsToId = 'id'; } $arrayOfOne = $obj->Find( - $belongsToId.'='.$this->$columnName.' '.$whereOrderBy, false, false, $extras); + $belongsToId . '=' . $this->$columnName . ' ' . $whereOrderBy, false, false, $extras); $this->$name = $arrayOfOne[0]; } return $this->$name; } - if(!empty($table->_hasMany[$name])) { + if (!empty($table->_hasMany[$name])) { $obj = $table->_hasMany[$name]; - if(($k = reset($table->keys))) { - $hasManyId = $k; - } - else { - $hasManyId = 'id'; + if (($k = reset($table->keys))) { + $hasManyId = $k; + } else { + $hasManyId = 'id'; } $this->$name = $obj->Find( - $obj->foreignKey.'='.$this->$hasManyId.' '.$whereOrderBy, false, false, $extras); + $obj->foreignKey . '=' . $this->$hasManyId . ' ' . $whereOrderBy, false, false, $extras); return $this->$name; } } ////////////////////////////////// // update metadata - function UpdateActiveTable($pkeys=false,$forceUpdate=false) + function UpdateActiveTable($pkeys = false, $forceUpdate = false) { - global $_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; - global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE; + global $_ADODB_ACTIVE_DBS, $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; + global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE; $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; @@ -415,11 +414,10 @@ class ADODB_Active_Record { if (!$forceUpdate && !empty($tables[$tableat])) { $tobj = $tables[$tableat]; - foreach($tobj->flds as $name => $fld) { + foreach ($tobj->flds as $name => $fld) { if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) { $this->$name = $fld->default_value; - } - else { + } else { $this->$name = null; } } @@ -427,11 +425,11 @@ class ADODB_Active_Record { } $db = $activedb->db; - $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; + $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_' . $table . '.cache'; if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { - $fp = fopen($fname,'r'); + $fp = fopen($fname, 'r'); @flock($fp, LOCK_SH); - $acttab = unserialize(fread($fp,100000)); + $acttab = unserialize(fread($fp, 100000)); fclose($fp); if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { // abs(rand()) randomizes deletion, reducing contention to delete/refresh file @@ -439,7 +437,7 @@ class ADODB_Active_Record { $activedb->tables[$table] = $acttab; //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); - return; + return; } else if ($db->debug) { ADOConnection::outp("Refreshing cached active record file: $fname"); } @@ -461,14 +459,14 @@ class ADODB_Active_Record { $ADODB_FETCH_MODE = $save; if (!$cols) { - $this->Error("Invalid table name: $table",'UpdateActiveTable'); + $this->Error("Invalid table name: $table", 'UpdateActiveTable'); return false; } $fld = reset($cols); if (!$pkeys) { if (isset($fld->primary_key)) { $pkeys = array(); - foreach($cols as $name => $fld) { + foreach ($cols as $name => $fld) { if (!empty($fld->primary_key)) { $pkeys[] = $name; } @@ -478,7 +476,7 @@ class ADODB_Active_Record { } } if (empty($pkeys)) { - $this->Error("No primary key found for table $table",'UpdateActiveTable'); + $this->Error("No primary key found for table $table", 'UpdateActiveTable'); return false; } @@ -486,55 +484,52 @@ class ADODB_Active_Record { $keys = array(); switch (ADODB_ASSOC_CASE) { - case ADODB_ASSOC_CASE_LOWER: - foreach($cols as $name => $fldobj) { - $name = strtolower($name); - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; + case ADODB_ASSOC_CASE_LOWER: + foreach ($cols as $name => $fldobj) { + $name = strtolower($name); + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - else { - $this->$name = null; + foreach ($pkeys as $k => $name) { + $keys[strtolower($name)] = strtolower($name); } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[strtolower($name)] = strtolower($name); - } - break; + break; - case ADODB_ASSOC_CASE_UPPER: - foreach($cols as $name => $fldobj) { - $name = strtoupper($name); + case ADODB_ASSOC_CASE_UPPER: + foreach ($cols as $name => $fldobj) { + $name = strtoupper($name); - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; - } - else { - $this->$name = null; + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[strtoupper($name)] = strtoupper($name); - } - break; - default: - foreach($cols as $name => $fldobj) { - $name = ($fldobj->name); + foreach ($pkeys as $k => $name) { + $keys[strtoupper($name)] = strtoupper($name); + } + break; + default: + foreach ($cols as $name => $fldobj) { + $name = ($fldobj->name); - if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { - $this->$name = $fldobj->default_value; + if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) { + $this->$name = $fldobj->default_value; + } else { + $this->$name = null; + } + $attr[$name] = $fldobj; } - else { - $this->$name = null; + foreach ($pkeys as $k => $name) { + $keys[$name] = $cols[$name]->name; } - $attr[$name] = $fldobj; - } - foreach($pkeys as $k => $name) { - $keys[$name] = $cols[$name]->name; - } - break; + break; } $activetab->keys = $keys; @@ -545,9 +540,9 @@ class ADODB_Active_Record { $activetab->_created = time(); $s = serialize($activetab); if (!function_exists('adodb_write_file')) { - include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); + include_once(ADODB_DIR . '/adodb-csvlib.inc.php'); } - adodb_write_file($fname,$s); + adodb_write_file($fname, $s); } if (isset($activedb->tables[$table])) { $oldtab = $activedb->tables[$table]; @@ -566,17 +561,16 @@ class ADODB_Active_Record { } // error handler for both PHP4+5. - function Error($err,$fn) + function Error($err, $fn) { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; - $fn = get_class($this).'::'.$fn; - $this->_lasterr = $fn.': '.$err; + $fn = get_class($this) . '::' . $fn; + $this->_lasterr = $fn . ': ' . $err; if ($this->_dbat < 0) { $db = false; - } - else { + } else { $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $db = $activedb->db; } @@ -584,8 +578,7 @@ class ADODB_Active_Record { if (function_exists('adodb_throw')) { if (!$db) { adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); - } - else { + } else { adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); } } else { @@ -602,8 +595,7 @@ class ADODB_Active_Record { if (!function_exists('adodb_throw')) { if ($this->_dbat < 0) { $db = false; - } - else { + } else { $db = $this->DB(); } @@ -622,14 +614,14 @@ class ADODB_Active_Record { } $db = $this->DB(); - return (int) $db->ErrorNo(); + return (int)$db->ErrorNo(); } // retrieve ADOConnection from _ADODB_Active_DBs function DB() { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; if ($this->_dbat < 0) { $false = false; @@ -644,7 +636,7 @@ class ADODB_Active_Record { // retrieve ADODB_Active_Table function &TableInfo() { - global $_ADODB_ACTIVE_DBS; + global $_ADODB_ACTIVE_DBS; $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; $table = $activedb->tables[$this->_tableat]; @@ -656,20 +648,20 @@ class ADODB_Active_Record { // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook function Reload() { - $db =& $this->DB(); + $db = $this->DB(); if (!$db) { return false; } - $table =& $this->TableInfo(); + $table = $this->TableInfo(); $where = $this->GenWhere($db, $table); - return($this->Load($where)); + return ($this->Load($where)); } // set a numeric array (using natural table field ordering) as object properties function Set(&$row) { - global $ACTIVE_RECORD_SAFETY; + global $ACTIVE_RECORD_SAFETY; $db = $this->DB(); @@ -682,11 +674,11 @@ class ADODB_Active_Record { $table = $this->TableInfo(); $sizeofFlds = sizeof($table->flds); - $sizeofRow = sizeof($row); + $sizeofRow = sizeof($row); if ($ACTIVE_RECORD_SAFETY && $table->_colsCount != $sizeofRow && $sizeofFlds != $sizeofRow) { # <AP> $bad_size = TRUE; - if($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) { + if ($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) { // Only keep string keys $keys = array_filter(array_keys($row), 'is_string'); if (sizeof($keys) == sizeof($table->flds)) { @@ -694,46 +686,45 @@ class ADODB_Active_Record { } } if ($bad_size) { - $this->Error("Table structure of $this->_table has changed","Load"); + $this->Error("Table structure of $this->_table has changed", "Load"); return false; } # </AP> - } - else { + } else { $keys = array_keys($row); } # <AP> reset($keys); $this->_original = array(); - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $value = $row[current($keys)]; $this->$name = $value; $this->_original[] = $value; - if(!next($keys)) { + if (!next($keys)) { break; } } $table =& $this->TableInfo(); - foreach($table->_belongsTo as $foreignTable) { + foreach ($table->_belongsTo as $foreignTable) { $ft = $foreignTable->TableInfo(); $propertyName = $ft->name; - foreach($ft->flds as $name=>$fld) { + foreach ($ft->flds as $name => $fld) { $value = $row[current($keys)]; $foreignTable->$name = $value; $foreignTable->_original[] = $value; - if(!next($keys)) { + if (!next($keys)) { break; } } } - foreach($table->_hasMany as $foreignTable) { + foreach ($table->_hasMany as $foreignTable) { $ft = $foreignTable->TableInfo(); - foreach($ft->flds as $name=>$fld) { + foreach ($ft->flds as $name => $fld) { $value = $row[current($keys)]; $foreignTable->$name = $value; $foreignTable->_original[] = $value; - if(!next($keys)) { + if (!next($keys)) { break; } } @@ -744,45 +735,42 @@ class ADODB_Active_Record { } // get last inserted id for INSERT - function LastInsertID(&$db,$fieldname) + function LastInsertID(&$db, $fieldname) { if ($db->hasInsertID) { - $val = $db->Insert_ID($this->_table,$fieldname); - } - else { + $val = $db->Insert_ID($this->_table, $fieldname); + } else { $val = false; } if (is_null($val) || $val === false) { // this might not work reliably in multi-user environment - return $db->GetOne("select max(".$fieldname.") from ".$this->_table); + return $db->GetOne("select max(" . $fieldname . ") from " . $this->_table); } return $val; } // quote data in where clause - function doquote(&$db, $val,$t) + function doquote(&$db, $val, $t) { - switch($t) { - case 'D': - case 'T': - if (empty($val)) { - return 'null'; - } - case 'C': - case 'X': - if (is_null($val)) { - return 'null'; - } - if (strlen($val)>0 && - (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'") - ) { - return $db->qstr($val); - break; - } - default: - return $val; - break; + switch ($t) { + case 'D': + case 'T': + if (empty($val)) { + return 'null'; + } + case 'C': + case 'X': + if (is_null($val)) { + return 'null'; + } + if (strlen($val) > 0 && + (strncmp($val, "'", 1) != 0 || substr($val, strlen($val) - 1, 1) != "'") + ) { + return $db->qstr($val); + } + default: + return $val; } } @@ -792,10 +780,10 @@ class ADODB_Active_Record { $keys = $table->keys; $parr = array(); - foreach($keys as $k) { + foreach ($keys as $k) { $f = $table->flds[$k]; if ($f) { - $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); + $parr[] = $k . ' = ' . $this->doquote($db, $this->$k, $db->MetaType($f->type)); } } return implode(' and ', $parr); @@ -804,7 +792,7 @@ class ADODB_Active_Record { //------------------------------------------------------------ Public functions below - function Load($where=null,$bindarr=false) + function Load($where = null, $bindarr = false) { $db = $this->DB(); if (!$db) { @@ -813,41 +801,39 @@ class ADODB_Active_Record { $this->_where = $where; $save = $db->SetFetchMode(ADODB_FETCH_NUM); - $qry = "select * from ".$this->_table; - $table =& $this->TableInfo(); + $qry = "select * from " . $this->_table; + $table = $this->TableInfo(); - if(($k = reset($table->keys))) { - $hasManyId = $k; - } - else { - $hasManyId = 'id'; + + if (($k = reset($table->keys))) { + $hasManyId = $k; + } else { + $hasManyId = 'id'; } - foreach($table->_belongsTo as $foreignTable) { - if(($k = reset($foreignTable->TableInfo()->keys))) { + foreach ($table->_belongsTo as $foreignTable) { + if (($k = reset($foreignTable->TableInfo()->keys))) { $belongsToId = $k; - } - else { + } else { $belongsToId = 'id'; } - $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. - $this->_table.'.'.$foreignTable->foreignKey.'='. - $foreignTable->_table.'.'.$belongsToId; + $qry .= ' LEFT JOIN ' . $foreignTable->_table . ' ON ' . + $this->_table . '.' . $foreignTable->foreignKey . '=' . + $foreignTable->_table . '.' . $belongsToId; } - foreach($table->_hasMany as $foreignTable) - { - $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '. - $this->_table.'.'.$hasManyId.'='. - $foreignTable->_table.'.'.$foreignTable->foreignKey; + foreach ($table->_hasMany as $foreignTable) { + $qry .= ' LEFT JOIN ' . $foreignTable->_table . ' ON ' . + $this->_table . '.' . $hasManyId . '=' . + $foreignTable->_table . '.' . $foreignTable->foreignKey; } - if($where) { - $qry .= ' WHERE '.$where; + if ($where) { + $qry .= ' WHERE ' . $where; } // Simple case: no relations. Load row and return. - if((count($table->_hasMany) + count($table->_belongsTo)) < 1) { - $row = $db->GetRow($qry,$bindarr); - if(!$row) { + if ((count($table->_hasMany) + count($table->_belongsTo)) < 1) { + $row = $db->GetRow($qry, $bindarr); + if (!$row) { return false; } $db->SetFetchMode($save); @@ -855,73 +841,71 @@ class ADODB_Active_Record { } // More complex case when relations have to be collated - $rows = $db->GetAll($qry,$bindarr); - if(!$rows) { + $rows = $db->GetAll($qry, $bindarr); + if (!$rows) { return false; } $db->SetFetchMode($save); - if(count($rows) < 1) { + if (count($rows) < 1) { return false; } $class = get_class($this); $isFirstRow = true; - if(($k = reset($this->TableInfo()->keys))) { - $myId = $k; - } - else { - $myId = 'id'; + if (($k = reset($this->TableInfo()->keys))) { + $myId = $k; + } else { + $myId = 'id'; } - $index = 0; $found = false; + $index = 0; + $found = false; /** @todo Improve by storing once and for all in table metadata */ /** @todo Also re-use info for hasManyId */ - foreach($this->TableInfo()->flds as $fld) { - if($fld->name == $myId) { + foreach ($this->TableInfo()->flds as $fld) { + if ($fld->name == $myId) { $found = true; break; } $index++; } - if(!$found) { - $this->outp_throw("Unable to locate key $myId for $class in Load()",'Load'); + if (!$found) { + $this->outp_throw("Unable to locate key $myId for $class in Load()", 'Load'); } - foreach($rows as $row) { + foreach ($rows as $row) { $rowId = intval($row[$index]); - if($rowId > 0) { - if($isFirstRow) { + if ($rowId > 0) { + if ($isFirstRow) { $isFirstRow = false; - if(!$this->Set($row)) { + if (!$this->Set($row)) { return false; } } - $obj = new $class($table,false,$db); + $obj = new $class($table, false, $db); $obj->Set($row); // TODO Copy/paste code below: bad! - if(count($table->_hasMany) > 0) { - foreach($table->_hasMany as $foreignTable) { + if (count($table->_hasMany) > 0) { + foreach ($table->_hasMany as $foreignTable) { $foreignName = $foreignTable->foreignName; - if(!empty($obj->$foreignName)) { - if(!is_array($this->$foreignName)) { + if (!empty($obj->$foreignName)) { + if (!is_array($this->$foreignName)) { $foreignObj = $this->$foreignName; $this->$foreignName = array(clone($foreignObj)); - } - else { + } else { $foreignObj = $obj->$foreignName; array_push($this->$foreignName, clone($foreignObj)); } } } } - if(count($table->_belongsTo) > 0) { - foreach($table->_belongsTo as $foreignTable) { + if (count($table->_belongsTo) > 0) { + foreach ($table->_belongsTo as $foreignTable) { $foreignName = $foreignTable->foreignName; - if(!empty($obj->$foreignName)) { - if(!is_array($this->$foreignName)) { + if (!empty($obj->$foreignName)) { + if (!is_array($this->$foreignName)) { $foreignObj = $this->$foreignName; $this->$foreignName = array(clone($foreignObj)); - } - else { + } else { $foreignObj = $obj->$foreignName; array_push($this->$foreignName, clone($foreignObj)); } @@ -938,8 +922,7 @@ class ADODB_Active_Record { { if ($this->_saved) { $ok = $this->Update(); - } - else { + } else { $ok = $this->Insert(); } @@ -967,9 +950,9 @@ class ADODB_Active_Record { $names = array(); $valstr = array(); - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $val = $this->$name; - if(!is_null($val) || !array_key_exists($name, $table->keys)) { + if (!is_null($val) || !array_key_exists($name, $table->keys)) { $valarr[] = $val; $names[] = $name; $valstr[] = $db->Param($cnt); @@ -977,21 +960,21 @@ class ADODB_Active_Record { } } - if (empty($names)){ - foreach($table->flds as $name=>$fld) { + if (empty($names)) { + foreach ($table->flds as $name => $fld) { $valarr[] = null; $names[] = $name; $valstr[] = $db->Param($cnt); $cnt += 1; } } - $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; - $ok = $db->Execute($sql,$valarr); + $sql = 'INSERT INTO ' . $this->_table . "(" . implode(',', $names) . ') VALUES (' . implode(',', $valstr) . ')'; + $ok = $db->Execute($sql, $valarr); if ($ok) { $this->_saved = true; $autoinc = false; - foreach($table->keys as $k) { + foreach ($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; @@ -999,7 +982,7 @@ class ADODB_Active_Record { } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); - $this->$k = $this->LastInsertID($db,$k); + $this->$k = $this->LastInsertID($db, $k); } } @@ -1015,23 +998,23 @@ class ADODB_Active_Record { } $table = $this->TableInfo(); - $where = $this->GenWhere($db,$table); - $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; + $where = $this->GenWhere($db, $table); + $sql = 'DELETE FROM ' . $this->_table . ' WHERE ' . $where; $ok = $db->Execute($sql); return $ok ? true : false; } // returns an array of active record objects - function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) + function Find($whereOrderBy, $bindarr = false, $pkeysArr = false, $extra = array()) { $db = $this->DB(); if (!$db || empty($this->_table)) { return false; } $table =& $this->TableInfo(); - $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, - array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); + $arr = $db->GetActiveRecordsClass(get_class($this), $this, $whereOrderBy, $bindarr, $pkeysArr, $extra, + array('foreignName' => $this->foreignName, 'belongsTo' => $table->_belongsTo, 'hasMany' => $table->_hasMany)); return $arr; } @@ -1039,15 +1022,15 @@ class ADODB_Active_Record { // subclasses...for instance when GetActiveRecordsClass invokes Find() // Why am I not invoking parent::Find? // Shockingly because I want to preserve PHP4 compatibility. - function packageFind($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()) + function packageFind($whereOrderBy, $bindarr = false, $pkeysArr = false, $extra = array()) { $db = $this->DB(); if (!$db || empty($this->_table)) { return false; } $table =& $this->TableInfo(); - $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra, - array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany)); + $arr = $db->GetActiveRecordsClass(get_class($this), $this, $whereOrderBy, $bindarr, $pkeysArr, $extra, + array('foreignName' => $this->foreignName, 'belongsTo' => $table->_belongsTo, 'hasMany' => $table->_hasMany)); return $arr; } @@ -1062,7 +1045,7 @@ class ADODB_Active_Record { $pkey = $table->keys; - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $val = $this->$name; /* if (is_null($val)) { @@ -1080,7 +1063,7 @@ class ADODB_Active_Record { continue; } $t = $db->MetaType($fld->type); - $arr[$name] = $this->doquote($db,$val,$t); + $arr[$name] = $this->doquote($db, $val, $t); $valarr[] = $val; } @@ -1091,23 +1074,23 @@ class ADODB_Active_Record { switch (ADODB_ASSOC_CASE) { case ADODB_ASSOC_CASE_LOWER: - foreach($pkey as $k => $v) { + foreach ($pkey as $k => $v) { $pkey[$k] = strtolower($v); } break; case ADODB_ASSOC_CASE_UPPER: - foreach($pkey as $k => $v) { + foreach ($pkey as $k => $v) { $pkey[$k] = strtoupper($v); } break; } - $ok = $db->Replace($this->_table,$arr,$pkey); + $ok = $db->Replace($this->_table, $arr, $pkey); if ($ok) { $this->_saved = true; // 1= update 2=insert if ($ok == 2) { $autoinc = false; - foreach($table->keys as $k) { + foreach ($table->keys as $k) { if (is_null($this->$k)) { $autoinc = true; break; @@ -1115,7 +1098,7 @@ class ADODB_Active_Record { } if ($autoinc && sizeof($table->keys) == 1) { $k = reset($table->keys); - $this->$k = $this->LastInsertID($db,$k); + $this->$k = $this->LastInsertID($db, $k); } } @@ -1144,7 +1127,7 @@ class ADODB_Active_Record { $pairs = array(); $i = 0; $cnt = 0; - foreach($table->flds as $name=>$fld) { + foreach ($table->flds as $name => $fld) { $orig = $this->_original[$i++] ?? null; $val = $this->$name; $neworig[] = $val; @@ -1157,9 +1140,8 @@ class ADODB_Active_Record { if (isset($fld->not_null) && $fld->not_null) { if (isset($fld->default_value) && strlen($fld->default_value)) { continue; - } - else { - $this->Error("Cannot set field $name to NULL","Update"); + } else { + $this->Error("Cannot set field $name to NULL", "Update"); return false; } } @@ -1168,17 +1150,23 @@ class ADODB_Active_Record { if ($val === $orig) { continue; } + $valarr[] = $val; - $pairs[] = $name.'='.$db->Param($cnt); + $pairs[] = $name . '=' . $db->Param($cnt); $cnt += 1; } - if (!$cnt) { return -1; } - $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; - $ok = $db->Execute($sql,$valarr); + + $sql = sprintf(/** @lang text */ 'UPDATE %s SET %s WHERE %s', + $this->_table, + implode(',', $pairs), + $where + ); + + $ok = $db->Execute($sql, $valarr); if ($ok) { $this->_original = $neworig; return 1; @@ -1195,7 +1183,7 @@ class ADODB_Active_Record { return array_keys($table->flds); } -}; +} function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr, $extra, $relations) diff --git a/adodb-csvlib.inc.php b/adodb-csvlib.inc.php index 87efd940..3231884a 100644 --- a/adodb-csvlib.inc.php +++ b/adodb-csvlib.inc.php @@ -270,7 +270,7 @@ $ADODB_INCLUDED_CSV = 1; */ function adodb_write_file($filename, $contents,$debug=false) { - # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows + # https://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows # So to simulate locking, we assume that rename is an atomic operation. # First we delete $filename, then we create a $tempfile write to it and # rename to the desired $filename. If the rename works, then we successfully diff --git a/adodb-datadict.inc.php b/adodb-datadict.inc.php index 1282fbf0..be6c78f7 100644 --- a/adodb-datadict.inc.php +++ b/adodb-datadict.inc.php @@ -540,16 +540,20 @@ class ADODB_DataDict { } /** - * Rename one column + * Rename one column. * - * Some DBMs can only do this together with changeing the type of the column (even if that stays the same, eg. mysql) - * @param string $tabname table-name - * @param string $oldcolumn column-name to be renamed - * @param string $newcolumn new column-name - * @param string $flds='' complete column-definition-string like for addColumnSQL, only used by mysql atm., default='' - * @return array with SQL strings + * Some DBs can only do this together with changing the type of the column + * (even if that stays the same, eg. MySQL). + * + * @param string $tabname Table name. + * @param string $oldcolumn Column to be renamed. + * @param string $newcolumn New column name. + * @param string $flds Complete column definition string like for {@see addColumnSQL}; + * This is currently only used by MySQL. Defaults to ''. + * + * @return array SQL statements. */ - function renameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') + function renameColumnSQL($tabname, $oldcolumn, $newcolumn, $flds='') { $tabname = $this->tableName($tabname); if ($flds) { @@ -563,17 +567,21 @@ class ADODB_DataDict { } /** - * Drop one column + * Drop one column. * - * Some DBM's can't do that on their own, you need to supply the complete definition of the new table, - * to allow, recreating the table and copying the content over to the new table - * @param string $tabname table-name - * @param string $flds column-name and type for the changed column - * @param string $tableflds='' complete definition of the new table, eg. for postgres, default '' - * @param array|string $tableoptions='' options for the new table see createTableSQL, default '' - * @return array with SQL strings + * Some DBs can't do that on their own (e.g. PostgreSQL), so you need + * to supply the complete definition of the new table, to allow recreating + * it and copying the content over to the new table. + * + * @param string $tabname Table name. + * @param string $flds Column name and type for the changed column. + * @param string $tableflds Complete definition of the new table. Defaults to ''. + * @param array|string $tableoptions Options for the new table {@see createTableSQL()}, + * defaults to ''. + * + * @return array SQL statements. */ - function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') + function dropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='') { $tabname = $this->tableName($tabname); if (!is_array($flds)) $flds = explode(',',$flds); @@ -1086,13 +1094,13 @@ class ADODB_DataDict { } } $flds = $holdflds; - - $sql = array_merge( - $this->addColumnSQL($tablename, $fields_to_add), - $this->alterColumnSql($tablename, $fields_to_alter) - ); } + $sql = array_merge( + $this->addColumnSQL($tablename, $fields_to_add), + $this->alterColumnSql($tablename, $fields_to_alter) + ); + if ($dropOldFlds) { foreach ($cols as $id => $v) { if (!isset($lines[$id])) { diff --git a/adodb-lib.inc.php b/adodb-lib.inc.php index 46fa5ddf..b8546ae9 100644 --- a/adodb-lib.inc.php +++ b/adodb-lib.inc.php @@ -1180,104 +1180,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; } /** @@ -1286,73 +1290,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$s</td></tr>' . PHP_EOL; } else { - $html = $ishtml; + $fmt = '%1$2d. %2$s in %3$s line %4$s' . 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)) { @@ -1362,29 +1353,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'] ?? 'unknown file'); + + $s .= sprintf($fmt, + $elements--, + $functionName . '(' . implode(', ', $args) . ')', + $file, + $element['line'] ?? 'unknown' + ); } - if ($html) { - $s .= '</pre>'; + + if ($isHtml) { + $s .= '</table>' . PHP_EOL . '</div>' . PHP_EOL; } + if ($printOrArr) { ADOConnection::outp($s); } diff --git a/adodb-memcache.lib.inc.php b/adodb-memcache.lib.inc.php index a251c9c3..ca71189d 100644 --- a/adodb-memcache.lib.inc.php +++ b/adodb-memcache.lib.inc.php @@ -35,7 +35,7 @@ if (empty($ADODB_INCLUDED_CSV)) { class ADODB_Cache_MemCache { /** - * @var bool Prevents parent class calling non-existant function + * @var bool Prevents parent class calling non-existent function */ public $createdir = false; diff --git a/adodb-perf.inc.php b/adodb-perf.inc.php index 91610536..3610ac40 100644 --- a/adodb-perf.inc.php +++ b/adodb-perf.inc.php @@ -1059,7 +1059,7 @@ Committed_AS: 348732 kB /** * Reorganise current database. * Default implementation loops over all <code>MetaTables()</code> and - * optimize each using <code>optmizeTable()</code> + * optimize each using <code>optimizeTable()</code> * * @author Markus Staab * @return bool true on success, false on error 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-xmlschema.inc.php b/adodb-xmlschema.inc.php index 662e2aae..415a97d7 100644 --- a/adodb-xmlschema.inc.php +++ b/adodb-xmlschema.inc.php @@ -188,7 +188,7 @@ class dbObject { /** * Creates a table object in ADOdb's datadict format * - * This class stores information about a database table. As charactaristics + * This class stores information about a database table. As characteristics * of the table are loaded from the external source, methods and properties * of this class are used to build up the table description in ADOdb's * datadict format. @@ -245,7 +245,7 @@ class dbTable extends dbObject { var $data; /** - * Iniitializes a new table object. + * Initializes a new table object. * * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. @@ -607,7 +607,7 @@ class dbTable extends dbObject { /** * Creates an index object in ADOdb's datadict format * - * This class stores information about a database index. As charactaristics + * This class stores information about a database index. As characteristics * of the index are loaded from the external source, methods and properties * of this class are used to build up the index description in ADOdb's * datadict format. diff --git a/adodb-xmlschema03.inc.php b/adodb-xmlschema03.inc.php index 3c8bce5d..1f121007 100644 --- a/adodb-xmlschema03.inc.php +++ b/adodb-xmlschema03.inc.php @@ -206,7 +206,7 @@ class dbObject { /** * Creates a table object in ADOdb's datadict format * - * This class stores information about a database table. As charactaristics + * This class stores information about a database table. As characteristics * of the table are loaded from the external source, methods and properties * of this class are used to build up the table description in ADOdb's * datadict format. @@ -263,7 +263,7 @@ class dbTable extends dbObject { var $data; /** - * Iniitializes a new table object. + * Initializes a new table object. * * @param string $prefix DB Object prefix * @param array $attributes Array of table attributes. @@ -656,7 +656,7 @@ class dbTable extends dbObject { /** * Creates an index object in ADOdb's datadict format * - * This class stores information about a database index. As charactaristics + * This class stores information about a database index. As characteristics * of the index are loaded from the external source, methods and properties * of this class are used to build up the index description in ADOdb's * datadict format. diff --git a/adodb.inc.php b/adodb.inc.php index 0f60bc75..a5903f42 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.9-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 } - } /** @@ -905,6 +957,20 @@ if (!defined('_ADODB_LAYER')) { } /** + * Low-level, driver-specific method to connect to the database. + * + * @param string $argHostname Host to connect to + * @param string $argUsername Userid to login + * @param string $argPassword Associated password + * @param string $argDatabaseName Database name + * + * @return bool + * @internal + * @TODO propagate *protected* visibility to child classes + */ + abstract protected function _connect($argHostname, $argUsername, $argPassword, $argDatabaseName); + + /** * Connect to database. * * @param string $argHostname Host to connect to @@ -965,7 +1031,10 @@ if (!defined('_ADODB_LAYER')) { } /** - * Always force a new connection to database. + * Low-level method to force a new connection to the database. + * + * Unless the child Driver class overrides it, this method is the same as + * {@see _connect()}. * * @param string $argHostname Host to connect to * @param string $argUsername Userid to login @@ -973,15 +1042,17 @@ if (!defined('_ADODB_LAYER')) { * @param string $argDatabaseName Database name * * @return bool + * @internal + * @TODO propagate *protected* visibility to child classes */ - function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { + protected function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); } /** - * Always force a new connection to database. + * Always force a new connection to the database. * - * Currently this only works with Oracle. + * This is only supported by some drivers. * * @param string $argHostname Host to connect to * @param string $argUsername Userid to login @@ -995,6 +1066,25 @@ if (!defined('_ADODB_LAYER')) { } /** + * Low-level method to establish a persistent connection to the database. + * + * Unless the child Driver class overrides it, this method is the same as + * {@see _connect()}. + * + * @param string $argHostname Host to connect to + * @param string $argUsername Userid to login + * @param string $argPassword Associated password + * @param string $argDatabaseName Database name + * + * @return bool + * @internal + * @TODO propagate *protected* visibility to child classes + */ + protected function _pconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { + return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName); + } + + /** * Establish persistent connection to database. * * @param string $argHostname Host to connect to @@ -1394,7 +1484,7 @@ if (!defined('_ADODB_LAYER')) { /** - * Complete a transation. + * Complete a transaction. * * Used together with StartTrans() to end a transaction. Monitors connection * for sql errors, and will commit or rollback as appropriate. @@ -2211,7 +2301,7 @@ if (!defined('_ADODB_LAYER')) { * This is only relevant if the returned string * is coming from a CHAR type field. * - * @return array|bool 1D array containning the first row of the query + * @return array|bool 1D array containing the first row of the query */ function GetCol($sql, $inputarr = false, $trim = false) { @@ -2394,7 +2484,9 @@ if (!defined('_ADODB_LAYER')) { } /** - * Insert or replace a single record. Note: this is not the same as MySQL's replace. + * Insert or replace a single record (upsert). + * + * Note: this is not the same as MySQL's replace. * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL. * Also note that no table locking is done currently, so it is possible that the * record be inserted twice by two programs... @@ -2935,15 +3027,15 @@ if (!defined('_ADODB_LAYER')) { /** * GetActiveRecordsClass Performs an 'ALL' query * - * @param mixed $class This string represents the class of the current active record - * @param mixed $table Table used by the active record object - * @param mixed $whereOrderBy Where, order, by clauses - * @param mixed $bindarr - * @param mixed $primkeyArr + * @param string $class This string represents the class of the current active record + * @param string $table Table used by the active record object + * @param string $whereOrderBy Where, order, by clauses + * @param array $bindarr + * @param array $primkeyArr * @param array $extra Query extras: limit, offset... * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo" * @access public - * @return void + * @return array|false */ function GetActiveRecordsClass( $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false, @@ -2960,8 +3052,7 @@ if (!defined('_ADODB_LAYER')) { } function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) { - $arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr); - return $arr; + return $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr); } /** @@ -2994,9 +3085,9 @@ if (!defined('_ADODB_LAYER')) { $this->_transmode = $transaction_mode; } /* -http://msdn2.microsoft.com/en-US/ms173763.aspx -http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html -http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html +https://msdn2.microsoft.com/en-US/ms173763.aspx +https://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html +https://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm */ function MetaTransaction($mode,$db) { @@ -3343,7 +3434,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 $d = ADOConnection::UnixDate($d); } - return adodb_date($this->fmtDate,$d); + return date($this->fmtDate,$d); } function BindDate($d) { @@ -3385,7 +3476,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 # strlen(14) allows YYYYMMDDHHMMSS format if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) { - return adodb_date($this->fmtTimeStamp,$ts); + return date($this->fmtTimeStamp,$ts); } if ($ts === 'null') { @@ -3396,7 +3487,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 return "'$ts'"; } $ts = ADOConnection::UnixTimeStamp($ts); - return adodb_date($this->fmtTimeStamp,$ts); + return date($this->fmtTimeStamp,$ts); } /** @@ -3409,7 +3500,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 if (is_object($v)) { // odbtp support //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) - return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); + return mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); } if (is_numeric($v) && strlen($v) !== 8) { @@ -3424,7 +3515,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } // h-m-s-MM-DD-YY - return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + return mktime(0,0,0,$rr[2],$rr[3],$rr[1]); } @@ -3438,7 +3529,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 if (is_object($v)) { // odbtp support //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 ) - return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); + return mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year); } if (!preg_match( @@ -3451,9 +3542,9 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 // h-m-s-MM-DD-YY if (!isset($rr[5])) { - return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]); + return mktime(0,0,0,$rr[2],$rr[3],$rr[1]); } - return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); + return mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]); } /** @@ -3479,8 +3570,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 // pre-TIMESTAMP_FIRST_YEAR } - return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); - + return ($gmt) ? gmdate($fmt,$tt) : date($fmt,$tt); } /** @@ -3498,7 +3588,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } # strlen(14) allows YYYYMMDDHHMMSS format if (is_numeric($v) && strlen($v)<14) { - return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v); + return ($gmt) ? gmdate($fmt,$v) : date($fmt,$v); } $tt = $this->UnixTimeStamp($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR @@ -3508,7 +3598,7 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 if ($tt == 0) { return $this->emptyTimeStamp; } - return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt); + return ($gmt) ? gmdate($fmt,$tt) : date($fmt,$tt); } /** @@ -3747,18 +3837,55 @@ http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_1 } // end class ADOConnection - - - //============================================================================================== - // CLASS ADOFetchObj - //============================================================================================== - /** - * Internal placeholder for record objects. Used by ADORecordSet->FetchObj(). + * RecordSet fields data as object. + * + * @see ADORecordSet::fetchObj(), ADORecordSet::fetchObject(), + * @see ADORecordSet::fetchNextObj(), ADORecordSet::fetchNextObject() */ - #[\AllowDynamicProperties] class ADOFetchObj { - }; + /** @var array The RecordSet's fields */ + protected $data; + + /** + * Constructor. + * + * @param array $fields Associative array with RecordSet's fields (name => value) + */ + public function __construct(array $fields = []) + { + $this->data = $fields; + } + + public function __set(string $name, $value) + { + $this->data[$name] = $value; + } + + public function __get(string $name) + { + if (isset($this->data[$name])) { + return $this->data[$name]; + } + ADOConnection::outp("Unknown field: $name"); + return null; + } + + public function __isset($name) + { + return isset($this->data[$name]); + } + + public function __debugInfo() + { + return $this->data; + } + + public static function __set_state(array $data) + { + return new self($data['data']); + } + } /** * Class ADODB_Iterator_empty @@ -3885,13 +4012,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 */ @@ -3976,7 +4096,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; /** @@ -3993,8 +4113,6 @@ class ADORecordSet implements IteratorAggregate { var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */ var $_closed = false; /** has recordset been closed */ var $_inited = false; /** Init() should only be called once */ - var $_obj; /** Used by FetchObj */ - var $_names; /** Used by FetchObj */ // Recordset pagination /** @var int Number of rows per page */ @@ -4025,6 +4143,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 */ @@ -4036,7 +4169,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; } @@ -4281,11 +4420,11 @@ class ADORecordSet implements IteratorAggregate { * it will return a 1 dimensional array of key-value pairs unless * $force_array == true. This recordset method is currently part of * the API, but may not be in later versions of ADOdb. By preference, use - * ADOconnnection::getAssoc() + * ADOConnection::getAssoc() * * @param bool $force_array (optional) Has only meaning if we have 2 data * columns. If false, a 1 dimensional - * array is returned, otherwise a 2 dimensional + * array is returned, otherwise a 2-dimensional * array is returned. If this sounds confusing, * read the source. * @@ -4295,127 +4434,97 @@ class ADORecordSet implements IteratorAggregate { * array[col0] => array(remaining cols), * return array[col0] => col1 * - * @return mixed[]|false - * + * @return array|false */ function getAssoc($force_array = false, $first2cols = false) { global $ADODB_FETCH_MODE; - /* - * Insufficient rows to show data - */ - if ($this->_numOfFields < 2) - return; + // Insufficient rows to show data + if ($this->_numOfFields < 2) { + return false; + } - /* - * Empty recordset - */ + // Empty recordset if (!$this->fields) { return array(); } - /* - * The number of fields is half the actual returned in BOTH mode - */ + // The number of fields is half the actual returned in BOTH mode $numberOfFields = $this->_numOfFields; - /* - * Get the fetch mode when the call was executed, this may be - * different than ADODB_FETCH_MODE - */ + // Get the fetch mode when the call was executed, this may be + // different from ADODB_FETCH_MODE $fetchMode = $this->adodbFetchMode; if ($fetchMode == ADODB_FETCH_BOTH || $fetchMode == ADODB_FETCH_DEFAULT) { - /* - * If we are using BOTH, we present the data as if it - * was in ASSOC mode. This could be enhanced by adding - * a BOTH_ASSOC_MODE class property - * We build a template of numeric keys. you could improve the - * speed by caching this, indexed by number of keys - */ - $testKeys = array_fill(0,$numberOfFields,0); + // If we are using BOTH, we present the data as if it were in ASSOC mode. + // This could be enhanced by adding a BOTH_ASSOC_MODE class property. + // We build a template of numeric keys. you could improve the speed + // by caching this, indexed by number of keys. + $testKeys = array_fill(0, $numberOfFields, 0); } $showArrayMethod = 0; - if ($numberOfFields == 2) - /* - * Key is always value of first element - * Value is always value of second element - */ + if ($numberOfFields == 2) { + // Key is always the value of first element + // Value is always value of second element $showArrayMethod = 1; + } - if ($force_array) + if ($force_array) { $showArrayMethod = 0; + } - if ($first2cols) + if ($first2cols) { $showArrayMethod = 1; + } - $results = array(); - - while (!$this->EOF){ + $results = array(); + while (!$this->EOF) { $myFields = $this->fields; if ($fetchMode == ADODB_FETCH_BOTH || $fetchMode == ADODB_FETCH_DEFAULT) { - /* - * extract the associative keys - */ - $myFields = array_diff_key($myFields,$testKeys); + // extract the associative keys + /** @noinspection PhpUndefinedVariableInspection */ + $myFields = array_diff_key($myFields, $testKeys); } - /* - * key is value of first element, rest is data, - * The key is not case processed - */ + // Key is value of the first element, the rest is data. + // The key is not case processed. $key = array_shift($myFields); switch ($showArrayMethod) { - case 0: - - if ($fetchMode != ADODB_FETCH_NUM) { - /* - * The driver should have already handled the key - * casing, but in case it did not. We will check and force - * this in later versions of ADOdb - */ - if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) - $myFields = array_change_key_case($myFields,CASE_UPPER); - - elseif (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) - $myFields = array_change_key_case($myFields,CASE_LOWER); - - /* - * We have already shifted the key off - * the front, so the rest is the value - */ - $results[$key] = $myFields; - - } - else - /* - * I want the values in a numeric array, - * nicely re-indexed from zero - */ - $results[$key] = array_values($myFields); - break; + case 0: + if ($fetchMode != ADODB_FETCH_NUM) { + // The driver should have already handled the key casing, but in case it did not. + // We will check and force this in later versions of ADOdb + if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER) { + $myFields = array_change_key_case($myFields, CASE_UPPER); + } + elseif (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER) { + $myFields = array_change_key_case($myFields, CASE_LOWER); + } - case 1: + // We have already shifted the key off the front, so the rest is the value + $results[$key] = $myFields; + } else + // I want the values in a numeric array, nicely re-indexed from zero + $results[$key] = array_values($myFields); + break; - /* - * Don't care how long the array is, - * I just want value of second column, and it doesn't - * matter whether the array is associative or numeric - */ - $results[$key] = array_shift($myFields); - break; + /** @noinspection PhpConditionAlreadyCheckedInspection */ + case 1: + // Don't care how long the array is, I just want value of second column, + // and it doesn't matter whether the array is associative or numeric + $results[$key] = array_shift($myFields); + break; } $this->MoveNext(); } - /* - * Done - */ + return $results; } @@ -4424,11 +4533,11 @@ class ADORecordSet implements IteratorAggregate { * @param mixed $v is the character timestamp in YYYY-MM-DD hh:mm:ss format * @param string [$fmt] is the format to apply to it, using date() * - * @return string a timestamp formated as user desires + * @return string a timestamp formatted as user desires */ function UserTimeStamp($v,$fmt='Y-m-d H:i:s') { if (is_numeric($v) && strlen($v)<14) { - return adodb_date($fmt,$v); + return date($fmt,$v); } $tt = $this->UnixTimeStamp($v); // $tt == -1 if pre TIMESTAMP_FIRST_YEAR @@ -4438,7 +4547,7 @@ class ADORecordSet implements IteratorAggregate { if ($tt === 0) { return $this->emptyTimeStamp; } - return adodb_date($fmt,$tt); + return date($fmt,$tt); } @@ -4458,7 +4567,7 @@ class ADORecordSet implements IteratorAggregate { } else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR } - return adodb_date($fmt,$tt); + return date($fmt,$tt); } @@ -4891,76 +5000,59 @@ class ADORecordSet implements IteratorAggregate { } /** - * Return the fields array of the current row as an object for convenience. - * The default case is lowercase field names. + * Return the current row as an object for convenience. * - * @return the object with the properties set to the fields of the current row + * @return ADOFetchObj The object with properties set to the current row's fields. */ - function FetchObj() { - return $this->FetchObject(false); + function fetchObj() { + return $this->fetchObject(false); } /** - * Return the fields array of the current row as an object for convenience. - * The default case is uppercase. + * Return the current row as an object for convenience. + * + * Field names are converted to uppercase by default. * - * @param bool $isUpper to set the object property names to uppercase + * @param bool $isUpper True to convert field names to uppercase. * - * @return ADOFetchObj The object with properties set to the fields of the current row + * @return ADOFetchObj The object with properties set to the current row's fields. */ - function FetchObject($isUpper=true) { - if (empty($this->_obj)) { - $this->_obj = new ADOFetchObj(); - $this->_names = array(); - for ($i=0; $i <$this->_numOfFields; $i++) { - $f = $this->FetchField($i); - $this->_names[] = $f->name; - } + function fetchObject($isUpper = true) { + $fields = []; + foreach ($this->fieldTypesArray() as $metadata) { + $fields[$metadata->name] = $this->fields($metadata->name); } - $o = clone($this->_obj); - - for ($i=0; $i <$this->_numOfFields; $i++) { - $name = $this->_names[$i]; - if ($isUpper) { - $n = strtoupper($name); - } else { - $n = $name; - } - - $o->$n = $this->Fields($name); + if ($isUpper) { + $fields = array_change_key_case($fields, CASE_UPPER); } - return $o; + return new ADOFetchObj($fields); } /** - * Return the fields array of the current row as an object for convenience. - * The default is lower-case field names. + * Return the current row as an object for convenience and move to next row. * - * @return ADOFetchObj|false The object with properties set to the fields of the current row + * @return ADOFetchObj|false The object with properties set to the current row's fields. * or false if EOF. - * - * Fixed bug reported by tim@orotech.net */ - function FetchNextObj() { - return $this->FetchNextObject(false); + function fetchNextObj() { + return $this->fetchNextObject(false); } /** - * Return the fields array of the current row as an object for convenience. - * The default is upper case field names. + * Return the current row as an object for convenience and move to next row. * - * @param bool $isUpper to set the object property names to uppercase + * Field names are converted to uppercase by default. * - * @return ADOFetchObj|false The object with properties set to the fields of the current row - * or false if EOF. + * @param bool $isUpper True to convert field names to uppercase. * - * Fixed bug reported by tim@orotech.net + * @return ADOFetchObj|false The object with properties set to the current row's fields. + * or false if EOF. */ - function FetchNextObject($isUpper=true) { + function fetchNextObject($isUpper=true) { $o = false; if ($this->_numOfRows != 0 && !$this->EOF) { - $o = $this->FetchObject($isUpper); + $o = $this->fetchObject($isUpper); $this->_currentRow++; if ($this->_fetch()) { return $o; @@ -5258,15 +5350,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/datadict/datadict-postgres.inc.php b/datadict/datadict-postgres.inc.php index 89bcc0ad..d1b718df 100644 --- a/datadict/datadict-postgres.inc.php +++ b/datadict/datadict-postgres.inc.php @@ -284,7 +284,7 @@ class ADODB2_postgres extends ADODB_DataDict // does not have alter column if (!$tableflds) { - if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); + if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definition for PostgreSQL"); return array(); } return $this->_recreate_copy_table($tabname, false, $tableflds,$tableoptions); @@ -306,7 +306,7 @@ class ADODB2_postgres extends ADODB_DataDict $has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; if (!$has_drop_column && !$tableflds) { if ($this->debug) { - ADOConnection::outp("dropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); + ADOConnection::outp("dropColumnSQL needs complete table-definition for PostgreSQL < 7.3"); } return array(); } @@ -335,7 +335,7 @@ class ADODB2_postgres extends ADODB_DataDict foreach($this->metaColumns($tabname) as $fld) { if (preg_match('/'.$fld->name.' (\w+)/i', $tableflds, $matches)) { $new_type = strtoupper($matches[1]); - // AlterColumn of a char column to a nummeric one needs an explicit conversation + // AlterColumn of a char column to a numeric one needs an explicit conversation if (in_array($new_type, array('I', 'I2', 'I4', 'I8', 'N', 'F')) && in_array($fld->type, array('varchar','char','text','bytea')) ) { @@ -412,7 +412,7 @@ class ADODB2_postgres extends ADODB_DataDict return $suffix; } - // search for a sequence for the given table (asumes the seqence-name contains the table-name!) + // search for a sequence for the given table (assumes the sequence-name contains the table-name!) // if yes return sql to drop it // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!! function _dropAutoIncrement($tabname) diff --git a/datadict/datadict-sqlite.inc.php b/datadict/datadict-sqlite.inc.php index d565f887..1d26123b 100644 --- a/datadict/datadict-sqlite.inc.php +++ b/datadict/datadict-sqlite.inc.php @@ -32,49 +32,59 @@ class ADODB2_sqlite extends ADODB_DataDict { public $blobAllowsDefaultValue = true; public $blobAllowsNotNull = true; - - function ActualType($meta) + + function actualType($meta) { - $meta = strtoupper($meta); - - /* - * Add support for custom meta types. We do this - * first, that allows us to override existing types - */ - if (isset($this->connection->customMetaTypes[$meta])) + + // Add support for custom meta types. + // We do this first, that allows us to override existing types + if (isset($this->connection->customMetaTypes[$meta])) { return $this->connection->customMetaTypes[$meta]['actual']; - + } + switch(strtoupper($meta)) { - case 'C': return 'VARCHAR'; // TEXT , TEXT affinity - case 'XL':return 'LONGTEXT'; // TEXT , TEXT affinity - case 'X': return 'TEXT'; // TEXT , TEXT affinity + case 'C': + case 'C2': + return 'VARCHAR'; // TEXT , TEXT affinity + case 'XL': + case 'X2': + return 'LONGTEXT'; // TEXT , TEXT affinity + case 'X': + return 'TEXT'; // TEXT , TEXT affinity - case 'C2': return 'VARCHAR'; // TEXT , TEXT affinity - case 'X2': return 'LONGTEXT'; // TEXT , TEXT affinity + case 'B': + return 'LONGBLOB'; // TEXT , NONE affinity , BLOB - case 'B': return 'LONGBLOB'; // TEXT , NONE affinity , BLOB + case 'D': + return 'DATE'; // NUMERIC , NUMERIC affinity + case 'T': + return 'DATETIME'; // NUMERIC , NUMERIC affinity - case 'D': return 'DATE'; // NUMERIC , NUMERIC affinity - case 'T': return 'DATETIME'; // NUMERIC , NUMERIC affinity - case 'L': return 'TINYINT'; // NUMERIC , INTEGER affinity + case 'I': + case 'R': + case 'I4': + return 'INTEGER'; // NUMERIC , INTEGER affinity + case 'L': + case 'I1': + return 'TINYINT'; // NUMERIC , INTEGER affinity + case 'I2': + return 'SMALLINT'; // NUMERIC , INTEGER affinity + case 'I8': + return 'BIGINT'; // NUMERIC , INTEGER affinity - case 'R': - case 'I4': - case 'I': return 'INTEGER'; // NUMERIC , INTEGER affinity - case 'I1': return 'TINYINT'; // NUMERIC , INTEGER affinity - case 'I2': return 'SMALLINT'; // NUMERIC , INTEGER affinity - case 'I8': return 'BIGINT'; // NUMERIC , INTEGER affinity + case 'F': + return 'DOUBLE'; // NUMERIC , REAL affinity + case 'N': + return 'NUMERIC'; // NUMERIC , NUMERIC affinity - case 'F': return 'DOUBLE'; // NUMERIC , REAL affinity - case 'N': return 'NUMERIC'; // NUMERIC , NUMERIC affinity - default: - return $meta; + default: + return $meta; } } // return string must begin with space - function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned) + function _createSuffix($fname, &$ftype, $fnotnull, $fdefault, $fautoinc, $fconstraint, $funsigned) { $suffix = ''; if ($funsigned) $suffix .= ' UNSIGNED'; @@ -85,22 +95,34 @@ class ADODB2_sqlite extends ADODB_DataDict { return $suffix; } - function AlterColumnSQL($tabname, $flds, $tableflds='', $tableoptions='') + function alterColumnSQL($tabname, $flds, $tableflds='', $tableoptions='') { - if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported natively by SQLite"); + if ($this->debug) { + ADOConnection::outp("AlterColumnSQL not supported natively by SQLite"); + } return array(); } - function DropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='') + function dropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='') { - if ($this->debug) ADOConnection::outp("DropColumnSQL not supported natively by SQLite"); - return array(); + if (SQLite3::version()['versionNumber'] < 3035000) { + if ($this->debug) { + ADOConnection::outp("DropColumnSQL is only supported since SQLite 3.35.0"); + } + return array(); + } + return parent::dropColumnSQL($tabname, $flds, $tableflds, $tableoptions); } - function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') + function renameColumnSQL($tabname, $oldcolumn, $newcolumn, $flds='') { - if ($this->debug) ADOConnection::outp("RenameColumnSQL not supported natively by SQLite"); - return array(); + if (SQLite3::version()['versionNumber'] < 3025000) { + if ($this->debug) { + ADOConnection::outp("renameColumnSQL is only supported since SQLite 3.25.0"); + } + return array(); + } + return parent::renameColumnSQL($tabname, $oldcolumn, $newcolumn, $flds); } } diff --git a/docs/changelog.md b/docs/changelog.md index 8d6f9991..1268cd4f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,52 @@ 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) +- sqlite: support for DROP and RENAME COLUMN + [#1053](https://github.com/ADOdb/ADOdb/issues/1053) + +### 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.9] - Unreleased ### Fixed @@ -1142,7 +1188,7 @@ other database types as well; all drivers derived from the above are also impact - odbc: clear fields before fetching. See PHPLens Issue No: 17539 - oci8: GetRowAssoc now works in ADODB_FETCH_ASSOC fetch mode - oci8: MetaType and MetaForeignKeys argument count are now strict-standards compliant -- oci8: Added trailing `;` on trigger creation for sequence fields, prevents occurence of ORA-24344 +- oci8: Added trailing `;` on trigger creation for sequence fields, prevents occurrence of ORA-24344 - oci8quercus: new oci8 driver with support for quercus jdbc data types. - pdo: Fixed concat recursion bug in 5.3. See PHPLens Issue No: 19285 - pgsql: Default driver (postgres/pgsql) is now postgres8 @@ -1481,6 +1527,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.8...master + [5.22.9]: https://github.com/adodb/adodb/compare/v5.22.8...hotfix/5.22 [5.22.8]: https://github.com/adodb/adodb/compare/v5.22.7...v5.22.8 [5.22.7]: https://github.com/adodb/adodb/compare/v5.22.6...v5.22.7 diff --git a/docs/changelog_v2.x.md b/docs/changelog_v2.x.md index af718b73..20f3e015 100644 --- a/docs/changelog_v2.x.md +++ b/docs/changelog_v2.x.md @@ -249,7 +249,7 @@ $conn->Connect($dsn); - Mssql date regex had error. Fixed - reported by Minh Hoang vb_user#yahoo.com. - DBTimeStamp() and DBDate() now accept iso dates and unix timestamps. This means that the PostgreSQL handling of dates in GetInsertSQL() and GetUpdateSQL() can be removed. Also if these functions are passed '' or null or false, we return a SQL null. - GetInsertSQL() and GetUpdateSQL() now accept a new parameter, $magicq to indicate whether quotes should be inserted based on magic quote settings - suggested by dj#4ict.com. -- Reformated docs slightly based on suggestions by Chris Small. +- Reformatted docs slightly based on suggestions by Chris Small. ## 1.65 - 28 Dec 2001 @@ -462,7 +462,7 @@ $conn->Connect($dsn); - Changed behaviour of RecordSet.GetMenu() to support size parameter (listbox) properly. - Added emptyDate and emptyTimeStamp to RecordSet class that defines how to represent empty dates. - Added MetaColumns($table) that returns an array of ADOFieldObject's listing the columns of a table. -- Added transaction support for PostgresSQL -- thanks to "Eric G. Werk" egw#netguide.dk. +- Added transaction support for PostgreSQL -- thanks to "Eric G. Werk" egw#netguide.dk. - Added adodb-session.php for session support. ## 0.80 - 30 Nov 2000 diff --git a/drivers/adodb-ado.inc.php b/drivers/adodb-ado.inc.php index df95c690..569f0fcb 100644 --- a/drivers/adodb-ado.inc.php +++ b/drivers/adodb-ado.inc.php @@ -122,7 +122,7 @@ class ADODB_ado extends ADOConnection { adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, - adSchemaReferentialContraints = 9, + adSchemaReferentialConstraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, @@ -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) { @@ -584,7 +574,7 @@ class ADORecordSet_ado extends ADORecordSet { $val=(float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; - $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) @@ -599,8 +589,8 @@ class ADORecordSet_ado extends ADORecordSet { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; - if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); - else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + if (($val % 86400) == 0) $this->fields[] = date('Y-m-d',$val); + else $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 1: // null diff --git a/drivers/adodb-ado5.inc.php b/drivers/adodb-ado5.inc.php index 04b45abf..4ef1da98 100644 --- a/drivers/adodb-ado5.inc.php +++ b/drivers/adodb-ado5.inc.php @@ -147,7 +147,7 @@ class ADODB_ado extends ADOConnection { adSchemaConstraintColumnUsage = 6, adSchemaConstraintTableUsage = 7, adSchemaKeyColumnUsage = 8, - adSchemaReferentialContraints = 9, + adSchemaReferentialConstraints = 9, adSchemaTableConstraints = 10, adSchemaColumnsDomainUsage = 11, adSchemaIndexes = 12, @@ -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) { @@ -631,7 +621,7 @@ class ADORecordSet_ado extends ADORecordSet { $val= (float) variant_cast($f->value,VT_R8)*3600*24-2209161600; else $val = $f->value; - $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 133:// A date value (yyyymmdd) @@ -646,8 +636,8 @@ class ADORecordSet_ado extends ADORecordSet { if (!is_numeric($f->value)) $val = variant_date_to_timestamp($f->value); else $val = $f->value; - if (($val % 86400) == 0) $this->fields[] = adodb_date('Y-m-d',$val); - else $this->fields[] = adodb_date('Y-m-d H:i:s',$val); + if (($val % 86400) == 0) $this->fields[] = date('Y-m-d',$val); + else $this->fields[] = date('Y-m-d H:i:s',$val); } break; case 1: // null 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 60cd093a..d0810fee 100644 --- a/drivers/adodb-db2.inc.php +++ b/drivers/adodb-db2.inc.php @@ -11,7 +11,7 @@ * about all the changes, see the update information on the ADOdb website * for version 5.21.0. * - * @link http://pecl.php.net/package/ibm_db2 PECL Extension For DB2 + * @link https://pecl.php.net/package/ibm_db2 PECL Extension For DB2 * * This file is part of ADOdb, a Database Abstraction Layer library for PHP. * @@ -179,7 +179,7 @@ class ADODB_db2 extends ADOConnection { $db2Options); } else - + $this->_connectionID = $db2Function($argDSN, '', '', @@ -193,7 +193,7 @@ class ADODB_db2 extends ADOConnection { if ($this->_connectionID && $argDatabasename) $this->execute("SET SCHEMA=$argDatabasename"); - + return $this->_connectionID != false; } @@ -228,7 +228,7 @@ class ADODB_db2 extends ADOConnection { $connectionParameters['dsn'] = $argDSN; $connectionParameters['database'] = $argDatabasename; $connectionParameters['catalogue'] = false; - + return $connectionParameters; } @@ -423,7 +423,7 @@ class ADODB_db2 extends ADOConnection { { if (empty($ts) && $ts !== 0) return 'null'; if (is_string($ts)) $ts = ADORecordSet::unixTimeStamp($ts); - return 'TO_DATE('.adodb_date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')"; + return 'TO_DATE('.date($this->fmtTimeStamp,$ts).",'YYYY-MM-DD HH24:MI:SS')"; } /** @@ -1859,17 +1859,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..cfbadf99 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; - } - 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; + parent::__construct($queryID, $mode); + + switch ($this->adodbFetchMode) { + 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 e1cba330..91a47c94 100644 --- a/drivers/adodb-ldap.inc.php +++ b/drivers/adodb-ldap.inc.php @@ -31,70 +31,101 @@ if (!defined('LDAP_ASSOC')) { define('LDAP_BOTH',ADODB_FETCH_BOTH); } -class ADODB_ldap extends ADOConnection { - var $databaseType = 'ldap'; - var $dataProvider = 'ldap'; +class ADODB_ldap extends ADOConnection +{ + public $databaseType = 'ldap'; + public $dataProvider = 'ldap'; - # Connection information - var $username = false; - var $password = false; + /** + * @var string[] Caches the version info + */ + protected $version; - # Used during searches - var $filter; - var $dn; - var $version; - var $port = 389; + /** + * @var string Bind error message, eg. "Binding: invalid credentials" + */ + protected $_bind_errmsg = "Binding: %s"; - # Options configuration information - var $LDAP_CONNECT_OPTIONS; - - # error on binding, eg. "Binding: invalid credentials" - var $_bind_errmsg = "Binding: %s"; + protected $connectionParameters = array( + LDAP_OPT_PROTOCOL_VERSION => 3, + LDAP_OPT_REFERRALS => 0 + ); - // returns true or false - function _connect( $host, $username, $password, $ldapbase) + /** + * Connect to an LDAP Server + * + * @param string|null $ldapServer The LDAP Server to connect to. + * @param string|null $username The username to connect as. + * @param string|null $password The password to connect with. + * @param string|null $ldapbase The Base DN + * + * @return bool|null True if connected successfully, false if connection failed, or null if the ldap extension + * isn't currently loaded. + */ + public function _connect($ldapServer, $username, $password, $ldapbase) { global $LDAP_CONNECT_OPTIONS; - if ( !function_exists( 'ldap_connect' ) ) return null; + if (!function_exists('ldap_connect')) { + return null; + } - if (strpos($host,'ldap://') === 0 || strpos($host,'ldaps://') === 0) { - $this->_connectionID = @ldap_connect($host); - } else { - $conn_info = array( $host,$this->port); + if (strpos($ldapServer, 'ldap://') !== 0 && strpos($ldapServer, 'ldaps://') !== 0) { + /* + * If ldap SSL not explicitly specified, fall back to insecure + */ + $ldapServer = sprintf('ldap://%s', $ldapServer); + } - if ( strstr( $host, ':' ) ) { - $conn_info = explode( ':', $host ); - } + $this->_connectionID = @ldap_connect($ldapServer); - $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] ); - } if (!$this->_connectionID) { - $e = 'Could not connect to ' . $conn_info[0]; + $e = 'Could not connect to ' . $ldapServer; $this->_errorMsg = $e; - if ($this->debug) ADOConnection::outp($e); + if ($this->debug) { + ADOConnection::outp($e); + } + return false; } - if(!empty($LDAP_CONNECT_OPTIONS)) { - $this->_inject_bind_options( $LDAP_CONNECT_OPTIONS ); + + if(!empty($LDAP_CONNECT_OPTIONS) && is_array($LDAP_CONNECT_OPTIONS)) { + // Convert options to connectionParameters() + trigger_error('$LDAP_CONNECT_OPTIONS is deprecated, use setConnectionParameter() instead', E_USER_DEPRECATED); + $this->_inject_bind_options($LDAP_CONNECT_OPTIONS); + } + + // Iterate over any connection parameters + foreach ($this->connectionParameters as $parameter => $value) { + if (!ldap_set_option($this->_connectionID, $parameter, $value)) { + if ($this->debug) { + $message = sprintf('Warning - Setting connection parameter %s to value %s failed', $parameter, + $value); + ADOConnection::outp($message); + $this->_errorMsg = $message; + } + } } if ($username) { - $bind = @ldap_bind( $this->_connectionID, $username, $password ); + $bind = @ldap_bind($this->_connectionID, $username, $password); } else { - $username = 'anonymous'; - $bind = @ldap_bind( $this->_connectionID ); + $bind = @ldap_bind($this->_connectionID); } if (!$bind) { - $e = sprintf($this->_bind_errmsg,ldap_error($this->_connectionID)); + $e = sprintf($this->_bind_errmsg, ldap_error($this->_connectionID)); $this->_errorMsg = $e; - if ($this->debug) ADOConnection::outp($e); + if ($this->debug) { + ADOConnection::outp($e); + } return false; } + $this->_errorMsg = ''; $this->database = $ldapbase; + return $this->_connectionID; } @@ -150,48 +181,95 @@ class ADODB_ldap extends ADOConnection { ); */ - function _inject_bind_options( $options ) { - foreach( $options as $option ) { - ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] ) - or die( "Unable to set server option: " . $option["OPTION_NAME"] ); + /** + * Converts old style global options into connection parameters + * + * @param array $options + * @return void + * @deprecated 5.23.0 + */ + private function _inject_bind_options($options) + { + foreach ($options as $option) { + $this->connectionParameters[$option["OPTION_NAME"]] = $option["OPTION_VALUE"]; + //ldap_set_option( $this->_connectionID, $option["OPTION_NAME"], $option["OPTION_VALUE"] ); + // or die( "Unable to set server option: " . $option["OPTION_NAME"] ); } } - function _query($sql,$inputarr=false) + + /** + * Execute a query against the AD Database. + * + * @param string|array $ldapQuery Query to execute. + * @param array $inputarr [Ignored] An optional array of parameters. + * + * @return resource|false + * @noinspection PhpParameterNameChangedDuringInheritanceInspection + */ + function _query($ldapQuery, $inputarr = false) { - $rs = @ldap_search( $this->_connectionID, $this->database, $sql ); - $this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql.': '.ldap_error($this->_connectionID); + $rs = ldap_search($this->_connectionID, $this->database, $ldapQuery); + + $this->_errorMsg = ($rs) ? '' : 'Search error on ' . $ldapQuery . ': ' . ldap_error($this->_connectionID); + return $rs; } - function ErrorMsg() - { - return $this->_errorMsg; - } - function ErrorNo() + /** + * Returns the last error number from previous AD operation. + * + * @return int The last error number. + */ + function errorNo() { return @ldap_errno($this->_connectionID); } - /* closes the LDAP connection */ + /** + * closes the LDAP connection + * + * @return void + */ function _close() { - @ldap_close( $this->_connectionID ); + if (is_resource($this->_connectionID)) { + @ldap_close($this->_connectionID); + } + $this->_connectionID = false; } - function SelectDB($db) { - $this->database = $db; + /** + * Switches the baseDN to a new value. Just like _connect(), + * its impossible to determine if the parameter is correct + * until you issue a query + * + * @return bool true + * @noinspection PhpParameterNameChangedDuringInheritanceInspection + */ + public function selectDB($baseDN) + { + $this->database = $baseDN; return true; - } // SelectDB + } - function ServerInfo() + /** + * Get server version info. + * + * @return string[]|false Array with multiple string elements that define the connection + */ + public function serverInfo() { - if( !empty( $this->version ) ) { + if (!empty($this->version)) { return $this->version; } + if (!is_resource($this->_connectionID)) { + return false; + } + $version = array(); /* Determines how aliases are handled during search. @@ -204,16 +282,20 @@ class ADODB_ldap extends ADOConnection { aliases are dereferenced when locating the base object but not during the search. Default: LDAP_DEREF_NEVER */ - ldap_get_option( $this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF'] ) ; - switch ( $version['LDAP_OPT_DEREF'] ) { + ldap_get_option($this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF']); + switch ($version['LDAP_OPT_DEREF']) { case 0: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_NEVER'; + break; case 1: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_SEARCHING'; + break; case 2: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_FINDING'; + break; case 3: $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_ALWAYS'; + break; } /* @@ -221,8 +303,8 @@ class ADODB_ldap extends ADOConnection { LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ - ldap_get_option( $this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT'] ); - if ( $version['LDAP_OPT_SIZELIMIT'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT']); + if ($version['LDAP_OPT_SIZELIMIT'] == 0) { $version['LDAP_OPT_SIZELIMIT'] = 'LDAP_NO_LIMIT'; } @@ -231,8 +313,8 @@ class ADODB_ldap extends ADOConnection { LDAP_NO_LIMIT (0) means no limit. Default: LDAP_NO_LIMIT */ - ldap_get_option( $this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT'] ); - if ( $version['LDAP_OPT_TIMELIMIT'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT']); + if ($version['LDAP_OPT_TIMELIMIT'] == 0) { $version['LDAP_OPT_TIMELIMIT'] = 'LDAP_NO_LIMIT'; } @@ -242,8 +324,8 @@ class ADODB_ldap extends ADOConnection { LDAP_OPT_OFF Default: ON */ - ldap_get_option( $this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS'] ); - if ( $version['LDAP_OPT_REFERRALS'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS']); + if ($version['LDAP_OPT_REFERRALS'] == 0) { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_ON'; @@ -255,8 +337,8 @@ class ADODB_ldap extends ADOConnection { LDAP_OPT_OFF Default: OFF */ - ldap_get_option( $this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART'] ); - if ( $version['LDAP_OPT_RESTART'] == 0 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART']); + if ($version['LDAP_OPT_RESTART'] == 0) { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_OFF'; } else { $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_ON'; @@ -268,18 +350,18 @@ class ADODB_ldap extends ADOConnection { LDAP_VERSION3 (3) Default: LDAP_VERSION2 (2) */ - ldap_get_option( $this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION'] ); - if ( $version['LDAP_OPT_PROTOCOL_VERSION'] == 2 ) { + ldap_get_option($this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION']); + if ($version['LDAP_OPT_PROTOCOL_VERSION'] == 2) { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION2'; } else { $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION3'; } /* The host name (or list of hosts) for the primary LDAP server. */ - ldap_get_option( $this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING'] ); - ldap_get_option( $this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN'] ); + ldap_get_option($this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME']); + ldap_get_option($this->_connectionID, LDAP_OPT_ERROR_NUMBER, $version['LDAP_OPT_ERROR_NUMBER']); + ldap_get_option($this->_connectionID, LDAP_OPT_ERROR_STRING, $version['LDAP_OPT_ERROR_STRING']); + ldap_get_option($this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN']); return $this->version = $version; } @@ -289,140 +371,206 @@ class ADODB_ldap extends ADOConnection { Class Name: Recordset --------------------------------------------------------------------------------------*/ -class ADORecordSet_ldap extends ADORecordSet{ +class ADORecordSet_ldap extends ADORecordSet +{ + + public $databaseType = "ldap"; + public $canSeek = false; + + protected $_entryID; /* keeps track of the entry resource identifier */ - var $databaseType = "ldap"; - var $canSeek = false; - var $_entryID; /* keeps track of the entry resource identifier */ + protected $seekData = array(); - function __construct($queryID,$mode=false) + /** + * Constructor + * + * @param resource $queryID + * @param boolean $mode + */ + public 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 ($this->adodbFetchMode) { + 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() + /** + * Recordset initialization stub + * + * @return void + */ + protected function _initrs() { - /* - This could be teaked to respect the $COUNTRECS directive from ADODB - It's currently being used in the _fetch() function and the - GetAssoc() function - */ - $this->_numOfRows = ldap_count_entries( $this->connection->_connectionID, $this->_queryID ); + // This could be tweaked to respect the $COUNTRECS directive from ADODB + // It's currently being used in the _fetch() function and the GetAssoc() function + $this->_numOfRows = ldap_count_entries($this->connection->_connectionID, $this->_queryID); } - /* - Return whole recordset as a multi-dimensional associative array - */ - function GetAssoc($force_array = false, $first2cols = false, $fetchMode = -1) + + /** + * Returns raw, database specific information about a field. + * + * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:recordset:fetchfield + * + * @param int $fieldOffset (Optional) The field number to get information for. + * + * @return ADOFieldObject|bool + */ + public function fetchField($fieldOffset = -1) { - $records = $this->_numOfRows; - $results = array(); - for ( $i=0; $i < $records; $i++ ) { - foreach ( $this->fields as $k=>$v ) { - if ( is_array( $v ) ) { - if ( $v['count'] == 1 ) { - $results[$i][$k] = $v[0]; - } else { - array_shift( $v ); - $results[$i][$k] = $v; - } - } - } + + if (!array_key_exists($fieldOffset, $this->seekData)) { + return false; } - return $results; + $o = new ADOFieldObject; + $o->name = $this->seekData[$fieldOffset]; + $o->max_length = 1024; + $o->type = 'C'; + + return $o; } - function GetRowAssoc($upper = ADODB_ASSOC_CASE) + /** + * Attempt to fetch a result row using the current fetch mode and return whether or not this was successful. + * + * @return bool True if row was fetched successfully, otherwise false. + */ + public function _fetch() { - $results = array(); - foreach ( $this->fields as $k=>$v ) { - if ( is_array( $v ) ) { - if ( $v['count'] == 1 ) { - $results[$k] = $v[0]; - } else { - array_shift( $v ); - $results[$k] = $v; - } - } + if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) { + $this->EOF = true; + return false; } + $this->EOF = false; - return $results; - } - - function GetRowNums() - { - $results = array(); - foreach ( $this->fields as $k=>$v ) { - static $i = 0; - if (is_array( $v )) { - if ( $v['count'] == 1 ) { - $results[$i] = $v[0]; - } else { - array_shift( $v ); - $results[$i] = $v; - } - $i++; - } + if ($this->_currentRow == 0) { + $this->_entryID = ldap_first_entry($this->connection->_connectionID, $this->_queryID); + } else { + $this->_entryID = ldap_next_entry($this->connection->_connectionID, $this->_entryID); } - return $results; - } - function _fetch() - { - if ( $this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0 ) { + $r = ldap_get_attributes($this->connection->_connectionID, $this->_entryID); + + if (!$r) { + $this->EOF = true; return false; } - if ( $this->_currentRow == 0 ) { - $this->_entryID = ldap_first_entry( $this->connection->_connectionID, $this->_queryID ); - } else { - $this->_entryID = ldap_next_entry( $this->connection->_connectionID, $this->_entryID ); + $rowentries = $r['count']; + unset($r['count']); + + $objectClass = false; + + if ($r[0] == 'objectClass') { + /* + * Always an array, move to the end of the row we want to use getAssoc + */ + $objectClass = $r['objectClass']; + unset($r[0]); + unset($r['objectClass']); + unset($objectClass['count']); } - $this->fields = ldap_get_attributes( $this->connection->_connectionID, $this->_entryID ); - $this->_numOfFields = $this->fields['count']; + $rowData = array(); + $rowIndex = 0; + for ($rowentry = 0; $rowentry < $rowentries; $rowentry++) { + if (!array_key_exists($rowentry, $r)) /* + * We moved objectClass + */ { + continue; + } - switch ( $this->fetchMode ) { + $eName = $r[$rowentry]; + $eData = $r[$eName]; + $dataEntries = $eData['count']; - case LDAP_ASSOC: - $this->fields = $this->GetRowAssoc(); - break; + unset($eData['count']); - case LDAP_NUM: - $this->fields = array_merge($this->GetRowNums(),$this->GetRowAssoc()); - break; + if ($dataEntries == 1) { + $element = $eData[0]; + } else { + $element = $eData; + } - case LDAP_BOTH: - default: - $this->fields = $this->GetRowNums(); + switch ($this->fetchMode) { + case LDAP_ASSOC: + $rowData[$eName] = $element; + break; + case LDAP_NUM: + $rowData[$rowIndex] = $element; + break; + case LDAP_BOTH: + default: + $rowData[$eName] = $element; + $rowData[$rowIndex] = $element; + break; + } + + $this->seekData[$rowIndex] = $eName; + + $rowIndex++; + } + + if ($objectClass) { + // Append the element onto the end of the row + switch ($this->fetchMode) { + case LDAP_ASSOC: + $rowData['objectClass'] = $objectClass; + break; + case LDAP_NUM: + $rowData[$rowIndex] = $objectClass; + break; + case LDAP_BOTH: + default: + $rowData['objectClass'] = $objectClass; + $rowData[$rowIndex] = $objectClass; + break; + } + } + + switch (ADODB_ASSOC_CASE) { + case ADODB_ASSOC_CASE_UPPER: + $rowData = array_change_key_case($rowData, CASE_UPPER); + array_map('strtoupper', $this->seekData); + break; + case ADODB_ASSOC_CASE_LOWER: + $rowData = array_change_key_case($rowData, CASE_LOWER); + array_map('strtolower', $this->seekData); break; } - return is_array( $this->fields ); + $this->bind = array_flip($this->seekData); + + $this->fields = $rowData; + $this->_numOfFields = count($rowData); + + return is_array($this->fields); } - function _close() { - @ldap_free_result( $this->_queryID ); + /** + * Frees the memory from a query recordset + * + * @return void + */ + public function _close() + { + if (is_resource($this->_queryID)) { + @ldap_free_result($this->_queryID); + } + $this->_queryID = false; } 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 a6678274..7d5282ed 100644 --- a/drivers/adodb-mysqli.inc.php +++ b/drivers/adodb-mysqli.inc.php @@ -22,6 +22,9 @@ * * @copyright 2000-2013 John Lim * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community + * + * @noinspection PhpComposerExtensionStubsInspection, SqlNoDataSourceInspection + * @noinspection PhpMissingFieldTypeInspection, PhpMissingParamTypeInspection, PhpMissingReturnTypeInspection */ // security - hide paths @@ -44,7 +47,8 @@ class ADODB_mysqli extends ADOConnection { var $dataProvider = 'mysql'; var $hasInsertID = true; var $hasAffectedRows = true; - var $metaTablesSQL = "SELECT + var $metaTablesSQL = /** @lang text */ + "SELECT TABLE_NAME, CASE WHEN TABLE_TYPE = 'VIEW' THEN 'V' ELSE 'T' END FROM INFORMATION_SCHEMA.TABLES @@ -66,7 +70,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; @@ -100,9 +103,13 @@ class ADODB_mysqli extends ADOConnection { /** * Tells the insert_id method how to obtain the last value, depending on whether * we are using a stored procedure or not + * @var bool + * @noinspection PhpPropertyOnlyWrittenInspection */ private $usePreparedStatement = false; private $useLastInsertStatement = false; + + /** @noinspection PhpPropertyOnlyWrittenInspection */ private $usingBoundVariables = false; private $statementAffectedRows = -1; @@ -218,21 +225,21 @@ class ADODB_mysqli extends ADOConnection { /** * Connect to a database. * - * @todo add: parameter int $port, parameter string $socket - * * @param string|null $argHostname (Optional) The host to connect to. * @param string|null $argUsername (Optional) The username to connect as. * @param string|null $argPassword (Optional) The password to connect with. - * @param string|null $argDatabasename (Optional) The name of the database to start in when connected. + * @param string|null $argDatabaseName (Optional) The name of the database to start in when connected. * @param bool $persist (Optional) Whether or not to use a persistent connection. * * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension * isn't currently loaded. + *@todo add: parameter int $port, parameter string $socket + * */ function _connect($argHostname = null, $argUsername = null, $argPassword = null, - $argDatabasename = null, + $argDatabaseName = null, $persist = false) { if(!extension_loaded("mysqli")) { @@ -255,15 +262,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) { @@ -295,20 +293,18 @@ class ADODB_mysqli extends ADOConnection { } } - #if (!empty($this->port)) $argHostname .= ":".$this->port; - /** @noinspection PhpCastIsUnnecessaryInspection */ $ok = @mysqli_real_connect($this->_connectionID, $argHostname, $argUsername, $argPassword, - $argDatabasename, + $argDatabaseName, # PHP7 compat: port must be int. Use default port if cast yields zero (int)$this->port != 0 ? (int)$this->port : 3306, $this->socket, $this->clientFlags); if ($ok) { - if ($argDatabasename) return $this->selectDB($argDatabasename); + if ($argDatabaseName) return $this->selectDB($argDatabaseName); return true; } else { if ($this->debug) { @@ -325,14 +321,14 @@ class ADODB_mysqli extends ADOConnection { * @param string|null $argHostname The host to connect to. * @param string|null $argUsername The username to connect as. * @param string|null $argPassword The password to connect with. - * @param string|null $argDatabasename The name of the database to start in when connected. + * @param string|null $argDatabaseName The name of the database to start in when connected. * * @return bool|null True if connected successfully, false if connection failed, or null if the mysqli extension * isn't currently loaded. */ - function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) + function _pconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) { - return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); + return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true); } /** @@ -565,7 +561,7 @@ class ADODB_mysqli extends ADOConnection { } // Reference on Last_Insert_ID on the recommended way to simulate sequences - var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; + var $_genIDSQL = /** @lang text */ "update %s set id=LAST_INSERT_ID(id+1);"; var $_genSeqSQL = "create table if not exists %s (id int not null)"; var $_genSeqCountSQL = "select count(*) from %s"; var $_genSeq2SQL = "insert into %s values (%s)"; @@ -688,7 +684,7 @@ class ADODB_mysqli extends ADOConnection { // parse index data into array while ($row = $rs->fetchRow()) { - if ($primary == FALSE AND $row[2] == 'PRIMARY') { + if (!$primary AND $row[2] == 'PRIMARY') { continue; } @@ -831,8 +827,6 @@ class ADODB_mysqli extends ADOConnection { $fraction = $dayFraction * 24 * 3600; return $date . ' + INTERVAL ' . $fraction.' SECOND'; - -// return "from_unixtime(unix_timestamp($date)+$fraction)"; } /** @@ -957,7 +951,7 @@ class ADODB_mysqli extends ADOConnection { $this->setFetchMode($savem); - $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"]; + $create_sql = $a_create_table["Create Table"] ?? $a_create_table["Create View"]; $matches = array(); @@ -982,7 +976,7 @@ class ADODB_mysqli extends ADOConnection { if ( $associative ) { $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; } else { - $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; + $foreign_keys[$ref_table][] = $my_field[$j] . '=' . $ref_field[$j]; } } } @@ -1000,9 +994,8 @@ class ADODB_mysqli extends ADOConnection { */ function MetaColumns($table, $normalize = true) { - $false = false; if (!$this->metaColumnsSQL) - return $false; + return false; global $ADODB_FETCH_MODE; $save = $ADODB_FETCH_MODE; @@ -1016,7 +1009,7 @@ class ADODB_mysqli extends ADOConnection { $SQL = "SELECT column_name, column_type FROM information_schema.columns - WHERE table_schema='{$this->database}' + WHERE table_schema='$this->database' AND table_name='$table'"; $schemaArray = $this->getAssoc($SQL); @@ -1028,7 +1021,7 @@ class ADODB_mysqli extends ADOConnection { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!is_object($rs)) - return $false; + return false; $retarr = array(); while (!$rs->EOF) { @@ -1101,7 +1094,6 @@ class ADODB_mysqli extends ADOConnection { */ function SelectDB($dbName) { -// $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); $this->database = $dbName; if ($this->_connectionID) { @@ -1160,21 +1152,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) @@ -1281,12 +1263,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) { @@ -1296,80 +1272,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) . "'", @@ -1378,37 +1313,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 @@ -1420,14 +1346,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 @@ -1435,19 +1356,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 { @@ -1458,11 +1372,10 @@ class ADODB_mysqli extends ADOConnection { } } - if($this->debug) + if ($this->debug) { ADOConnection::outp("Query: " . $sql . " failed. " . $this->errorMsg()); - + } return false; - } /** @@ -1584,12 +1497,9 @@ 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) { + switch ($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = MYSQLI_NUM; break; @@ -1602,8 +1512,6 @@ class ADORecordSet_mysqli extends ADORecordSet{ $this->fetchMode = MYSQLI_BOTH; break; } - $this->adodbFetchMode = $mode; - parent::__construct($queryID); } function _initrs() @@ -1647,7 +1555,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ { $fieldnr = $fieldOffset; if ($fieldOffset != -1) { - $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr); + @mysqli_field_seek($this->_queryID, $fieldnr); } $o = @mysqli_fetch_field($this->_queryID); if (!$o) return false; @@ -1860,16 +1768,16 @@ class ADORecordSet_mysqli extends ADORecordSet{ * * @param string|object $t The type to get the MetaType character for. * @param int $len (Optional) Redundant. Will always be set to -1. - * @param bool|object $fieldobj (Optional) + * @param bool|object $fieldObj (Optional) * * @return string The MetaType */ - function metaType($t, $len = -1, $fieldobj = false) + function metaType($t, $len = -1, $fieldObj = false) { if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; + $fieldObj = $t; + $t = $fieldObj->type; + $len = $fieldObj->max_length; } $t = strtoupper($t); @@ -1895,7 +1803,8 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_STRING : case MYSQLI_TYPE_ENUM : case MYSQLI_TYPE_SET : - case 253 : + /** @noinspection PhpMissingBreakStatementInspection */ + case MYSQLI_TYPE_VAR_STRING : if ($len <= $this->blobSize) { return 'C'; } @@ -1915,7 +1824,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_BLOB : case MYSQLI_TYPE_LONG_BLOB : case MYSQLI_TYPE_MEDIUM_BLOB : - return !empty($fieldobj->binary) ? 'B' : 'X'; + return !empty($fieldObj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': @@ -1945,7 +1854,7 @@ class ADORecordSet_mysqli extends ADORecordSet{ case MYSQLI_TYPE_LONGLONG : case MYSQLI_TYPE_SHORT : case MYSQLI_TYPE_TINY : - if (!empty($fieldobj->primary_key)) { + if (!empty($fieldObj->primary_key)) { return 'R'; } return 'I'; @@ -1979,16 +1888,16 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array * * @param string|object $t The type to get the MetaType character for. * @param int $len (Optional) Redundant. Will always be set to -1. - * @param bool|object $fieldobj (Optional) + * @param bool|object $fieldObj (Optional) * * @return string The MetaType */ - function MetaType($t, $len = -1, $fieldobj = false) + function MetaType($t, $len = -1, $fieldObj = false) { if (is_object($t)) { - $fieldobj = $t; - $t = $fieldobj->type; - $len = $fieldobj->max_length; + $fieldObj = $t; + $t = $fieldObj->type; + $len = $fieldObj->max_length; } $t = strtoupper($t); @@ -2012,7 +1921,8 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_STRING : case MYSQLI_TYPE_ENUM : case MYSQLI_TYPE_SET : - case 253 : + /** @noinspection PhpMissingBreakStatementInspection */ + case MYSQLI_TYPE_VAR_STRING : if ($len <= $this->blobSize) { return 'C'; } @@ -2032,7 +1942,7 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_BLOB : case MYSQLI_TYPE_LONG_BLOB : case MYSQLI_TYPE_MEDIUM_BLOB : - return !empty($fieldobj->binary) ? 'B' : 'X'; + return !empty($fieldObj->binary) ? 'B' : 'X'; case 'YEAR': case 'DATE': @@ -2062,7 +1972,7 @@ class ADORecordSet_array_mysqli extends ADORecordSet_array case MYSQLI_TYPE_LONGLONG : case MYSQLI_TYPE_SHORT : case MYSQLI_TYPE_TINY : - if (!empty($fieldobj->primary_key)) { + if (!empty($fieldObj->primary_key)) { return 'R'; } return 'I'; diff --git a/drivers/adodb-netezza.inc.php b/drivers/adodb-netezza.inc.php index dc7c58e4..76858909 100644 --- a/drivers/adodb-netezza.inc.php +++ b/drivers/adodb-netezza.inc.php @@ -61,7 +61,7 @@ class ADODB_netezza extends ADODB_postgres64 { var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt. var $ansiOuter = true; var $autoRollback = true; // apparently pgsql does not autorollback properly before 4.3.4 - // http://bugs.php.net/bug.php?id=25404 + // https://bugs.php.net/bug.php?id=25404 function MetaColumns($table,$upper=true) diff --git a/drivers/adodb-oci8.inc.php b/drivers/adodb-oci8.inc.php index b92281c7..f08700fd 100644 --- a/drivers/adodb-oci8.inc.php +++ b/drivers/adodb-oci8.inc.php @@ -69,13 +69,23 @@ class ADODB_oci8 extends ADOConnection { var $_stmt; var $_commit = OCI_COMMIT_ON_SUCCESS; var $_initdate = true; // init date to YYYY-MM-DD - var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW') and table_name not like 'BIN\$%'"; // bin$ tables are recycle bin tables - var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net - var $metaColumnsSQL2 = "select column_name,data_type,data_length, data_scale, data_precision, - case when nullable = 'Y' then 'NULL' - else 'NOT NULL' end as nulls, - data_default from all_tab_cols - where owner='%s' and table_name='%s' order by column_id"; // when there is a schema + var $metaTablesSQL = <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; + var $metaColumnsSQL2 = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM all_tab_columns + WHERE owner = '%s' AND table_name = '%s' + ORDER BY column_id + ENDSQL; // When there is a schema var $_bindInputArray = true; var $hasGenID = true; var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL"; @@ -95,7 +105,7 @@ END; var $_bind = array(); var $_nestedSQL = true; var $_getarray = false; // currently not working - var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER + var $leftOuter = ''; // oracle weirdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER var $session_sharing_force_blob = false; // alter session on updateblob if set to true var $firstrows = true; // enable first rows optimization on SelectLimit() var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit. @@ -105,8 +115,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 */ @@ -163,7 +171,7 @@ END; } $fld->max_length = $rs->fields[4]; } - $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); + $fld->not_null = $rs->fields[5] == 'N'; $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; @@ -193,21 +201,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 +265,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; } @@ -371,7 +406,7 @@ END; $ds = $d->format($this->fmtDate); } else { - $ds = adodb_date($this->fmtDate,$d); + $ds = date($this->fmtDate,$d); } return "TO_DATE(".$ds.",'".$this->dateformat."')"; @@ -400,7 +435,7 @@ END; $tss = $ts->format("'Y-m-d H:i:s'"); } else { - $tss = adodb_date("'Y-m-d H:i:s'",$ts); + $tss = date("'Y-m-d H:i:s'",$ts); } return $tss; @@ -547,7 +582,7 @@ END; $ok = true; } - return $ok ? true : false; + return (bool)$ok; } function CommitTrans($ok=true) @@ -1252,9 +1287,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 +1433,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 +1604,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,13 +1631,11 @@ 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; - } - switch ($mode) { + parent::__construct($queryID, $mode); + + switch ($this->adodbFetchMode) { case ADODB_FETCH_ASSOC: $this->fetchMode = OCI_ASSOC; break; @@ -1620,8 +1649,6 @@ class ADORecordset_oci8 extends ADORecordSet { break; } $this->fetchMode += OCI_RETURN_NULLS + OCI_RETURN_LOBS; - $this->adodbFetchMode = $mode; - $this->_queryID = $queryID; } /** @@ -1821,7 +1848,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) { @@ -1876,11 +1903,3 @@ class ADORecordset_oci8 extends ADORecordSet { } } } - -class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 { - - function MoveNext() - { - return adodb_movenext($this); - } -} diff --git a/drivers/adodb-oci8po.inc.php b/drivers/adodb-oci8po.inc.php index 71c203b6..3ecebebc 100644 --- a/drivers/adodb-oci8po.inc.php +++ b/drivers/adodb-oci8po.inc.php @@ -32,8 +32,17 @@ include_once(ADODB_DIR.'/drivers/adodb-oci8.inc.php'); class ADODB_oci8po extends ADODB_oci8 { var $databaseType = 'oci8po'; var $dataProvider = 'oci8'; - var $metaColumnsSQL = "select lower(cname),coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net - var $metaTablesSQL = "select lower(table_name),table_type from cat where table_type in ('TABLE','VIEW')"; + var $metaTablesSQL = <<<ENDSQL + SELECT lower(table_name), table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT lower(column_name), data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; function Param($name,$type='C') { 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-odbc_oracle.inc.php b/drivers/adodb-odbc_oracle.inc.php index d8461e1a..8c608295 100644 --- a/drivers/adodb-odbc_oracle.inc.php +++ b/drivers/adodb-odbc_oracle.inc.php @@ -33,8 +33,17 @@ class ADODB_odbc_oracle extends ADODB_odbc { var $concat_operator='||'; var $fmtDate = "'Y-m-d 00:00:00'"; var $fmtTimeStamp = "'Y-m-d h:i:sA'"; - var $metaTablesSQL = 'select table_name from cat'; - var $metaColumnsSQL = "select cname,coltype,width from col where tname='%s' order by colno"; + var $metaTablesSQL = <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; var $sysDate = "TRUNC(SYSDATE)"; var $sysTimeStamp = 'SYSDATE'; @@ -42,9 +51,8 @@ class ADODB_odbc_oracle extends ADODB_odbc { function MetaTables($ttype = false, $showSchema = false, $mask = false) { - $false = false; $rs = $this->Execute($this->metaTablesSQL); - if ($rs === false) return $false; + if ($rs === false) return false; $arr = $rs->GetArray(); $arr2 = array(); for ($i=0; $i < sizeof($arr); $i++) { @@ -60,19 +68,20 @@ class ADODB_odbc_oracle extends ADODB_odbc { $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table))); if ($rs === false) { - $false = false; - return $false; + return false; } $retarr = array(); - while (!$rs->EOF) { //print_r($rs->fields); + while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; - - if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; - else $retarr[strtoupper($fld->name)] = $fld; + if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) { + $retarr[] = $fld; + } else { + $retarr[strtoupper($fld->name)] = $fld; + } $rs->MoveNext(); } diff --git a/drivers/adodb-odbtp.inc.php b/drivers/adodb-odbtp.inc.php index e3f81481..46bf9c4a 100644 --- a/drivers/adodb-odbtp.inc.php +++ b/drivers/adodb-odbtp.inc.php @@ -81,7 +81,7 @@ class ADODB_odbtp extends ADOConnection{ if ($isfld) return "convert(date, $d, 120)"; if (is_string($d)) $d = ADORecordSet::UnixDate($d); - $d = adodb_date($this->fmtDate,$d); + $d = date($this->fmtDate,$d); return "convert(date, $d, 120)"; } @@ -91,7 +91,7 @@ class ADODB_odbtp extends ADOConnection{ if ($isfld) return "convert(datetime, $d, 120)"; if (is_string($d)) $d = ADORecordSet::UnixDate($d); - $d = adodb_date($this->fmtDate,$d); + $d = date($this->fmtDate,$d); return "convert(datetime, $d, 120)"; } */ @@ -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..7c061b07 100644 --- a/drivers/adodb-oracle.inc.php +++ b/drivers/adodb-oracle.inc.php @@ -41,7 +41,7 @@ class ADODB_oracle extends ADOConnection { { if (is_string($d)) $d = ADORecordSet::UnixDate($d); if (is_object($d)) $ds = $d->format($this->fmtDate); - else $ds = adodb_date($this->fmtDate,$d); + else $ds = date($this->fmtDate,$d); return 'TO_DATE('.$ds.",'YYYY-MM-DD')"; } @@ -51,7 +51,7 @@ class ADODB_oracle extends ADOConnection { if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts); if (is_object($ts)) $ds = $ts->format($this->fmtDate); - else $ds = adodb_date($this->fmtTimeStamp,$ts); + else $ds = date($this->fmtTimeStamp,$ts); return 'TO_DATE('.$ds.",'RRRR-MM-DD, HH:MI:SS AM')"; } @@ -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..7fbc8efa 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($this->adodbFetchMode) { + 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-pdo_firebird.inc.php b/drivers/adodb-pdo_firebird.inc.php index f8847c3c..922407c5 100644 --- a/drivers/adodb-pdo_firebird.inc.php +++ b/drivers/adodb-pdo_firebird.inc.php @@ -33,7 +33,7 @@ class ADODB_pdo_firebird extends ADODB_pdo var $arrayClass = 'ADORecordSet_array_pdo_firebird'; /** - * Gets the version iformation from the server + * Gets the version information from the server * * @return string[] */ diff --git a/drivers/adodb-pdo_oci.inc.php b/drivers/adodb-pdo_oci.inc.php index 20b47def..8603e393 100644 --- a/drivers/adodb-pdo_oci.inc.php +++ b/drivers/adodb-pdo_oci.inc.php @@ -26,8 +26,17 @@ class ADODB_pdo_oci extends ADODB_pdo_base { var $sysTimeStamp = 'SYSDATE'; var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS' var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)"; - var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW')"; - var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; + var $metaTablesSQL = <<<ENDSQL + SELECT table_name, table_type + FROM user_catalog + WHERE table_type IN ('TABLE', 'VIEW') AND table_name NOT LIKE 'BIN\$%' + ENDSQL; // bin$ tables are recycle bin tables + var $metaColumnsSQL = <<<ENDSQL + SELECT column_name, data_type, data_length, data_scale, data_precision, nullable, data_default + FROM user_tab_columns + WHERE table_name = '%s' + ORDER BY column_id + ENDSQL; var $_initdate = true; var $_hasdual = true; @@ -58,9 +67,8 @@ class ADODB_pdo_oci extends ADODB_pdo_base { function MetaColumns($table,$normalize=true) { - global $ADODB_FETCH_MODE; + global $ADODB_FETCH_MODE; - $false = false; $save = $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_NUM; if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); @@ -70,20 +78,22 @@ class ADODB_pdo_oci extends ADODB_pdo_base { if (isset($savem)) $this->SetFetchMode($savem); $ADODB_FETCH_MODE = $save; if (!$rs) { - return $false; + return false; } $retarr = array(); - while (!$rs->EOF) { //print_r($rs->fields); + while (!$rs->EOF) { $fld = new ADOFieldObject(); $fld->name = $rs->fields[0]; $fld->type = $rs->fields[1]; $fld->max_length = $rs->fields[2]; $fld->scale = $rs->fields[3]; - if ($rs->fields[1] == 'NUMBER' && $rs->fields[3] == 0) { - $fld->type ='INT'; + if ($rs->fields[1] == 'NUMBER') { + if ($rs->fields[3] == 0) { + $fld->type = 'INT'; + } $fld->max_length = $rs->fields[4]; } - $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0); + $fld->not_null = $rs->fields[5] == 'N'; $fld->binary = (strpos($fld->type,'BLOB') !== false); $fld->default_value = $rs->fields[6]; @@ -93,7 +103,7 @@ class ADODB_pdo_oci extends ADODB_pdo_base { } $rs->Close(); if (empty($retarr)) - return $false; + return false; else return $retarr; } diff --git a/drivers/adodb-postgres64.inc.php b/drivers/adodb-postgres64.inc.php index b1d161d7..73f31995 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='||'; @@ -78,21 +82,21 @@ class ADODB_postgres64 extends ADOConnection{ // http://bugs.php.net/bug.php?id=25404 var $uniqueIisR = true; - var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database + var $_bindInputArray = false; // requires PostgreSQL 7.3+ and ability to modify database var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance. /** @var int $_pnum Number of the last assigned query parameter {@see param()} */ 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, // and writes these time in this format: "2001-03-01 18:59:26+02". // There is no code for the "+02" time zone information, so I just left that out. // I'm not familiar enough with both ADODB as well as Postgres - // to know what the concequences are. The other values are correct (wheren't in 0.94) + // to know what the consequences are. The other values are correct (weren't in 0.94) // -- Freek Dijkstra /** @@ -135,7 +139,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'); @@ -157,7 +169,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); @@ -165,7 +179,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); } @@ -180,13 +196,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; @@ -196,7 +213,6 @@ class ADODB_postgres64 extends ADOConnection{ return pg_query($this->_connectionID, 'commit'); } - // returns true/false function RollbackTrans() { if ($this->transOff) return true; @@ -263,8 +279,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; @@ -344,15 +358,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'); @@ -368,24 +391,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; } /* @@ -397,19 +421,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; @@ -429,7 +455,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 @@ -490,7 +516,6 @@ class ADODB_postgres64 extends ADOConnection{ global $ADODB_FETCH_MODE; $schema = false; - $false = false; $this->_findschema($table,$schema); if ($normalize) $table = strtolower($table); @@ -504,7 +529,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 @@ -522,6 +547,8 @@ class ADODB_postgres64 extends ADOConnection{ $rskey->Close(); unset($rskey); + } else { + $keys = []; } $rsdefa = array(); @@ -574,28 +601,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') @@ -639,7 +667,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;'; } @@ -656,8 +684,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 @@ -748,22 +775,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; } @@ -910,17 +921,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? @@ -940,23 +951,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 ($this->adodbFetchMode) { + 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) @@ -964,8 +973,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() @@ -977,7 +985,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); @@ -1044,7 +1051,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]); } } @@ -1085,7 +1092,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; @@ -1095,71 +1102,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..00638e70 100644 --- a/drivers/adodb-sqlite.inc.php +++ b/drivers/adodb-sqlite.inc.php @@ -36,8 +36,8 @@ class ADODB_sqlite extends ADOConnection { var $hasInsertID = true; /// supports autoincrement ID? var $hasAffectedRows = true; /// supports affected rows for update/delete? var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; - var $sysDate = "adodb_date('Y-m-d')"; - var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; + var $sysDate = "date('Y-m-d')"; + var $sysTimeStamp = "date('Y-m-d H:i:s')"; var $fmtTimeStamp = "'Y-m-d H:i:s'"; function ServerInfo() @@ -164,14 +164,12 @@ class ADODB_sqlite extends ADOConnection { function SQLDate($fmt, $col=false) { $fmt = $this->qstr($fmt); - return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; + return ($col) ? "strftime($fmt,$col)" : "strftime($fmt)"; } function _createFunctions() { - @sqlite_create_function($this->_connectionID, 'adodb_date', 'adodb_date', 1); - @sqlite_create_function($this->_connectionID, 'adodb_date2', 'adodb_date2', 2); } @@ -425,14 +423,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 ($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE_NUM; break; @@ -443,9 +438,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 +454,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 7e5f5ffd..e6bc9014 100644 --- a/drivers/adodb-sqlite3.inc.php +++ b/drivers/adodb-sqlite3.inc.php @@ -306,13 +306,11 @@ class ADODB_sqlite3 extends ADOConnection { $fmt = str_replace($fromChars,$toChars,$fmt); $fmt = $this->qstr($fmt); - return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; + return ($col) ? "strftime($fmt,$col)" : "strftime($fmt)"; } function _createFunctions() { - $this->_connectionID->createFunction('adodb_date', 'adodb_date', 1); - $this->_connectionID->createFunction('adodb_date2', 'adodb_date2', 2); } /** @noinspection PhpUnusedParameterInspection */ @@ -725,13 +723,10 @@ 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; - } - switch($mode) { + parent::__construct($queryID, $mode); + switch($this->adodbFetchMode) { case ADODB_FETCH_NUM: $this->fetchMode = SQLITE3_NUM; break; @@ -742,9 +737,6 @@ class ADORecordset_sqlite3 extends ADORecordSet { $this->fetchMode = SQLITE3_BOTH; break; } - $this->adodbFetchMode = $mode; - - $this->_queryID = $queryID; $this->_inited = true; $this->fields = array(); @@ -761,7 +753,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..abadb51c 100644 --- a/drivers/adodb-sybase.inc.php +++ b/drivers/adodb-sybase.inc.php @@ -315,15 +315,11 @@ 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; - } - if (!$mode) $this->fetchMode = ADODB_FETCH_ASSOC; - else $this->fetchMode = $mode; - parent::__construct($id); + parent::__construct($queryID, $mode); + + $this->fetchMode = $this->adodbFetchMode ?: ADODB_FETCH_ASSOC; } /* Returns: an object containing field information. @@ -412,7 +408,7 @@ class ADORecordSet_array_sybase extends ADORecordSet_array { $themth = $ADODB_sybase_mths[$themth]; if ($themth <= 0) return false; // h-m-s-MM-DD-YY - return adodb_mktime(0,0,0,$themth,$rr[2],$rr[3]); + return mktime(0,0,0,$themth,$rr[2],$rr[3]); } static function UnixTimeStamp($v) @@ -439,6 +435,6 @@ class ADORecordSet_array_sybase extends ADORecordSet_array { break; } // h-m-s-MM-DD-YY - return adodb_mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); + return mktime($rr[4],$rr[5],0,$themth,$rr[2],$rr[3]); } } diff --git a/pear/Auth/Container/ADOdb.php b/pear/Auth/Container/ADOdb.php index 807da9d7..ad6aaf44 100644 --- a/pear/Auth/Container/ADOdb.php +++ b/pear/Auth/Container/ADOdb.php @@ -289,7 +289,7 @@ class Auth_Container_ADOdb extends Auth_Container $retVal = array(); - // Find if db_fileds contains a *, i so assume all col are selected + // Find if db_fields contains a *, i so assume all col are selected if(strstr($this->options['db_fields'], '*')){ $sql_from = "*"; } 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..fef0a019 --- /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_it import MarkdownIt + +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 + """ + md = MarkdownIt() + html = md.render(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..2bf19466 --- /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 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 if t.tag is not None else 0) + 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/requirements.txt b/scripts/requirements.txt new file mode 100644 index 00000000..887964ad --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,8 @@ +# ADOdb Python helper scripts required packages + +GitPython==3.1.44 +markdown-it-py==3.0.0 +PyGithub==2.5.0 +PyYAML==6.0.2 +requests==2.32.3 +tweepy==4.15.0 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-active-relationsx.php b/tests/test-active-relationsx.php index 4d3a80db..9e637f94 100644 --- a/tests/test-active-relationsx.php +++ b/tests/test-active-relationsx.php @@ -106,7 +106,7 @@ $err_count = 0; $db->Execute("insert into songs (recordid, name, artistid) values(1,'No Hiding Place', 1)"); $db->Execute("insert into songs (recordid, name, artistid) values(2,'American Gangster Time', 1)"); - // This class _implicitely_ relies on the 'people' table (pluralized form of 'person') + // This class _implicitly_ relies on the 'people' table (pluralized form of 'person') class Person extends ADOdb_Active_Record { function __construct() @@ -115,7 +115,7 @@ $err_count = 0; $this->hasMany('children'); } } - // This class _implicitely_ relies on the 'children' table + // This class _implicitly_ relies on the 'children' table class Child extends ADOdb_Active_Record { function __construct() @@ -124,7 +124,7 @@ $err_count = 0; $this->belongsTo('person'); } } - // This class _explicitely_ relies on the 'children' table and shares its metadata with Child + // This class _explicitly_ relies on the 'children' table and shares its metadata with Child class Kid extends ADOdb_Active_Record { function __construct() @@ -133,7 +133,7 @@ $err_count = 0; $this->belongsTo('person'); } } - // This class _explicitely_ relies on the 'children' table but does not share its metadata + // This class _explicitly_ relies on the 'children' table but does not share its metadata class Rugrat extends ADOdb_Active_Record { function __construct() diff --git a/tests/test.php b/tests/test.php index 08a2e301..f887e120 100644 --- a/tests/test.php +++ b/tests/test.php @@ -819,12 +819,12 @@ END Adodb; print "<p><b>Fail: GetRow returns {$val2[0]}</b></p>"; } - print "<p>FetchObject/FetchNextObject Test</p>"; + print "<p>fetchObject/fetchNextObject Test</p>"; $rs = $db->Execute('select * from ADOXYZ'); if ($rs) { if (empty($rs->connection)) print "<b>Connection object missing from recordset</b></br>"; - while ($o = $rs->FetchNextObject()) { // calls FetchObject internally + while ($o = $rs->fetchNextObject()) { // calls fetchObject internally if (!is_string($o->FIRSTNAME) || !is_string($o->LASTNAME)) { print_r($o); print "<p><b>Firstname is not string</b></p>"; @@ -836,12 +836,12 @@ END Adodb; die("<p>ADOXYZ table cannot be read - die()"); } $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; - print "<p>FetchObject/FetchNextObject Test 2</p>"; + print "<p>fetchObject/fetchNextObject Test 2</p>"; #$db->debug=99; $rs = $db->Execute('select * from ADOXYZ'); if (empty($rs->connection)) print "<b>Connection object missing from recordset</b></br>"; print_r($rs->fields); - while ($o = $rs->FetchNextObject()) { // calls FetchObject internally + while ($o = $rs->fetchNextObject()) { // calls fetchObject internally if (!is_string($o->FIRSTNAME) || !is_string($o->LASTNAME)) { print_r($o); print "<p><b>Firstname is not string</b></p>"; @@ -1088,11 +1088,11 @@ END Adodb; $e = $db->ErrorMsg(); $e2 = $db->ErrorNo(); echo "Testing error handling, should see illegal column 'x' error=<i>$e ($e2) </i><br>"; if (!$e || !$e2) Err("Error handling did not work"); - print "Testing FetchNextObject for 1 object "; + print "Testing fetchNextObject for 1 object "; $rs = $db->Execute("select distinct lastname,firstname from ADOXYZ where firstname='Caroline'"); $fcnt = 0; if ($rs) - while ($o = $rs->FetchNextObject()) { + while ($o = $rs->fetchNextObject()) { $fcnt += 1; } if ($fcnt == 1) print " OK<BR>"; @@ -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/testsqlite.php b/tests/testsqlite.php new file mode 100644 index 00000000..9f461e45 --- /dev/null +++ b/tests/testsqlite.php @@ -0,0 +1,158 @@ +<?php +include '../adodb.inc.php'; +// set up timezone +$default_timezone = 'UTC'; +date_default_timezone_set($default_timezone); +$database = 'sqltest.sqlite'; + +// Create SQLITE test database +$slitedb = new SQLite3($database); + +// connect to test database +$driver = 'sqlite3'; +$db = adoNewConnection($driver); +$server = ''; +$user = ''; +$password = ''; +$db->connect($server, $user, $password, $database); + +// drop table if it exits +$rs = $db->execute('drop table testtable;'); + +// create test table +$sql = 'CREATE TABLE IF NOT EXISTS testtable ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + description TEXT, + whenithappened DATETIME +)'; +$rs = $db->execute($sql); + +// populate test table +$db->execute("INSERT INTO 'testtable' VALUES (5,'DEE658E8D9ACEB94A1E554CF7EDA6944','1728-02-28 09:17:52');"); +$db->execute("INSERT INTO 'testtable' VALUES (6,'367B5BAE40BEB89BA5AB22DFD79C93ED','1933-05-14 01:29:16');"); +$db->execute("INSERT INTO 'testtable' VALUES (7,'4F67DEF4750E6B25FA339CAA10A82A6E','2071-06-19 15:05:25');"); +$db->execute("INSERT INTO 'testtable' VALUES (8,'C0EB1A398C56D7CE70A04E702A43E402','1667-06-05 17:23:17');"); +$db->execute("INSERT INTO 'testtable' VALUES (9,'9A4E57224F44AC0799076C91062F9485','1453-06-29 03:05:28');"); +$db->execute("INSERT INTO 'testtable' VALUES (10,'85B5837F5A27A11070CAFA199D4F0B1B','2073-07-30 00:18:08');"); +$db->execute("INSERT INTO 'testtable' VALUES (11,'1D23F47779EB50AE8E1E1A83EE85AAF6','1819-02-12 19:24:06');"); +$db->execute("INSERT INTO 'testtable' VALUES (12,'30AA2C1946647145CC1F600D933A629A','1961-11-03 08:02:49');"); +$db->execute("INSERT INTO 'testtable' VALUES (13,'EB1ACD052F65CBC9AEBC7555A54FD2F8','1707-09-30 21:01:26');"); +$db->execute("INSERT INTO 'testtable' VALUES (14,'AAAA1087D8579A32431E2A88D3C1BCF2','1444-12-03 09:21:53');"); +$db->execute("INSERT INTO 'testtable' VALUES (15,'A4CAA5F87768299D2E54E66B7A1D826C','2007-09-01 03:35:32');"); +$db->execute("INSERT INTO 'testtable' VALUES (16,'9F77801A55697494BF71F79372A42D68','1502-10-21 12:55:00');"); +$db->execute("INSERT INTO 'testtable' VALUES (17,'603D8088154FE3B5630402F03F4239D4','1846-05-23 09:08:53');"); +$db->execute("INSERT INTO 'testtable' VALUES (18,'2D1A9307C08ADA0D4343ABCEC445DC4B','1503-05-24 19:46:01');"); +$db->execute("INSERT INTO 'testtable' VALUES (19,'495A03828CA0DF1051313EF9393B1C04','1671-04-05 17:47:42');"); +$db->execute("INSERT INTO 'testtable' VALUES (20,'8A80F450A8B3FE1F2CE18644282614FC','1757-02-16 21:14:58');"); +$db->execute("INSERT INTO 'testtable' VALUES (21,'0C68910EAF1785B8999C2A2F4308AD28','1973-02-01 20:35:10');"); +$db->execute("INSERT INTO 'testtable' VALUES (22,'91A44DE32E0A49A7E5619FE244EB78E8','1924-06-03 03:24:22');"); +$db->execute("INSERT INTO 'testtable' VALUES (23,'6588C425286C15465DD68E6459BB0037','1614-04-10 19:12:42');"); +$db->execute("INSERT INTO 'testtable' VALUES (24,'256F51DEED8907B0CE55291E83A47345','1602-08-31 02:37:22');"); +$db->execute("INSERT INTO 'testtable' VALUES (25,'00760C3E69F078DFD635ECE2C70141D8','2091-03-13 03:59:41');"); +$db->execute("INSERT INTO 'testtable' VALUES (26,'98F58B63EC27E489045318334719D453','1490-09-06 02:50:04');"); + +//========================== +// This code tests sqlite access. It also validates that the commands above worked! +//========================== +$sql = 'select * from testtable order by id limit 4'; +$rs = $db->execute($sql); +$actual = $rs->getRows(); +$expected = array( + array( + 0 => 5, + 'id' => 5, + 1 => 'DEE658E8D9ACEB94A1E554CF7EDA6944', + 'description' => 'DEE658E8D9ACEB94A1E554CF7EDA6944', + 2 => '1728-02-28 09:17:52', + 'whenithappened' => '1728-02-28 09:17:52', + ), + array( + 0 => 6, + 'id' => 6, + 1 => '367B5BAE40BEB89BA5AB22DFD79C93ED', + 'description' => '367B5BAE40BEB89BA5AB22DFD79C93ED', + 2 => '1933-05-14 01:29:16', + 'whenithappened' => '1933-05-14 01:29:16', + ), + array( + 0 => 7, + 'id' => 7, + 1 => '4F67DEF4750E6B25FA339CAA10A82A6E', + 'description' => '4F67DEF4750E6B25FA339CAA10A82A6E', + 2 => '2071-06-19 15:05:25', + 'whenithappened' => '2071-06-19 15:05:25', + ), + array( + 0 => 8, + 'id' => 8, + 1 => 'C0EB1A398C56D7CE70A04E702A43E402', + 'description' => 'C0EB1A398C56D7CE70A04E702A43E402', + 2 => '1667-06-05 17:23:17', + 'whenithappened' => '1667-06-05 17:23:17', + ), +); +print "Test sqlite access first 4 rows: " . htmlspecialchars($sql); +testAssert($rs, $actual, $expected); + +//========================== +// This code tests SQLDate +//========================== +$tests = [ + '', + '1974-02-25', + '1474-02-25 05:04:16', + '2474-02-25 05:04:16', +]; +foreach ($tests as $dt) { + testDateFormatting($dt, false); +} +function testDateFormatting($dt, $debug = false) { + global $db; + if ($dt=='') { + $date = $db->SQLDate('%d-%m-%Y Q %H:%M:%S'); + } else { + $date = $db->SQLDate('%d-%m-%Y Q %H:%M:%S', $db->qStr($dt)); + } + $sql = "SELECT $date"; + print "Test SQLDate: " . htmlspecialchars($sql); + $db->debug = $debug; + $rs = $db->SelectLimit($sql, 1); + if ($dt=='') { + $d = date('d-m-Y') . ' Q ' . date('H:i:s'); + } else { + $ts = ADOConnection::UnixTimeStamp($dt); + $d = date('d-m-Y', $ts) . ' Q ' . date('H:i:s', $ts); + } + testAssert($rs, reset($rs->fields), $d); +} + +//========================== +// These are helper functions +//========================== + +function Err($msg) { + print "$msg\n"; + flush(); +} + +function Trace($Msg) { + echo "\n".$Msg; +} + +function DieTrace($Msg) { + die("\n".$Msg); +} + +function testAssert($rs, $actual, $expected) { + global $db; + if (!$rs) { + echo "\n"; + Err("SQLDate query returned no recordset"); + echo $db->ErrorMsg(), "\n"; + } elseif ($expected != $actual) { + echo "\n"; + Err("SQLDate failed\nexpected: $expected\nact:$actual\nerror: " . $db->ErrorMsg()); + } else { + echo " \u{2713}\n"; + } +}
\ No newline at end of file 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?? diff --git a/xsl/convert-0.1-0.2.xsl b/xsl/convert-0.1-0.2.xsl index 5b2e3cec..bf4b5ed0 100644 --- a/xsl/convert-0.1-0.2.xsl +++ b/xsl/convert-0.1-0.2.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:element name="schema"> @@ -202,4 +202,4 @@ http://adodb-xmlschema.sourceforge.net <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> diff --git a/xsl/convert-0.1-0.3.xsl b/xsl/convert-0.1-0.3.xsl index 3202dce4..581e659c 100644 --- a/xsl/convert-0.1-0.3.xsl +++ b/xsl/convert-0.1-0.3.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:element name="schema"> @@ -218,4 +218,4 @@ http://adodb-xmlschema.sourceforge.net <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> diff --git a/xsl/convert-0.2-0.1.xsl b/xsl/convert-0.2-0.1.xsl index 6398e3e5..334849b2 100644 --- a/xsl/convert-0.2-0.1.xsl +++ b/xsl/convert-0.2-0.1.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:element name="schema"> @@ -204,4 +204,4 @@ http://adodb-xmlschema.sourceforge.net <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> diff --git a/xsl/convert-0.2-0.3.xsl b/xsl/convert-0.2-0.3.xsl index 9e1f2ae3..259401bb 100644 --- a/xsl/convert-0.2-0.3.xsl +++ b/xsl/convert-0.2-0.3.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:element name="schema"> @@ -278,4 +278,4 @@ http://adodb-xmlschema.sourceforge.net <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> diff --git a/xsl/remove-0.2.xsl b/xsl/remove-0.2.xsl index c82c3ad8..46d57b8e 100644 --- a/xsl/remove-0.2.xsl +++ b/xsl/remove-0.2.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:comment> @@ -51,4 +51,4 @@ Uninstallation Schema <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> diff --git a/xsl/remove-0.3.xsl b/xsl/remove-0.3.xsl index 4b1cd02e..96c0f76c 100644 --- a/xsl/remove-0.3.xsl +++ b/xsl/remove-0.3.xsl @@ -8,7 +8,7 @@ <xsl:template match="/"> <xsl:comment> ADODB XMLSchema -http://adodb-xmlschema.sourceforge.net +https://adodb-xmlschema.sourceforge.net </xsl:comment> <xsl:comment> @@ -51,4 +51,4 @@ Uninstallation Schema <xsl:value-of select="normalize-space(text())"/> </xsl:element> </xsl:template> -</xsl:stylesheet>
\ No newline at end of file +</xsl:stylesheet> |
