Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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