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