source: branches/comu-ver2/data/module/adodb/adodb-active-record.inc.php @ 18701

Revision 18701, 16.9 KB checked in by nanasess, 14 years ago (diff)

Copyright の更新(#601)

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id Revision Date
  • Property svn:mime-type set to text/x-httpd-php
Line 
1<?php
2/*
3
4@version v4.992 10 Nov 2009  (c) 2000-2010 John Lim (jlim#natsoft.com). 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
19global $_ADODB_ACTIVE_DBS;
20global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
21global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
22global $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
29class ADODB_Active_DB {
30    var $db; // ADOConnection
31    var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
32}
33
34class 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
42function 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
65class 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    // I have an ON INSERT trigger on a table that sets other columns in the table.
350    // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
351    function Reload()
352    {
353        $db =& $this->DB(); if (!$db) return false;
354        $table =& $this->TableInfo();
355        $where = $this->GenWhere($db, $table);
356        return($this->Load($where));
357    }
358   
359    // set a numeric array (using natural table field ordering) as object properties
360    function Set(&$row)
361    {
362    global $ACTIVE_RECORD_SAFETY;
363   
364        $db =& $this->DB();
365       
366        if (!$row) {
367            $this->_saved = false;     
368            return false;
369        }
370       
371        $this->_saved = true;
372       
373        $table =& $this->TableInfo();
374        if ($ACTIVE_RECORD_SAFETY && sizeof($table->flds) != sizeof($row)) {
375            $bad_size = TRUE;
376            if (sizeof($row) == 2 * sizeof($table->flds)) {
377                // Only keep string keys
378                $keys = array_filter(array_keys($row), 'is_string');
379                if (sizeof($keys) == sizeof($table->flds))
380                    $bad_size = FALSE;
381            }
382            if ($bad_size) {
383                $this->Error("Table structure of $this->_table has changed","Load");
384                return false;
385            }
386        }
387        else
388            $keys = array_keys($row);
389     
390        reset($keys);
391        $this->_original = array();
392        foreach($table->flds as $name=>$fld) {
393            $value = $row[current($keys)];
394            $this->$name = $value;
395            $this->_original[] = $value;
396            next($keys);
397        }
398        # </AP>
399        return true;
400    }
401   
402    // get last inserted id for INSERT
403    function LastInsertID(&$db,$fieldname)
404    {
405        if ($db->hasInsertID)
406            $val = $db->Insert_ID($this->_table,$fieldname);
407        else
408            $val = false;
409           
410        if (is_null($val) || $val === false) {
411            // this might not work reliably in multi-user environment
412            return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
413        }
414        return $val;
415    }
416   
417    // quote data in where clause
418    function doquote(&$db, $val,$t)
419    {
420        switch($t) {
421        case 'D':
422        case 'T':
423            if (empty($val)) return 'null';
424           
425        case 'B':
426        case 'N':
427        case 'C':
428        case 'X':
429            if (is_null($val)) return 'null';
430           
431            if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") {
432                return $db->qstr($val);
433                break;
434            }
435        default:
436            return $val;
437            break;
438        }
439    }
440   
441    // generate where clause for an UPDATE/SELECT
442    function GenWhere(&$db, &$table)
443    {
444        $keys = $table->keys;
445        $parr = array();
446       
447        foreach($keys as $k) {
448            $f = $table->flds[$k];
449            if ($f) {
450                $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
451            }
452        }
453        return implode(' and ', $parr);
454    }
455   
456   
457    //------------------------------------------------------------ Public functions below
458   
459    function Load($where,$bindarr=false)
460    {
461        $db =& $this->DB(); if (!$db) return false;
462        $this->_where = $where;
463       
464        $save = $db->SetFetchMode(ADODB_FETCH_NUM);
465        $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr);
466        $db->SetFetchMode($save);
467       
468        return $this->Set($row);
469    }
470   
471    // false on error
472    function Save()
473    {
474        if ($this->_saved) $ok = $this->Update();
475        else $ok = $this->Insert();
476       
477        return $ok;
478    }
479   
480    // false on error
481    function Insert()
482    {
483        $db =& $this->DB(); if (!$db) return false;
484        $cnt = 0;
485        $table =& $this->TableInfo();
486       
487        $valarr = array();
488        $names = array();
489        $valstr = array();
490
491        foreach($table->flds as $name=>$fld) {
492            $val = $this->$name;
493            if(!is_null($val) || !array_key_exists($name, $table->keys)) {
494                $valarr[] = $val;
495                $names[] = $name;
496                $valstr[] = $db->Param($cnt);
497                $cnt += 1;
498            }
499        }
500       
501        if (empty($names)){
502            foreach($table->flds as $name=>$fld) {
503                $valarr[] = null;
504                $names[] = $name;
505                $valstr[] = $db->Param($cnt);
506                $cnt += 1;
507            }
508        }
509        $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
510        $ok = $db->Execute($sql,$valarr);
511       
512        if ($ok) {
513            $this->_saved = true;
514            $autoinc = false;
515            foreach($table->keys as $k) {
516                if (is_null($this->$k)) {
517                    $autoinc = true;
518                    break;
519                }
520            }
521            if ($autoinc && sizeof($table->keys) == 1) {
522                $k = reset($table->keys);
523                $this->$k = $this->LastInsertID($db,$k);
524            }
525        }
526       
527        $this->_original = $valarr;
528        return !empty($ok);
529    }
530   
531    function Delete()
532    {
533        $db =& $this->DB(); if (!$db) return false;
534        $table =& $this->TableInfo();
535       
536        $where = $this->GenWhere($db,$table);
537        $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
538        $ok = $db->Execute($sql);
539       
540        return $ok ? true : false;
541    }
542   
543    // returns an array of active record objects
544    function &Find($whereOrderBy,$bindarr=false,$pkeysArr=false)
545    {
546        $db =& $this->DB(); if (!$db || empty($this->_table)) return false;
547        $arr =& $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr);
548        return $arr;
549    }
550   
551    // returns 0 on error, 1 on update, 2 on insert
552    function Replace()
553    {
554    global $ADODB_ASSOC_CASE;
555       
556        $db =& $this->DB(); if (!$db) return false;
557        $table =& $this->TableInfo();
558       
559        $pkey = $table->keys;
560       
561        foreach($table->flds as $name=>$fld) {
562            $val = $this->$name;
563            /*
564            if (is_null($val)) {
565                if (isset($fld->not_null) && $fld->not_null) {
566                    if (isset($fld->default_value) && strlen($fld->default_value)) continue;
567                    else {
568                        $this->Error("Cannot update null into $name","Replace");
569                        return false;
570                    }
571                }
572            }*/
573            if (is_null($val) && !empty($fld->auto_increment)) {
574                continue;
575            }
576            $t = $db->MetaType($fld->type);
577            $arr[$name] = $this->doquote($db,$val,$t);
578            $valarr[] = $val;
579        }
580       
581        if (!is_array($pkey)) $pkey = array($pkey);
582       
583       
584        if ($ADODB_ASSOC_CASE == 0)
585            foreach($pkey as $k => $v)
586                $pkey[$k] = strtolower($v);
587        elseif ($ADODB_ASSOC_CASE == 1)
588            foreach($pkey as $k => $v)
589                $pkey[$k] = strtoupper($v);
590               
591        $ok = $db->Replace($this->_table,$arr,$pkey);
592        if ($ok) {
593            $this->_saved = true; // 1= update 2=insert
594            if ($ok == 2) {
595                $autoinc = false;
596                foreach($table->keys as $k) {
597                    if (is_null($this->$k)) {
598                        $autoinc = true;
599                        break;
600                    }
601                }
602                if ($autoinc && sizeof($table->keys) == 1) {
603                    $k = reset($table->keys);
604                    $this->$k = $this->LastInsertID($db,$k);
605                }
606            }
607           
608            $this->_original =& $valarr;
609        }
610        return $ok;
611    }
612
613    // returns 0 on error, 1 on update, -1 if no change in data (no update)
614    function Update()
615    {
616        $db =& $this->DB(); if (!$db) return false;
617        $table =& $this->TableInfo();
618       
619        $where = $this->GenWhere($db, $table);
620       
621        if (!$where) {
622            $this->error("Where missing for table $table", "Update");
623            return false;
624        }
625        $valarr = array();
626        $neworig = array();
627        $pairs = array();
628        $i = -1;
629        $cnt = 0;
630        foreach($table->flds as $name=>$fld) {
631            $i += 1;
632            $val = $this->$name;
633            $neworig[] = $val;
634           
635            if (isset($table->keys[$name])) {
636                continue;
637            }
638           
639            if (is_null($val)) {
640                if (isset($fld->not_null) && $fld->not_null) {
641                    if (isset($fld->default_value) && strlen($fld->default_value)) continue;
642                    else {
643                        $this->Error("Cannot set field $name to NULL","Update");
644                        return false;
645                    }
646                }
647            }
648           
649            if (isset($this->_original[$i]) && $val == $this->_original[$i]) {
650                continue;
651            }           
652            $valarr[] = $val;
653            $pairs[] = $name.'='.$db->Param($cnt);
654            $cnt += 1;
655        }
656       
657       
658        if (!$cnt) return -1;
659        $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
660        $ok = $db->Execute($sql,$valarr);
661        if ($ok) {
662            $this->_original =& $neworig;
663            return 1;
664        }
665        return 0;
666    }
667   
668    function GetAttributeNames()
669    {
670        $table =& $this->TableInfo();
671        if (!$table) return false;
672        return array_keys($table->flds);
673    }
674   
675};
676
677?>
Note: See TracBrowser for help on using the repository browser.