source: branches/feature-module-update/data/module/DB/storage.php @ 15532

Revision 15532, 14.5 KB checked in by nanasess, 17 years ago (diff)

svn:mime-type 修正

  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * Provides an object interface to a table row
7 *
8 * PHP versions 4 and 5
9 *
10 * LICENSE: This source file is subject to version 3.0 of the PHP license
11 * that is available through the world-wide-web at the following URI:
12 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
13 * the PHP License and are unable to obtain it through the web, please
14 * send a note to license@php.net so we can mail you a copy immediately.
15 *
16 * @category   Database
17 * @package    DB
18 * @author     Stig Bakken <stig@php.net>
19 * @copyright  1997-2005 The PHP Group
20 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
21 * @version    CVS: $Id$
22 * @link       http://pear.php.net/package/DB
23 */
24
25/**
26 * Obtain the DB class so it can be extended from
27 */
28require_once 'DB.php';
29
30/**
31 * Provides an object interface to a table row
32 *
33 * It lets you add, delete and change rows using objects rather than SQL
34 * statements.
35 *
36 * @category   Database
37 * @package    DB
38 * @author     Stig Bakken <stig@php.net>
39 * @copyright  1997-2005 The PHP Group
40 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
41 * @version    Release: @package_version@
42 * @link       http://pear.php.net/package/DB
43 */
44class DB_storage extends PEAR
45{
46    // {{{ properties
47
48    /** the name of the table (or view, if the backend database supports
49        updates in views) we hold data from */
50    var $_table = null;
51
52    /** which column(s) in the table contains primary keys, can be a
53        string for single-column primary keys, or an array of strings
54        for multiple-column primary keys */
55    var $_keycolumn = null;
56
57    /** DB connection handle used for all transactions */
58    var $_dbh = null;
59
60    /** an assoc with the names of database fields stored as properties
61        in this object */
62    var $_properties = array();
63
64    /** an assoc with the names of the properties in this object that
65        have been changed since they were fetched from the database */
66    var $_changes = array();
67
68    /** flag that decides if data in this object can be changed.
69        objects that don't have their table's key column in their
70        property lists will be flagged as read-only. */
71    var $_readonly = false;
72
73    /** function or method that implements a validator for fields that
74        are set, this validator function returns true if the field is
75        valid, false if not */
76    var $_validator = null;
77
78    // }}}
79    // {{{ constructor
80
81    /**
82     * Constructor
83     *
84     * @param $table string the name of the database table
85     *
86     * @param $keycolumn mixed string with name of key column, or array of
87     * strings if the table has a primary key of more than one column
88     *
89     * @param $dbh object database connection object
90     *
91     * @param $validator mixed function or method used to validate
92     * each new value, called with three parameters: the name of the
93     * field/column that is changing, a reference to the new value and
94     * a reference to this object
95     *
96     */
97    function DB_storage($table, $keycolumn, &$dbh, $validator = null)
98    {
99        $this->PEAR('DB_Error');
100        $this->_table = $table;
101        $this->_keycolumn = $keycolumn;
102        $this->_dbh = $dbh;
103        $this->_readonly = false;
104        $this->_validator = $validator;
105    }
106
107    // }}}
108    // {{{ _makeWhere()
109
110    /**
111     * Utility method to build a "WHERE" clause to locate ourselves in
112     * the table.
113     *
114     * XXX future improvement: use rowids?
115     *
116     * @access private
117     */
118    function _makeWhere($keyval = null)
119    {
120        if (is_array($this->_keycolumn)) {
121            if ($keyval === null) {
122                for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
123                    $keyval[] = $this->{$this->_keycolumn[$i]};
124                }
125            }
126            $whereclause = '';
127            for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
128                if ($i > 0) {
129                    $whereclause .= ' AND ';
130                }
131                $whereclause .= $this->_keycolumn[$i];
132                if (is_null($keyval[$i])) {
133                    // there's not much point in having a NULL key,
134                    // but we support it anyway
135                    $whereclause .= ' IS NULL';
136                } else {
137                    $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
138                }
139            }
140        } else {
141            if ($keyval === null) {
142                $keyval = @$this->{$this->_keycolumn};
143            }
144            $whereclause = $this->_keycolumn;
145            if (is_null($keyval)) {
146                // there's not much point in having a NULL key,
147                // but we support it anyway
148                $whereclause .= ' IS NULL';
149            } else {
150                $whereclause .= ' = ' . $this->_dbh->quote($keyval);
151            }
152        }
153        return $whereclause;
154    }
155
156    // }}}
157    // {{{ setup()
158
159    /**
160     * Method used to initialize a DB_storage object from the
161     * configured table.
162     *
163     * @param $keyval mixed the key[s] of the row to fetch (string or array)
164     *
165     * @return int DB_OK on success, a DB error if not
166     */
167    function setup($keyval)
168    {
169        $whereclause = $this->_makeWhere($keyval);
170        $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
171        $sth = $this->_dbh->query($query);
172        if (DB::isError($sth)) {
173            return $sth;
174        }
175        $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
176        if (DB::isError($row)) {
177            return $row;
178        }
179        if (!$row) {
180            return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
181                                     $query, null, true);
182        }
183        foreach ($row as $key => $value) {
184            $this->_properties[$key] = true;
185            $this->$key = $value;
186        }
187        return DB_OK;
188    }
189
190    // }}}
191    // {{{ insert()
192
193    /**
194     * Create a new (empty) row in the configured table for this
195     * object.
196     */
197    function insert($newpk)
198    {
199        if (is_array($this->_keycolumn)) {
200            $primarykey = $this->_keycolumn;
201        } else {
202            $primarykey = array($this->_keycolumn);
203        }
204        settype($newpk, "array");
205        for ($i = 0; $i < sizeof($primarykey); $i++) {
206            $pkvals[] = $this->_dbh->quote($newpk[$i]);
207        }
208
209        $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
210                                  implode(",", $primarykey) . ") VALUES(" .
211                                  implode(",", $pkvals) . ")");
212        if (DB::isError($sth)) {
213            return $sth;
214        }
215        if (sizeof($newpk) == 1) {
216            $newpk = $newpk[0];
217        }
218        $this->setup($newpk);
219    }
220
221    // }}}
222    // {{{ toString()
223
224    /**
225     * Output a simple description of this DB_storage object.
226     * @return string object description
227     */
228    function toString()
229    {
230        $info = strtolower(get_class($this));
231        $info .= " (table=";
232        $info .= $this->_table;
233        $info .= ", keycolumn=";
234        if (is_array($this->_keycolumn)) {
235            $info .= "(" . implode(",", $this->_keycolumn) . ")";
236        } else {
237            $info .= $this->_keycolumn;
238        }
239        $info .= ", dbh=";
240        if (is_object($this->_dbh)) {
241            $info .= $this->_dbh->toString();
242        } else {
243            $info .= "null";
244        }
245        $info .= ")";
246        if (sizeof($this->_properties)) {
247            $info .= " [loaded, key=";
248            $keyname = $this->_keycolumn;
249            if (is_array($keyname)) {
250                $info .= "(";
251                for ($i = 0; $i < sizeof($keyname); $i++) {
252                    if ($i > 0) {
253                        $info .= ",";
254                    }
255                    $info .= $this->$keyname[$i];
256                }
257                $info .= ")";
258            } else {
259                $info .= $this->$keyname;
260            }
261            $info .= "]";
262        }
263        if (sizeof($this->_changes)) {
264            $info .= " [modified]";
265        }
266        return $info;
267    }
268
269    // }}}
270    // {{{ dump()
271
272    /**
273     * Dump the contents of this object to "standard output".
274     */
275    function dump()
276    {
277        foreach ($this->_properties as $prop => $foo) {
278            print "$prop = ";
279            print htmlentities($this->$prop);
280            print "<br />\n";
281        }
282    }
283
284    // }}}
285    // {{{ &create()
286
287    /**
288     * Static method used to create new DB storage objects.
289     * @param $data assoc. array where the keys are the names
290     *              of properties/columns
291     * @return object a new instance of DB_storage or a subclass of it
292     */
293    function &create($table, &$data)
294    {
295        $classname = strtolower(get_class($this));
296        $obj =& new $classname($table);
297        foreach ($data as $name => $value) {
298            $obj->_properties[$name] = true;
299            $obj->$name = &$value;
300        }
301        return $obj;
302    }
303
304    // }}}
305    // {{{ loadFromQuery()
306
307    /**
308     * Loads data into this object from the given query.  If this
309     * object already contains table data, changes will be saved and
310     * the object re-initialized first.
311     *
312     * @param $query SQL query
313     *
314     * @param $params parameter list in case you want to use
315     * prepare/execute mode
316     *
317     * @return int DB_OK on success, DB_WARNING_READ_ONLY if the
318     * returned object is read-only (because the object's specified
319     * key column was not found among the columns returned by $query),
320     * or another DB error code in case of errors.
321     */
322// XXX commented out for now
323/*
324    function loadFromQuery($query, $params = null)
325    {
326        if (sizeof($this->_properties)) {
327            if (sizeof($this->_changes)) {
328                $this->store();
329                $this->_changes = array();
330            }
331            $this->_properties = array();
332        }
333        $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
334        if (DB::isError($rowdata)) {
335            return $rowdata;
336        }
337        reset($rowdata);
338        $found_keycolumn = false;
339        while (list($key, $value) = each($rowdata)) {
340            if ($key == $this->_keycolumn) {
341                $found_keycolumn = true;
342            }
343            $this->_properties[$key] = true;
344            $this->$key = &$value;
345            unset($value); // have to unset, or all properties will
346                           // refer to the same value
347        }
348        if (!$found_keycolumn) {
349            $this->_readonly = true;
350            return DB_WARNING_READ_ONLY;
351        }
352        return DB_OK;
353    }
354 */
355
356    // }}}
357    // {{{ set()
358
359    /**
360     * Modify an attriute value.
361     */
362    function set($property, $newvalue)
363    {
364        // only change if $property is known and object is not
365        // read-only
366        if ($this->_readonly) {
367            return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
368                                     null, null, null, true);
369        }
370        if (@isset($this->_properties[$property])) {
371            if (empty($this->_validator)) {
372                $valid = true;
373            } else {
374                $valid = @call_user_func($this->_validator,
375                                         $this->_table,
376                                         $property,
377                                         $newvalue,
378                                         $this->$property,
379                                         $this);
380            }
381            if ($valid) {
382                $this->$property = $newvalue;
383                if (empty($this->_changes[$property])) {
384                    $this->_changes[$property] = 0;
385                } else {
386                    $this->_changes[$property]++;
387                }
388            } else {
389                return $this->raiseError(null, DB_ERROR_INVALID, null,
390                                         null, "invalid field: $property",
391                                         null, true);
392            }
393            return true;
394        }
395        return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
396                                 null, "unknown field: $property",
397                                 null, true);
398    }
399
400    // }}}
401    // {{{ &get()
402
403    /**
404     * Fetch an attribute value.
405     *
406     * @param string attribute name
407     *
408     * @return attribute contents, or null if the attribute name is
409     * unknown
410     */
411    function &get($property)
412    {
413        // only return if $property is known
414        if (isset($this->_properties[$property])) {
415            return $this->$property;
416        }
417        $tmp = null;
418        return $tmp;
419    }
420
421    // }}}
422    // {{{ _DB_storage()
423
424    /**
425     * Destructor, calls DB_storage::store() if there are changes
426     * that are to be kept.
427     */
428    function _DB_storage()
429    {
430        if (sizeof($this->_changes)) {
431            $this->store();
432        }
433        $this->_properties = array();
434        $this->_changes = array();
435        $this->_table = null;
436    }
437
438    // }}}
439    // {{{ store()
440
441    /**
442     * Stores changes to this object in the database.
443     *
444     * @return DB_OK or a DB error
445     */
446    function store()
447    {
448        foreach ($this->_changes as $name => $foo) {
449            $params[] = &$this->$name;
450            $vars[] = $name . ' = ?';
451        }
452        if ($vars) {
453            $query = 'UPDATE ' . $this->_table . ' SET ' .
454                implode(', ', $vars) . ' WHERE ' .
455                $this->_makeWhere();
456            $stmt = $this->_dbh->prepare($query);
457            $res = $this->_dbh->execute($stmt, $params);
458            if (DB::isError($res)) {
459                return $res;
460            }
461            $this->_changes = array();
462        }
463        return DB_OK;
464    }
465
466    // }}}
467    // {{{ remove()
468
469    /**
470     * Remove the row represented by this object from the database.
471     *
472     * @return mixed DB_OK or a DB error
473     */
474    function remove()
475    {
476        if ($this->_readonly) {
477            return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
478                                     null, null, null, true);
479        }
480        $query = 'DELETE FROM ' . $this->_table .' WHERE '.
481            $this->_makeWhere();
482        $res = $this->_dbh->query($query);
483        if (DB::isError($res)) {
484            return $res;
485        }
486        foreach ($this->_properties as $prop => $foo) {
487            unset($this->$prop);
488        }
489        $this->_properties = array();
490        $this->_changes = array();
491        return DB_OK;
492    }
493
494    // }}}
495}
496
497/*
498 * Local variables:
499 * tab-width: 4
500 * c-basic-offset: 4
501 * End:
502 */
503
504?>
Note: See TracBrowser for help on using the repository browser.