Tree
[ class tree: Tree ] [ index: Tree ] [ all elements ]

Source for file Memory.php

Documentation is available at Memory.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 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. // | Authors: Wolfram Kriesing <wolfram@kriesing.de>                      |
  17. // +----------------------------------------------------------------------+
  18. //
  19. //  $Id: Memory.php 320808 2011-12-09 20:54:48Z danielc $
  20.  
  21. require_once 'Tree/Common.php';
  22.  
  23. /**
  24.  * this class can be used to step through a tree using ['parent'],['child']
  25.  * the tree is saved as flat data in a db, where at least the parent
  26.  * needs to be given if a previous member is given too then the order
  27.  * on a level can be determined too
  28.  * actually this class was used for a navigation tree
  29.  * now it is extended to serve any kind of tree
  30.  * you can unambigiously refer to any element by using the following
  31.  * syntax
  32.  * tree->data[currentId][<where>]...[<where>]
  33.  * <where> can be either "parent", "child", "next" or "previous", this way
  34.  * you can "walk" from any point to any other point in the tree
  35.  * by using <where> in any order you want
  36.  * example (in parentheses the id):
  37.  * root
  38.  *  +---level 1_1 (1)
  39.  *  |      +----level 2_1 (2)
  40.  *  |      +----level 2_2 (3)
  41.  *  |              +-----level 3_1 (4)
  42.  *  +---level 1_2 (5)
  43.  *
  44.  *  the database table to this structure (without defined order)
  45.  *  id     parentId        name
  46.  *  1         0         level 1_1
  47.  *  2         1         level 2_1
  48.  *  3         1         level 2_1
  49.  *  4         3         level 3_1
  50.  *  5         0         level 1_2
  51.  *
  52.  * now you can refer to elements for example like this (all examples assume you
  53.  * know the structure):
  54.  * go from "level 3_1" to "level 1_1": $tree->data[4]['parent']['parent']
  55.  * go from "level 3_1" to "level 1_2":
  56.  *          $tree->data[4]['parent']['parent']['next']
  57.  * go from "level 2_1" to "level 3_1": $tree->data[2]['next']['child']
  58.  * go from "level 2_2" to "level 2_1": $tree->data[3]['previous']
  59.  * go from "level 1_2" to "level 3_1":
  60.  *          $tree->data[5]['previous']['child']['next']['child']
  61.  *
  62.  * on a pentium 1.9 GHz 512 MB RAM, Linux 2.4, Apache 1.3.19, PHP 4.0.6
  63.  * performance statistics for version 1.26, using examples/Tree/Tree.php
  64.  *  -   reading from DB and preparing took: 0.14958894252777
  65.  *  -   building took: 0.074488043785095
  66.  *  -  buildStructure took: 0.05151903629303
  67.  *  -  setting up the tree time: 0.29579293727875
  68.  *  -  number of elements: 1564
  69.  *  -  deepest level: 17
  70.  * so you can use it for tiny-big trees too :-)
  71.  * but watch the db traffic, which might be considerable, depending
  72.  * on your setup.
  73.  *
  74.  * FIXXXME there is one really bad thing about the entire class, at some points
  75.  * there are references to $this->data returned, or the programmer can even
  76.  * access this->data, which means he can change the structure, since this->data
  77.  * can not be set to read-only, therefore this->data has to be handled
  78.  * with great care!!! never do something like this:
  79.  * <code>
  80.  * $x = &$tree->data[<some-id>]; $x = $y;
  81.  * </code>
  82.  * this overwrites the element in the structure !!!
  83.  *
  84.  *
  85.  * @access   public
  86.  * @author   Wolfram Kriesing <wolfram@kriesing.de>
  87.  * @version  2001/06/27
  88.  * @package  Tree
  89.  */
  90. class Tree_Memory extends Tree_Common
  91. {
  92.     /**
  93.      * this array contains the pure data from the DB
  94.      * which are always kept, since all other structures will
  95.      * only make references on any element
  96.      * and those data are extended by the elements 'parent' 'children' etc...
  97.      * @var    array $data 
  98.      */
  99.     var $data = array();
  100.  
  101.     /**
  102.      * this array contains references to this->data but it
  103.      * additionally represents the directory structure
  104.      * that means the array has as many dimensions as the
  105.      * tree structure has levels
  106.      * but this array is only used internally from outside you can do
  107.      * everything using the node-id's
  108.      *
  109.      * @var    array $structure 
  110.      * @access private
  111.      */
  112.     var $structure = array();
  113.  
  114.     /**
  115.      * it contains all the parents and their children, where the parentId is the
  116.      * key and all the children are the values, this is for speeding up
  117.      * the tree-building process
  118.      *
  119.      * @var    array   $children 
  120.      */
  121.     var $children = array();
  122.  
  123.     /**
  124.      * @access private
  125.      * @var    boolean saves if tree nodes shall be removed recursively
  126.      * @see    setRemoveRecursively()
  127.      */
  128.     var $removeRecursively = false;
  129.  
  130.  
  131.     /**
  132.      * @access public
  133.      * @var integer  the debug mode, if > 0 then debug info are shown,
  134.      *               actually those messages only show performance
  135.      *               times
  136.      */
  137.     var $debug = 0;
  138.  
  139.     /**
  140.      * @see &getNode()
  141.      * @see &_getNode()
  142.      * @access private
  143.      * @var integer variable only used in the method getNode and _getNode
  144.      */
  145.     var $_getNodeMaxLevel;
  146.  
  147.     /**
  148.      * @see    &getNode()
  149.      * @see    &_getNode()
  150.      * @access private
  151.      * @var    integer  variable only used in the method getNode and
  152.      *                   _getNode
  153.      */
  154.     var $_getNodeCurParent;
  155.  
  156.     /**
  157.      * the maximum depth of the tree
  158.      * @access private
  159.      * @var    int     the maximum depth of the tree
  160.      */
  161.     var $_treeDepth = 0;
  162.  
  163.     // {{{ Tree_Memory()
  164.  
  165.     /**
  166.      * set up this object
  167.      *
  168.      * @version   2001/06/27
  169.      * @access    public
  170.      * @author    Wolfram Kriesing <wolfram@kriesing.de>
  171.      * @param mixed   this is a DSN for the PEAR::DB, can be
  172.      *                             either an object/string
  173.      * @param array   additional options you can set
  174.      */
  175.     function Tree_Memory($type$dsn ''$options = array())
  176.     {
  177.         // set the options for $this
  178.         $this->Tree_Options($options);
  179.         include_once "Tree/Memory/$type.php";
  180.         $className 'Tree_Memory_'.$type;
  181.         $this->dataSourceClass =new $className($dsn$options);
  182.  
  183.         // copy the options to be able to get them via getOption(s)
  184.         // FIXXME this is not really cool, maybe overwrite
  185.         // the *Option* methods!!!
  186.         if (isset($this->dataSourceClass->options)) {
  187.             $this->options = $this->dataSourceClass->options;
  188.         }
  189.  
  190.     }
  191.  
  192.     // }}}
  193.     // {{{ switchDataSource()
  194.  
  195.     /**
  196.      * use this to switch data sources on the run
  197.      * i.e. if you are reading the data from a db-tree and want to save it
  198.      * as xml data (which will work one day too)
  199.      * or reading the data from an xml file and writing it in the db
  200.      * which should already work
  201.      *
  202.      * @version 2002/01/17
  203.      * @access  public
  204.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  205.      * @param   string  this is a DSN of the for that PEAR::DB uses it
  206.      *                   only that additionally you can add parameters
  207.      *                   like ...?table=test_table to define the table.
  208.      * @param   array   additional options you can set
  209.      * @return  boolean true on success
  210.      */
  211.     function switchDataSource($type$dsn ''$options = array())
  212.     {
  213.         $data $this->getNode();
  214.         //$this->Tree($dsn, $options);
  215.         $this->Tree_Memory($type$GLOBALS['dummy']$options);
  216.  
  217.         // this method prepares data retreived using getNode to be used
  218.         // in this type of tree
  219.         $this->dataSourceClass->setData($data);
  220.         $this->setup();
  221.     }
  222.  
  223.     // }}}
  224.     // {{{ setupByRawData()
  225.  
  226.     /**
  227.      *
  228.      *
  229.      * @version 2002/01/19
  230.      * @access  public
  231.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  232.      * @return  boolean true if the setup succeeded
  233.      * @
  234.      */
  235.     function setupByRawData($string)
  236.     {
  237.         // expects
  238.         // for XML an XML-String,
  239.         // for DB-a result set, may be or an array, dont know here
  240.         // not implemented yet
  241.         $res $this->dataSourceClass->setupByRawData($string);
  242.         return $this->_setup($res);
  243.     }
  244.  
  245.     // }}}
  246.     // {{{ setup()
  247.  
  248.     /**
  249.      * @version 2002/01/19
  250.      * @access  public
  251.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  252.      * @param   array   the result of a query which retreives (all)
  253.      *                   the tree data from a source
  254.      * @return  true or Tree_Error
  255.      */
  256.     function setup($data = null)
  257.     {
  258.         if ($this->debug{
  259.             $startTime split(' ',microtime());
  260.             $startTime $startTime[1]+$startTime[0];
  261.         }
  262.  
  263.         if (PEAR::isError($res $this->dataSourceClass->setup($data))) {
  264.             return $res;
  265.         }
  266.  
  267.         if ($this->debug{
  268.             $endTime split(' ',microtime());
  269.             $endTime $endTime[1]+$endTime[0];
  270.             echo ' reading and preparing tree data took: '.
  271.                     ($endTime $startTime'<br>';
  272.         }
  273.  
  274.         return $this->_setup($res);
  275.     }
  276.  
  277.     // }}}
  278.     // {{{ _setup()
  279.  
  280.     /**
  281.      * retreive all the navigation data from the db and build the
  282.      * tree in the array data and structure
  283.      *
  284.      * @version 2001/11/20
  285.      * @access  private
  286.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  287.      * @return  boolean     true on success
  288.      */
  289.     function _setup($setupData)
  290.     {
  291.         // TODO sort by prevId (parentId,prevId $addQuery) too if it exists
  292.         // in the table, or the root might be wrong TODO since the prevId
  293.         // of the root should be 0
  294.         if (!$setupData{
  295.             return false;
  296.         }
  297.  
  298.         //FIXXXXXME validate the structure.
  299.         // i.e. a problem occurs, if you give one node, which has a parentId=1,
  300.         // it screws up everything!!!
  301.         //empty the data structures, since we are reading the data
  302.         // from the db (again)
  303.         $this->structure = array();
  304.         $this->data = array();
  305.         $this->children = array();
  306.         // build an array where all the parents have their children as a member
  307.         // this i do to speed up the buildStructure
  308.         $columnNameMappings $this->getOption('columnNameMaps');
  309.         foreach ($setupData as $values{
  310.             if (is_array($values)) {
  311.                 $this->data[$values['id']] $values;
  312.                 $this->children[$values['parentId']][$values['id'];
  313.             }
  314.         }
  315.  
  316.         // walk through all the children on each level and set the
  317.         // next/previous relations of those children, since all children
  318.         // for "children[$id]" are on the same level we can do
  319.         // this here :-)
  320.         foreach ($this->children as $children{
  321.             $lastPrevId = 0;
  322.             if (count($children)) {
  323.                 foreach ($children as $key{
  324.                     if ($lastPrevId{
  325.                         // remember the nextId too, so the build process can
  326.                         // be speed up
  327.                         $this->data[$lastPrevId]['nextId'$key;
  328.                         $this->data[$lastPrevId]['next'=   &$this->data[$key];
  329.  
  330.                         $this->data[$key]['prevId'$lastPrevId;
  331.                         $this->data[$key]['previous'&$this->data$lastPrevId ];
  332.                     }
  333.                     $lastPrevId $key;
  334.                 }
  335.             }
  336.         }
  337.  
  338.         if ($this->debug{
  339.             $startTime split(' ',microtime());
  340.             $startTime $startTime[1$startTime[0];
  341.         }
  342.  
  343.         // when NO prevId is given, sort the entries in each level by the given
  344.         // sort order (to be defined) and set the prevId so the build can work
  345.         // properly does a prevId exist?
  346.         if (!isset($setupData[0]['prevId'])) {
  347.             $lastPrevId = 0;
  348.             $lastParentId = 0;
  349.             $level = 0;
  350.             // build the entire recursive relations, so you have 'parentId',
  351.             // 'childId', 'nextId', 'prevId' and the references 'child',
  352.             // 'parent', 'next', 'previous' set in the property 'data'
  353.             foreach($this->data as $key => $value{
  354.                 // most if checks in this foreach are for the following reason,
  355.                 // if not stated otherwise:
  356.                 // dont make an data[''] or data[0] since this was not read
  357.                 // from the DB, because id is autoincrement and starts at 1
  358.                 // and also in an xml tree there can not be an element </>
  359.                 // i hope :-)
  360.                 if ($value['parentId']{
  361.                     $this->data[$key]['parent'&$this->data[$value['parentId']];
  362.                     // the parent has an extra array which contains a reference
  363.                     // to all it's children, set it here
  364.                     $this->data$value['parentId']]['children'][=
  365.                                                         &$this->data[$key];
  366.                 }
  367.  
  368.                 // was a child saved (in the above 'if')
  369.                 // see comment above
  370.                 if (isset($this->children[$key]&&
  371.                     count($this->children[$key])
  372.                 {
  373.                     // refer to the first child in the [child]
  374.                     // and [childId] keys
  375.                     $this->data[$key]['childId'$this->children[$key][0];
  376.                     $this->data[$key]['child'=
  377.                                 &$this->data[$this->children[$key][0]];
  378.                 }
  379.  
  380.                 $lastParentId $value['parentId'];
  381.             }
  382.         }
  383.  
  384.         if ($this->debug{
  385.             $endTime split(' ',microtime());
  386.             $endTime $endTime[1]+$endTime[0];
  387.             echo ' building took: ' ($endTime $startTime' <br>';
  388.         }
  389.  
  390.         // build the property 'structure'
  391.         // empty it, just to be sure everything
  392.         //will be set properly
  393.         $this->structure = array();
  394.  
  395.         if ($this->debug{
  396.             $startTime split(' ',microtime());
  397.             $startTime $startTime[1$startTime[0];
  398.         }
  399.  
  400.         // build all the children that are on the root level, if we wouldnt
  401.         // do that. We would have to create a root element with an id 0,
  402.         // but since this is not read from the db we dont add another element.
  403.         // The user wants to get what he had saved
  404.         if (isset($this->children[0])) {
  405.             foreach ($this->children[0as $rootElement{
  406.                 $this->buildStructure($rootElement$this->structure);
  407.             }
  408.         }
  409.  
  410.         if ($this->debug{
  411.             $endTime split(' ',microtime());
  412.             $endTime $endTime[1$endTime[0];
  413.             echo ' buildStructure took: ' ($endTime $startTime).' <br>';
  414.         }
  415.         return true;
  416.     }
  417.  
  418.     // }}}
  419.     // {{{ add()
  420.  
  421.     /**
  422.      * adds _one_ new element in the tree under the given parent
  423.      * the values' keys given have to match the db-columns, because the
  424.      * value gets inserted in the db directly
  425.      * to add an entire node containing children and so on see 'addNode()'
  426.      * @see addNode()
  427.      * @version 2001/10/09
  428.      * @access  public
  429.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  430.      * @param   array   this array contains the values that shall be inserted
  431.      *                   in the db-table
  432.      * @param   int the parent id
  433.      * @param   int the prevId
  434.      * @return  mixed   either boolean false on failure or the id
  435.      *                   of the inserted row
  436.      */
  437.     function add($newValues$parentId = 0$prevId = 0)
  438.     {
  439.         // see comments in 'move' and 'remove'
  440.  
  441.         if (method_exists($this->dataSourceClass'add')) {
  442.             return $this->dataSourceClass->add($newValues$parentId$prevId);
  443.         else {
  444.             return $this->_throwError('method not implemented yet.' ,
  445.                                 __LINE__);
  446.         }
  447.     }
  448.  
  449.     // }}}
  450.     // {{{ remove()
  451.  
  452.     /**
  453.      * removes the given node and all children if removeRecursively is on
  454.      *
  455.      * @version    2002/01/24
  456.      * @access     public
  457.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  458.      * @param      mixed   $id     the id of the node to be removed
  459.      * @return     boolean true on success
  460.      */
  461.     function remove($id)
  462.     {
  463.         // if removing recursively is not allowed, which means every child
  464.         // should be removed
  465.         // then check if this element has a child and return
  466.         // "sorry baby cant remove :-) "
  467.         if ($this->removeRecursively != true{
  468.             if (isset($this->data[$id]['child'])) {
  469.                 // TODO raise PEAR warning
  470.                 return $this->_throwError("Element with id=$id has children ".
  471.                                           "that cant be removed. Set ".
  472.                                           "'setRemoveRecursively' to true to ".
  473.                                           "allow this.",
  474.                                           __LINE__
  475.                                         );
  476.             }
  477.         }
  478.  
  479.         // see comment in 'move'
  480.         // if the prevId is in use we need to update the prevId of the element
  481.         // after the one that is removed too, to have the prevId of the one
  482.         // that is removed!!!
  483.         if (method_exists($this->dataSourceClass'remove')) {
  484.             return $this->dataSourceClass->remove($id);
  485.         else {
  486.             return $this->_throwError('method not implemented yet.'__LINE__);
  487.         }
  488.     }
  489.  
  490.     // }}}
  491.     // {{{ _remove()
  492.  
  493.     /**
  494.      * collects the ID's of the elements to be removed
  495.      *
  496.      * @version    2001/10/09
  497.      * @access     public
  498.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  499.      * @param      mixed   $id   the id of the node to be removed
  500.      * @return     boolean true on success
  501.      */
  502.     function _remove($element)
  503.     {
  504.         return $element['id'];
  505.     }
  506.  
  507.     // }}}
  508.     // {{{ move()
  509.  
  510.     /**
  511.      * move an entry under a given parent or behind a given entry.
  512.      * !!! the 'move behind another element' is only implemented for nested
  513.      * trees now!!!.
  514.      * If a newPrevId is given the newParentId is dismissed!
  515.      * call it either like this:
  516.      *      $tree->move(x, y)
  517.      *      to move the element (or entire tree) with the id x
  518.      *      under the element with the id y
  519.      * or
  520.      * <code>
  521.      *      // ommit the second parameter by setting it to 0
  522.      *      $tree->move(x, 0, y);
  523.      * </code>
  524.      *      to move the element (or entire tree) with the id x
  525.      *      behind the element with the id y
  526.      * or
  527.      * <code>
  528.      *      $tree->move(array(x1,x2,x3), ...
  529.      * </code>
  530.      *      the first parameter can also be an array of elements that shall
  531.      *      be moved
  532.      *      the second and third para can be as described above
  533.      *
  534.      * @version 2002/06/08
  535.      * @access  public
  536.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  537.      * @param   integer the id(s) of the element(s) that shall be moved
  538.      * @param   integer the id of the element which will be the new parent
  539.      * @param   integer if prevId is given the element with the id idToMove
  540.      *                   shall be moved _behind_ the element
  541.      *                   with id=prevId if it is 0 it will be put at
  542.      *                   the beginning
  543.      * @return     boolean     true for success
  544.      */
  545.     function move($idsToMove$newParentId$newPrevId = 0)
  546.     {
  547.         settype($idsToMove,'array');
  548.         $errors = array();
  549.         foreach ($idsToMove as $idToMove{
  550.             $ret $this->_move($idToMove$newParentId$newPrevId);
  551.             if (PEAR::isError($ret)) {
  552.                 $errors[$ret;
  553.             }
  554.         }
  555. // FIXXME return a Tree_Error, not an array !!!!!
  556.         if (count($errors)) {
  557.             return $errors;
  558.         }
  559.         return true;
  560.     }
  561.  
  562.     // }}}
  563.     // {{{ _move()
  564.  
  565.     /**
  566.      * this method moves one tree element
  567.      *
  568.      * @see move()
  569.      * @version 2001/10/10
  570.      * @access  public
  571.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  572.      * @param   integer     the id of the element that shall be moved
  573.      * @param   integer the id of the element which will be
  574.      *                   the new parent
  575.      * @param   integer if prevId is given the element with the id idToMove
  576.      *                   shall be moved _behind_ the element with id=prevId
  577.      *                   if it is 0 it will be put at the beginning
  578.      * @return  mixed   true for success, Tree_Error on failure
  579.      */
  580.     function _move($idToMove$newParentId$prevId = 0)
  581.     {
  582.         // itself can not be a parent of itself
  583.         if ($idToMove == $newParentId{
  584.             // TODO PEAR-ize error
  585.             return TREE_ERROR_INVALID_PARENT;
  586.         }
  587.  
  588.         // check if $newParentId is a child (or a child-child ...) of $idToMove
  589.         // if so prevent moving, because that is not possible
  590.         // does this element have children?
  591.         if ($this->hasChildren($idToMove)) {
  592.             $allChildren $this->getChildren($idToMove);
  593.             // FIXXME what happens here we are changing $allChildren,
  594.             // doesnt this change the property data too??? since getChildren
  595.             // (might, not yet) return a reference use while since foreach
  596.             // only works on a copy of the data to loop through, but we are
  597.             // changing $allChildren in the loop
  598.             while (list($aChildeach ($allChildren)) {
  599.                 // remove the first element because if array_merge is called
  600.                 // the array pointer seems to be
  601.                 array_shift($allChildren);
  602.                 // set to the beginning and this way the beginning is always
  603.                 // the current element, simply work off and truncate in front
  604.                 if (@$aChild['children']{
  605.                     $allChildren array_merge($allChildren,
  606.                                                 $aChild['children']
  607.                                             );
  608.                 }
  609.                 if ($newParentId == $aChild['id']{
  610.                     // TODO PEAR-ize error
  611.                     return TREE_ERROR_INVALID_PARENT;
  612.                 }
  613.             }
  614.         }
  615.  
  616.         // what happens if i am using the prevId too, then the db class also
  617.         // needs to know where the element should be moved to
  618.         // and it has to change the prevId of the element that will be after it
  619.         // so we may be simply call some method like 'update' too?
  620.         if (method_exists($this->dataSourceClass'move')) {
  621.             return $this->dataSourceClass->move($idToMove,
  622.                                                 $newParentId,
  623.                                                 $prevId
  624.                                             );
  625.         else {
  626.             return $this->_throwError('method not implemented yet.'__LINE__);
  627.         }
  628.     }
  629.  
  630.     // }}}
  631.     // {{{ update()
  632.  
  633.     /**
  634.      * update data in a node
  635.      *
  636.      * @version    2002/01/29
  637.      * @access     public
  638.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  639.      * @param      integer the ID of the element that shall be updated
  640.      * @param      array   the data to update
  641.      * @return     mixed   either boolean or
  642.      *                      an error object if the method is not implemented
  643.      */
  644.     function update($id$data)
  645.     {
  646.         if (method_exists($this->dataSourceClass'update')) {
  647.             return $this->dataSourceClass->update($id,$data);
  648.         else {
  649.             return $this->_throwError(
  650.                         'method not implemented yet.'__LINE__
  651.                     );
  652.         }
  653.     }
  654.  
  655.     // }}}
  656.  
  657.     //
  658.     //
  659.     //  from here all methods are not interacting on the  'dataSourceClass'
  660.     //
  661.     //
  662.  
  663.     // {{{ buildStructure()
  664.     /**
  665.      * builds the structure in the parameter $insertIn
  666.      * this function works recursively down into depth of the folder structure
  667.      * it builds an array which goes as deep as the structure goes
  668.      *
  669.      * @access  public
  670.      * @version 2001/05/02
  671.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  672.      * @param   integer the parent for which it's structure shall
  673.      *                   be built
  674.      * @param   integer the array where to build the structure in
  675.      *                   given as a reference to be sure the substructure is built
  676.      *                   in the same array as passed to the function
  677.      * @return  boolean returns always true
  678.      *
  679.      */
  680.     function buildStructure($parentId&$insertIn)
  681.     {
  682.         // create the element, so it exists in the property "structure"
  683.         // also if there are no children below
  684.         $insertIn[$parentId= array();
  685.  
  686.         // set the level, since we are walking through the structure here.
  687.         // Anyway we can do this here, instead of up in the setup method :-)
  688.         // always set the level to one higher than the parent's level, easy ha?
  689.         // this applies only to the root element(s)
  690.         if (isset($this->data[$parentId]['parent']['level'])) {
  691.             $this->data[$parentId]['level'=
  692.                             $this->data[$parentId]['parent']['level']+1;
  693.             if ($this->data[$parentId]['level']>$this->_treeDepth{
  694.                 $this->_treeDepth $this->data[$parentId]['level'];
  695.             }
  696.         else {
  697.             // set first level number to 0
  698.             $this->data[$parentId]['level'= 0;
  699.         }
  700.  
  701.         if (isset($this->children[$parentId])
  702.             && count($this->children[$parentId])) {
  703.             // go thru all the folders
  704.             foreach ($this->children[$parentIdas $child{
  705.                 // build the structure under this folder,
  706.                 // use the current folder as the new parent and call
  707.                 // build recursively to build all the children by calling build
  708.                 // with $insertIn[someindex] the array is filled
  709.                 // since the array was empty before
  710.                 $this->buildStructure($child$insertIn[$parentId]);
  711.             }
  712.         }
  713.         return true;
  714.     }
  715.  
  716.     // }}}
  717.     // {{{ walk()
  718.  
  719.     /**
  720.      * this method only serves to call the _walk method and
  721.      * reset $this->walkReturn that will be returned by all the walk-steps
  722.      *
  723.      * @version 2001/11/25
  724.      * @access  public
  725.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  726.      * @param   mixed   the name of the function to call for each walk step,
  727.      *                   or an array for a method, where [0] is the method name
  728.      *                   and [1] the object
  729.      * @param   array   the id to start walking through the tree, everything
  730.      *                   below is walked through
  731.      * @param   string  the return of all the walk data will be of the given
  732.      *                   type (values: string, array)
  733.      * @return  mixed   this is all the return data collected from all
  734.      *                   the walk-steps
  735.      */
  736.     function walk($walkFunction$id = 0$returnType 'string')
  737.     {
  738.         // by default all of structure is used
  739.         $useNode $this->structure;
  740.         if ($id == 0{
  741.             $keys array_keys($this->structure);
  742.             $id $keys[0];
  743.         else {
  744.             // get the path, to be able to go to the element in this->structure
  745.             $path $this->getPath($id);
  746.             // pop off the last element, since it is the one requested
  747.             array_pop($path);
  748.             // start at the root of structure
  749.             $curNode $this->structure;
  750.             foreach ($path as $node{
  751.                 // go as deep into structure as path defines
  752.                 $curNode $curNode[$node['id']];
  753.             }
  754.             // empty it first, so we dont have the other stuff in there
  755.             // from before
  756.             $useNode = array();
  757.             // copy only the branch of the tree that the parameter
  758.             // $id requested
  759.             $useNode[$id$curNode[$id];
  760.         }
  761.  
  762.         // a new walk starts, unset the return value
  763.         unset($this->walkReturn);
  764.         return $this->_walk($walkFunction$useNode$returnType);
  765.     }
  766.  
  767.     // }}}
  768.     // {{{ _walk()
  769.  
  770.     /**
  771.      * walks through the entire tree and returns the current element and the level
  772.      * so a user can use this to build a treemap or whatever
  773.      *
  774.      * @version 2001/06/xx
  775.      * @access  private
  776.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  777.      * @param   mixed   the name of the function to call for each walk step,
  778.      *                   or an array for a method, where [0] is the method name
  779.      *                   and [1] the object
  780.      *
  781.      * @param   array   the reference in the this->structure, to walk
  782.      *                   everything below
  783.      * @param   string  the return of all the walk data will be
  784.      *                   of the given type (values: string, array, ifArray)
  785.      * @return  mixed   this is all the return data collected from all
  786.      *                   the walk-steps
  787.      */
  788.     function _walk($walkFunction&$curLevel$returnType)
  789.     {
  790.         if (count($curLevel)) {
  791.             foreach ($curLevel as $key => $value{
  792.                 $ret call_user_func($walkFunction$this->data[$key]);
  793.                 switch ($returnType{
  794.                 case 'array':
  795.                     $this->walkReturn[$ret;
  796.                     break;
  797.                 // this only adds the element if the $ret is an array
  798.                 // and contains data
  799.                 case 'ifArray':
  800.                     if (is_array($ret)) {
  801.                         $this->walkReturn[$ret;
  802.                     }
  803.                     break;
  804.                 default:
  805.                     $this->walkReturn.= $ret;
  806.                     break;
  807.                 }
  808.                 $this->_walk($walkFunction$value$returnType);
  809.             }
  810.         }
  811.         return $this->walkReturn;
  812.     }
  813.  
  814.     // }}}
  815.     // {{{ addNode()
  816.  
  817.     /**
  818.      * Adds multiple elements. You have to pass those elements
  819.      * in a multidimensional array which represents the tree structure
  820.      * as it shall be added (this array can of course also simply contain
  821.      * one element).
  822.      * The following array $x passed as the parameter
  823.      *      $x[0] = array('name'     => 'bla',
  824.      *                    'parentId' => '30',
  825.      *                    array('name'    => 'bla1',
  826.      *                          'comment' => 'foo',
  827.      *                          array('name' => 'bla2'),
  828.      *                          array('name' => 'bla2_1')
  829.      *                          ),
  830.      *                    array('name'=>'bla1_1'),
  831.      *                    );
  832.      *      $x[1] = array('name'     => 'fooBla',
  833.      *                    'parentId' => '30');
  834.      *
  835.      * would add the following tree (or subtree/node) under the parent
  836.      * with the id 30 (since 'parentId'=30 in $x[0] and in $x[1]):
  837.      *  +--bla
  838.      *  |   +--bla1
  839.      *  |   |    +--bla2
  840.      *  |   |    +--bla2_1
  841.      *  |   +--bla1_1
  842.      *  +--fooBla
  843.      *
  844.      * @see add()
  845.      * @version 2001/12/19
  846.      * @access  public
  847.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  848.      * @param   array   the tree to be inserted, represents the tree structure,
  849.      *                              see add() for the exact member of each node
  850.      * @return  mixed   either boolean false on failure
  851.      *                   or the id of the inserted row
  852.      */
  853.     function addNode($node)
  854.     {
  855.         if (count($node)) {
  856.             foreach ($node as $aNode{
  857.                 $newNode = array();
  858.                 // this should always have data, if not the passed
  859.                 // structure has an error
  860.                 foreach ($aNode as $name => $value{
  861.                     // collect the data that need to go in the DB
  862.                     if (!is_array($value)) {
  863.                         $newEntry[$name$value;
  864.                     else {
  865.                         // collect the children
  866.                         $newNode[$value;
  867.                     }
  868.                 }
  869.                 // add the element and get the id, that it got, to have
  870.                 // the parentId for the children
  871.                 $insertedId $this->add($newEntry);
  872.                 // if inserting suceeded, we have received the id
  873.                 // under which we can insert the children
  874.                 if ($insertedId!= false{
  875.                     // if there are children, set their parentId.
  876.                     // So they kknow where they belong in the tree
  877.                     if (count($newNode)) {
  878.                         foreach($newNode as $key => $aNewNode{
  879.                             $newNode[$key]['parentId'$insertedId;
  880.                         }
  881.                     }
  882.                     // call yourself recursively to insert the children
  883.                     // and its children
  884.                     $this->addNode($newNode);
  885.                 }
  886.             }
  887.         }
  888.     }
  889.  
  890.     // }}}
  891.     // {{{ getPath()
  892.  
  893.     /**
  894.      * gets the path to the element given by its id
  895.      * !!! ATTENTION watch out that you never change any of the data returned,
  896.      * since they are references to the internal property $data
  897.      *
  898.      * @access  public
  899.      * @version 2001/10/10
  900.      * @access  public
  901.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  902.      * @param   mixed   the id of the node to get the path for
  903.      * @return  array   this array contains all elements from the root
  904.      *                       to the element given by the id
  905.      *
  906.      */
  907.     function getPath($id)
  908.     {
  909.         // empty the path, to be clean
  910.         $path = array();
  911.  
  912.         // FIXXME may its better to use a for(level) to count down,
  913.         // since a while is always a little risky
  914.         // until there are no more parents
  915.         while (@$this->data[$id]['parent']{
  916.             // curElement is already a reference, so save it in path
  917.             $path[&$this->data[$id];
  918.             // get the next parent id, for the while to retreive the parent's parent
  919.             $id $this->data[$id]['parent']['id'];
  920.         }
  921.         // dont forget the last one
  922.         $path[&$this->data[$id];
  923.  
  924.         return array_reverse($path);
  925.     }
  926.  
  927.     // }}}
  928.     // {{{ setRemoveRecursively()
  929.  
  930.      /**
  931.      * sets the remove-recursively mode, either true or false
  932.      *
  933.      * @version 2001/10/09
  934.      * @access  public
  935.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  936.      * @param   boolean set to true if removing a tree level
  937.      *                   shall remove all it's children and theit children
  938.      *
  939.      */
  940.     function setRemoveRecursively($case=true)
  941.     {
  942.         $this->removeRecursively $case;
  943.     }
  944.  
  945.     // }}}
  946.     // {{{ _getElement()
  947.  
  948.     /**
  949.      *
  950.      *
  951.      * @version    2002/01/21
  952.      * @access     private
  953.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  954.      * @param      int     the element ID
  955.      *
  956.      */
  957.     function &_getElement($id$what '')
  958.     {
  959.         // We should not return false, since that might be a value of the
  960.         // element that is requested.
  961.         $element = null;
  962.  
  963.         if ($what == ''{
  964.             $element $this->data[$id];
  965.         }
  966.         $elementId $this->_getElementId($id$what);
  967.         if ($elementId !== null{
  968.             $element $this->data[$elementId];
  969.         }
  970.         // we should not return false, since that might be a value
  971.         // of the element that is requested
  972.         return $element;
  973.     }
  974.  
  975.     // }}}
  976.     // {{{ _getElementId()
  977.  
  978.     /**
  979.      *
  980.      *
  981.      * @version    2002/01/21
  982.      * @access     private
  983.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  984.      * @param      int     the element ID
  985.      *
  986.      */
  987.     function _getElementId($id$what)
  988.     {
  989.         if (isset($this->data[$id][$what]&& $this->data[$id][$what]{
  990.             return $this->data[$id][$what]['id'];
  991.         }
  992.         return null;
  993.     }
  994.  
  995.     // }}}
  996.     // {{{ getElement()
  997.  
  998.     /**
  999.      * gets an element as a reference
  1000.      *
  1001.      * @version 2002/01/21
  1002.      * @access  private
  1003.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1004.      * @param   int the element ID
  1005.      *
  1006.      */
  1007.     function &getElement($id)
  1008.     {
  1009.         $element &$this->_getElement($id);
  1010.         return $element;
  1011.     }
  1012.  
  1013.     // }}}
  1014.     // {{{ getElementContent()
  1015.  
  1016.     /**
  1017.      *
  1018.      *
  1019.      * @version 2002/02/06
  1020.      * @access  private
  1021.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1022.      * @param   mixed    either the id of an element
  1023.      *                       or the path to the element
  1024.      *
  1025.      */
  1026.     function getElementContent($idOrPath$fieldName)
  1027.     {
  1028.         if (is_string($idOrPath)) {
  1029.             $idOrPath $this->getIdByPath($idOrPath);
  1030.         }
  1031.         return $this->data[$idOrPath][$fieldName];
  1032.     }
  1033.  
  1034.     // }}}
  1035.     // {{{ getElementsContent()
  1036.  
  1037.     /**
  1038.      *
  1039.      *
  1040.      * @version    2002/02/06
  1041.      * @access     private
  1042.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1043.      * @param      int     the element ID
  1044.      *
  1045.      */
  1046.     function getElementsContent($ids$fieldName{
  1047.         // i dont know if this method is not just overloading the file.
  1048.         // Since it only serves my lazyness
  1049.         // is this effective here? i can also loop in the calling code!?
  1050.         $fields = array();
  1051.         if (is_array($ids&& count($ids)) {
  1052.             foreach ($ids as $aId{
  1053.                 $fields[$this->getElementContent($aId$fieldName);
  1054.             }
  1055.         }
  1056.         return $fields;
  1057.     }
  1058.  
  1059.     // }}}
  1060.     // {{{ getElementByPath()
  1061.  
  1062.     /**
  1063.      * gets an element given by it's path as a reference
  1064.      *
  1065.      * @version 2002/01/21
  1066.      * @access  public
  1067.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1068.      * @param   string  the path to search for
  1069.      * @param   integer the id where to search for the path
  1070.      * @param   string  the name of the key that contains the node name
  1071.      * @param   string  the path separator
  1072.      * @return  integer the id of the searched element
  1073.      *
  1074.      */
  1075.     function &getElementByPath($path$startId = 0$nodeName 'name'$seperator '/')
  1076.     {
  1077.         $element = null;
  1078.         $id $this->getIdByPath($path,$startId);
  1079.         if ($id{
  1080.             $element &$this->getElement($id);
  1081.         }
  1082.         // return null since false might be interpreted as id 0
  1083.         return $element;
  1084.     }
  1085.  
  1086.     // }}}
  1087.     // {{{ getLevel()
  1088.  
  1089.     /**
  1090.      * get the level, which is how far below the root are we?
  1091.      *
  1092.      * @version 2001/11/25
  1093.      * @access  public
  1094.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1095.      * @param   mixed   $id     the id of the node to get the level for
  1096.      *
  1097.      */
  1098.     function getLevel($id)
  1099.     {
  1100.         return $this->data[$id]['level'];
  1101.     }
  1102.  
  1103.     // }}}
  1104.     // {{{ getChild()
  1105.  
  1106.     /**
  1107.      * returns the child if the node given has one
  1108.      * !!! ATTENTION watch out that you never change any of the data returned,
  1109.      * since they are references to the internal property $data
  1110.      *
  1111.      * @version 2001/11/27
  1112.      * @access  public
  1113.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1114.      * @param   mixed   the id of the node to get the child for
  1115.      *
  1116.      */
  1117.     function &getChild($id)
  1118.     {
  1119.         $element &$this->_getElement($id'child');
  1120.         return $element;
  1121.     }
  1122.  
  1123.     // }}}
  1124.     // {{{ getParent()
  1125.  
  1126.     /**
  1127.      * returns the child if the node given has one
  1128.      * !!! ATTENTION watch out that you never change any of the data returned,
  1129.      * since they are references to the internal property $data
  1130.      *
  1131.      * @version 2001/11/27
  1132.      * @access  public
  1133.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1134.      * @param   mixed   $id     the id of the node to get the child for
  1135.      *
  1136.      */
  1137.     function &getParent($id)
  1138.     {
  1139.         $element &$this->_getElement($id'parent');
  1140.         return $element;
  1141.     }
  1142.  
  1143.     // }}}
  1144.     // {{{ getNext()
  1145.  
  1146.     /**
  1147.      * returns the next element if the node given has one
  1148.      * !!! ATTENTION watch out that you never change any of the data returned,
  1149.      * since they are references to the internal property $data
  1150.      *
  1151.      * @version 2002/01/17
  1152.      * @access  public
  1153.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1154.      * @param   mixed   the id of the node to get the child for
  1155.      * @return  mixed   reference to the next element or false if there is none
  1156.      */
  1157.     function &getNext($id)
  1158.     {
  1159.         $element &$this->_getElement($id'next');
  1160.         return $element;
  1161.     }
  1162.  
  1163.     // }}}
  1164.     // {{{ getPrevious()
  1165.  
  1166.     /**
  1167.      * returns the previous element if the node given has one
  1168.      * !!! ATTENTION watch out that you never change any of the data returned,
  1169.      * since they are references to the internal property $data
  1170.      *
  1171.      * @version 2002/02/05
  1172.      * @access  public
  1173.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1174.      * @param   mixed   the id of the node to get the child for
  1175.      * @return  mixed   reference to the next element or false if there is none
  1176.      */
  1177.     function &getPrevious($id)
  1178.     {
  1179.         $element &$this->_getElement($id'previous');
  1180.         return $element;
  1181.     }
  1182.  
  1183.     // }}}
  1184.     // {{{ getNode()
  1185.  
  1186.     /**
  1187.      * returns the node for the given id
  1188.      * !!! ATTENTION watch out that you never change any of the data returned,
  1189.      * since they are references to the internal property $data
  1190.      *
  1191.      * @version    2001/11/28
  1192.      * @access     public
  1193.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1194.      * @param      mixed   $id     the id of the node to get
  1195.      *
  1196.      */
  1197. /*
  1198.     function &getNode($id)
  1199.     {
  1200.         //$element = &$this->_getElement($id);
  1201.         //return $element;
  1202.     }
  1203. */
  1204.  
  1205.     // }}}
  1206.     // {{{ getIdByPath()
  1207.  
  1208.     /**
  1209.      * return the id of the element which is referenced by $path
  1210.      * this is useful for xml-structures, like: getIdByPath('/root/sub1/sub2')
  1211.      * this requires the structure to use each name uniquely
  1212.      * if this is not given it will return the first proper path found
  1213.      * i.e. there should only be one path /x/y/z
  1214.      *
  1215.      * @version    2001/11/28
  1216.      * @access     public
  1217.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1218.      * @param      string  $path       the path to search for
  1219.      * @param      integer $startId    the id where to search for the path
  1220.      * @param      string  $nodeName   the name of the key that contains the node name
  1221.      * @param      string  $seperator  the path seperator
  1222.      * @return     integer the id of the searched element
  1223.      *
  1224.      */
  1225.     function getIdByPath($path$startId = 0$nodeName 'name'$seperator '/')
  1226. // should this method be called getElementIdByPath ????
  1227.     {
  1228.         // if no start ID is given get the root
  1229.         if ($startId == 0{
  1230.             $startId $this->getFirstRootId();
  1231.         else {   // if a start id is given, get its first child to start searching there
  1232.             $startId $this->getChildId($startId);
  1233.             if ($startId==false{                 // is there a child to this element?
  1234.                 return false;
  1235.             }
  1236.         }
  1237.  
  1238.         if (strpos($path,$seperator=== 0{  // if a seperator is at the beginning strip it off
  1239.             $path substr($path,strlen($seperator));
  1240.         }
  1241.         $nodes explode($seperator$path);
  1242.         $curId $startId;
  1243.         foreach ($nodes as $key => $aNodeName{
  1244.             $nodeFound = false;
  1245.             do {
  1246.                 if ($this->data[$curId][$nodeName== $aNodeName{
  1247.                     $nodeFound = true;
  1248.                     // do only save the child if we are not already at the end of path
  1249.                     // because then we need curId to return it
  1250.                     if ($key (count($nodes- 1)) {
  1251.                         $curId $this->getChildId($curId);
  1252.                     }
  1253.                     break;
  1254.                 }
  1255.                 $curId $this->getNextId($curId);
  1256.             while($curId);
  1257.  
  1258.             if ($nodeFound == false{
  1259.                 return false;
  1260.             }
  1261.         }
  1262.         return $curId;
  1263.         // FIXXME to be implemented
  1264.     }
  1265.  
  1266.     // }}}
  1267.     // {{{ getFirstRoot()
  1268.  
  1269.     /**
  1270.      * this gets the first element that is in the root node
  1271.      * i think that there can't be a "getRoot" method since there might
  1272.      * be multiple number of elements in the root node, at least the
  1273.      * way it works now
  1274.      *
  1275.      * @access     public
  1276.      * @version    2001/12/10
  1277.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1278.      * @return     returns the first root element
  1279.      */
  1280.     function &getFirstRoot()
  1281.     {
  1282.         // could also be reset($this->data) i think since php keeps the order
  1283.         // ... but i didnt try
  1284.         reset($this->structure);
  1285.         return $this->data[key($this->structure)];
  1286.     }
  1287.  
  1288.     // }}}
  1289.     // {{{ getRoot()
  1290.  
  1291.     /**
  1292.      * since in a nested tree there can only be one root
  1293.      * which i think (now) is correct, we also need an alias for this method
  1294.      * this also makes all the methods in Tree_Common, which access the
  1295.      * root element work properly!
  1296.      *
  1297.      * @access     public
  1298.      * @version    2002/07/26
  1299.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1300.      * @return     returns the first root element
  1301.      */
  1302.     function &getRoot()
  1303.     {
  1304.         $tmp $this->getFirstRoot();
  1305.         return $tmp;
  1306.     }
  1307.  
  1308.     // }}}
  1309.     // {{{ getRoot()
  1310.  
  1311.     /**
  1312.      * gets the tree under the given element in one array, sorted
  1313.      * so you can go through the elements from begin to end and list them
  1314.      * as they are in the tree, where every child (until the deepest) is retreived
  1315.      *
  1316.      * @see        &_getNode()
  1317.      * @access     public
  1318.      * @version    2001/12/17
  1319.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1320.      * @param      integer  the id where to start walking
  1321.      * @param      integer  this number says how deep into
  1322.      *                       the structure the elements shall be
  1323.      *                       retreived
  1324.      * @return     array    sorted as listed in the tree
  1325.      */
  1326.     function &getNode($startId=0$depth=0)
  1327.     {
  1328.         if ($startId == 0{
  1329.             $level = 0;
  1330.         else {
  1331.             $level $this->getLevel($startId);
  1332.         }
  1333.  
  1334.         $this->_getNodeMaxLevel $depth ($depth $level: 0 ;
  1335.         //!!!        $this->_getNodeCurParent = $this->data['parent']['id'];
  1336.  
  1337.         // if the tree is empty dont walk through it
  1338.         if (!count($this->data)) {
  1339.             $tmp = null;
  1340.             return $tmp;
  1341.         }
  1342.  
  1343.         $ret $this->walk(array(&$this,'_getNode')$startId'ifArray');
  1344.         return $ret;
  1345.     }
  1346.  
  1347.     // }}}
  1348.     // {{{ _getNode()
  1349.  
  1350.     /**
  1351.      * this is used for walking through the tree structure
  1352.      * until a given level, this method should only be used by getNode
  1353.      *
  1354.      * @see        &getNode()
  1355.      * @see        walk()
  1356.      * @see        _walk()
  1357.      * @access     private
  1358.      * @version    2001/12/17
  1359.      * @author     Wolfram Kriesing <wolfram@kriesing.de>
  1360.      * @param      array    the node passed by _walk
  1361.      * @return     mixed    either returns the node, or nothing
  1362.      *                       if the level _getNodeMaxLevel is reached
  1363.      */
  1364.     function &_getNode(&$node)
  1365.     {
  1366.         if ($this->_getNodeMaxLevel{
  1367.             if ($this->getLevel($node['id']$this->_getNodeMaxLevel{
  1368.                 return $node;
  1369.             }
  1370.             $tmp = null;
  1371.             return $tmp;
  1372.         }
  1373.         return $node;
  1374.     }
  1375.  
  1376.     // }}}
  1377.     // {{{ getChildren()
  1378.  
  1379.     /**
  1380.      * returns the children of the given ids
  1381.      *
  1382.      * @version 2001/12/17
  1383.      * @access  public
  1384.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1385.      * @param   integer $id the id of the node to check for children
  1386.      * @param   integer the children of how many levels shall be returned
  1387.      * @return  boolean true if the node has children
  1388.      */
  1389.     function getChildren($ids$levels = 1)
  1390.     {
  1391.         //FIXXME $levels to be implemented
  1392.         $ret = array();
  1393.         if (is_array($ids)) {
  1394.             foreach ($ids as $aId{
  1395.                 if ($this->hasChildren($aId)) {
  1396.                     $ret[$aId$this->data[$aId]['children'];
  1397.                 }
  1398.             }
  1399.         else {
  1400.             if ($this->hasChildren($ids)) {
  1401.                 $ret $this->data[$ids]['children'];
  1402.             }
  1403.         }
  1404.         return $ret;
  1405.     }
  1406.  
  1407.     // }}}
  1408.     // {{{ isNode()
  1409.  
  1410.     /**
  1411.      * returns if the given element is a valid node
  1412.      *
  1413.      * @version 2001/12/21
  1414.      * @access  public
  1415.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1416.      * @param   integer $id the id of the node to check for children
  1417.      * @return  boolean true if the node has children
  1418.      */
  1419.     function isNode($id = 0)
  1420.     {
  1421.         return isset($this->data[$id]);
  1422.     }
  1423.  
  1424.     // }}}
  1425.     // {{{ varDump()
  1426.  
  1427.     /**
  1428.      * this is for debugging, dumps the entire data-array
  1429.      * an extra method is needed, since this array contains recursive
  1430.      * elements which make a normal print_f or var_dump not show all the data
  1431.      *
  1432.      * @version 2002/01/21
  1433.      * @access  public
  1434.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1435.      * @params  mixed   either the id of the node to dump, this will dump
  1436.      *                   everything below the given node or an array of nodes
  1437.      *                   to dump. This only dumps the elements passed
  1438.      *                   as an array. 0 or no parameter if the entire tree shall
  1439.      *                   be dumped if you want to dump only a single element
  1440.      *                   pass it as an array using array($element).
  1441.      */
  1442.     function varDump($node = 0)
  1443.     {
  1444.         $dontDump = array('parent''child''children''next''previous');
  1445.  
  1446.         // if $node is an array, we assume it is a collection of elements
  1447.         if (!is_array($node)) {
  1448.             $nodes $this->getNode($node);
  1449.         else {
  1450.             $nodes $node;
  1451.         }
  1452.         // if $node==0 then the entire tree is retreived
  1453.         if (count($node)) {
  1454.             echo '<table border="1"><tr><th>name</th>';
  1455.             $keys = array();
  1456.             foreach ($this->getRoot(as $key => $x{
  1457.                 if (!is_array($x)) {
  1458.                     echo "<th>$key</th>";
  1459.                     $keys[$key;
  1460.                 }
  1461.             }
  1462.             echo '</tr>';
  1463.  
  1464.             foreach ($nodes as $aNode{
  1465.                 echo '<tr><td nowrap="nowrap">';
  1466.                 $prefix '';
  1467.                 for ($i = 0; $i $aNode['level']$i++$prefix .= '- ';
  1468.                 echo "$prefix {$aNode['name']}</td>";
  1469.                 foreach ($keys as $aKey{
  1470.                     if (!is_array($key)) {
  1471.                         $val = isset($aNode[$aKey]$aNode[$aKey'&nbsp;';
  1472.                         echo "<td>$val</td>";
  1473.                     }
  1474.                 }
  1475.                 echo '</tr>';
  1476.             }
  1477.             echo '</table>';
  1478.         }
  1479.     }
  1480.  
  1481.     // }}}
  1482.  
  1483.     //### TODO's ###
  1484.  
  1485.     // {{{ copy()
  1486.     /**
  1487.      * NOT IMPLEMENTED YET
  1488.      * copies a part of the tree under a given parent
  1489.      *
  1490.      * @version 2001/12/19
  1491.      * @access  public
  1492.      * @author  Wolfram Kriesing <wolfram@kriesing.de>
  1493.      * @param   the id of the element which is copied, all its children are copied too
  1494.      * @param   the id that shall be the new parent
  1495.      * @return  boolean     true on success
  1496.      */
  1497.     function copy($srcId$destId)
  1498.     {
  1499.         if (method_exists($this->dataSourceClass'copy')) {
  1500.             return $this->dataSourceClass->copy($srcId$destId);
  1501.         else {
  1502.             return $this->_throwError('method not implemented yet.'__LINE__);
  1503.         }
  1504. /*
  1505.     remove all array elements after 'parent' since those had been created
  1506.     and remove id and set parentId and that should be it, build the tree and pass it to addNode
  1507.  
  1508.     those are the fields in one data-entry
  1509. id=>41
  1510. parentId=>39
  1511. name=>Java
  1512. parent=>Array
  1513. prevId=>58
  1514. previous=>Array
  1515. childId=>77
  1516. child=>Array
  1517. nextId=>104
  1518. next=>Array
  1519. children=>Array
  1520. level=>2
  1521.  
  1522.         $this->getNode
  1523.         foreach($this->data[$srcId] as $key=>$value)
  1524.             echo "$key=>$value<br>";
  1525. */
  1526.     }
  1527.  
  1528.     // }}}
  1529. }

Documentation generated on Mon, 11 Mar 2019 15:47:06 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.