Documentation TYPO3 par Ameos |
00001 <?php 00002 /* 00003 00004 @version V4.93 10 Oct 2006 (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. 00005 Latest version is available at http://adodb.sourceforge.net 00006 00007 Released under both BSD license and Lesser GPL library license. 00008 Whenever there is any discrepancy between the two licenses, 00009 the BSD license will take precedence. 00010 00011 Active Record implementation. Superset of Zend Framework's. 00012 00013 Version 0.04 00014 00015 See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 00016 for info on Ruby on Rails Active Record implementation 00017 */ 00018 00019 global $_ADODB_ACTIVE_DBS; 00020 global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info 00021 00022 // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat 00023 $_ADODB_ACTIVE_DBS = array(); 00024 00025 00026 class ADODB_Active_DB { 00027 var $db; // ADOConnection 00028 var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename 00029 } 00030 00031 class ADODB_Active_Table { 00032 var $name; // table name 00033 var $flds; // assoc array of adofieldobjs, indexed by fieldname 00034 var $keys; // assoc array of primary keys, indexed by fieldname 00035 var $_created; // only used when stored as a cached file 00036 } 00037 00038 // returns index into $_ADODB_ACTIVE_DBS 00039 function ADODB_SetDatabaseAdapter(&$db) 00040 { 00041 global $_ADODB_ACTIVE_DBS; 00042 00043 foreach($_ADODB_ACTIVE_DBS as $k => $d) { 00044 if (PHP_VERSION >= 5) { 00045 if ($d->db == $db) return $k; 00046 } else { 00047 if ($d->db->_connectionID == $db->_connectionID && $db->database == $d->db->database) 00048 return $k; 00049 } 00050 } 00051 00052 $obj = new ADODB_Active_DB(); 00053 $obj->db =& $db; 00054 $obj->tables = array(); 00055 00056 $_ADODB_ACTIVE_DBS[] = $obj; 00057 00058 return sizeof($_ADODB_ACTIVE_DBS)-1; 00059 } 00060 00061 00062 class ADODB_Active_Record { 00063 var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat] 00064 var $_table; // tablename, if set in class definition then use it as table name 00065 var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat] 00066 var $_where; // where clause set in Load() 00067 var $_saved = false; // indicates whether data is already inserted. 00068 var $_lasterr = false; // last error message 00069 var $_original = false; // the original values loaded or inserted, refreshed on update 00070 00071 // should be static 00072 function SetDatabaseAdapter(&$db) 00073 { 00074 return ADODB_SetDatabaseAdapter($db); 00075 } 00076 00077 // php4 constructor 00078 function ADODB_Active_Record($table = false, $pkeyarr=false, $db=false) 00079 { 00080 ADODB_Active_Record::__construct($table,$pkeyarr,$db); 00081 } 00082 00083 // php5 constructor 00084 function __construct($table = false, $pkeyarr=false, $db=false) 00085 { 00086 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS; 00087 00088 if ($db == false && is_object($pkeyarr)) { 00089 $db = $pkeyarr; 00090 $pkeyarr = false; 00091 } 00092 00093 if (!$table) { 00094 if (!empty($this->_table)) $table = $this->_table; 00095 else $table = $this->_pluralize(get_class($this)); 00096 } 00097 if ($db) { 00098 $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db); 00099 } else 00100 $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1; 00101 00102 00103 if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor'); 00104 00105 $this->_table = $table; 00106 $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future 00107 $this->UpdateActiveTable($pkeyarr); 00108 } 00109 00110 function __wakeup() 00111 { 00112 $class = get_class($this); 00113 new $class; 00114 } 00115 00116 function _pluralize($table) 00117 { 00118 $ut = strtoupper($table); 00119 $len = strlen($table); 00120 $lastc = $ut[$len-1]; 00121 $lastc2 = substr($ut,$len-2); 00122 switch ($lastc) { 00123 case 'S': 00124 return $table.'es'; 00125 case 'Y': 00126 return substr($table,0,$len-1).'ies'; 00127 case 'X': 00128 return $table.'es'; 00129 case 'H': 00130 if ($lastc2 == 'CH' || $lastc2 == 'SH') 00131 return $table.'es'; 00132 default: 00133 return $table.'s'; 00134 } 00135 } 00136 00138 00139 // update metadata 00140 function UpdateActiveTable($pkeys=false,$forceUpdate=false) 00141 { 00142 global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS; 00143 00144 $activedb =& $_ADODB_ACTIVE_DBS[$this->_dbat]; 00145 00146 $table = $this->_table; 00147 $tables = $activedb->tables; 00148 $tableat = $this->_tableat; 00149 if (!$forceUpdate && !empty($tables[$tableat])) { 00150 $tobj =& $tables[$tableat]; 00151 foreach($tobj->flds as $name => $fld) 00152 $this->$name = null; 00153 return; 00154 } 00155 00156 $db =& $activedb->db; 00157 $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache'; 00158 if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) { 00159 $fp = fopen($fname,'r'); 00160 @flock($fp, LOCK_SH); 00161 $acttab = unserialize(fread($fp,100000)); 00162 fclose($fp); 00163 if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { 00164 // abs(rand()) randomizes deletion, reducing contention to delete/refresh file 00165 // ideally, you should cache at least 32 secs 00166 $activedb->tables[$table] = $acttab; 00167 00168 //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname"); 00169 return; 00170 } else if ($db->debug) { 00171 ADOConnection::outp("Refreshing cached active record file: $fname"); 00172 } 00173 } 00174 $activetab = new ADODB_Active_Table(); 00175 $activetab->name = $table; 00176 00177 00178 $cols = $db->MetaColumns($table); 00179 if (!$cols) { 00180 $this->Error("Invalid table name: $table",'UpdateActiveTable'); 00181 return false; 00182 } 00183 $fld = reset($cols); 00184 if (!$pkeys) { 00185 if (isset($fld->primary_key)) { 00186 $pkeys = array(); 00187 foreach($cols as $name => $fld) { 00188 if (!empty($fld->primary_key)) $pkeys[] = $name; 00189 } 00190 } else 00191 $pkeys = $this->GetPrimaryKeys($db, $table); 00192 } 00193 if (empty($pkeys)) { 00194 $this->Error("No primary key found for table $table",'UpdateActiveTable'); 00195 return false; 00196 } 00197 00198 $attr = array(); 00199 $keys = array(); 00200 00201 switch($ADODB_ASSOC_CASE) { 00202 case 0: 00203 foreach($cols as $name => $fldobj) { 00204 $name = strtolower($name); 00205 $this->$name = null; 00206 $attr[$name] = $fldobj; 00207 } 00208 foreach($pkeys as $k => $name) { 00209 $keys[strtolower($name)] = strtolower($name); 00210 } 00211 break; 00212 00213 case 1: 00214 foreach($cols as $name => $fldobj) { 00215 $name = strtoupper($name); 00216 $this->$name = null; 00217 $attr[$name] = $fldobj; 00218 } 00219 00220 foreach($pkeys as $k => $name) { 00221 $keys[strtoupper($name)] = strtoupper($name); 00222 } 00223 break; 00224 default: 00225 foreach($cols as $name => $fldobj) { 00226 $name = ($fldobj->$name); 00227 $this->$name = null; 00228 $attr[$name] = $fldobj; 00229 } 00230 foreach($pkeys as $k => $name) { 00231 $keys[$name] = $cols[$name]->name; 00232 } 00233 break; 00234 } 00235 00236 $activetab->keys = $keys; 00237 $activetab->flds = $attr; 00238 00239 if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) { 00240 $activetab->_created = time(); 00241 $s = serialize($activetab); 00242 if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php'); 00243 adodb_write_file($fname,$s); 00244 } 00245 $activedb->tables[$table] = $activetab; 00246 } 00247 00248 function GetPrimaryKeys(&$db, $table) 00249 { 00250 return $db->MetaPrimaryKeys($table); 00251 } 00252 00253 // error handler for both PHP4+5. 00254 function Error($err,$fn) 00255 { 00256 global $_ADODB_ACTIVE_DBS; 00257 00258 $fn = get_class($this).'::'.$fn; 00259 $this->_lasterr = $fn.': '.$err; 00260 00261 if ($this->_dbat < 0) $db = false; 00262 else { 00263 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00264 $db =& $activedb->db; 00265 } 00266 00267 if (function_exists('adodb_throw')) { 00268 if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false); 00269 else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db); 00270 } else 00271 if (!$db || $db->debug) ADOConnection::outp($this->_lasterr); 00272 00273 } 00274 00275 // return last error message 00276 function ErrorMsg() 00277 { 00278 if (!function_exists('adodb_throw')) { 00279 if ($this->_dbat < 0) $db = false; 00280 else $db = $this->DB(); 00281 00282 // last error could be database error too 00283 if ($db && $db->ErrorMsg()) return $db->ErrorMsg(); 00284 } 00285 return $this->_lasterr; 00286 } 00287 00288 // retrieve ADOConnection from _ADODB_Active_DBs 00289 function &DB() 00290 { 00291 global $_ADODB_ACTIVE_DBS; 00292 00293 if ($this->_dbat < 0) { 00294 $false = false; 00295 $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB"); 00296 return $false; 00297 } 00298 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00299 $db =& $activedb->db; 00300 return $db; 00301 } 00302 00303 // retrieve ADODB_Active_Table 00304 function &TableInfo() 00305 { 00306 global $_ADODB_ACTIVE_DBS; 00307 00308 $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat]; 00309 $table =& $activedb->tables[$this->_tableat]; 00310 return $table; 00311 } 00312 00313 // set a numeric array (using natural table field ordering) as object properties 00314 function Set(&$row) 00315 { 00316 $db =& $this->DB(); 00317 00318 if (!$row) { 00319 $this->_saved = false; 00320 return false; 00321 } 00322 00323 $this->_saved = true; 00324 00325 $table =& $this->TableInfo(); 00326 if (sizeof($table->flds) != sizeof($row)) { 00327 $this->Error("Table structure of $this->_table has changed","Load"); 00328 return false; 00329 } 00330 00331 $cnt = 0; 00332 foreach($table->flds as $name=>$fld) { 00333 $this->$name = $row[$cnt]; 00334 $cnt += 1; 00335 } 00336 $this->_original = $row; 00337 return true; 00338 } 00339 00340 // get last inserted id for INSERT 00341 function LastInsertID(&$db,$fieldname) 00342 { 00343 if ($db->hasInsertID) 00344 $val = $db->Insert_ID($this->_table,$fieldname); 00345 else 00346 $val = false; 00347 00348 if (is_null($val) || $val === false) { 00349 // this might not work reliably in multi-user environment 00350 return $db->GetOne("select max(".$fieldname.") from ".$this->_table); 00351 } 00352 return $val; 00353 } 00354 00355 // quote data in where clause 00356 function doquote(&$db, $val,$t) 00357 { 00358 switch($t) { 00359 case 'D': 00360 case 'T': 00361 if (empty($val)) return 'null'; 00362 00363 case 'C': 00364 case 'X': 00365 if (is_null($val)) return 'null'; 00366 00367 if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") { 00368 return $db->qstr($val); 00369 break; 00370 } 00371 default: 00372 return $val; 00373 break; 00374 } 00375 } 00376 00377 // generate where clause for an UPDATE/SELECT 00378 function GenWhere(&$db, &$table) 00379 { 00380 $keys = $table->keys; 00381 $parr = array(); 00382 00383 foreach($keys as $k) { 00384 $f = $table->flds[$k]; 00385 if ($f) { 00386 $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type)); 00387 } 00388 } 00389 return implode(' and ', $parr); 00390 } 00391 00392 00393 //------------------------------------------------------------ Public functions below 00394 00395 function Load($where,$bindarr=false) 00396 { 00397 $db =& $this->DB(); if (!$db) return false; 00398 $this->_where = $where; 00399 00400 $save = $db->SetFetchMode(ADODB_FETCH_NUM); 00401 $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr); 00402 $db->SetFetchMode($save); 00403 00404 return $this->Set($row); 00405 } 00406 00407 // false on error 00408 function Save() 00409 { 00410 if ($this->_saved) $ok = $this->Update(); 00411 else $ok = $this->Insert(); 00412 00413 return $ok; 00414 } 00415 00416 // false on error 00417 function Insert() 00418 { 00419 $db =& $this->DB(); if (!$db) return false; 00420 $cnt = 0; 00421 $table =& $this->TableInfo(); 00422 00423 $valarr = array(); 00424 $names = array(); 00425 $valstr = array(); 00426 00427 foreach($table->flds as $name=>$fld) { 00428 $val = $this->$name; 00429 if(!is_null($val) || !array_key_exists($name, $table->keys)) { 00430 $valarr[] = $val; 00431 $names[] = $name; 00432 $valstr[] = $db->Param($cnt); 00433 $cnt += 1; 00434 } 00435 } 00436 00437 if (empty($names)){ 00438 foreach($table->flds as $name=>$fld) { 00439 $valarr[] = null; 00440 $names[] = $name; 00441 $valstr[] = $db->Param($cnt); 00442 $cnt += 1; 00443 } 00444 } 00445 $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')'; 00446 $ok = $db->Execute($sql,$valarr); 00447 00448 if ($ok) { 00449 $this->_saved = true; 00450 $autoinc = false; 00451 foreach($table->keys as $k) { 00452 if (is_null($this->$k)) { 00453 $autoinc = true; 00454 break; 00455 } 00456 } 00457 if ($autoinc && sizeof($table->keys) == 1) { 00458 $k = reset($table->keys); 00459 $this->$k = $this->LastInsertID($db,$k); 00460 } 00461 } 00462 00463 $this->_original = $valarr; 00464 return !empty($ok); 00465 } 00466 00467 function Delete() 00468 { 00469 $db =& $this->DB(); if (!$db) return false; 00470 $table =& $this->TableInfo(); 00471 00472 $where = $this->GenWhere($db,$table); 00473 $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where; 00474 $ok = $db->Execute($sql); 00475 00476 return $ok ? true : false; 00477 } 00478 00479 // returns an array of active record objects 00480 function &Find($whereOrderBy,$bindarr=false,$pkeysArr=false) 00481 { 00482 $db =& $this->DB(); if (!$db || empty($this->_table)) return false; 00483 $arr =& $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr); 00484 return $arr; 00485 } 00486 00487 // returns 0 on error, 1 on update, 2 on insert 00488 function Replace() 00489 { 00490 global $ADODB_ASSOC_CASE; 00491 00492 $db =& $this->DB(); if (!$db) return false; 00493 $table =& $this->TableInfo(); 00494 00495 $pkey = $table->keys; 00496 00497 foreach($table->flds as $name=>$fld) { 00498 $val = $this->$name; 00499 /* 00500 if (is_null($val)) { 00501 if (isset($fld->not_null) && $fld->not_null) { 00502 if (isset($fld->default_value) && strlen($fld->default_value)) continue; 00503 else { 00504 $this->Error("Cannot update null into $name","Replace"); 00505 return false; 00506 } 00507 } 00508 }*/ 00509 if (is_null($val) && !empty($fld->auto_increment)) { 00510 continue; 00511 } 00512 $t = $db->MetaType($fld->type); 00513 $arr[$name] = $this->doquote($db,$val,$t); 00514 $valarr[] = $val; 00515 } 00516 00517 if (!is_array($pkey)) $pkey = array($pkey); 00518 00519 00520 if ($ADODB_ASSOC_CASE == 0) 00521 foreach($pkey as $k => $v) 00522 $pkey[$k] = strtolower($v); 00523 elseif ($ADODB_ASSOC_CASE == 0) 00524 foreach($pkey as $k => $v) 00525 $pkey[$k] = strtoupper($v); 00526 00527 $ok = $db->Replace($this->_table,$arr,$pkey); 00528 if ($ok) { 00529 $this->_saved = true; // 1= update 2=insert 00530 if ($ok == 2) { 00531 $autoinc = false; 00532 foreach($table->keys as $k) { 00533 if (is_null($this->$k)) { 00534 $autoinc = true; 00535 break; 00536 } 00537 } 00538 if ($autoinc && sizeof($table->keys) == 1) { 00539 $k = reset($table->keys); 00540 $this->$k = $this->LastInsertID($db,$k); 00541 } 00542 } 00543 00544 $this->_original =& $valarr; 00545 } 00546 return $ok; 00547 } 00548 00549 // returns 0 on error, 1 on update, -1 if no change in data (no update) 00550 function Update() 00551 { 00552 $db =& $this->DB(); if (!$db) return false; 00553 $table =& $this->TableInfo(); 00554 00555 $where = $this->GenWhere($db, $table); 00556 00557 if (!$where) { 00558 $this->error("Where missing for table $table", "Update"); 00559 return false; 00560 } 00561 $valarr = array(); 00562 $neworig = array(); 00563 $pairs = array(); 00564 $i = -1; 00565 $cnt = 0; 00566 foreach($table->flds as $name=>$fld) { 00567 $i += 1; 00568 $val = $this->$name; 00569 $neworig[] = $val; 00570 00571 if (isset($table->keys[$name])) { 00572 continue; 00573 } 00574 00575 if (is_null($val)) { 00576 if (isset($fld->not_null) && $fld->not_null) { 00577 if (isset($fld->default_value) && strlen($fld->default_value)) continue; 00578 else { 00579 $this->Error("Cannot set field $name to NULL","Update"); 00580 return false; 00581 } 00582 } 00583 } 00584 00585 if (isset($this->_original[$i]) && $val == $this->_original[$i]) { 00586 continue; 00587 } 00588 $valarr[] = $val; 00589 $pairs[] = $name.'='.$db->Param($cnt); 00590 $cnt += 1; 00591 } 00592 00593 00594 if (!$cnt) return -1; 00595 $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where; 00596 $ok = $db->Execute($sql,$valarr); 00597 if ($ok) { 00598 $this->_original =& $neworig; 00599 return 1; 00600 } 00601 return 0; 00602 } 00603 00604 function GetAttributeNames() 00605 { 00606 $table =& $this->TableInfo(); 00607 if (!$table) return false; 00608 return array_keys($table->flds); 00609 } 00610 00611 }; 00612 00613 ?>