web/enmi/Zend/Ldap/Node.php
changeset 19 1c2f13fd785c
parent 0 4eba9c11703f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/enmi/Zend/Ldap/Node.php	Thu Jan 20 19:30:54 2011 +0100
@@ -0,0 +1,1101 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Node.php 22662 2010-07-24 17:37:36Z mabe $
+ */
+
+/**
+ * @see Zend_Ldap
+ */
+require_once 'Zend/Ldap.php';
+/**
+ * @see Zend_Ldap_Node_Abstract
+ */
+require_once 'Zend/Ldap/Node/Abstract.php';
+
+/**
+ * Zend_Ldap_Node provides an object oriented view into a LDAP node.
+ *
+ * @category   Zend
+ * @package    Zend_Ldap
+ * @subpackage Node
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
+{
+    /**
+     * Holds the node's new DN if node is renamed.
+     *
+     * @var Zend_Ldap_Dn
+     */
+    protected $_newDn;
+    /**
+     * Holds the node's orginal attributes (as loaded).
+     *
+     * @var array
+     */
+    protected $_originalData;
+    /**
+     * This node will be added
+     *
+     * @var boolean
+     */
+    protected $_new;
+    /**
+     * This node will be deleted
+     *
+     * @var boolean
+     */
+    protected $_delete;
+    /**
+     * Holds the connection to the LDAP server if in connected mode.
+     *
+     * @var Zend_Ldap
+     */
+    protected $_ldap;
+
+    /**
+     * Holds an array of the current node's children.
+     *
+     * @var array
+     */
+    protected $_children;
+
+    /**
+     * Controls iteration status
+     *
+     * @var boolean
+     */
+    private $_iteratorRewind = false;
+
+    /**
+     * Constructor.
+     *
+     * Constructor is protected to enforce the use of factory methods.
+     *
+     * @param  Zend_Ldap_Dn $dn
+     * @param  array        $data
+     * @param  boolean      $fromDataSource
+     * @param  Zend_Ldap    $ldap
+     * @throws Zend_Ldap_Exception
+     */
+    protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
+    {
+        parent::__construct($dn, $data, $fromDataSource);
+        if ($ldap !== null) $this->attachLdap($ldap);
+        else $this->detachLdap();
+    }
+
+    /**
+     * Serialization callback
+     *
+     * Only DN and attributes will be serialized.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array('_dn', '_currentData', '_newDn', '_originalData',
+            '_new', '_delete', '_children');
+    }
+
+    /**
+     * Deserialization callback
+     *
+     * Enforces a detached node.
+     *
+     * @return null
+     */
+    public function __wakeup()
+    {
+        $this->detachLdap();
+    }
+
+    /**
+     * Gets the current LDAP connection.
+     *
+     * @return Zend_Ldap
+     * @throws Zend_Ldap_Exception
+     */
+    public function getLdap()
+    {
+        if ($this->_ldap === null) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
+        }
+        else return $this->_ldap;
+    }
+
+    /**
+     * Attach node to an LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @uses   Zend_Ldap_Dn::isChildOf()
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function attachLdap(Zend_Ldap $ldap)
+    {
+        if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
+                Zend_Ldap_Exception::LDAP_OTHER);
+        }
+
+        if ($ldap !== $this->_ldap) {
+            $this->_ldap = $ldap;
+            if (is_array($this->_children)) {
+                foreach ($this->_children as $child) {
+                    $child->attachLdap($ldap);
+                }
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Detach node from LDAP connection
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function detachLdap()
+    {
+        $this->_ldap = null;
+        if (is_array($this->_children)) {
+            foreach ($this->_children as $child) {
+                $child->detachLdap();
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Checks if the current node is attached to a LDAP server.
+     *
+     * This is an offline method.
+     *
+     * @return boolean
+     */
+    public function isAttached()
+    {
+        return ($this->_ldap !== null);
+    }
+
+    /**
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _loadData(array $data, $fromDataSource)
+    {
+        parent::_loadData($data, $fromDataSource);
+        if ($fromDataSource === true) {
+            $this->_originalData = $data;
+        } else {
+            $this->_originalData = array();
+        }
+        $this->_children = null;
+        $this->_markAsNew(($fromDataSource === true) ? false : true);
+        $this->_markAsToBeDeleted(false);
+    }
+
+    /**
+     * Factory method to create a new detached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  array                     $objectClass
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function create($dn, array $objectClass = array())
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $new = new self($dn, array(), false, null);
+        $new->_ensureRdnAttributeValues();
+        $new->setAttribute('objectClass', $objectClass);
+        return $new;
+    }
+
+    /**
+     * Factory method to create an attached Zend_Ldap_Node for a given DN.
+     *
+     * @param  string|array|Zend_Ldap_Dn $dn
+     * @param  Zend_Ldap                 $ldap
+     * @return Zend_Ldap_Node|null
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromLdap($dn, Zend_Ldap $ldap)
+    {
+        if (is_string($dn) || is_array($dn)) {
+            $dn = Zend_Ldap_Dn::factory($dn);
+        } else if ($dn instanceof Zend_Ldap_Dn) {
+            $dn = clone $dn;
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
+        }
+        $data = $ldap->getEntry($dn, array('*', '+'), true);
+        if ($data === null) {
+            return null;
+        }
+        $entry = new self($dn, $data, true, $ldap);
+        return $entry;
+    }
+
+    /**
+     * Factory method to create a detached Zend_Ldap_Node from array data.
+     *
+     * @param  array   $data
+     * @param  boolean $fromDataSource
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public static function fromArray(array $data, $fromDataSource = false)
+    {
+        if (!array_key_exists('dn', $data)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
+        }
+        if (is_string($data['dn']) || is_array($data['dn'])) {
+            $dn = Zend_Ldap_Dn::factory($data['dn']);
+        } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
+            $dn = clone $data['dn'];
+        } else {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
+        }
+        $fromDataSource = ($fromDataSource === true) ? true : false;
+        $new = new self($dn, $data, $fromDataSource, null);
+        $new->_ensureRdnAttributeValues();
+        return $new;
+    }
+
+    /**
+     * Ensures that teh RDN attributes are correctly set.
+     *
+     * @return void
+     */
+    protected function _ensureRdnAttributeValues()
+    {
+        foreach ($this->getRdnArray() as $key => $value) {
+            Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
+        }
+    }
+
+    /**
+     * Marks this node as new.
+     *
+     * Node will be added (instead of updated) on calling update() if $new is true.
+     *
+     * @param boolean $new
+     */
+    protected function _markAsNew($new)
+    {
+        $this->_new = ($new === false) ? false : true;
+    }
+
+    /**
+     * Tells if the node is consiedered as new (not present on the server)
+     *
+     * Please note, that this doesn't tell you if the node is present on the server.
+     * Use {@link exits()} to see if a node is already there.
+     *
+     * @return boolean
+     */
+    public function isNew()
+    {
+        return $this->_new;
+    }
+
+    /**
+     * Marks this node as to be deleted.
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @param boolean $delete
+     */
+    protected function _markAsToBeDeleted($delete)
+    {
+        $this->_delete = ($delete === true) ? true : false;
+    }
+
+
+    /**
+    * Is this node going to be deleted once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeDeleted()
+    {
+        return $this->_delete;
+    }
+
+    /**
+     * Marks this node as to be deleted
+     *
+     * Node will be deleted on calling update() if $delete is true.
+     *
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function delete()
+    {
+        $this->_markAsToBeDeleted(true);
+        return $this;
+    }
+
+    /**
+    * Is this node going to be moved once update() is called?
+    *
+    * @return boolean
+    */
+    public function willBeMoved()
+    {
+        if ($this->isNew() || $this->willBeDeleted()) {
+            return false;
+        } else if ($this->_newDn !== null) {
+            return ($this->_dn != $this->_newDn);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sends all pending changes to the LDAP server
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function update(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        if (!($ldap instanceof Zend_Ldap)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
+        }
+
+        if ($this->willBeDeleted()) {
+            if ($ldap->exists($this->_dn)) {
+                $ldap->delete($this->_dn);
+            }
+            return $this;
+        }
+
+        if ($this->isNew()) {
+            $data = $this->getData();
+            $ldap->add($this->_getDn(), $data);
+            $this->_loadData($data, true);
+            return $this;
+        }
+
+        $changedData = $this->getChangedData();
+        if ($this->willBeMoved()) {
+            $recursive = $this->hasChildren();
+            $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
+            foreach ($this->_newDn->getRdn() as $key => $value) {
+                if (array_key_exists($key, $changedData)) {
+                    unset($changedData[$key]);
+                }
+            }
+            $this->_dn = $this->_newDn;
+            $this->_newDn = null;
+        }
+        if (count($changedData) > 0) {
+            $ldap->update($this->_getDn(), $changedData);
+        }
+        $this->_originalData = $this->_currentData;
+        return $this;
+    }
+
+    /**
+     * Gets the DN of the current node as a Zend_Ldap_Dn.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    protected function _getDn()
+    {
+        return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
+    }
+
+    /**
+     * Gets the current DN of the current node as a Zend_Ldap_Dn.
+     * The method returns a clone of the node's DN to prohibit modification.
+     *
+     * This is an offline method.
+     *
+     * @return Zend_Ldap_Dn
+     */
+    public function getCurrentDn()
+    {
+        $dn = clone parent::_getDn();
+        return $dn;
+    }
+
+    /**
+     * Sets the new DN for this node
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function setDn($newDn)
+    {
+        if ($newDn instanceof Zend_Ldap_Dn) {
+            $this->_newDn = clone $newDn;
+        } else {
+            $this->_newDn = Zend_Ldap_Dn::factory($newDn);
+        }
+        $this->_ensureRdnAttributeValues();
+        return $this;
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function move($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * {@see setDn()}
+     *
+     * This is an offline method.
+     *
+     * @param  Zend_Ldap_Dn|string|array $newDn
+     * @throws Zend_Ldap_Exception
+     * @return Zend_Ldap_Node Provides a fluid interface
+     */
+    public function rename($newDn)
+    {
+        return $this->setDn($newDn);
+    }
+
+    /**
+     * Sets the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setObjectClass($value)
+    {
+        $this->setAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Appends to the objectClass.
+     *
+     * This is an offline method.
+     *
+     * @param  array|string $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendObjectClass($value)
+    {
+        $this->appendToAttribute('objectClass', $value);
+        return $this;
+    }
+
+    /**
+     * Returns a LDIF representation of the current node
+     *
+     * @param  array $options Additional options used during encoding
+     * @return string
+     */
+    public function toLdif(array $options = array())
+    {
+        $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
+        /**
+         * Zend_Ldap_Ldif_Encoder
+         */
+        require_once 'Zend/Ldap/Ldif/Encoder.php';
+        return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
+    }
+
+    /**
+     * Gets changed node data.
+     *
+     * The array contains all changed attributes.
+     * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChangedData()
+    {
+        $changed = array();
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changed[$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                $changed[$key] = $value;
+            }
+        }
+        return $changed;
+    }
+
+    /**
+     * Returns all changes made.
+     *
+     * This is an offline method.
+     *
+     * @return array
+     */
+    public function getChanges()
+    {
+        $changes = array(
+            'add'     => array(),
+            'delete'  => array(),
+            'replace' => array());
+        foreach ($this->_currentData as $key => $value) {
+            if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
+                $changes['add'][$key] = $value;
+            } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
+                if (empty($value)) {
+                    $changes['delete'][$key] = $value;
+                } else {
+                    $changes['replace'][$key] = $value;
+                }
+            }
+        }
+        return $changes;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToAttribute($name, $value)
+    {
+        $this->_setAttribute($name, $value, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string  $name
+     * @param  mixed   $value
+     * @param  boolean $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setAttribute($name, $value, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
+    }
+
+    /**
+     * Sets a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, false);
+        return $this;
+    }
+
+    /**
+     * Appends to a LDAP date/time attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function appendToDateTimeAttribute($name, $value, $utc = false)
+    {
+        $this->_setDateTimeAttribute($name, $value, $utc, true);
+        return $this;
+    }
+
+    /**
+     * Checks if the attribute can be set and sets it accordingly.
+     *
+     * @param  string        $name
+     * @param  integer|array $value
+     * @param  boolean       $utc
+     * @param  boolean       $append
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _setDateTimeAttribute($name, $value, $utc, $append)
+    {
+        $this->_assertChangeableAttribute($name);
+        Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
+    }
+
+    /**
+     * Sets a LDAP password.
+     *
+     * @param  string $password
+     * @param  string $hashType
+     * @param  string $attribName
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
+        $attribName = 'userPassword')
+    {
+        $this->_assertChangeableAttribute($attribName);
+        Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
+        return $this;
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function deleteAttribute($name)
+    {
+        if ($this->existsAttribute($name, true)) {
+            $this->_setAttribute($name, null, false);
+        }
+        return $this;
+    }
+
+    /**
+     * Removes duplicate values from a LDAP attribute
+     *
+     * @param  string $attribName
+     * @return void
+     */
+    public function removeDuplicatesFromAttribute($attribName)
+    {
+        Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
+    }
+
+    /**
+     * Remove given values from a LDAP attribute
+     *
+     * @param  string      $attribName
+     * @param  mixed|array $value
+     * @return void
+     */
+    public function removeFromAttribute($attribName, $value)
+    {
+        Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
+    }
+
+    /**
+     * @param  string $name
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    protected function _assertChangeableAttribute($name)
+    {
+        $name = strtolower($name);
+        $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
+        if ($name == 'dn') {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
+        }
+        else if (array_key_exists($name, $rdn)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
+        } else if (in_array($name, self::$_systemAttributes)) {
+            /**
+             * @see Zend_Ldap_Exception
+             */
+            require_once 'Zend/Ldap/Exception.php';
+            throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
+        }
+        else return true;
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __set($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function __unset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Sets a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @param  mixed  $value
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetSet($name, $value)
+    {
+        $this->setAttribute($name, $value);
+    }
+
+    /**
+     * Deletes a LDAP attribute.
+     * Implements ArrayAccess.
+     *
+     * This method deletes the attribute.
+     *
+     * This is an offline method.
+     *
+     * @param  string $name
+     * @return null
+     * @throws Zend_Ldap_Exception
+     */
+    public function offsetUnset($name)
+    {
+        $this->deleteAttribute($name);
+    }
+
+    /**
+     * Check if node exists on LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function exists(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        return $ldap->exists($this->_getDn());
+    }
+
+    /**
+     * Reload node attributes from LDAP.
+     *
+     * This is an online method.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node Provides a fluid interface
+     * @throws Zend_Ldap_Exception
+     */
+    public function reload(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        parent::reload($ldap);
+        return $this;
+    }
+
+    /**
+     * Search current subtree with given options.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
+    {
+        /**
+         * @see Zend_Ldap_Node_Collection
+         */
+        require_once 'Zend/Ldap/Node/Collection.php';
+        return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
+            'Zend_Ldap_Node_Collection');
+    }
+
+    /**
+     * Count items in current subtree found by given filter.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  integer                          $scope
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
+    {
+        return $this->getLdap()->count($filter, $this->_getDn(), $scope);
+    }
+
+    /**
+     * Count children of current node.
+     *
+     * This is an online method.
+     *
+     * @return integer
+     * @throws Zend_Ldap_Exception
+     */
+    public function countChildren()
+    {
+        return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
+    }
+
+    /**
+     * Gets children of current node.
+     *
+     * This is an online method.
+     *
+     * @param  string|Zend_Ldap_Filter_Abstract $filter
+     * @param  string                           $sort
+     * @return Zend_Ldap_Node_Collection
+     * @throws Zend_Ldap_Exception
+     */
+    public function searchChildren($filter, $sort = null)
+    {
+        return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
+    }
+
+    /**
+     * Checks if current node has children.
+     * Returns whether the current element has children.
+     *
+     * Can be used offline but returns false if children have not been retrieved yet.
+     *
+     * @return boolean
+     * @throws Zend_Ldap_Exception
+     */
+    public function hasChildren()
+    {
+        if (!is_array($this->_children)) {
+            if ($this->isAttached()) {
+                return ($this->countChildren() > 0);
+            } else {
+                return false;
+            }
+        } else {
+            return (count($this->_children) > 0);
+        }
+    }
+
+    /**
+     * Returns the children for the current node.
+     *
+     * Can be used offline but returns an empty array if children have not been retrieved yet.
+     *
+     * @return Zend_Ldap_Node_ChildrenIterator
+     * @throws Zend_Ldap_Exception
+     */
+    public function getChildren()
+    {
+        if (!is_array($this->_children)) {
+            $this->_children = array();
+            if ($this->isAttached()) {
+                $children = $this->searchChildren('(objectClass=*)', null);
+                foreach ($children as $child) {
+                    $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
+                }
+            }
+        }
+        /**
+         * @see Zend_Ldap_Node_ChildrenIterator
+         */
+        require_once 'Zend/Ldap/Node/ChildrenIterator.php';
+        return new Zend_Ldap_Node_ChildrenIterator($this->_children);
+    }
+
+    /**
+     * Returns the parent of the current node.
+     *
+     * @param  Zend_Ldap $ldap
+     * @return Zend_Ldap_Node
+     * @throws Zend_Ldap_Exception
+     */
+    public function getParent(Zend_Ldap $ldap = null)
+    {
+        if ($ldap !== null) {
+            $this->attachLdap($ldap);
+        }
+        $ldap = $this->getLdap();
+        $parentDn = $this->_getDn()->getParentDn(1);
+        return self::fromLdap($parentDn, $ldap);
+    }
+
+    /**
+     * Return the current attribute.
+     * Implements Iterator
+     *
+     * @return array
+     */
+    public function current()
+    {
+        return $this;
+    }
+
+    /**
+     * Return the attribute name.
+     * Implements Iterator
+     *
+     * @return string
+     */
+    public function key()
+    {
+        return $this->getRdnString();
+    }
+
+    /**
+     * Move forward to next attribute.
+     * Implements Iterator
+     */
+    public function next()
+    {
+        $this->_iteratorRewind = false;
+    }
+
+    /**
+     * Rewind the Iterator to the first attribute.
+     * Implements Iterator
+     */
+    public function rewind()
+    {
+        $this->_iteratorRewind = true;
+    }
+
+    /**
+     * Check if there is a current attribute
+     * after calls to rewind() or next().
+     * Implements Iterator
+     *
+     * @return boolean
+     */
+    public function valid()
+    {
+        return $this->_iteratorRewind;
+    }
+}
\ No newline at end of file