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