adodb-active-record.inc.php 26 KB

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