Редакция 2 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
2 | alex-w | 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-2007 The PHP Group |
||
20 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
21 | * @version CVS: $Id: storage.php,v 1.24 2007/08/12 05:27:25 aharvey Exp $ |
||
22 | * @link http://pear.php.net/package/DB |
||
23 | */ |
||
24 | |||
25 | /** |
||
26 | * Obtain the DB class so it can be extended from |
||
27 | */ |
||
28 | require_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-2007 The PHP Group |
||
40 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
41 | * @version Release: 1.7.13 |
||
42 | * @link http://pear.php.net/package/DB |
||
43 | */ |
||
44 | class 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 | $params = array(); |
||
449 | $vars = array(); |
||
450 | foreach ($this->_changes as $name => $foo) { |
||
451 | $params[] = &$this->$name; |
||
452 | $vars[] = $name . ' = ?'; |
||
453 | } |
||
454 | if ($vars) { |
||
455 | $query = 'UPDATE ' . $this->_table . ' SET ' . |
||
456 | implode(', ', $vars) . ' WHERE ' . |
||
457 | $this->_makeWhere(); |
||
458 | $stmt = $this->_dbh->prepare($query); |
||
459 | $res = $this->_dbh->execute($stmt, $params); |
||
460 | if (DB::isError($res)) { |
||
461 | return $res; |
||
462 | } |
||
463 | $this->_changes = array(); |
||
464 | } |
||
465 | return DB_OK; |
||
466 | } |
||
467 | |||
468 | // }}} |
||
469 | // {{{ remove() |
||
470 | |||
471 | /** |
||
472 | * Remove the row represented by this object from the database. |
||
473 | * |
||
474 | * @return mixed DB_OK or a DB error |
||
475 | */ |
||
476 | function remove() |
||
477 | { |
||
478 | if ($this->_readonly) { |
||
479 | return $this->raiseError(null, DB_WARNING_READ_ONLY, null, |
||
480 | null, null, null, true); |
||
481 | } |
||
482 | $query = 'DELETE FROM ' . $this->_table .' WHERE '. |
||
483 | $this->_makeWhere(); |
||
484 | $res = $this->_dbh->query($query); |
||
485 | if (DB::isError($res)) { |
||
486 | return $res; |
||
487 | } |
||
488 | foreach ($this->_properties as $prop => $foo) { |
||
489 | unset($this->$prop); |
||
490 | } |
||
491 | $this->_properties = array(); |
||
492 | $this->_changes = array(); |
||
493 | return DB_OK; |
||
494 | } |
||
495 | |||
496 | // }}} |
||
497 | } |
||
498 | |||
499 | /* |
||
500 | * Local variables: |
||
501 | * tab-width: 4 |
||
502 | * c-basic-offset: 4 |
||
503 | * End: |
||
504 | */ |
||
505 | |||
506 | ?> |