[ Index ]
 

Code source de Horde 3.1.3

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/Horde/ -> DataTree.php (source)

   1  <?php
   2  
   3  /**
   4   * List every object in an array, similar to PEAR/html/menu.php.
   5   */
   6  define('DATATREE_FORMAT_TREE', 1);
   7  
   8  /**
   9   * Get a full list - an array of keys.
  10   */
  11  define('DATATREE_FORMAT_FLAT', 2);
  12  
  13  /**
  14   * The root element (top-level parent) of each DataTree group.
  15   */
  16  define('DATATREE_ROOT', -1);
  17  
  18  /**
  19   * Build a normal select query.
  20   */
  21  define('DATATREE_BUILD_SELECT', 0);
  22  
  23  /**
  24   * Build a count only query.
  25   */
  26  define('DATATREE_BUILD_COUNT', 1);
  27  
  28  /**
  29   * The DataTree:: class provides a common abstracted interface into the
  30   * various backends for the Horde DataTree system.
  31   *
  32   * A piece of data is just a title that is saved in the page for the null
  33   * driver or can be saved in a database to be accessed from everywhere. Every
  34   * stored object must have a different name (inside each groupid).
  35   *
  36   * Required values for $params:<pre>
  37   *   'group' -- Define each group of objects we want to build.</pre>
  38   *
  39   * $Horde: framework/DataTree/DataTree.php,v 1.151.2.18 2006/07/13 08:58:44 jan Exp $
  40   *
  41   * Copyright 1999-2003 Stephane Huther <shuther@bigfoot.com>
  42   * Copyright 2001-2006 Chuck Hagenbuch <chuck@horde.org>
  43   *
  44   * See the enclosed file COPYING for license information (LGPL). If you did
  45   * not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  46   *
  47   * @author  Stephane Huther <shuther@bigfoot.com>
  48   * @author  Chuck Hagenbuch <chuck@horde.org>
  49   * @since   Horde 2.1
  50   * @package Horde_DataTree
  51   */
  52  class DataTree {
  53  
  54      /**
  55       * Array of all data: indexed by id. The format is: array(id =>
  56       * 'name' => name, 'parent' => parent).
  57       *
  58       * @var array
  59       */
  60      var $_data = array();
  61  
  62      /**
  63       * A hash that can be used to map a full object name
  64       * (parent:child:object) to that object's unique ID.
  65       *
  66       * @var array
  67       */
  68      var $_nameMap = array();
  69  
  70       /**
  71       * Actual attribute sorting hash.
  72       *
  73       * @var array
  74       */
  75      var $_sortHash = null;
  76  
  77      /**
  78       * Hash containing connection parameters.
  79       *
  80       * @var array
  81       */
  82      var $_params = array();
  83  
  84      /**
  85       * Constructor.
  86       *
  87       * @param array $params  A hash containing any additional configuration or
  88       *                       connection parameters a subclass might need.
  89       *                       We always need 'group', a string that defines the
  90       *                       prefix for each set of hierarchical data.
  91       */
  92      function DataTree($params = null)
  93      {
  94          $this->_params = $params;
  95      }
  96  
  97      /**
  98       * Returns a parameter of this DataTree instance.
  99       *
 100       * @param string $param  The parameter to return.
 101       *
 102       * @return mixed  The parameter's value or null if it doesn't exist.
 103       */
 104      function getParam($param)
 105      {
 106          return isset($this->_params[$param]) ? $this->_params[$param] : null;
 107      }
 108  
 109      /**
 110       * Removes an object.
 111       *
 112       * @param string $object  The object to remove.
 113       * @param $force          Force removal of every child object?
 114       */
 115      function remove($object, $force = false)
 116      {
 117          if (is_a($object, 'DataTreeObject')) {
 118              $object = $object->getName();
 119          }
 120  
 121          if (!$this->exists($object)) {
 122              return PEAR::raiseError($object . ' does not exist');
 123          }
 124  
 125          $children = $this->getNumberOfChildren($object);
 126          if ($children) {
 127              /* TODO: remove children if $force == true */
 128              return PEAR::raiseError(sprintf(_("Cannot remove, %d children exist."), count($children)));
 129          }
 130  
 131          $id = $this->getId($object);
 132          $pid = $this->getParent($object);
 133          $order = $this->_data[$id]['order'];
 134          unset($this->_data[$id]);
 135          unset($this->_nameMap[$id]);
 136  
 137          // Shift down the order positions.
 138          $this->_reorder($pid, $order);
 139  
 140          return $id;
 141      }
 142  
 143      /**
 144       * Removes all DataTree objects owned by a certain user.
 145       *
 146       * @param string $user  A user name.
 147       */
 148      function removeUserData($user)
 149      {
 150          return PEAR::raiseError('not supported');
 151      }
 152  
 153      /**
 154       * Move an object to a new parent.
 155       *
 156       * @param mixed $object      The object to move.
 157       * @param string $newparent  The new parent object. Defaults to the root.
 158       */
 159      function move($object, $newparent = null)
 160      {
 161          $cid = $this->getId($object);
 162          if (is_a($cid, 'PEAR_Error')) {
 163              return PEAR::raiseError(sprintf('Object to move does not exist: %s', $cid->getMessage()));
 164          }
 165  
 166          if (!is_null($newparent)) {
 167              $pid = $this->getId($newparent);
 168              if (is_a($pid, 'PEAR_Error')) {
 169                  return PEAR::raiseError(sprintf('New parent does not exist: %s', $pid->getMessage()));
 170              }
 171          } else {
 172              $pid = DATATREE_ROOT;
 173          }
 174  
 175          $this->_data[$cid]['parent'] = $pid;
 176  
 177          return true;
 178      }
 179  
 180      /**
 181       * Change an object's name.
 182       *
 183       * @param mixed  $old_object       The old object.
 184       * @param string $new_object_name  The new object name.
 185       */
 186      function rename($old_object, $new_object_name)
 187      {
 188          /* Check whether the object exists at all */
 189          if (!$this->exists($old_object)) {
 190              return PEAR::raiseError($old_object . ' does not exist');
 191          }
 192  
 193          /* Check for duplicates - get parent and create new object
 194           * name */
 195          $parent = $this->getName($this->getParent($old_object));
 196          if ($this->exists($parent . ':' . $new_object_name)) {
 197              return PEAR::raiseError('Duplicate name ' . $new_object_name);
 198          }
 199  
 200          /* Replace the old name with the new one in the cache */
 201          $old_object_id = $this->getId($old_object);
 202          $this->_data[$old_object_id]['name'] = $new_object_name;
 203  
 204          return true;
 205      }
 206  
 207      /**
 208       * Changes the order of the children of an object.
 209       *
 210       * @param string $parent  The full id path of the parent object.
 211       * @param mixed $order    If an array it specifies the new positions for
 212       *                        all child objects.
 213       *                        If an integer and $cid is specified, the position
 214       *                        where the child specified by $cid is inserted. If
 215       *                        $cid is not specified, the position gets deleted,
 216       *                        causing the following positions to shift up.
 217       * @param integer $cid    See $order.
 218       */
 219      function reorder($parents, $order = null, $cid = null)
 220      {
 221          return PEAR::raiseError('not supported');
 222      }
 223  
 224      /**
 225       * Change order of children of an object.
 226       *
 227       * @param string $pid     The parent object id string path.
 228       * @param mixed $order    Specific new order position or an array containing
 229       *                        the new positions for the given parent.
 230       * @param integer $cid    If provided indicates insertion of a new child to
 231       *                        the parent to avoid incrementing it when
 232       *                        shifting up all other children's order. If not
 233       *                        provided indicates deletion, so shift all other
 234       *                        positions down one.
 235       */
 236      function _reorder($pid, $order = null, $cid = null)
 237      {
 238          if (!is_array($order) && !is_null($order)) {
 239              // Single update (add/del).
 240              if (is_null($cid)) {
 241                  // No id given so shuffle down.
 242                  foreach ($this->_data as $c_key => $c_val) {
 243                      if ($this->_data[$c_key]['parent'] == $pid
 244                          && $this->_data[$c_key]['order'] > $order) {
 245                          $this->_data[$c_key]['order']--;
 246                      }
 247                  }
 248              } else {
 249                  // We have an id so shuffle up.
 250                  foreach ($this->_data as $c_key => $c_val) {
 251                      if ($c_key != $cid  && $this->_data[$c_key]['parent'] == $pid
 252                          && $this->_data[$c_key]['order'] >= $order) {
 253                          $this->_data[$c_key]['order']++;
 254                      }
 255                  }
 256              }
 257          } elseif (is_array($order) && count($order)) {
 258              // Multi update.
 259              foreach ($order as $order_position => $cid) {
 260                  $this->_data[$cid]['order'] = $order_position;
 261              }
 262          }
 263      }
 264  
 265      /**
 266       * Explicitly set the order for a datatree object.
 267       *
 268       * @abstract
 269       *
 270       * @param integer $id     The datatree object id to change.
 271       * @param integer $order  The new order.
 272       */
 273      function setOrder($id, $order)
 274      {
 275          return PEAR::raiseError('not supported');
 276      }
 277  
 278      /**
 279       * Dynamically determines the object class.
 280       *
 281       * @param array $attributes  The set of attributes that contain the class
 282       *                           information. Defaults to DataTreeObject.
 283       */
 284      function _defineObjectClass($attributes)
 285      {
 286          global $registry;
 287  
 288          $class = 'DataTreeObject';
 289          if (!is_array($attributes)) {
 290              return $class;
 291          }
 292  
 293          foreach ($attributes as $attr) {
 294              if ($attr['name'] == 'DataTree') {
 295                  if ($attr['key'] == 'objectClass') {
 296                      $class = $attr['value'];
 297                      break;
 298                  } elseif ($attr['key'] == 'objectType') {
 299                      $result = explode('/', $attr['value']);
 300                      $class = $registry->callByPackage($result[0], 'defineClass', array('type' => $result[1]));
 301                      break;
 302                  }
 303              }
 304          }
 305  
 306          return $class;
 307      }
 308  
 309      /**
 310       * Returns a DataTreeObject (or subclass) object of the data in the
 311       * object defined by $object.
 312       *
 313       * @param string $object  The object to fetch: 'parent:sub-parent:name'.
 314       * @param string $class   Subclass of DataTreeObject to use. Defaults to
 315       *                        DataTreeObject. Null forces the driver to look
 316       *                        into the attributes table to determine the
 317       *                        subclass to use. If none is found it uses
 318       *                        DataTreeObject.
 319       */
 320      function &getObject($object, $class = 'DataTreeObject')
 321      {
 322          if (empty($object)) {
 323              $error = PEAR::raiseError('No object requested.');
 324              return $error;
 325          }
 326  
 327          $this->_load($object);
 328          if (!$this->exists($object)) {
 329              $error = PEAR::raiseError($object . ' not found.');
 330              return $error;
 331          }
 332  
 333          $use_attributes = is_null($class) || is_callable(array($class, '_fromAttributes'));
 334          if ($use_attributes) {
 335              $attributes = $this->getAttributes($this->getId($object));
 336              if (is_a($attributes, 'PEAR_Error')) {
 337                  return $attributes;
 338              }
 339  
 340              if (is_null($class)) {
 341                  $class = $this->_defineObjectClass($attributes);
 342              }
 343          }
 344  
 345          if (!class_exists($class)) {
 346              $error = PEAR::raiseError($class . ' not found.');
 347              return $error;
 348          }
 349  
 350          $dataOb = &new $class($object);
 351          $dataOb->setDataTree($this);
 352          /* If the class has a _fromAttributes method, load data from
 353           * the attributes backend. */
 354          if ($use_attributes) {
 355              $dataOb->_fromAttributes($attributes);
 356          } else {
 357              /* Otherwise load it from the old data storage field. */
 358              $dataOb->setData($this->getData($this->getId($object)));
 359          }
 360  
 361          $dataOb->setOrder($this->getOrder($object));
 362          return $dataOb;
 363      }
 364  
 365      /**
 366       * Returns a DataTreeObject (or subclass) object of the data in the
 367       * object with the ID $id.
 368       *
 369       * @param integer $id    An object id.
 370       * @param string $class  Subclass of DataTreeObject to use. Defaults to
 371       *                       DataTreeObject. Null forces the driver to look
 372       *                       into the attributes table to determine the
 373       *                       subclass to use. If none is found it uses
 374       *                       DataTreeObject.
 375       */
 376      function &getObjectById($id, $class = 'DataTreeObject')
 377      {
 378          if (empty($id)) {
 379              $object = PEAR::raiseError('No id requested.');
 380              return $object;
 381          }
 382  
 383          $result = $this->_loadById($id);
 384          if (is_a($result, 'PEAR_Error')) {
 385              return $result;
 386          }
 387  
 388          $use_attributes = is_null($class) || is_callable(array($class, '_fromAttributes'));
 389          if ($use_attributes) {
 390              $attributes = $this->getAttributes($id);
 391              if (is_a($attributes, 'PEAR_Error')) {
 392                  return $attributes;
 393              }
 394  
 395              if (is_null($class)) {
 396                  $class = $this->_defineObjectClass($attributes);
 397              }
 398          }
 399  
 400          if (!class_exists($class)) {
 401              return PEAR::raiseError($class . ' not found.');
 402          }
 403  
 404          $name = $this->getName($id);
 405          $dataOb = &new $class($name);
 406          $dataOb->setDataTree($this);
 407          /* If the class has a _fromAttributes method, load data from
 408           * the attributes backend. */
 409          if ($use_attributes) {
 410              $dataOb->_fromAttributes($attributes);
 411          } else {
 412              /* Otherwise load it from the old data storage field. */
 413              $dataOb->setData($this->getData($id));
 414          }
 415  
 416          $dataOb->setOrder($this->getOrder($name));
 417          return $dataOb;
 418      }
 419  
 420      /**
 421       * Returns an array of DataTreeObject (or subclass) objects
 422       * corresponding to the objects in $ids, with the object
 423       * names as the keys of the array.
 424       *
 425       * @param array $ids     An array of object ids.
 426       * @param string $class  Subclass of DataTreeObject to use. Defaults to
 427       *                       DataTreeObject. Null forces the driver to look
 428       *                       into the attributes table to determine the
 429       *                       subclass to use. If none is found it uses
 430       *                       DataTreeObject.
 431       */
 432      function &getObjects($ids, $class = 'DataTreeObject')
 433      {
 434          $result = $this->_loadById($ids);
 435          if (is_a($result, 'PEAR_Error')) {
 436              return $result;
 437          }
 438  
 439          $defineClass = is_null($class);
 440          $attributes = is_null($class) || is_callable(array($class, '_fromAttributes'));
 441  
 442          if ($attributes) {
 443              $data = $this->getAttributes($ids);
 444          } else {
 445              $data = $this->getData($ids);
 446          }
 447  
 448          $objects = array();
 449          foreach ($ids as $id) {
 450              $name = $this->getName($id);
 451              if (!empty($name) && !empty($data[$id])) {
 452                  if ($defineClass) {
 453                      $class = $this->_defineObjectClass($data[$id]);
 454                  }
 455  
 456                  if (!class_exists($class)) {
 457                      return PEAR::raiseError($class . ' not found.');
 458                  }
 459  
 460                  $objects[$name] = &new $class($name);
 461                  $objects[$name]->setDataTree($this);
 462                  if ($attributes) {
 463                      $objects[$name]->_fromAttributes($data[$id]);
 464                  } else {
 465                      $objects[$name]->setData($data[$id]);
 466                  }
 467                  $objects[$name]->setOrder($this->getOrder($name));
 468              }
 469          }
 470  
 471          return $objects;
 472      }
 473  
 474      /**
 475       * Export a list of objects.
 476       *
 477       * @param constant $format      Format of the export
 478       * @param string   $startleaf   The name of the leaf from which we start
 479       *                              the export tree.
 480       * @param boolean  $reload      Re-load the requested chunk? Defaults to
 481       *                              false (only what is currently loaded).
 482       * @param string   $rootname    The label to use for the root element.
 483       *                              Defaults to DATATREE_ROOT.
 484       * @param integer  $maxdepth    The maximum number of levels to return.
 485       *                              Defaults to DATATREE_ROOT, which is no
 486       *                              limit.
 487       * @param boolean  $loadTree    Load a tree starting at $root, or just the
 488       *                              requested level and direct parents?
 489       *                              Defaults to single level.
 490       * @param string   $sortby_name Attribute name to use for sorting.
 491       * @param string   $sortby_key  Attribute key to use for sorting.
 492       * @param integer  $direction   Sort direction:
 493       *                               0 - ascending
 494       *                               1 - descending
 495       *
 496       * @return mixed  The tree representation of the objects, or a PEAR_Error
 497       *                on failure.
 498       */
 499      function get($format, $startleaf = DATATREE_ROOT, $reload = false,
 500                   $rootname = DATATREE_ROOT, $maxdepth = -1, $loadTree = false,
 501                   $sortby_name = null, $sortby_key = null, $direction = 0)
 502      {
 503          /* Set sorting hash */
 504          if (!is_null($sortby_name) || !is_null($sortby_name)) {
 505              $this->_sortHash = DataTree::sortHash($startleaf, $sortby_name, $sortby_key, $direction);
 506          }
 507  
 508          $this->_load($startleaf, $loadTree, $reload, $sortby_name, $sortby_key, $direction);
 509          $out = array();
 510  
 511          switch ($format) {
 512          case DATATREE_FORMAT_TREE:
 513              $startid = $this->getId($startleaf, $maxdepth);
 514              if (is_a($startid, 'PEAR_Error')) {
 515                  return $startid;
 516              }
 517              $this->_extractAllLevelTree($out, $startid, $maxdepth);
 518              break;
 519  
 520          case DATATREE_FORMAT_FLAT:
 521              $startid = $this->getId($startleaf);
 522              if (is_a($startid, 'PEAR_Error')) {
 523                  return $startid;
 524              }
 525              $this->_extractAllLevelList($out, $startid, $maxdepth);
 526              if (!empty($out[DATATREE_ROOT])) {
 527                  $out[DATATREE_ROOT] = $rootname;
 528              }
 529              break;
 530  
 531          default:
 532              return PEAR::raiseError('Not supported');
 533          }
 534  
 535          if (!is_null($this->_sortHash)) {
 536              /* Reset sorting hash. */
 537              $this->_sortHash = null;
 538              /* Reverse since the attribute sorting combined with tree up-ward
 539               * sorting produces a reversed object order. */
 540              $out = array_reverse($out, true);
 541          }
 542  
 543          return $out;
 544      }
 545  
 546      /**
 547       * Counts objects.
 548       *
 549       * @param string $startleaf  The name of the leaf from which we start
 550       *                           counting.
 551       *
 552       * @return integer  The number of the objects below $startleaf.
 553       */
 554      function count($startleaf = DATATREE_ROOT)
 555      {
 556          return $this->_count($startleaf);
 557      }
 558  
 559      /**
 560       * Create attribute sort hash
 561       *
 562       * @since Horde 3.1
 563       *
 564       * @param string  $root         The name of the leaf from which we start
 565       *                              the export tree.
 566       * @param string  $sortby_name  Attribute name to use for sorting.
 567       * @param string  $sortby_key   Attribute key to use for sorting.
 568       * @param integer $direction    Sort direction:
 569       *                              0 - ascending
 570       *                              1 - descending
 571       *
 572       * @return string The sort hash
 573       */
 574      function sortHash($root, $sortby_name = null, $sortby_key = null,
 575                        $direction = 0)
 576      {
 577          return sprintf('%s-%s-%s-%s', $root, $sortby_name, $sortby_key, $direction);
 578      }
 579  
 580      /**
 581       * Export a list of objects just like get() above, but uses an
 582       * object id to fetch the list of objects.
 583       *
 584       * @param constant $format    Format of the export.
 585       * @param string  $startleaf  The id of the leaf from which we start the
 586       *                            export tree.
 587       * @param boolean $reload     Reload the requested chunk? Defaults to
 588       *                            false (only what is currently loaded).
 589       * @param string  $rootname   The label to use for the root element.
 590       *                            Defaults to DATATREE_ROOT.
 591       * @param integer $maxdepth   The maximum number of levels to return
 592       *                            Defaults to -1, which is no limit.
 593       *
 594       * @return mixed  The tree representation of the objects, or a PEAR_Error
 595       *                on failure.
 596       */
 597      function getById($format, $startleaf = DATATREE_ROOT, $reload = false,
 598                       $rootname = DATATREE_ROOT, $maxdepth = -1)
 599      {
 600          $this->_loadById($startleaf);
 601          $out = array();
 602  
 603          switch ($format) {
 604          case DATATREE_FORMAT_TREE:
 605              $this->_extractAllLevelTree($out, $startleaf, $maxdepth);
 606              break;
 607  
 608          case DATATREE_FORMAT_FLAT:
 609              $this->_extractAllLevelList($out, $startleaf, $maxdepth);
 610              if (!empty($out[DATATREE_ROOT])) {
 611                  $out[DATATREE_ROOT] = $rootname;
 612              }
 613              break;
 614  
 615          default:
 616              return PEAR::raiseError('Not supported');
 617          }
 618  
 619          return $out;
 620      }
 621  
 622      /**
 623       * Returns a list of all groups (root nodes) of the data tree.
 624       *
 625       * @return array  The the group IDs
 626       */
 627      function getGroups()
 628      {
 629          return PEAR::raiseError('not supported');
 630      }
 631  
 632      /**
 633       * Retrieve data for an object from the datatree_data field.
 634       *
 635       * @abstract
 636       *
 637       * @param integer $cid  The object id to fetch, or an array of object ids.
 638       */
 639      function getData($cid)
 640      {
 641          return PEAR::raiseError('not supported');
 642      }
 643  
 644      /**
 645       * Import a list of objects. Used by drivers to populate the internal
 646       * $_data array.
 647       *
 648       * @param array   $data     The data to import.
 649       * @param string  $charset  The charset to convert the object name from.
 650       */
 651      function set($data, $charset = null)
 652      {
 653          $cids = array();
 654          foreach ($data as $id => $cat) {
 655              if (!is_null($charset)) {
 656                  $cat[1] = String::convertCharset($cat[1], $charset);
 657              }
 658              $cids[$cat[0]] = $cat[1];
 659              $cparents[$cat[0]] = $cat[2];
 660              $corders[$cat[0]] = $cat[3];
 661              $sorders[$cat[0]] = $id;
 662          }
 663  
 664          foreach ($cids as $id => $name) {
 665              $this->_data[$id]['name'] = $name;
 666              $this->_data[$id]['order'] = $corders[$id];
 667              if (!is_null($this->_sortHash)) {
 668                  $this->_data[$id]['sorter'][$this->_sortHash] = $sorders[$id];
 669              }
 670              if (!empty($cparents[$id])) {
 671                  $parents = explode(':', substr($cparents[$id], 1));
 672                  $par = $parents[count($parents) - 1];
 673                  $this->_data[$id]['parent'] = $par;
 674  
 675                  if (!empty($this->_nameMap[$par])) {
 676                      // If we've already loaded the direct parent of
 677                      // this object, use that to find the full name.
 678                      $this->_nameMap[$id] = $this->_nameMap[$par] . ':' . $name;
 679                  } else {
 680                      // Otherwise, run through parents one by one to
 681                      // build it up.
 682                      $this->_nameMap[$id] = '';
 683                      foreach ($parents as $parID) {
 684                          if (!empty($cids[$parID])) {
 685                              $this->_nameMap[$id] .= ':' . $cids[$parID];
 686                          }
 687                      }
 688                      $this->_nameMap[$id] = substr($this->_nameMap[$id], 1) . ':' . $name;
 689                  }
 690              } else {
 691                  $this->_data[$id]['parent'] = DATATREE_ROOT;
 692                  $this->_nameMap[$id] = $name;
 693              }
 694          }
 695  
 696          return true;
 697      }
 698  
 699      /**
 700       * Extract one level of data for a parent leaf, sorted first by
 701       * their order and then by name. This function is a way to get a
 702       * collection of $leaf's children.
 703       *
 704       * @param string $leaf  Name of the parent from which to start.
 705       *
 706       * @return array
 707       */
 708      function _extractOneLevel($leaf = DATATREE_ROOT)
 709      {
 710          $out = array();
 711          foreach ($this->_data as $id => $vals) {
 712              if ($vals['parent'] == $leaf) {
 713                  $out[$id] = $vals;
 714              }
 715          }
 716  
 717          if (is_null($this->_sortHash)) {
 718              uasort($out, array($this, '_cmp'));
 719          } else {
 720              uasort($out, array($this, '_cmpSorted'));
 721          }
 722  
 723          return $out;
 724      }
 725  
 726      /**
 727       * Extract all levels of data, starting from a given parent
 728       * leaf in the datatree.
 729       *
 730       * @param array $out         This is an iterating function, so $out is
 731       *                           passed by reference to contain the result.
 732       * @param string  $parent    The name of the parent from which to begin.
 733       * @param integer $maxdepth  Max of levels of depth to check.
 734       *
 735       * @note If nothing is returned that means there is no child, but
 736       * don't forget to add the parent if any subsequent operations are
 737       * required!
 738       */
 739      function _extractAllLevelTree(&$out, $parent = DATATREE_ROOT, $maxdepth = -1)
 740      {
 741          if ($maxdepth == 0) {
 742              return false;
 743          }
 744  
 745          $out[$parent] = true;
 746  
 747          $k = $this->_extractOneLevel($parent);
 748          foreach ($k as $object => $v) {
 749              if (!is_array($out[$parent])) {
 750                  $out[$parent] = array();
 751              }
 752              $out[$parent][$object] = true;
 753              $this->_extractAllLevelTree($out[$parent], $object, $maxdepth - 1);
 754          }
 755      }
 756  
 757      /**
 758       * Extract all levels of data, starting from any parent in
 759       * the tree.
 760       *
 761       * Returned array format: array(parent => array(child => true))
 762       *
 763       * @param array $out         This is an iterating function, so $out is
 764       *                           passed by reference to contain the result.
 765       * @param string  $parent    The name of the parent from which to begin.
 766       * @param integer $maxdepth  Max number of levels of depth to check.
 767       */
 768      function _extractAllLevelList(&$out, $parent = DATATREE_ROOT, $maxdepth = -1)
 769      {
 770          if ($maxdepth == 0) {
 771              return false;
 772          }
 773  
 774          // This is redundant most of the time, so make sure we need to
 775          // do it.
 776          if (empty($out[$parent])) {
 777              $out[$parent] = $this->getName($parent);
 778          }
 779  
 780          $k = array_keys($this->_extractOneLevel($parent));
 781          foreach ($k as $object) {
 782              $out[$object] = $this->getName($object);
 783              $this->_extractAllLevelList($out, $object, $maxdepth - 1);
 784          }
 785      }
 786  
 787      /**
 788       * Returns a child's direct parent ID.
 789       *
 790       * @param mixed $child  Either the object, an array containing the
 791       *                      path elements, or the object name for which
 792       *                      to look up the parent's ID.
 793       *
 794       * @return integer  The unique ID of the parent.
 795       */
 796      function getParent($child)
 797      {
 798          if (is_a($child, 'DataTreeObject')) {
 799              $child = $child->getName();
 800          }
 801          $id = $this->getId($child);
 802          if (is_a($id, 'PEAR_Error')) {
 803              return $id;
 804          }
 805  
 806          return $this->_data[$id]['parent'];
 807      }
 808  
 809      /**
 810       * Get a $child's direct parent ID.
 811       *
 812       * @param integer $childId  Get the parent of this object.
 813       *
 814       * @return integer  The unique ID of the parent.
 815       */
 816      function getParentById($childId)
 817      {
 818          $this->_loadById($childId);
 819          return isset($this->_data[$childId]) ?
 820              $this->_data[$childId]['parent'] :
 821              PEAR::raiseError($childId . ' not found');
 822      }
 823  
 824      /**
 825       * Get a list of parents all the way up to the root object for
 826       * $child.
 827       *
 828       * @param mixed   $child   The name of the child
 829       * @param boolean $getids  If true, return parent IDs; otherwise, return
 830       *                         names.
 831       *
 832       * @return array  [child] [parent] in a tree format.
 833       */
 834      function getParents($child, $getids = false)
 835      {
 836          $pid = $this->getParent($child);
 837          if (is_a($pid, 'PEAR_Error')) {
 838              return PEAR::raiseError('Parents not found: ' . $pid->getMessage());
 839          }
 840          $pname = $this->getName($pid);
 841          if ($getids) {
 842              $parents = array($pid => true);
 843          } else {
 844              $parents = array($pname => true);
 845          }
 846  
 847          if ($pid != DATATREE_ROOT) {
 848              if ($getids) {
 849                  $parents[$pid] = $this->getParents($pname, $getids);
 850              } else {
 851                  $parents[$pname] = $this->getParents($pname, $getids);
 852              }
 853          }
 854  
 855          return $parents;
 856      }
 857  
 858      /**
 859       * Get a list of parents all the way up to the root object for
 860       * $child.
 861       *
 862       * @param integer $childId  The id of the child.
 863       * @param array $parents    The array, as we build it up.
 864       *
 865       * @return array  A flat list of all of the parents of $child,
 866       *                hashed in $id => $name format.
 867       */
 868      function getParentList($childId, $parents = array())
 869      {
 870          $pid = $this->getParentById($childId);
 871          if (is_a($pid, 'PEAR_Error')) {
 872              return PEAR::raiseError('Parents not found: ' . $pid->getMessage());
 873          }
 874  
 875          if ($pid != DATATREE_ROOT) {
 876              $parents[$pid] = $this->getName($pid);
 877              $parents = $this->getParentList($pid, $parents);
 878          }
 879  
 880          return $parents;
 881      }
 882  
 883      /**
 884       * Get a parent ID string (id:cid format) for the specified object.
 885       *
 886       * @param mixed $object  The object to return a parent string for.
 887       *
 888       * @return string|PEAR_Error  The ID "path" to the parent object or
 889       *                            PEAR_Error on failure.
 890       */
 891      function getParentIdString($object)
 892      {
 893          $ptree = $this->getParents($object, true);
 894          if (is_a($ptree, 'PEAR_Error')) {
 895              return $ptree;
 896          }
 897  
 898          $pids = '';
 899          while ((list($id, $parent) = each($ptree)) && is_array($parent)) {
 900              $pids = ':' . $id . $pids;
 901              $ptree = $parent;
 902          }
 903  
 904          return $pids;
 905      }
 906  
 907      /**
 908       * Get the number of children an object has, only counting immediate
 909       * children, not grandchildren, etc.
 910       *
 911       * @param mixed $parent  Either the object or the name for which to count
 912       *                       the children, defaults to the root
 913       *                       (DATATREE_ROOT).
 914       *
 915       * @return integer
 916       */
 917      function getNumberOfChildren($parent = DATATREE_ROOT)
 918      {
 919          if (is_a($parent, 'DataTreeObject')) {
 920              $parent = $parent->getName();
 921          }
 922          $this->_load($parent);
 923          $out = $this->_extractOneLevel($this->getId($parent));
 924          return is_array($out) ? count($out) : 0;
 925      }
 926  
 927      /**
 928       * Check if an object exists or not. The root element DATATREE_ROOT always exists.
 929       *
 930       * @param mixed $object  The name of the object.
 931       *
 932       * @return boolean  True if the object exists, false otherwise.
 933       */
 934      function exists($object)
 935      {
 936          if (empty($object)) {
 937              return false;
 938          }
 939          if (is_a($object, 'DataTreeObject')) {
 940              $object = $object->getName();
 941          } elseif (is_array($object)) {
 942              $object = implode(':', $object);
 943          }
 944  
 945          if ($object == DATATREE_ROOT) {
 946              return true;
 947          }
 948  
 949          $idMap = array_flip($this->_nameMap);
 950          if (isset($idMap[$object])) {
 951              return true;
 952          }
 953  
 954          $this->_load($object);
 955          $idMap = array_flip($this->_nameMap);
 956          return isset($idMap[$object]);
 957      }
 958  
 959      /**
 960       * Get the name of an object from its id.
 961       *
 962       * @param integer $id  The id for which to look up the name.
 963       *
 964       * @return string
 965       */
 966      function getName($id)
 967      {
 968          /* If no id or if id is a PEAR error, return null. */
 969          if (empty($id) || is_a($id, 'PEAR_Error')) {
 970              return null;
 971          }
 972  
 973          /* If checking name of root, return DATATREE_ROOT. */
 974          if ($id == DATATREE_ROOT) {
 975              return DATATREE_ROOT;
 976          }
 977  
 978          /* If found in the name map, return the name. */
 979          if (isset($this->_nameMap[$id])) {
 980              return $this->_nameMap[$id];
 981          }
 982  
 983          /* Not found in name map, try loading this id into the name
 984           * map. */
 985          $this->_loadById($id);
 986  
 987          /* If id loaded return the name, otherwise return null. */
 988          return isset($this->_nameMap[$id]) ?
 989              $this->_nameMap[$id] :
 990              null;
 991      }
 992  
 993      /**
 994       * Get the id of an object from its name.
 995       *
 996       * @param mixed $name  Either the object, an array containing the
 997       *                     path elements, or the object name for which
 998       *                     to look up the id.
 999       *
1000       * @return string
1001       */
1002      function getId($name)
1003      {
1004          /* Check if $name is not a string. */
1005          if (is_a($name, 'DataTreeObject')) {
1006              /* DataTreeObject, get the string name. */
1007              $name = $name->getName();
1008          } elseif (is_array($name)) {
1009              /* Path array, implode to get the string name. */
1010              $name = implode(':', $name);
1011          }
1012  
1013          /* If checking id of root, return DATATREE_ROOT. */
1014          if ($name == DATATREE_ROOT) {
1015              return DATATREE_ROOT;
1016          }
1017  
1018          /* Check if the name actually exists, if not return a PEAR
1019           * error. */
1020          if (!$this->exists($name)) {
1021              return PEAR::raiseError($name . ' does not exist');
1022          }
1023  
1024          /* Flip the name map to look up the id using the name as key. */
1025          $idMap = array_flip($this->_nameMap);
1026          return $idMap[$name];
1027      }
1028  
1029      /**
1030       * Get the order position of an object.
1031       *
1032       * @param mixed $child  Either the object or the name.
1033       *
1034       * @return mixed  The object's order position or a PEAR error on failure.
1035       */
1036      function getOrder($child)
1037      {
1038          if (is_a($child, 'DataTreeObject')) {
1039              $child = $child->getName();
1040          }
1041          $id = $this->getId($child);
1042          if (is_a($id, 'PEAR_Error')) {
1043              return $id;
1044          }
1045  
1046          return isset($this->_data[$id]['order']) ?
1047              $this->_data[$id]['order'] :
1048              null;
1049      }
1050  
1051      /**
1052       * Replace all occurences of ':' in an object name with '.'.
1053       *
1054       * @param string $name  The name of the object.
1055       *
1056       * @return string  The encoded name.
1057       */
1058      function encodeName($name)
1059      {
1060          return str_replace(':', '.', $name);
1061      }
1062  
1063      /**
1064       * Get the short name of an object, returns only the last portion of the
1065       * full name. For display purposes only.
1066       *
1067       * @static
1068       *
1069       * @param string $name  The name of the object.
1070       *
1071       * @return string  The object's short name.
1072       */
1073      function getShortName($name)
1074      {
1075          /* If there are several components to the name, explode and get the
1076           * last one, otherwise just return the name. */
1077          if (strpos($name, ':') !== false) {
1078              $name = explode(':', $name);
1079              $name = array_pop($name);
1080          }
1081          return $name;
1082      }
1083  
1084      /**
1085       * Returns a tree sorted by the specified attribute name and/or key.
1086       *
1087       * @abstract
1088       *
1089       * @since Horde 3.1
1090       *
1091       * @param string $root         Which portion of the tree to sort.
1092       *                             Defaults to all of it.
1093       * @param boolean $loadTree    Sort the tree starting at $root, or just the
1094       *                             requested level and direct parents?
1095       *                             Defaults to single level.
1096       * @param string $sortby_name  Attribute name to use for sorting.
1097       * @param string $sortby_key   Attribute key to use for sorting.
1098       * @param integer $direction   Sort direction:
1099       *                             0 - ascending
1100       *                             1 - descending
1101       */
1102      function getSortedTree($root, $loadTree = false, $sortby_name = null,
1103                             $sortby_key = null, $direction = 0)
1104      {
1105          return PEAR::raiseError('not supported');
1106      }
1107  
1108      /**
1109       * Adds an object.
1110       *
1111       * @abstract
1112       *
1113       * @param mixed $object        The object to add (string or
1114       *                             DataTreeObject).
1115       * @param boolean $id_as_name  True or false to indicate if object ID is to
1116       *                             be used as object name. Used in situations
1117       *                             where there is no available unique input for
1118       *                             object name.
1119       */
1120      function add($object, $id_as_name = false)
1121      {
1122          return PEAR::raiseError('not supported');
1123      }
1124  
1125      /**
1126       * Add an object.
1127       *
1128       * @param string   $name  The short object name.
1129       * @param integer  $id    The new object's unique ID.
1130       * @param integer  $pid   The unique ID of the object's parent.
1131       * @param integer  $order The ordering data for the object.
1132       *
1133       * @access protected
1134       */
1135      function _add($name, $id, $pid, $order = '')
1136      {
1137          $this->_data[$id] = array('name' => $name,
1138                                    'parent' => $pid,
1139                                    'order' => $order);
1140          $this->_nameMap[$id] = $name;
1141  
1142          /* Shift along the order positions. */
1143          $this->_reorder($pid, $order, $id);
1144  
1145          return true;
1146      }
1147  
1148      /**
1149       * Retrieve data for an object from the horde_datatree_attributes
1150       * table.
1151       *
1152       * @abstract
1153       *
1154       * @param integer | array $cid  The object id to fetch,
1155       *                              or an array of object ids.
1156       *
1157       * @return array  A hash of attributes, or a multi-level hash
1158       *                of object ids => their attributes.
1159       */
1160      function getAttributes($cid)
1161      {
1162          return PEAR::raiseError('not supported');
1163      }
1164  
1165      /**
1166       * Returns the number of objects matching a set of attribute criteria.
1167       *
1168       * @abstract
1169       *
1170       * @see buildAttributeQuery()
1171       *
1172       * @param array   $criteria   The array of criteria.
1173       * @param string  $parent     The parent node to start searching from.
1174       * @param boolean $allLevels  Return all levels, or just the direct
1175       *                            children of $parent? Defaults to all levels.
1176       * @param string  $restrict   Only return attributes with the same
1177       *                            attribute_name or attribute_id.
1178       */
1179      function countByAttributes($criteria, $parent = DATATREE_ROOT,
1180                                 $allLevels = true, $restrict = 'name')
1181      {
1182          return PEAR::raiseError('not supported');
1183      }
1184  
1185      /**
1186       * Returns a set of object ids based on a set of attribute criteria.
1187       *
1188       * @abstract
1189       *
1190       * @see buildAttributeQuery()
1191       *
1192       * @param array   $criteria     The array of criteria.
1193       * @param string  $parent       The parent node to start searching from.
1194       * @param boolean $allLevels    Return all levels, or just the direct
1195       *                              children of $parent? Defaults to all levels.
1196       * @param string  $restrict     Only return attributes with the same
1197       *                              attribute_name or attribute_id.
1198       * @param integer $from         The object to start to fetching
1199       * @param integer $count        The number of objects to fetch
1200       * @param string  $sortby_name  Attribute name to use for sorting.
1201       * @param string  $sortby_key   Attribute key to use for sorting.
1202       * @param integer $direction    Sort direction:
1203       *                                0 - ascending
1204       *                                1 - descending
1205       */
1206      function getByAttributes($criteria, $parent = DATATREE_ROOT,
1207                               $allLevels = true, $restrict = 'name', $from = 0,
1208                               $count = 0, $sortby_name = null,
1209                               $sortby_key = null, $direction = 0)
1210      {
1211          return PEAR::raiseError('not supported');
1212      }
1213  
1214      /**
1215       * Sorts IDs by attribute values. IDs without attributes will be added to
1216       * the end of the sorted list.
1217       *
1218       * @abstract
1219       *
1220       * @param array $unordered_ids  Array of ids to sort.
1221       * @param array $sortby_name    Attribute name to use for sorting.
1222       * @param array $sortby_key     Attribute key to use for sorting.
1223       * @param array $direction      Sort direction:
1224       *                                0 - ascending
1225       *                                1 - descending
1226       *
1227       * @return array  Sorted ids.
1228       */
1229      function sortByAttributes($unordered_ids, $sortby_name = null,
1230                                $sortby_key = null, $direction = 0)
1231      {
1232          return PEAR::raiseError('not supported');
1233      }
1234  
1235      /**
1236       * Update the data in an object. Does not change the object's
1237       * parent or name, just serialized data or attributes.
1238       *
1239       * @abstract
1240       *
1241       * @param DataTree $object  A DataTree object.
1242       */
1243      function updateData($object)
1244      {
1245          return PEAR::raiseError('not supported');
1246      }
1247  
1248      /**
1249       * Sort two objects by their order field, and if that is the same,
1250       * alphabetically (case insensitive) by name.
1251       *
1252       * You never call this function; it's used in uasort() calls. Do
1253       * NOT use usort(); you'll lose key => value associations.
1254       *
1255       * @param array $a  The first object
1256       * @param array $b  The second object
1257       *
1258       * @return integer  1 if $a should be first,
1259       *                 -1 if $b should be first,
1260       *                  0 if they are entirely equal.
1261       */
1262      function _cmp($a, $b)
1263      {
1264          if ($a['order'] > $b['order']) {
1265              return 1;
1266          } elseif ($a['order'] < $b['order']) {
1267              return -1;
1268          } else {
1269              return strcasecmp($a['name'], $b['name']);
1270          }
1271      }
1272  
1273       /**
1274       * Sorts two objects by their sorter hash field.
1275       *
1276       * You never call this function; it's used in uasort() calls. Do NOT use
1277       * usort(); you'll lose key => value associations.
1278       *
1279       * @since Horde 3.1
1280       *
1281       * @param array $a  The first object
1282       * @param array $b  The second object
1283       *
1284       * @return integer  1 if $a should be first,
1285       *                 -1 if $b should be first,
1286       *                  0 if they are entirely equal.
1287       */
1288      function _cmpSorted($a, $b)
1289      {
1290          if ($a['sorter'][$this->_sortHash] < $b['sorter'][$this->_sortHash]) {
1291              return 1;
1292          } else {
1293              return 0;
1294          }
1295      }
1296  
1297      /**
1298       * Attempts to return a concrete DataTree instance based on $driver.
1299       *
1300       * @param mixed $driver  The type of concrete DataTree subclass to return.
1301       *                       This is based on the storage driver ($driver). The
1302       *                       code is dynamically included. If $driver is an array,
1303       *                       then we will look in $driver[0]/lib/DataTree/ for
1304       *                       the subclass implementation named $driver[1].php.
1305       * @param array $params  A hash containing any additional configuration or
1306       *                       connection parameters a subclass might need.
1307       *                       Here, we need 'group' = a string that defines
1308       *                       top-level groups of objects.
1309       *
1310       * @return DataTree  The newly created concrete DataTree instance, or false
1311       *                   on an error.
1312       */
1313      function &factory($driver, $params = null)
1314      {
1315          $driver = basename($driver);
1316  
1317          if (is_null($params)) {
1318              $params = Horde::getDriverConfig('datatree', $driver);
1319          }
1320  
1321          if (empty($driver)) {
1322              $driver = 'null';
1323          }
1324  
1325          include_once 'Horde/DataTree/' . $driver . '.php';
1326          $class = 'DataTree_' . $driver;
1327          if (class_exists($class)) {
1328              $dt = &new $class($params);
1329          } else {
1330              $dt = PEAR::raiseError('Class definition of ' . $class . ' not found.');
1331          }
1332   
1333          return $dt;
1334      }
1335  
1336      /**
1337       * Attempts to return a reference to a concrete DataTree instance based on
1338       * $driver.
1339       *
1340       * It will only create a new instance if no DataTree instance with the same
1341       * parameters currently exists.
1342       *
1343       * This should be used if multiple DataTree sources (and, thus, multiple
1344       * DataTree instances) are required.
1345       *
1346       * This method must be invoked as: $var = &DataTree::singleton();
1347       *
1348       * @param mixed $driver  Type of concrete DataTree subclass to return,
1349       *                       based on storage driver ($driver). The code is
1350       *                       dynamically included. If $driver is an array, then
1351       *                       look in $driver[0]/lib/DataTree/ for subclass
1352       *                       implementation named $driver[1].php.
1353       * @param array $params  A hash containing any additional configuration or
1354       *                       connection parameters a subclass might need.
1355       *
1356       * @return DataTree  The concrete DataTree reference, or false on an error.
1357       */
1358      function &singleton($driver, $params = null)
1359      {
1360          static $instances;
1361          if (!isset($instances)) {
1362              $instances = array();
1363          }
1364  
1365          if (is_null($params)) {
1366              $params = Horde::getDriverConfig('datatree', $driver);
1367          }
1368  
1369          $signature = serialize(array($driver, $params));
1370          if (!isset($instances[$signature])) {
1371              $instances[$signature] = &DataTree::factory($driver, $params);
1372          }
1373  
1374          return $instances[$signature];
1375      }
1376  
1377  }
1378  
1379  /**
1380   * Class that can be extended to save arbitrary information as part of a stored
1381   * object.
1382   *
1383   * @author  Stephane Huther <shuther@bigfoot.com>
1384   * @author  Chuck Hagenbuch <chuck@horde.org>
1385   * @since   Horde 2.1
1386   * @package Horde_DataTree
1387   */
1388  class DataTreeObject {
1389  
1390      /**
1391       * This object's DataTree instance.
1392       *
1393       * @var DataTree
1394       */
1395      var $datatree;
1396  
1397      /**
1398       * Key-value hash that will be serialized.
1399       *
1400       * @see getData()
1401       * @var array
1402       */
1403      var $data = array();
1404  
1405      /**
1406       * The unique name of this object.
1407       * These names have the same requirements as other object names - they must
1408       * be unique, etc.
1409       *
1410       * @var string
1411       */
1412      var $name;
1413  
1414      /**
1415       * If this object has ordering data, store it here.
1416       *
1417       * @var integer
1418       */
1419      var $order = null;
1420  
1421      /**
1422       * DataTreeObject constructor.
1423       * Just sets the $name parameter.
1424       *
1425       * @param string $name  The object name.
1426       */
1427      function DataTreeObject($name)
1428      {
1429          $this->setName($name);
1430      }
1431  
1432      /**
1433       * Sets the {@link DataTree} instance used to retrieve this object.
1434       *
1435       * @param DataTree $datatree  A {@link DataTree} instance.
1436       */
1437      function setDataTree(&$datatree)
1438      {
1439          $this->datatree = &$datatree;
1440      }
1441  
1442      /**
1443       * Gets the name of this object.
1444       *
1445       * @return string The object name.
1446       */
1447      function getName()
1448      {
1449          return $this->name;
1450      }
1451  
1452      /**
1453       * Sets the name of this object.
1454       *
1455       * NOTE: Use with caution. This may throw out of sync the cached datatree
1456       * tables if not used properly.
1457       *
1458       * @param string $name  The name to set this object's name to.
1459       */
1460      function setName($name)
1461      {
1462          $this->name = $name;
1463      }
1464  
1465      /**
1466       * Gets the short name of this object.
1467       * For display purposes only.
1468       *
1469       * @return string  The object's short name.
1470       */
1471      function getShortName()
1472      {
1473          return DataTree::getShortName($this->name);
1474      }
1475  
1476      /**
1477       * Gets the ID of this object.
1478       *
1479       * @return string  The object's ID.
1480       */
1481      function getId()
1482      {
1483          return $this->datatree->getId($this);
1484      }
1485  
1486      /**
1487       * Gets the data array.
1488       *
1489       * @return array  The internal data array.
1490       */
1491      function getData()
1492      {
1493          return $this->data;
1494      }
1495  
1496      /**
1497       * Sets the data array.
1498       *
1499       * @param array  The data array to store internally.
1500       */
1501      function setData($data)
1502      {
1503          $this->data = $data;
1504      }
1505  
1506      /**
1507       * Sets the order of this object in its object collection.
1508       *
1509       * @param integer $order
1510       */
1511      function setOrder($order)
1512      {
1513          $this->order = $order;
1514      }
1515  
1516      /**
1517       * Returns this object's parent.
1518       *
1519       * @param string $class   Subclass of DataTreeObject to use. Defaults to
1520       *                        DataTreeObject. Null forces the driver to look
1521       *                        into the attributes table to determine the
1522       *                        subclass to use. If none is found it uses
1523       *                        DataTreeObject.
1524       *
1525       * @return DataTreeObject  This object's parent
1526       */
1527      function &getParent($class = 'DataTreeObject')
1528      {
1529          $id = $this->datatree->getParent($this);
1530          if (is_a($id, 'PEAR_Error')) {
1531              return $id;
1532          }
1533          return $this->datatree->getObjectById($id, $class);
1534      }
1535  
1536      /**
1537       * Returns a child of this object.
1538       *
1539       * @param string $name         The child's name.
1540       * @param boolean $autocreate  If true and no child with the given name
1541       *                             exists, one gets created.
1542       */
1543      function &getChild($name, $autocreate = true)
1544      {
1545          $name = $this->getShortName() . ':' . $name;
1546  
1547          /* If the child shouldn't get created, we don't check for its
1548           * existance to return the "not found" error of
1549           * getObject(). */
1550          if (!$autocreate || $this->datatree->exists($name)) {
1551              $child = &$this->datatree->getObject($name);
1552          } else {
1553              $child = &new DataTreeObject($name);
1554              $child->setDataTree($this->datatree);
1555              $this->datatree->add($child);
1556          }
1557  
1558          return $child;
1559      }
1560  
1561      /**
1562       * Saves any changes to this object to the backend permanently. New objects
1563       * are added instead.
1564       *
1565       * @return boolean|PEAR_Error  PEAR_Error on failure.
1566       */
1567      function save()
1568      {
1569          if ($this->datatree->exists($this)) {
1570              return $this->datatree->updateData($this);
1571          } else {
1572              return $this->datatree->add($this);
1573          }
1574      }
1575  
1576      /**
1577       * Delete this object from the backend permanently.
1578       *
1579       * @return boolean|PEAR_Error  PEAR_Error on failure.
1580       */
1581      function delete()
1582      {
1583          return $this->datatree->remove($this);
1584      }
1585  
1586      /**
1587       * Gets one of the attributes of the object, or null if it isn't defined.
1588       *
1589       * @param string $attribute  The attribute to get.
1590       *
1591       * @return mixed  The value of the attribute, or null.
1592       */
1593      function get($attribute)
1594      {
1595          return isset($this->data[$attribute])
1596              ? $this->data[$attribute]
1597              : null;
1598      }
1599  
1600      /**
1601       * Sets one of the attributes of the object.
1602       *
1603       * @param string $attribute  The attribute to set.
1604       * @param mixed $value       The value for $attribute.
1605       */
1606      function set($attribute, $value)
1607      {
1608          $this->data[$attribute] = $value;
1609      }
1610  
1611  }


Généré le : Sun Feb 25 18:01:28 2007 par Balluche grâce à PHPXref 0.7