[TASK] Update adodb to v5.20.3
[Packages/TYPO3.CMS.git] / typo3 / sysext / adodb / adodb / adodb-active-record.inc.php
1 <?php
2 /*
3
4 @version v5.20.3 01-Jan-2016
5 @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
6 @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
7 Latest version is available at http://adodb.sourceforge.net
8
9 Released under both BSD license and Lesser GPL library license.
10 Whenever there is any discrepancy between the two licenses,
11 the BSD license will take precedence.
12
13 Active Record implementation. Superset of Zend Framework's.
14
15 Version 0.92
16
17 See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord
18 for info on Ruby on Rails Active Record implementation
19 */
20
21
22 global $_ADODB_ACTIVE_DBS;
23 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
24 global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
25 global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record.
26
27 // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
28 $_ADODB_ACTIVE_DBS = array();
29 $ACTIVE_RECORD_SAFETY = true;
30 $ADODB_ACTIVE_DEFVALS = false;
31 $ADODB_ACTIVE_CACHESECS = 0;
32
33 class ADODB_Active_DB {
34 var $db; // ADOConnection
35 var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
36 }
37
38 class ADODB_Active_Table {
39 var $name; // table name
40 var $flds; // assoc array of adofieldobjs, indexed by fieldname
41 var $keys; // assoc array of primary keys, indexed by fieldname
42 var $_created; // only used when stored as a cached file
43 var $_belongsTo = array();
44 var $_hasMany = array();
45 }
46
47 // $db = database connection
48 // $index = name of index - can be associative, for an example see
49 // http://phplens.com/lens/lensforum/msgs.php?id=17790
50 // returns index into $_ADODB_ACTIVE_DBS
51 function ADODB_SetDatabaseAdapter(&$db, $index=false)
52 {
53 global $_ADODB_ACTIVE_DBS;
54
55 foreach($_ADODB_ACTIVE_DBS as $k => $d) {
56 if (PHP_VERSION >= 5) {
57 if ($d->db === $db) {
58 return $k;
59 }
60 } else {
61 if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) {
62 return $k;
63 }
64 }
65 }
66
67 $obj = new ADODB_Active_DB();
68 $obj->db = $db;
69 $obj->tables = array();
70
71 if ($index == false) {
72 $index = sizeof($_ADODB_ACTIVE_DBS);
73 }
74
75 $_ADODB_ACTIVE_DBS[$index] = $obj;
76
77 return sizeof($_ADODB_ACTIVE_DBS)-1;
78 }
79
80
81 class ADODB_Active_Record {
82 static $_changeNames = true; // dynamically pluralize table names
83 static $_quoteNames = false;
84
85 static $_foreignSuffix = '_id'; //
86 var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
87 var $_table; // tablename, if set in class definition then use it as table name
88 var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
89 var $_where; // where clause set in Load()
90 var $_saved = false; // indicates whether data is already inserted.
91 var $_lasterr = false; // last error message
92 var $_original = false; // the original values loaded or inserted, refreshed on update
93
94 var $foreignName; // CFR: class name when in a relationship
95
96 var $lockMode = ' for update '; // you might want to change to
97
98 static function UseDefaultValues($bool=null)
99 {
100 global $ADODB_ACTIVE_DEFVALS;
101 if (isset($bool)) {
102 $ADODB_ACTIVE_DEFVALS = $bool;
103 }
104 return $ADODB_ACTIVE_DEFVALS;
105 }
106
107 // should be static
108 static function SetDatabaseAdapter(&$db, $index=false)
109 {
110 return ADODB_SetDatabaseAdapter($db, $index);
111 }
112
113
114 public function __set($name, $value)
115 {
116 $name = str_replace(' ', '_', $name);
117 $this->$name = $value;
118 }
119
120 // php5 constructor
121 function __construct($table = false, $pkeyarr=false, $db=false)
122 {
123 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS;
124
125 if ($db == false && is_object($pkeyarr)) {
126 $db = $pkeyarr;
127 $pkeyarr = false;
128 }
129
130 if (!$table) {
131 if (!empty($this->_table)) {
132 $table = $this->_table;
133 }
134 else $table = $this->_pluralize(get_class($this));
135 }
136 $this->foreignName = strtolower(get_class($this)); // CFR: default foreign name
137 if ($db) {
138 $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
139 } else if (!isset($this->_dbat)) {
140 if (sizeof($_ADODB_ACTIVE_DBS) == 0) {
141 $this->Error(
142 "No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",
143 'ADODB_Active_Record::__constructor'
144 );
145 }
146 end($_ADODB_ACTIVE_DBS);
147 $this->_dbat = key($_ADODB_ACTIVE_DBS);
148 }
149
150 $this->_table = $table;
151 $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
152
153 $this->UpdateActiveTable($pkeyarr);
154 }
155
156 function __wakeup()
157 {
158 $class = get_class($this);
159 new $class;
160 }
161
162 function _pluralize($table)
163 {
164 if (!ADODB_Active_Record::$_changeNames) {
165 return $table;
166 }
167
168 $ut = strtoupper($table);
169 $len = strlen($table);
170 $lastc = $ut[$len-1];
171 $lastc2 = substr($ut,$len-2);
172 switch ($lastc) {
173 case 'S':
174 return $table.'es';
175 case 'Y':
176 return substr($table,0,$len-1).'ies';
177 case 'X':
178 return $table.'es';
179 case 'H':
180 if ($lastc2 == 'CH' || $lastc2 == 'SH') {
181 return $table.'es';
182 }
183 default:
184 return $table.'s';
185 }
186 }
187
188 // CFR Lamest singular inflector ever - @todo Make it real!
189 // Note: There is an assumption here...and it is that the argument's length >= 4
190 function _singularize($tables)
191 {
192
193 if (!ADODB_Active_Record::$_changeNames) {
194 return $table;
195 }
196
197 $ut = strtoupper($tables);
198 $len = strlen($tables);
199 if($ut[$len-1] != 'S') {
200 return $tables; // I know...forget oxen
201 }
202 if($ut[$len-2] != 'E') {
203 return substr($tables, 0, $len-1);
204 }
205 switch($ut[$len-3]) {
206 case 'S':
207 case 'X':
208 return substr($tables, 0, $len-2);
209 case 'I':
210 return substr($tables, 0, $len-3) . 'y';
211 case 'H';
212 if($ut[$len-4] == 'C' || $ut[$len-4] == 'S') {
213 return substr($tables, 0, $len-2);
214 }
215 default:
216 return substr($tables, 0, $len-1); // ?
217 }
218 }
219
220 function hasMany($foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
221 {
222 $ar = new $foreignClass($foreignRef);
223 $ar->foreignName = $foreignRef;
224 $ar->UpdateActiveTable();
225 $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
226 $table =& $this->TableInfo();
227 $table->_hasMany[$foreignRef] = $ar;
228 # $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
229 }
230
231 // use when you don't want ADOdb to auto-pluralize tablename
232 static function TableHasMany($table, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
233 {
234 $ar = new ADODB_Active_Record($table);
235 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
236 }
237
238 // use when you don't want ADOdb to auto-pluralize tablename
239 static function TableKeyHasMany($table, $tablePKey, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
240 {
241 if (!is_array($tablePKey)) {
242 $tablePKey = array($tablePKey);
243 }
244 $ar = new ADODB_Active_Record($table,$tablePKey);
245 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
246 }
247
248
249 // use when you want ADOdb to auto-pluralize tablename for you. Note that the class must already be defined.
250 // e.g. class Person will generate relationship for table Persons
251 static function ClassHasMany($parentclass, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
252 {
253 $ar = new $parentclass();
254 $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
255 }
256
257
258 function belongsTo($foreignRef,$foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
259 {
260 global $inflector;
261
262 $ar = new $parentClass($this->_pluralize($foreignRef));
263 $ar->foreignName = $foreignRef;
264 $ar->parentKey = $parentKey;
265 $ar->UpdateActiveTable();
266 $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
267
268 $table =& $this->TableInfo();
269 $table->_belongsTo[$foreignRef] = $ar;
270 # $this->$foreignRef = $this->_belongsTo[$foreignRef];
271 }
272
273 static function ClassBelongsTo($class, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
274 {
275 $ar = new $class();
276 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
277 }
278
279 static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
280 {
281 $ar = new ADOdb_Active_Record($table);
282 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
283 }
284
285 static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
286 {
287 if (!is_array($tablePKey)) {
288 $tablePKey = array($tablePKey);
289 }
290 $ar = new ADOdb_Active_Record($table, $tablePKey);
291 $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
292 }
293
294
295 /**
296 * __get Access properties - used for lazy loading
297 *
298 * @param mixed $name
299 * @access protected
300 * @return mixed
301 */
302 function __get($name)
303 {
304 return $this->LoadRelations($name, '', -1, -1);
305 }
306
307 /**
308 * @param string $name
309 * @param string $whereOrderBy : eg. ' AND field1 = value ORDER BY field2'
310 * @param offset
311 * @param limit
312 * @return mixed
313 */
314 function LoadRelations($name, $whereOrderBy='', $offset=-1,$limit=-1)
315 {
316 $extras = array();
317 $table = $this->TableInfo();
318 if ($limit >= 0) {
319 $extras['limit'] = $limit;
320 }
321 if ($offset >= 0) {
322 $extras['offset'] = $offset;
323 }
324
325 if (strlen($whereOrderBy)) {
326 if (!preg_match('/^[ \n\r]*AND/i', $whereOrderBy)) {
327 if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i', $whereOrderBy)) {
328 $whereOrderBy = 'AND ' . $whereOrderBy;
329 }
330 }
331 }
332
333 if(!empty($table->_belongsTo[$name])) {
334 $obj = $table->_belongsTo[$name];
335 $columnName = $obj->foreignKey;
336 if(empty($this->$columnName)) {
337 $this->$name = null;
338 }
339 else {
340 if ($obj->parentKey) {
341 $key = $obj->parentKey;
342 }
343 else {
344 $key = reset($table->keys);
345 }
346
347 $arrayOfOne = $obj->Find($key.'='.$this->$columnName.' '.$whereOrderBy,false,false,$extras);
348 if ($arrayOfOne) {
349 $this->$name = $arrayOfOne[0];
350 return $arrayOfOne[0];
351 }
352 }
353 }
354 if(!empty($table->_hasMany[$name])) {
355 $obj = $table->_hasMany[$name];
356 $key = reset($table->keys);
357 $id = @$this->$key;
358 if (!is_numeric($id)) {
359 $db = $this->DB();
360 $id = $db->qstr($id);
361 }
362 $objs = $obj->Find($obj->foreignKey.'='.$id. ' '.$whereOrderBy,false,false,$extras);
363 if (!$objs) {
364 $objs = array();
365 }
366 $this->$name = $objs;
367 return $objs;
368 }
369
370 return array();
371 }
372 //////////////////////////////////
373
374 // update metadata
375 function UpdateActiveTable($pkeys=false,$forceUpdate=false)
376 {
377 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
378 global $ADODB_ACTIVE_DEFVALS,$ADODB_FETCH_MODE;
379
380 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
381
382 $table = $this->_table;
383 $tables = $activedb->tables;
384 $tableat = $this->_tableat;
385 if (!$forceUpdate && !empty($tables[$tableat])) {
386
387 $acttab = $tables[$tableat];
388 foreach($acttab->flds as $name => $fld) {
389 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) {
390 $this->$name = $fld->default_value;
391 }
392 else {
393 $this->$name = null;
394 }
395 }
396 return;
397 }
398 $db = $activedb->db;
399 $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
400 if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
401 $fp = fopen($fname,'r');
402 @flock($fp, LOCK_SH);
403 $acttab = unserialize(fread($fp,100000));
404 fclose($fp);
405 if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) {
406 // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
407 // ideally, you should cache at least 32 secs
408
409 foreach($acttab->flds as $name => $fld) {
410 if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) {
411 $this->$name = $fld->default_value;
412 }
413 else {
414 $this->$name = null;
415 }
416 }
417
418 $activedb->tables[$table] = $acttab;
419
420 //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
421 return;
422 } else if ($db->debug) {
423 ADOConnection::outp("Refreshing cached active record file: $fname");
424 }
425 }
426 $activetab = new ADODB_Active_Table();
427 $activetab->name = $table;
428
429 $save = $ADODB_FETCH_MODE;
430 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
431 if ($db->fetchMode !== false) {
432 $savem = $db->SetFetchMode(false);
433 }
434
435 $cols = $db->MetaColumns($table);
436
437 if (isset($savem)) {
438 $db->SetFetchMode($savem);
439 }
440 $ADODB_FETCH_MODE = $save;
441
442 if (!$cols) {
443 $this->Error("Invalid table name: $table",'UpdateActiveTable');
444 return false;
445 }
446 $fld = reset($cols);
447 if (!$pkeys) {
448 if (isset($fld->primary_key)) {
449 $pkeys = array();
450 foreach($cols as $name => $fld) {
451 if (!empty($fld->primary_key)) {
452 $pkeys[] = $name;
453 }
454 }
455 } else
456 $pkeys = $this->GetPrimaryKeys($db, $table);
457 }
458 if (empty($pkeys)) {
459 $this->Error("No primary key found for table $table",'UpdateActiveTable');
460 return false;
461 }
462
463 $attr = array();
464 $keys = array();
465
466 switch($ADODB_ASSOC_CASE) {
467 case 0:
468 foreach($cols as $name => $fldobj) {
469 $name = strtolower($name);
470 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
471 $this->$name = $fldobj->default_value;
472 }
473 else {
474 $this->$name = null;
475 }
476 $attr[$name] = $fldobj;
477 }
478 foreach($pkeys as $k => $name) {
479 $keys[strtolower($name)] = strtolower($name);
480 }
481 break;
482
483 case 1:
484 foreach($cols as $name => $fldobj) {
485 $name = strtoupper($name);
486
487 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
488 $this->$name = $fldobj->default_value;
489 }
490 else {
491 $this->$name = null;
492 }
493 $attr[$name] = $fldobj;
494 }
495
496 foreach($pkeys as $k => $name) {
497 $keys[strtoupper($name)] = strtoupper($name);
498 }
499 break;
500 default:
501 foreach($cols as $name => $fldobj) {
502 $name = ($fldobj->name);
503
504 if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value)) {
505 $this->$name = $fldobj->default_value;
506 }
507 else {
508 $this->$name = null;
509 }
510 $attr[$name] = $fldobj;
511 }
512 foreach($pkeys as $k => $name) {
513 $keys[$name] = $cols[$name]->name;
514 }
515 break;
516 }
517
518 $activetab->keys = $keys;
519 $activetab->flds = $attr;
520
521 if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
522 $activetab->_created = time();
523 $s = serialize($activetab);
524 if (!function_exists('adodb_write_file')) {
525 include(ADODB_DIR.'/adodb-csvlib.inc.php');
526 }
527 adodb_write_file($fname,$s);
528 }
529 if (isset($activedb->tables[$table])) {
530 $oldtab = $activedb->tables[$table];
531
532 if ($oldtab) {
533 $activetab->_belongsTo = $oldtab->_belongsTo;
534 $activetab->_hasMany = $oldtab->_hasMany;
535 }
536 }
537 $activedb->tables[$table] = $activetab;
538 }
539
540 function GetPrimaryKeys(&$db, $table)
541 {
542 return $db->MetaPrimaryKeys($table);
543 }
544
545 // error handler for both PHP4+5.
546 function Error($err,$fn)
547 {
548 global $_ADODB_ACTIVE_DBS;
549
550 $fn = get_class($this).'::'.$fn;
551 $this->_lasterr = $fn.': '.$err;
552
553 if ($this->_dbat < 0) {
554 $db = false;
555 }
556 else {
557 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
558 $db = $activedb->db;
559 }
560
561 if (function_exists('adodb_throw')) {
562 if (!$db) {
563 adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
564 }
565 else {
566 adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
567 }
568 } else {
569 if (!$db || $db->debug) {
570 ADOConnection::outp($this->_lasterr);
571 }
572 }
573
574 }
575
576 // return last error message
577 function ErrorMsg()
578 {
579 if (!function_exists('adodb_throw')) {
580 if ($this->_dbat < 0) {
581 $db = false;
582 }
583 else {
584 $db = $this->DB();
585 }
586
587 // last error could be database error too
588 if ($db && $db->ErrorMsg()) {
589 return $db->ErrorMsg();
590 }
591 }
592 return $this->_lasterr;
593 }
594
595 function ErrorNo()
596 {
597 if ($this->_dbat < 0) {
598 return -9999; // no database connection...
599 }
600 $db = $this->DB();
601
602 return (int) $db->ErrorNo();
603 }
604
605
606 // retrieve ADOConnection from _ADODB_Active_DBs
607 function DB()
608 {
609 global $_ADODB_ACTIVE_DBS;
610
611 if ($this->_dbat < 0) {
612 $false = false;
613 $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
614 return $false;
615 }
616 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
617 $db = $activedb->db;
618 return $db;
619 }
620
621 // retrieve ADODB_Active_Table
622 function &TableInfo()
623 {
624 global $_ADODB_ACTIVE_DBS;
625 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
626 $table = $activedb->tables[$this->_tableat];
627 return $table;
628 }
629
630
631 // I have an ON INSERT trigger on a table that sets other columns in the table.
632 // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
633 function Reload()
634 {
635 $db = $this->DB();
636 if (!$db) {
637 return false;
638 }
639 $table = $this->TableInfo();
640 $where = $this->GenWhere($db, $table);
641 return($this->Load($where));
642 }
643
644
645 // set a numeric array (using natural table field ordering) as object properties
646 function Set(&$row)
647 {
648 global $ACTIVE_RECORD_SAFETY;
649
650 $db = $this->DB();
651
652 if (!$row) {
653 $this->_saved = false;
654 return false;
655 }
656
657 $this->_saved = true;
658
659 $table = $this->TableInfo();
660 if ($ACTIVE_RECORD_SAFETY && sizeof($table->flds) != sizeof($row)) {
661 # <AP>
662 $bad_size = TRUE;
663 if (sizeof($row) == 2 * sizeof($table->flds)) {
664 // Only keep string keys
665 $keys = array_filter(array_keys($row), 'is_string');
666 if (sizeof($keys) == sizeof($table->flds)) {
667 $bad_size = FALSE;
668 }
669 }
670 if ($bad_size) {
671 $this->Error("Table structure of $this->_table has changed","Load");
672 return false;
673 }
674 # </AP>
675 }
676 else
677 $keys = array_keys($row);
678
679 # <AP>
680 reset($keys);
681 $this->_original = array();
682 foreach($table->flds as $name=>$fld) {
683 $value = $row[current($keys)];
684 $this->$name = $value;
685 $this->_original[] = $value;
686 next($keys);
687 }
688
689 # </AP>
690 return true;
691 }
692
693 // get last inserted id for INSERT
694 function LastInsertID(&$db,$fieldname)
695 {
696 if ($db->hasInsertID) {
697 $val = $db->Insert_ID($this->_table,$fieldname);
698 }
699 else {
700 $val = false;
701 }
702
703 if (is_null($val) || $val === false) {
704 // this might not work reliably in multi-user environment
705 return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
706 }
707 return $val;
708 }
709
710 // quote data in where clause
711 function doquote(&$db, $val,$t)
712 {
713 switch($t) {
714 case 'L':
715 if (strpos($db->databaseType,'postgres') !== false) {
716 return $db->qstr($val);
717 }
718 case 'D':
719 case 'T':
720 if (empty($val)) {
721 return 'null';
722 }
723 case 'B':
724 case 'N':
725 case 'C':
726 case 'X':
727 if (is_null($val)) {
728 return 'null';
729 }
730
731 if (strlen($val)>0 &&
732 (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")
733 ) {
734 return $db->qstr($val);
735 break;
736 }
737 default:
738 return $val;
739 break;
740 }
741 }
742
743 // generate where clause for an UPDATE/SELECT
744 function GenWhere(&$db, &$table)
745 {
746 $keys = $table->keys;
747 $parr = array();
748
749 foreach($keys as $k) {
750 $f = $table->flds[$k];
751 if ($f) {
752 $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
753 }
754 }
755 return implode(' and ', $parr);
756 }
757
758
759 function _QName($n,$db=false)
760 {
761 if (!ADODB_Active_Record::$_quoteNames) {
762 return $n;
763 }
764 if (!$db) {
765 $db = $this->DB();
766 if (!$db) {
767 return false;
768 }
769 }
770 return $db->nameQuote.$n.$db->nameQuote;
771 }
772
773 //------------------------------------------------------------ Public functions below
774
775 function Load($where=null,$bindarr=false, $lock = false)
776 {
777 global $ADODB_FETCH_MODE;
778
779 $db = $this->DB();
780 if (!$db) {
781 return false;
782 }
783 $this->_where = $where;
784
785 $save = $ADODB_FETCH_MODE;
786 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
787 if ($db->fetchMode !== false) {
788 $savem = $db->SetFetchMode(false);
789 }
790
791 $qry = "select * from ".$this->_table;
792
793 if($where) {
794 $qry .= ' WHERE '.$where;
795 }
796 if ($lock) {
797 $qry .= $this->lockMode;
798 }
799
800 $row = $db->GetRow($qry,$bindarr);
801
802 if (isset($savem)) {
803 $db->SetFetchMode($savem);
804 }
805 $ADODB_FETCH_MODE = $save;
806
807 return $this->Set($row);
808 }
809
810 function LoadLocked($where=null, $bindarr=false)
811 {
812 $this->Load($where,$bindarr,true);
813 }
814
815 # useful for multiple record inserts
816 # see http://phplens.com/lens/lensforum/msgs.php?id=17795
817 function Reset()
818 {
819 $this->_where=null;
820 $this->_saved = false;
821 $this->_lasterr = false;
822 $this->_original = false;
823 $vars=get_object_vars($this);
824 foreach($vars as $k=>$v){
825 if(substr($k,0,1)!=='_'){
826 $this->{$k}=null;
827 }
828 }
829 $this->foreignName=strtolower(get_class($this));
830 return true;
831 }
832
833 // false on error
834 function Save()
835 {
836 if ($this->_saved) {
837 $ok = $this->Update();
838 }
839 else {
840 $ok = $this->Insert();
841 }
842
843 return $ok;
844 }
845
846
847 // false on error
848 function Insert()
849 {
850 $db = $this->DB();
851 if (!$db) {
852 return false;
853 }
854 $cnt = 0;
855 $table = $this->TableInfo();
856
857 $valarr = array();
858 $names = array();
859 $valstr = array();
860
861 foreach($table->flds as $name=>$fld) {
862 $val = $this->$name;
863 if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) {
864 $valarr[] = $val;
865 $names[] = $this->_QName($name,$db);
866 $valstr[] = $db->Param($cnt);
867 $cnt += 1;
868 }
869 }
870
871 if (empty($names)){
872 foreach($table->flds as $name=>$fld) {
873 $valarr[] = null;
874 $names[] = $name;
875 $valstr[] = $db->Param($cnt);
876 $cnt += 1;
877 }
878 }
879 $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
880 $ok = $db->Execute($sql,$valarr);
881
882 if ($ok) {
883 $this->_saved = true;
884 $autoinc = false;
885 foreach($table->keys as $k) {
886 if (is_null($this->$k)) {
887 $autoinc = true;
888 break;
889 }
890 }
891 if ($autoinc && sizeof($table->keys) == 1) {
892 $k = reset($table->keys);
893 $this->$k = $this->LastInsertID($db,$k);
894 }
895 }
896
897 $this->_original = $valarr;
898 return !empty($ok);
899 }
900
901 function Delete()
902 {
903 $db = $this->DB();
904 if (!$db) {
905 return false;
906 }
907 $table = $this->TableInfo();
908
909 $where = $this->GenWhere($db,$table);
910 $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
911 $ok = $db->Execute($sql);
912
913 return $ok ? true : false;
914 }
915
916 // returns an array of active record objects
917 function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
918 {
919 $db = $this->DB();
920 if (!$db || empty($this->_table)) {
921 return false;
922 }
923 $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra);
924 return $arr;
925 }
926
927 // returns 0 on error, 1 on update, 2 on insert
928 function Replace()
929 {
930 global $ADODB_ASSOC_CASE;
931
932 $db = $this->DB();
933 if (!$db) {
934 return false;
935 }
936 $table = $this->TableInfo();
937
938 $pkey = $table->keys;
939
940 foreach($table->flds as $name=>$fld) {
941 $val = $this->$name;
942 /*
943 if (is_null($val)) {
944 if (isset($fld->not_null) && $fld->not_null) {
945 if (isset($fld->default_value) && strlen($fld->default_value)) {
946 continue;
947 }
948 else {
949 $this->Error("Cannot update null into $name","Replace");
950 return false;
951 }
952 }
953 }*/
954 if (is_null($val) && !empty($fld->auto_increment)) {
955 continue;
956 }
957
958 if (is_array($val)) {
959 continue;
960 }
961
962 $t = $db->MetaType($fld->type);
963 $arr[$name] = $this->doquote($db,$val,$t);
964 $valarr[] = $val;
965 }
966
967 if (!is_array($pkey)) {
968 $pkey = array($pkey);
969 }
970
971 if ($ADODB_ASSOC_CASE == 0) {
972 foreach($pkey as $k => $v)
973 $pkey[$k] = strtolower($v);
974 }
975 elseif ($ADODB_ASSOC_CASE == 1) {
976 foreach($pkey as $k => $v) {
977 $pkey[$k] = strtoupper($v);
978 }
979 }
980
981 $ok = $db->Replace($this->_table,$arr,$pkey);
982 if ($ok) {
983 $this->_saved = true; // 1= update 2=insert
984 if ($ok == 2) {
985 $autoinc = false;
986 foreach($table->keys as $k) {
987 if (is_null($this->$k)) {
988 $autoinc = true;
989 break;
990 }
991 }
992 if ($autoinc && sizeof($table->keys) == 1) {
993 $k = reset($table->keys);
994 $this->$k = $this->LastInsertID($db,$k);
995 }
996 }
997
998 $this->_original = $valarr;
999 }
1000 return $ok;
1001 }
1002
1003 // returns 0 on error, 1 on update, -1 if no change in data (no update)
1004 function Update()
1005 {
1006 $db = $this->DB();
1007 if (!$db) {
1008 return false;
1009 }
1010 $table = $this->TableInfo();
1011
1012 $where = $this->GenWhere($db, $table);
1013
1014 if (!$where) {
1015 $this->error("Where missing for table $table", "Update");
1016 return false;
1017 }
1018 $valarr = array();
1019 $neworig = array();
1020 $pairs = array();
1021 $i = -1;
1022 $cnt = 0;
1023 foreach($table->flds as $name=>$fld) {
1024 $i += 1;
1025 $val = $this->$name;
1026 $neworig[] = $val;
1027
1028 if (isset($table->keys[$name]) || is_array($val)) {
1029 continue;
1030 }
1031
1032 if (is_null($val)) {
1033 if (isset($fld->not_null) && $fld->not_null) {
1034 if (isset($fld->default_value) && strlen($fld->default_value)) {
1035 continue;
1036 }
1037 else {
1038 $this->Error("Cannot set field $name to NULL","Update");
1039 return false;
1040 }
1041 }
1042 }
1043
1044 if (isset($this->_original[$i]) && strcmp($val,$this->_original[$i]) == 0) {
1045 continue;
1046 }
1047
1048 if (is_null($this->_original[$i]) && is_null($val)) {
1049 continue;
1050 }
1051
1052 $valarr[] = $val;
1053 $pairs[] = $this->_QName($name,$db).'='.$db->Param($cnt);
1054 $cnt += 1;
1055 }
1056
1057
1058 if (!$cnt) {
1059 return -1;
1060 }
1061
1062 $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
1063 $ok = $db->Execute($sql,$valarr);
1064 if ($ok) {
1065 $this->_original = $neworig;
1066 return 1;
1067 }
1068 return 0;
1069 }
1070
1071 function GetAttributeNames()
1072 {
1073 $table = $this->TableInfo();
1074 if (!$table) {
1075 return false;
1076 }
1077 return array_keys($table->flds);
1078 }
1079
1080 };
1081
1082 function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr,
1083 $extra)
1084 {
1085 global $_ADODB_ACTIVE_DBS;
1086
1087
1088 $save = $db->SetFetchMode(ADODB_FETCH_NUM);
1089 $qry = "select * from ".$table;
1090
1091 if (!empty($whereOrderBy)) {
1092 $qry .= ' WHERE '.$whereOrderBy;
1093 }
1094 if(isset($extra['limit'])) {
1095 $rows = false;
1096 if(isset($extra['offset'])) {
1097 $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset'],$bindarr);
1098 } else {
1099 $rs = $db->SelectLimit($qry, $extra['limit'],-1,$bindarr);
1100 }
1101 if ($rs) {
1102 while (!$rs->EOF) {
1103 $rows[] = $rs->fields;
1104 $rs->MoveNext();
1105 }
1106 }
1107 } else
1108 $rows = $db->GetAll($qry,$bindarr);
1109
1110 $db->SetFetchMode($save);
1111
1112 $false = false;
1113
1114 if ($rows === false) {
1115 return $false;
1116 }
1117
1118
1119 if (!class_exists($class)) {
1120 $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
1121 return $false;
1122 }
1123 $arr = array();
1124 // arrRef will be the structure that knows about our objects.
1125 // It is an associative array.
1126 // We will, however, return arr, preserving regular 0.. order so that
1127 // obj[0] can be used by app developpers.
1128 $arrRef = array();
1129 $bTos = array(); // Will store belongTo's indices if any
1130 foreach($rows as $row) {
1131
1132 $obj = new $class($table,$primkeyArr,$db);
1133 if ($obj->ErrorNo()){
1134 $db->_errorMsg = $obj->ErrorMsg();
1135 return $false;
1136 }
1137 $obj->Set($row);
1138 $arr[] = $obj;
1139 } // foreach($rows as $row)
1140
1141 return $arr;
1142 }