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