PageRenderTime 83ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/gacl/adodb/adodb-active-record.inc.php

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