[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/adodb/ -> adodb-active-record.inc.php (source)

   1  <?php
   2  /*
   3  
   4  @version V4.98 13 Feb 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
   5    Latest version is available at http://adodb.sourceforge.net
   6   
   7    Released under both BSD license and Lesser GPL library license. 
   8    Whenever there is any discrepancy between the two licenses, 
   9    the BSD license will take precedence.
  10    
  11    Active Record implementation. Superset of Zend Framework's.
  12    
  13    Version 0.09
  14    
  15    See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord 
  16        for info on Ruby on Rails Active Record implementation
  17  */
  18  
  19  global $_ADODB_ACTIVE_DBS;
  20  global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
  21  global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
  22  global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record.
  23  
  24  // array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
  25  $_ADODB_ACTIVE_DBS = array();
  26  $ACTIVE_RECORD_SAFETY = true;
  27  $ADODB_ACTIVE_DEFVALS = false;
  28  
  29  class ADODB_Active_DB {
  30      var $db; // ADOConnection
  31      var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
  32  }
  33  
  34  class ADODB_Active_Table {
  35      var $name; // table name
  36      var $flds; // assoc array of adofieldobjs, indexed by fieldname
  37      var $keys; // assoc array of primary keys, indexed by fieldname
  38      var $_created; // only used when stored as a cached file
  39  }
  40  
  41  // returns index into $_ADODB_ACTIVE_DBS
  42  function ADODB_SetDatabaseAdapter(&$db)
  43  {
  44      global $_ADODB_ACTIVE_DBS;
  45      
  46          foreach($_ADODB_ACTIVE_DBS as $k => $d) {
  47              if (PHP_VERSION >= 5) {
  48                  if ($d->db === $db) return $k;
  49              } else {
  50                  if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database) 
  51                      return $k;
  52              }
  53          }
  54          
  55          $obj = new ADODB_Active_DB();
  56          $obj->db =& $db;
  57          $obj->tables = array();
  58          
  59          $_ADODB_ACTIVE_DBS[] = $obj;
  60          
  61          return sizeof($_ADODB_ACTIVE_DBS)-1;
  62  }
  63  
  64  
  65  class ADODB_Active_Record {
  66      var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
  67      var $_table; // tablename, if set in class definition then use it as table name
  68      var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
  69      var $_where; // where clause set in Load()
  70      var $_saved = false; // indicates whether data is already inserted.
  71      var $_lasterr = false; // last error message
  72      var $_original = false; // the original values loaded or inserted, refreshed on update
  73      
  74      // should be static
  75  	function UseDefaultValues($bool=null)
  76      {
  77      global $ADODB_ACTIVE_DEFVALS;
  78          if (isset($bool)) $ADODB_ACTIVE_DEFVALS = $bool;
  79          return $ADODB_ACTIVE_DEFVALS;
  80      }
  81  
  82      // should be static
  83  	function SetDatabaseAdapter(&$db) 
  84      {
  85          return ADODB_SetDatabaseAdapter($db);
  86      }
  87      
  88      // php4 constructor
  89  	function ADODB_Active_Record($table = false, $pkeyarr=false, $db=false)
  90      {
  91          ADODB_Active_Record::__construct($table,$pkeyarr,$db);
  92      }
  93      
  94      // php5 constructor
  95  	function __construct($table = false, $pkeyarr=false, $db=false)
  96      {
  97      global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS;
  98      
  99          if ($db == false && is_object($pkeyarr)) {
 100              $db = $pkeyarr;
 101              $pkeyarr = false;
 102          }
 103          
 104          if (!$table) { 
 105              if (!empty($this->_table)) $table = $this->_table;
 106              else $table = $this->_pluralize(get_class($this));
 107          }
 108          if ($db) {
 109              $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
 110          } else
 111              $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
 112          
 113          
 114          if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
 115          
 116          $this->_table = $table;
 117          $this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
 118          $this->UpdateActiveTable($pkeyarr);
 119      }
 120      
 121  	function __wakeup()
 122      {
 123            $class = get_class($this);
 124            new $class;
 125      }
 126      
 127  	function _pluralize($table)
 128      {
 129          $ut = strtoupper($table);
 130          $len = strlen($table);
 131          $lastc = $ut[$len-1];
 132          $lastc2 = substr($ut,$len-2);
 133          switch ($lastc) {
 134          case 'S':
 135              return $table.'es';    
 136          case 'Y':
 137              return substr($table,0,$len-1).'ies';
 138          case 'X':    
 139              return $table.'es';
 140          case 'H': 
 141              if ($lastc2 == 'CH' || $lastc2 == 'SH')
 142                  return $table.'es';
 143          default:
 144              return $table.'s';
 145          }
 146      }
 147      
 148      //////////////////////////////////
 149      
 150      // update metadata
 151  	function UpdateActiveTable($pkeys=false,$forceUpdate=false)
 152      {
 153      global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
 154      global $ADODB_ACTIVE_DEFVALS;
 155  
 156          $activedb =& $_ADODB_ACTIVE_DBS[$this->_dbat];
 157  
 158          $table = $this->_table;
 159          $tables = $activedb->tables;
 160          $tableat = $this->_tableat;
 161          if (!$forceUpdate && !empty($tables[$tableat])) {
 162              $tobj =& $tables[$tableat];
 163              foreach($tobj->flds as $name => $fld) {
 164                  if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value)) 
 165                      $this->$name = $fld->default_value;
 166                  else
 167                      $this->$name = null;
 168              }
 169              return;
 170          }
 171          
 172          $db =& $activedb->db;
 173          $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
 174          if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
 175              $fp = fopen($fname,'r');
 176              @flock($fp, LOCK_SH);
 177              $acttab = unserialize(fread($fp,100000));
 178              fclose($fp);
 179              if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) { 
 180                  // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
 181                  // ideally, you should cache at least 32 secs
 182                  $activedb->tables[$table] = $acttab;
 183                  
 184                  //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
 185                    return;
 186              } else if ($db->debug) {
 187                  ADOConnection::outp("Refreshing cached active record file: $fname");
 188              }
 189          }
 190          $activetab = new ADODB_Active_Table();
 191          $activetab->name = $table;
 192          
 193          
 194          $cols = $db->MetaColumns($table);
 195          if (!$cols) {
 196              $this->Error("Invalid table name: $table",'UpdateActiveTable'); 
 197              return false;
 198          }
 199          $fld = reset($cols);
 200          if (!$pkeys) {
 201              if (isset($fld->primary_key)) {
 202                  $pkeys = array();
 203                  foreach($cols as $name => $fld) {
 204                      if (!empty($fld->primary_key)) $pkeys[] = $name;
 205                  }
 206              } else    
 207                  $pkeys = $this->GetPrimaryKeys($db, $table);
 208          }
 209          if (empty($pkeys)) {
 210              $this->Error("No primary key found for table $table",'UpdateActiveTable');
 211              return false;
 212          }
 213          
 214          $attr = array();
 215          $keys = array();
 216          
 217          switch($ADODB_ASSOC_CASE) {
 218          case 0:
 219              foreach($cols as $name => $fldobj) {
 220                  $name = strtolower($name);
 221                  if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
 222                      $this->$name = $fldobj->default_value;
 223                  else
 224                      $this->$name = null;
 225                  $attr[$name] = $fldobj;
 226              }
 227              foreach($pkeys as $k => $name) {
 228                  $keys[strtolower($name)] = strtolower($name);
 229              }
 230              break;
 231              
 232          case 1: 
 233              foreach($cols as $name => $fldobj) {
 234                  $name = strtoupper($name);
 235                 
 236                  if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
 237                      $this->$name = $fldobj->default_value;
 238                  else
 239                      $this->$name = null;
 240                  $attr[$name] = $fldobj;
 241              }
 242              
 243              foreach($pkeys as $k => $name) {
 244                  $keys[strtoupper($name)] = strtoupper($name);
 245              }
 246              break;
 247          default:
 248              foreach($cols as $name => $fldobj) {
 249                  $name = ($fldobj->name);
 250                  
 251                  if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
 252                      $this->$name = $fldobj->default_value;
 253                  else
 254                      $this->$name = null;
 255                  $attr[$name] = $fldobj;
 256              }
 257              foreach($pkeys as $k => $name) {
 258                  $keys[$name] = $cols[$name]->name;
 259              }
 260              break;
 261          }
 262          
 263          $activetab->keys = $keys;
 264          $activetab->flds = $attr;
 265  
 266          if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
 267              $activetab->_created = time();
 268              $s = serialize($activetab);
 269              if (!function_exists('adodb_write_file')) include (ADODB_DIR.'/adodb-csvlib.inc.php');
 270              adodb_write_file($fname,$s);
 271          }
 272          $activedb->tables[$table] = $activetab;
 273      }
 274      
 275  	function GetPrimaryKeys(&$db, $table)
 276      {
 277          return $db->MetaPrimaryKeys($table);
 278      }
 279      
 280      // error handler for both PHP4+5. 
 281  	function Error($err,$fn)
 282      {
 283      global $_ADODB_ACTIVE_DBS;
 284      
 285          $fn = get_class($this).'::'.$fn;
 286          $this->_lasterr = $fn.': '.$err;
 287          
 288          if ($this->_dbat < 0) $db = false;
 289          else {
 290              $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
 291              $db =& $activedb->db;
 292          }
 293          
 294          if (function_exists('adodb_throw')) {    
 295              if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
 296              else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
 297          } else
 298              if (!$db || $db->debug) ADOConnection::outp($this->_lasterr);
 299          
 300      }
 301      
 302      // return last error message
 303  	function ErrorMsg()
 304      {
 305          if (!function_exists('adodb_throw')) {
 306              if ($this->_dbat < 0) $db = false;
 307              else $db = $this->DB();
 308          
 309              // last error could be database error too
 310              if ($db && $db->ErrorMsg()) return $db->ErrorMsg();
 311          }
 312          return $this->_lasterr;
 313      }
 314      
 315  	function ErrorNo() 
 316      {
 317          if ($this->_dbat < 0) return -9999; // no database connection...
 318          $db = $this->DB();
 319          
 320          return (int) $db->ErrorNo();
 321      }
 322  
 323  
 324      // retrieve ADOConnection from _ADODB_Active_DBs
 325      function &DB()
 326      {
 327      global $_ADODB_ACTIVE_DBS;
 328      
 329          if ($this->_dbat < 0) {
 330              $false = false;
 331              $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
 332              return $false;
 333          }
 334          $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
 335          $db =& $activedb->db;
 336          return $db;
 337      }
 338      
 339      // retrieve ADODB_Active_Table
 340      function &TableInfo()
 341      {
 342      global $_ADODB_ACTIVE_DBS;
 343      
 344          $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
 345          $table =& $activedb->tables[$this->_tableat];
 346          return $table;
 347      }
 348      
 349      // set a numeric array (using natural table field ordering) as object properties
 350  	function Set(&$row)
 351      {
 352      global $ACTIVE_RECORD_SAFETY;
 353      
 354          $db =& $this->DB();
 355          
 356          if (!$row) {
 357              $this->_saved = false;        
 358              return false;
 359          }
 360          
 361          $this->_saved = true;
 362          
 363          $table =& $this->TableInfo();
 364          if ($ACTIVE_RECORD_SAFETY && sizeof($table->flds) != sizeof($row)) {
 365              $bad_size = TRUE;
 366              if (sizeof($row) == 2 * sizeof($table->flds)) {
 367                  // Only keep string keys
 368                  $keys = array_filter(array_keys($row), 'is_string');
 369                  if (sizeof($keys) == sizeof($table->flds))
 370                      $bad_size = FALSE;
 371              }
 372              if ($bad_size) {
 373                  $this->Error("Table structure of $this->_table has changed","Load");
 374                  return false;
 375              }
 376          }
 377          else
 378              $keys = array_keys($row);
 379        
 380          reset($keys);
 381          $this->_original = array();
 382          foreach($table->flds as $name=>$fld) {
 383              $value = $row[current($keys)];
 384              $this->$name = $value;
 385              $this->_original[] = $value;
 386              next($keys);
 387          }
 388          # </AP>
 389          return true;
 390      }
 391      
 392      // get last inserted id for INSERT
 393  	function LastInsertID(&$db,$fieldname)
 394      {
 395          if ($db->hasInsertID)
 396              $val = $db->Insert_ID($this->_table,$fieldname);
 397          else
 398              $val = false;
 399              
 400          if (is_null($val) || $val === false) {
 401              // this might not work reliably in multi-user environment
 402              return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
 403          }
 404          return $val;
 405      }
 406      
 407      // quote data in where clause
 408  	function doquote(&$db, $val,$t)
 409      {
 410          switch($t) {
 411          case 'D':
 412          case 'T':
 413              if (empty($val)) return 'null';
 414              
 415          case 'C':
 416          case 'X':
 417              if (is_null($val)) return 'null';
 418              
 419              if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") { 
 420                  return $db->qstr($val);
 421                  break;
 422              }
 423          default:
 424              return $val;
 425              break;
 426          }
 427      }
 428      
 429      // generate where clause for an UPDATE/SELECT
 430  	function GenWhere(&$db, &$table)
 431      {
 432          $keys = $table->keys;
 433          $parr = array();
 434          
 435          foreach($keys as $k) {
 436              $f = $table->flds[$k];
 437              if ($f) {
 438                  $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
 439              }
 440          }
 441          return implode(' and ', $parr);
 442      }
 443      
 444      
 445      //------------------------------------------------------------ Public functions below
 446      
 447  	function Load($where,$bindarr=false)
 448      {
 449          $db =& $this->DB(); if (!$db) return false;
 450          $this->_where = $where;
 451          
 452          $save = $db->SetFetchMode(ADODB_FETCH_NUM);
 453          $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr);
 454          $db->SetFetchMode($save);
 455          
 456          return $this->Set($row);
 457      }
 458      
 459      // false on error
 460  	function Save()
 461      {
 462          if ($this->_saved) $ok = $this->Update();
 463          else $ok = $this->Insert();
 464          
 465          return $ok;
 466      }
 467      
 468      // false on error
 469  	function Insert()
 470      {
 471          $db =& $this->DB(); if (!$db) return false;
 472          $cnt = 0;
 473          $table =& $this->TableInfo();
 474          
 475          $valarr = array();
 476          $names = array();
 477          $valstr = array();
 478  
 479          foreach($table->flds as $name=>$fld) {
 480              $val = $this->$name;
 481              if(!is_null($val) || !array_key_exists($name, $table->keys)) {
 482                  $valarr[] = $val;
 483                  $names[] = $name;
 484                  $valstr[] = $db->Param($cnt);
 485                  $cnt += 1;
 486              }
 487          }
 488          
 489          if (empty($names)){
 490              foreach($table->flds as $name=>$fld) {
 491                  $valarr[] = null;
 492                  $names[] = $name;
 493                  $valstr[] = $db->Param($cnt);
 494                  $cnt += 1;
 495              }
 496          }
 497          $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
 498          $ok = $db->Execute($sql,$valarr);
 499          
 500          if ($ok) {
 501              $this->_saved = true;
 502              $autoinc = false;
 503              foreach($table->keys as $k) {
 504                  if (is_null($this->$k)) {
 505                      $autoinc = true;
 506                      break;
 507                  }
 508              }
 509              if ($autoinc && sizeof($table->keys) == 1) {
 510                  $k = reset($table->keys);
 511                  $this->$k = $this->LastInsertID($db,$k);
 512              }
 513          }
 514          
 515          $this->_original = $valarr;
 516          return !empty($ok);
 517      }
 518      
 519  	function Delete()
 520      {
 521          $db =& $this->DB(); if (!$db) return false;
 522          $table =& $this->TableInfo();
 523          
 524          $where = $this->GenWhere($db,$table);
 525          $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
 526          $ok = $db->Execute($sql);
 527          
 528          return $ok ? true : false;
 529      }
 530      
 531      // returns an array of active record objects
 532      function &Find($whereOrderBy,$bindarr=false,$pkeysArr=false)
 533      {
 534          $db =& $this->DB(); if (!$db || empty($this->_table)) return false;
 535          $arr =& $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr);
 536          return $arr;
 537      }
 538      
 539      // returns 0 on error, 1 on update, 2 on insert
 540  	function Replace()
 541      {
 542      global $ADODB_ASSOC_CASE;
 543          
 544          $db =& $this->DB(); if (!$db) return false;
 545          $table =& $this->TableInfo();
 546          
 547          $pkey = $table->keys;
 548          
 549          foreach($table->flds as $name=>$fld) {
 550              $val = $this->$name;
 551              /*
 552              if (is_null($val)) {
 553                  if (isset($fld->not_null) && $fld->not_null) {
 554                      if (isset($fld->default_value) && strlen($fld->default_value)) continue;
 555                      else {
 556                          $this->Error("Cannot update null into $name","Replace");
 557                          return false;
 558                      }
 559                  }
 560              }*/
 561              if (is_null($val) && !empty($fld->auto_increment)) {
 562                  continue;
 563              }
 564              $t = $db->MetaType($fld->type);
 565              $arr[$name] = $this->doquote($db,$val,$t);
 566              $valarr[] = $val;
 567          }
 568          
 569          if (!is_array($pkey)) $pkey = array($pkey);
 570          
 571          
 572          if ($ADODB_ASSOC_CASE == 0) 
 573              foreach($pkey as $k => $v)
 574                  $pkey[$k] = strtolower($v);
 575          elseif ($ADODB_ASSOC_CASE == 1) 
 576              foreach($pkey as $k => $v)
 577                  $pkey[$k] = strtoupper($v);
 578                  
 579          $ok = $db->Replace($this->_table,$arr,$pkey);
 580          if ($ok) {
 581              $this->_saved = true; // 1= update 2=insert
 582              if ($ok == 2) {
 583                  $autoinc = false;
 584                  foreach($table->keys as $k) {
 585                      if (is_null($this->$k)) {
 586                          $autoinc = true;
 587                          break;
 588                      }
 589                  }
 590                  if ($autoinc && sizeof($table->keys) == 1) {
 591                      $k = reset($table->keys);
 592                      $this->$k = $this->LastInsertID($db,$k);
 593                  }
 594              }
 595              
 596              $this->_original =& $valarr;
 597          } 
 598          return $ok;
 599      }
 600  
 601      // returns 0 on error, 1 on update, -1 if no change in data (no update)
 602  	function Update()
 603      {
 604          $db =& $this->DB(); if (!$db) return false;
 605          $table =& $this->TableInfo();
 606          
 607          $where = $this->GenWhere($db, $table);
 608          
 609          if (!$where) {
 610              $this->error("Where missing for table $table", "Update");
 611              return false;
 612          }
 613          $valarr = array(); 
 614          $neworig = array();
 615          $pairs = array();
 616          $i = -1;
 617          $cnt = 0;
 618          foreach($table->flds as $name=>$fld) {
 619              $i += 1;
 620              $val = $this->$name;
 621              $neworig[] = $val;
 622              
 623              if (isset($table->keys[$name])) {
 624                  continue;
 625              }
 626              
 627              if (is_null($val)) {
 628                  if (isset($fld->not_null) && $fld->not_null) {
 629                      if (isset($fld->default_value) && strlen($fld->default_value)) continue;
 630                      else {
 631                          $this->Error("Cannot set field $name to NULL","Update");
 632                          return false;
 633                      }
 634                  }
 635              }
 636              
 637              if (isset($this->_original[$i]) && $val == $this->_original[$i]) {
 638                  continue;
 639              }            
 640              $valarr[] = $val;
 641              $pairs[] = $name.'='.$db->Param($cnt);
 642              $cnt += 1;
 643          }
 644          
 645          
 646          if (!$cnt) return -1;
 647          $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
 648          $ok = $db->Execute($sql,$valarr);
 649          if ($ok) {
 650              $this->_original =& $neworig;
 651              return 1;
 652          }
 653          return 0;
 654      }
 655      
 656  	function GetAttributeNames()
 657      {
 658          $table =& $this->TableInfo();
 659          if (!$table) return false;
 660          return array_keys($table->flds);
 661      }
 662      
 663  };
 664  
 665  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7