/usr/share/php/Horde/Rdo/Base.php is in php-horde-rdo 2.0.2-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | <?php
/**
* @category Horde
* @package Rdo
*/
/**
* Horde_Rdo_Base abstract class (Rampage Data Objects). Entity
* classes extend this baseline.
*
* @category Horde
* @package Rdo
*/
abstract class Horde_Rdo_Base implements IteratorAggregate, ArrayAccess
{
/**
* The Horde_Rdo_Mapper instance associated with this Rdo object. The
* Mapper takes care of all backend access.
*
* @see Horde_Rdo_Mapper
* @var Horde_Rdo_Mapper
*/
protected $_mapper;
/**
* This object's fields.
*
* @var array
*/
protected $_fields = array();
/**
* Constructor. Can be called directly by a programmer, or is
* called in Horde_Rdo_Mapper::map(). Takes an associative array
* of initial object values.
*
* @param array $fields Initial values for the new object.
*
* @see Horde_Rdo_Mapper::map()
*/
public function __construct($fields = array())
{
$this->setFields($fields);
}
/**
* When Rdo objects are cloned, unset the unique id that
* identifies them so that they can be modified and saved to the
* backend as new objects. If you don't really want a new object,
* don't clone.
*/
public function __clone()
{
// @TODO Support composite primary keys
unset($this->{$this->getMapper()->primaryKey});
// @TODO What about associated objects?
}
/**
* Fetch fields that haven't yet been loaded. Lazy-loaded fields
* and lazy-loaded relationships are handled this way. Once a
* field is retrieved, it is cached in the $_fields array so it
* doesn't need to be fetched again.
*
* @param string $field The name of the field to access.
*
* @return mixed The value of $field or null.
*/
public function __get($field)
{
// Honor any explicit getters.
$fieldMethod = 'get' . ucfirst($field);
// If an Rdo_Base subclass has a __call() method, is_callable
// returns true on every method name, so use method_exists
// instead.
if (method_exists($this, $fieldMethod)) {
return call_user_func(array($this, $fieldMethod));
}
if (isset($this->_fields[$field])) {
return $this->_fields[$field];
}
$mapper = $this->getMapper();
// Look for lazy fields first, then relationships.
if (in_array($field, $mapper->lazyFields)) {
// @TODO Support composite primary keys
$query = new Horde_Rdo_Query($mapper);
$query->setFields($field)
->addTest($mapper->primaryKey, '=', $this->{$mapper->primaryKey});
list($sql, $params) = $query->getQuery();
$this->_fields[$field] = $mapper->adapter->selectValue($sql, $params);;
return $this->_fields[$field];
} elseif (isset($mapper->lazyRelationships[$field])) {
$rel = $mapper->lazyRelationships[$field];
} else {
return null;
}
// Try to find the Mapper class for the object the
// relationship is with, and fail if we can't.
if (isset($rel['mapper'])) {
if ($mapper->factory) {
$m = $mapper->factory->create($rel['mapper']);
} else {
// @TODO - should be getting this instance from somewhere
// else external, and not passing the adapter along
// automatically.
$m = new $rel['mapper']($mapper->adapter);
}
} else {
$m = $mapper->tableToMapper($field);
if (is_null($m)) {
return null;
}
}
// Based on the kind of relationship, fetch the appropriate
// objects and fill the cache.
switch ($rel['type']) {
case Horde_Rdo::ONE_TO_ONE:
case Horde_Rdo::MANY_TO_ONE:
if (isset($rel['query'])) {
$query = $this->_fillPlaceholders($rel['query']);
$this->_fields[$field] = $m->findOne($query);
} elseif (!empty($this->{$rel['foreignKey']})) {
$this->_fields[$field] = $m->findOne($this->{$rel['foreignKey']});
if (empty($this->_fields[$field])) {
throw new Horde_Rdo_Exception('The referenced object with key ' . $this->{$rel['foreignKey']} . ' does not exist. Your data is inconsistent');
}
} else {
$this->_fields[$field] = null;
}
break;
case Horde_Rdo::ONE_TO_MANY:
$this->_fields[$field] = $m->find(array($rel['foreignKey'] => $this->{$rel['foreignKey']}));
break;
case Horde_Rdo::MANY_TO_MANY:
$key = $mapper->primaryKey;
$query = new Horde_Rdo_Query();
$on = isset($rel['on']) ? $rel['on'] : $m->primaryKey;
$query->addRelationship($field, array('mapper' => $mapper,
'table' => $rel['through'],
'type' => Horde_Rdo::MANY_TO_MANY,
'query' => array("$m->table.$on" => new Horde_Rdo_Query_Literal($rel['through'] . '.' . $on), $key => $this->$key)));
$this->_fields[$field] = $m->find($query);
break;
}
return $this->_fields[$field];
}
/**
* Implements getter for ArrayAccess interface.
*
* @see __get()
*/
public function offsetGet($field)
{
return $this->__get($field);
}
/**
* Set a field's value.
*
* @param string $field The field to set
* @param mixed $value The field's new value
*/
public function __set($field, $value)
{
// Honor any explicit setters.
$fieldMethod = 'set' . ucfirst($field);
// If an Rdo_Base subclass has a __call() method, is_callable
// returns true on every method name, so use method_exists
// instead.
if (method_exists($this, $fieldMethod)) {
return call_user_func(array($this, $fieldMethod), $value);
}
$this->_fields[$field] = $value;
}
/**
* Implements setter for ArrayAccess interface.
*
* @see __set()
*/
public function offsetSet($field, $value)
{
$this->__set($field, $value);
}
/**
* Allow using isset($rdo->foo) to check for field or
* relationship presence.
*
* @param string $field The field name to check existence of.
*/
public function __isset($field)
{
$m = $this->getMapper();
return isset($this->_fields[$field])
|| isset($m->fields[$field])
|| isset($m->lazyFields[$field])
|| isset($m->relationships[$field])
|| isset($m->lazyRelationships[$field]);
}
/**
* Implements isset() for ArrayAccess interface.
*
* @see __isset()
*/
public function offsetExists($field)
{
return $this->__isset($field);
}
/**
* Allow using unset($rdo->foo) to unset a basic
* field. Relationships cannot be unset in this way.
*
* @param string $field The field name to unset.
*/
public function __unset($field)
{
// @TODO Should unsetting a MANY_TO_MANY relationship remove
// the relationship?
unset($this->_fields[$field]);
}
/**
* Implements unset() for ArrayAccess interface.
*
* @see __unset()
*/
public function offsetUnset($field)
{
$this->__unset($field);
}
/**
* Set field values for the object
*
* @param array $fields Initial values for the new object.
*
* @see Horde_Rdo_Mapper::map()
*/
public function setFields($fields = array())
{
$this->_fields = $fields;
}
/**
* Implement the IteratorAggregate interface. Looping over an Rdo
* object goes through each property of the object in turn.
*
* @return Horde_Rdo_Iterator The Iterator instance.
*/
public function getIterator()
{
return new Horde_Rdo_Iterator($this);
}
/**
* Get a Mapper instance that can be used to manage this
* object. The Mapper instance can come from a few places:
*
* - If the class <RdoClassName>Mapper exists, it will be used
* automatically.
*
* - Any Rdo instance created with Horde_Rdo_Mapper::map() will have a
* $mapper object set automatically.
*
* - Subclasses can override getMapper() to return the correct
* mapper object.
*
* - The programmer can call $rdoObject->setMapper($mapper) to provide a
* mapper object.
*
* A Horde_Rdo_Exception will be thrown if none of these
* conditions are met.
*
* @return Horde_Rdo_Mapper The Mapper instance managing this object.
*/
public function getMapper()
{
if (!$this->_mapper) {
$class = get_class($this) . 'Mapper';
if (class_exists($class)) {
$this->_mapper = new $class();
} else {
throw new Horde_Rdo_Exception('No Horde_Rdo_Mapper object found. Override getMapper() or define the ' . get_class($this) . 'Mapper class.');
}
}
return $this->_mapper;
}
/**
* Associate this Rdo object with the Mapper instance that will
* manage it. Called automatically by Horde_Rdo_Mapper:map().
*
* @param Horde_Rdo_Mapper $mapper The Mapper to manage this Rdo object.
*
* @see Horde_Rdo_Mapper::map()
*/
public function setMapper($mapper)
{
$this->_mapper = $mapper;
}
/**
* Adds a relation to one of the relationships defined in the mapper.
*
* - For one-to-one relations, simply updates the relation field.
* - For one-to-many relations, updates the related object's relation field.
* - For many-to-many relations, adds an entry in the "through" table.
* - Performs a no-op if the peer is already related.
*
* This is a proxy to the mapper's addRelation() method.
*
* @param string $relationship The relationship key in the mapper.
* @param Horde_Rdo_Base $peer The object to add the relation.
*
* @throws Horde_Rdo_Exception
*/
public function addRelation($relationship, Horde_Rdo_Base $peer)
{
$this->mapper->addRelation($relationship, $this, $peer);
}
/**
* Checks whether a relation to a peer is defined through one of the
* relationships in the mapper.
*
* @param string $relationship The relationship key in the mapper.
* @param Horde_Rdo_Base $peer The object to check for the relation.
* If this is null, check if there is any peer
* for this relation.
*
* @return boolean True if related.
* @throws Horde_Rdo_Exception
*/
public function hasRelation($relationship, Horde_Rdo_Base $peer = null)
{
$mapper = $this->getMapper();
if (isset($mapper->relationships[$relationship])) {
$rel = $mapper->relationships[$relationship];
} elseif (isset($mapper->lazyRelationships[$relationship])) {
$rel = $mapper->lazyRelationships[$relationship];
} else {
throw new Horde_Rdo_Exception('The requested relation is not defined in the mapper');
}
$result = $this->$relationship;
switch ($rel['type']) {
case Horde_Rdo::ONE_TO_ONE:
case Horde_Rdo::MANY_TO_ONE:
if (empty($peer) || empty($result)) {
return (bool) $result;
}
$key = $result->mapper->primaryKey;
return $result->$key == $peer->$key;
case Horde_Rdo::ONE_TO_MANY:
case Horde_Rdo::MANY_TO_MANY:
if (empty($peer)) {
return (bool) count($result);
}
$key = $peer->mapper->primaryKey;
foreach ($result as $item) {
if ($item->$key == $peer->$key) {
return true;
}
}
break;
}
return false;
}
/**
* Removes a relation to one of the relationships defined in the mapper.
*
* - For one-to-one and one-to-many relations, simply sets the relation
* field to 0.
* - For many-to-many, either deletes all relations to this object or just
* the relation to a given peer object.
* - Performs a no-op if the peer is already unrelated.
*
* This is a proxy to the mapper's removeRelation method.
*
* @param string $relationship The relationship key in the mapper
* @param Horde_Rdo_Base $peer The object to remove from the relation
* @return integer The number of relations affected
* @throws Horde_Rdo_Exception
*/
public function removeRelation($relationship, Horde_Rdo_Base $peer = null)
{
return $this->mapper->removeRelation($relationship, $this, $peer);
}
/**
* Save any changes to the backend.
*
* @return boolean Success.
*/
public function save()
{
return $this->getMapper()->update($this) == 1;
}
/**
* Delete this object from the backend.
*
* @return boolean Success or failure.
*/
public function delete()
{
return $this->getMapper()->delete($this) == 1;
}
/**
* Take a query array and replace @field@ placeholders with values
* from this object.
*
* @param array $query The query to process placeholders on.
*
* @return array The query with placeholders filled in.
*/
protected function _fillPlaceholders($query)
{
foreach (array_keys($query) as $field) {
$value = $query[$field];
if (preg_match('/^@(.*)@$/', $value, $matches)) {
$query[$field] = $this->{$matches[1]};
}
}
return $query;
}
}
|