web/lib/Zend/Ldap/Node.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Ldap
       
    17  * @subpackage Node
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Node.php 22662 2010-07-24 17:37:36Z mabe $
       
    21  */
       
    22 
       
    23 /**
       
    24  * @see Zend_Ldap
       
    25  */
       
    26 require_once 'Zend/Ldap.php';
       
    27 /**
       
    28  * @see Zend_Ldap_Node_Abstract
       
    29  */
       
    30 require_once 'Zend/Ldap/Node/Abstract.php';
       
    31 
       
    32 /**
       
    33  * Zend_Ldap_Node provides an object oriented view into a LDAP node.
       
    34  *
       
    35  * @category   Zend
       
    36  * @package    Zend_Ldap
       
    37  * @subpackage Node
       
    38  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    39  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    40  */
       
    41 class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator
       
    42 {
       
    43     /**
       
    44      * Holds the node's new DN if node is renamed.
       
    45      *
       
    46      * @var Zend_Ldap_Dn
       
    47      */
       
    48     protected $_newDn;
       
    49     /**
       
    50      * Holds the node's orginal attributes (as loaded).
       
    51      *
       
    52      * @var array
       
    53      */
       
    54     protected $_originalData;
       
    55     /**
       
    56      * This node will be added
       
    57      *
       
    58      * @var boolean
       
    59      */
       
    60     protected $_new;
       
    61     /**
       
    62      * This node will be deleted
       
    63      *
       
    64      * @var boolean
       
    65      */
       
    66     protected $_delete;
       
    67     /**
       
    68      * Holds the connection to the LDAP server if in connected mode.
       
    69      *
       
    70      * @var Zend_Ldap
       
    71      */
       
    72     protected $_ldap;
       
    73 
       
    74     /**
       
    75      * Holds an array of the current node's children.
       
    76      *
       
    77      * @var array
       
    78      */
       
    79     protected $_children;
       
    80 
       
    81     /**
       
    82      * Controls iteration status
       
    83      *
       
    84      * @var boolean
       
    85      */
       
    86     private $_iteratorRewind = false;
       
    87 
       
    88     /**
       
    89      * Constructor.
       
    90      *
       
    91      * Constructor is protected to enforce the use of factory methods.
       
    92      *
       
    93      * @param  Zend_Ldap_Dn $dn
       
    94      * @param  array        $data
       
    95      * @param  boolean      $fromDataSource
       
    96      * @param  Zend_Ldap    $ldap
       
    97      * @throws Zend_Ldap_Exception
       
    98      */
       
    99     protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null)
       
   100     {
       
   101         parent::__construct($dn, $data, $fromDataSource);
       
   102         if ($ldap !== null) $this->attachLdap($ldap);
       
   103         else $this->detachLdap();
       
   104     }
       
   105 
       
   106     /**
       
   107      * Serialization callback
       
   108      *
       
   109      * Only DN and attributes will be serialized.
       
   110      *
       
   111      * @return array
       
   112      */
       
   113     public function __sleep()
       
   114     {
       
   115         return array('_dn', '_currentData', '_newDn', '_originalData',
       
   116             '_new', '_delete', '_children');
       
   117     }
       
   118 
       
   119     /**
       
   120      * Deserialization callback
       
   121      *
       
   122      * Enforces a detached node.
       
   123      *
       
   124      * @return null
       
   125      */
       
   126     public function __wakeup()
       
   127     {
       
   128         $this->detachLdap();
       
   129     }
       
   130 
       
   131     /**
       
   132      * Gets the current LDAP connection.
       
   133      *
       
   134      * @return Zend_Ldap
       
   135      * @throws Zend_Ldap_Exception
       
   136      */
       
   137     public function getLdap()
       
   138     {
       
   139         if ($this->_ldap === null) {
       
   140             /**
       
   141              * @see Zend_Ldap_Exception
       
   142              */
       
   143             require_once 'Zend/Ldap/Exception.php';
       
   144             throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER);
       
   145         }
       
   146         else return $this->_ldap;
       
   147     }
       
   148 
       
   149     /**
       
   150      * Attach node to an LDAP connection
       
   151      *
       
   152      * This is an offline method.
       
   153      *
       
   154      * @uses   Zend_Ldap_Dn::isChildOf()
       
   155      * @param  Zend_Ldap $ldap
       
   156      * @return Zend_Ldap_Node Provides a fluid interface
       
   157      * @throws Zend_Ldap_Exception
       
   158      */
       
   159     public function attachLdap(Zend_Ldap $ldap)
       
   160     {
       
   161         if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) {
       
   162             /**
       
   163              * @see Zend_Ldap_Exception
       
   164              */
       
   165             require_once 'Zend/Ldap/Exception.php';
       
   166             throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.',
       
   167                 Zend_Ldap_Exception::LDAP_OTHER);
       
   168         }
       
   169 
       
   170         if ($ldap !== $this->_ldap) {
       
   171             $this->_ldap = $ldap;
       
   172             if (is_array($this->_children)) {
       
   173                 foreach ($this->_children as $child) {
       
   174                     $child->attachLdap($ldap);
       
   175                 }
       
   176             }
       
   177         }
       
   178         return $this;
       
   179     }
       
   180 
       
   181     /**
       
   182      * Detach node from LDAP connection
       
   183      *
       
   184      * This is an offline method.
       
   185      *
       
   186      * @return Zend_Ldap_Node Provides a fluid interface
       
   187      */
       
   188     public function detachLdap()
       
   189     {
       
   190         $this->_ldap = null;
       
   191         if (is_array($this->_children)) {
       
   192             foreach ($this->_children as $child) {
       
   193                 $child->detachLdap();
       
   194             }
       
   195         }
       
   196         return $this;
       
   197     }
       
   198 
       
   199     /**
       
   200      * Checks if the current node is attached to a LDAP server.
       
   201      *
       
   202      * This is an offline method.
       
   203      *
       
   204      * @return boolean
       
   205      */
       
   206     public function isAttached()
       
   207     {
       
   208         return ($this->_ldap !== null);
       
   209     }
       
   210 
       
   211     /**
       
   212      * @param  array   $data
       
   213      * @param  boolean $fromDataSource
       
   214      * @throws Zend_Ldap_Exception
       
   215      */
       
   216     protected function _loadData(array $data, $fromDataSource)
       
   217     {
       
   218         parent::_loadData($data, $fromDataSource);
       
   219         if ($fromDataSource === true) {
       
   220             $this->_originalData = $data;
       
   221         } else {
       
   222             $this->_originalData = array();
       
   223         }
       
   224         $this->_children = null;
       
   225         $this->_markAsNew(($fromDataSource === true) ? false : true);
       
   226         $this->_markAsToBeDeleted(false);
       
   227     }
       
   228 
       
   229     /**
       
   230      * Factory method to create a new detached Zend_Ldap_Node for a given DN.
       
   231      *
       
   232      * @param  string|array|Zend_Ldap_Dn $dn
       
   233      * @param  array                     $objectClass
       
   234      * @return Zend_Ldap_Node
       
   235      * @throws Zend_Ldap_Exception
       
   236      */
       
   237     public static function create($dn, array $objectClass = array())
       
   238     {
       
   239         if (is_string($dn) || is_array($dn)) {
       
   240             $dn = Zend_Ldap_Dn::factory($dn);
       
   241         } else if ($dn instanceof Zend_Ldap_Dn) {
       
   242             $dn = clone $dn;
       
   243         } else {
       
   244             /**
       
   245              * @see Zend_Ldap_Exception
       
   246              */
       
   247             require_once 'Zend/Ldap/Exception.php';
       
   248             throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
       
   249         }
       
   250         $new = new self($dn, array(), false, null);
       
   251         $new->_ensureRdnAttributeValues();
       
   252         $new->setAttribute('objectClass', $objectClass);
       
   253         return $new;
       
   254     }
       
   255 
       
   256     /**
       
   257      * Factory method to create an attached Zend_Ldap_Node for a given DN.
       
   258      *
       
   259      * @param  string|array|Zend_Ldap_Dn $dn
       
   260      * @param  Zend_Ldap                 $ldap
       
   261      * @return Zend_Ldap_Node|null
       
   262      * @throws Zend_Ldap_Exception
       
   263      */
       
   264     public static function fromLdap($dn, Zend_Ldap $ldap)
       
   265     {
       
   266         if (is_string($dn) || is_array($dn)) {
       
   267             $dn = Zend_Ldap_Dn::factory($dn);
       
   268         } else if ($dn instanceof Zend_Ldap_Dn) {
       
   269             $dn = clone $dn;
       
   270         } else {
       
   271             /**
       
   272              * @see Zend_Ldap_Exception
       
   273              */
       
   274             require_once 'Zend/Ldap/Exception.php';
       
   275             throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.');
       
   276         }
       
   277         $data = $ldap->getEntry($dn, array('*', '+'), true);
       
   278         if ($data === null) {
       
   279             return null;
       
   280         }
       
   281         $entry = new self($dn, $data, true, $ldap);
       
   282         return $entry;
       
   283     }
       
   284 
       
   285     /**
       
   286      * Factory method to create a detached Zend_Ldap_Node from array data.
       
   287      *
       
   288      * @param  array   $data
       
   289      * @param  boolean $fromDataSource
       
   290      * @return Zend_Ldap_Node
       
   291      * @throws Zend_Ldap_Exception
       
   292      */
       
   293     public static function fromArray(array $data, $fromDataSource = false)
       
   294     {
       
   295         if (!array_key_exists('dn', $data)) {
       
   296             /**
       
   297              * @see Zend_Ldap_Exception
       
   298              */
       
   299             require_once 'Zend/Ldap/Exception.php';
       
   300             throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.');
       
   301         }
       
   302         if (is_string($data['dn']) || is_array($data['dn'])) {
       
   303             $dn = Zend_Ldap_Dn::factory($data['dn']);
       
   304         } else if ($data['dn'] instanceof Zend_Ldap_Dn) {
       
   305             $dn = clone $data['dn'];
       
   306         } else {
       
   307             /**
       
   308              * @see Zend_Ldap_Exception
       
   309              */
       
   310             require_once 'Zend/Ldap/Exception.php';
       
   311             throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.');
       
   312         }
       
   313         $fromDataSource = ($fromDataSource === true) ? true : false;
       
   314         $new = new self($dn, $data, $fromDataSource, null);
       
   315         $new->_ensureRdnAttributeValues();
       
   316         return $new;
       
   317     }
       
   318 
       
   319     /**
       
   320      * Ensures that teh RDN attributes are correctly set.
       
   321      *
       
   322      * @return void
       
   323      */
       
   324     protected function _ensureRdnAttributeValues()
       
   325     {
       
   326         foreach ($this->getRdnArray() as $key => $value) {
       
   327             Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false);
       
   328         }
       
   329     }
       
   330 
       
   331     /**
       
   332      * Marks this node as new.
       
   333      *
       
   334      * Node will be added (instead of updated) on calling update() if $new is true.
       
   335      *
       
   336      * @param boolean $new
       
   337      */
       
   338     protected function _markAsNew($new)
       
   339     {
       
   340         $this->_new = ($new === false) ? false : true;
       
   341     }
       
   342 
       
   343     /**
       
   344      * Tells if the node is consiedered as new (not present on the server)
       
   345      *
       
   346      * Please note, that this doesn't tell you if the node is present on the server.
       
   347      * Use {@link exits()} to see if a node is already there.
       
   348      *
       
   349      * @return boolean
       
   350      */
       
   351     public function isNew()
       
   352     {
       
   353         return $this->_new;
       
   354     }
       
   355 
       
   356     /**
       
   357      * Marks this node as to be deleted.
       
   358      *
       
   359      * Node will be deleted on calling update() if $delete is true.
       
   360      *
       
   361      * @param boolean $delete
       
   362      */
       
   363     protected function _markAsToBeDeleted($delete)
       
   364     {
       
   365         $this->_delete = ($delete === true) ? true : false;
       
   366     }
       
   367 
       
   368 
       
   369     /**
       
   370     * Is this node going to be deleted once update() is called?
       
   371     *
       
   372     * @return boolean
       
   373     */
       
   374     public function willBeDeleted()
       
   375     {
       
   376         return $this->_delete;
       
   377     }
       
   378 
       
   379     /**
       
   380      * Marks this node as to be deleted
       
   381      *
       
   382      * Node will be deleted on calling update() if $delete is true.
       
   383      *
       
   384      * @return Zend_Ldap_Node Provides a fluid interface
       
   385      */
       
   386     public function delete()
       
   387     {
       
   388         $this->_markAsToBeDeleted(true);
       
   389         return $this;
       
   390     }
       
   391 
       
   392     /**
       
   393     * Is this node going to be moved once update() is called?
       
   394     *
       
   395     * @return boolean
       
   396     */
       
   397     public function willBeMoved()
       
   398     {
       
   399         if ($this->isNew() || $this->willBeDeleted()) {
       
   400             return false;
       
   401         } else if ($this->_newDn !== null) {
       
   402             return ($this->_dn != $this->_newDn);
       
   403         } else {
       
   404             return false;
       
   405         }
       
   406     }
       
   407 
       
   408     /**
       
   409      * Sends all pending changes to the LDAP server
       
   410      *
       
   411      * @param  Zend_Ldap $ldap
       
   412      * @return Zend_Ldap_Node Provides a fluid interface
       
   413      * @throws Zend_Ldap_Exception
       
   414      */
       
   415     public function update(Zend_Ldap $ldap = null)
       
   416     {
       
   417         if ($ldap !== null) {
       
   418             $this->attachLdap($ldap);
       
   419         }
       
   420         $ldap = $this->getLdap();
       
   421         if (!($ldap instanceof Zend_Ldap)) {
       
   422             /**
       
   423              * @see Zend_Ldap_Exception
       
   424              */
       
   425             require_once 'Zend/Ldap/Exception.php';
       
   426             throw new Zend_Ldap_Exception(null, 'No LDAP connection available');
       
   427         }
       
   428 
       
   429         if ($this->willBeDeleted()) {
       
   430             if ($ldap->exists($this->_dn)) {
       
   431                 $ldap->delete($this->_dn);
       
   432             }
       
   433             return $this;
       
   434         }
       
   435 
       
   436         if ($this->isNew()) {
       
   437             $data = $this->getData();
       
   438             $ldap->add($this->_getDn(), $data);
       
   439             $this->_loadData($data, true);
       
   440             return $this;
       
   441         }
       
   442 
       
   443         $changedData = $this->getChangedData();
       
   444         if ($this->willBeMoved()) {
       
   445             $recursive = $this->hasChildren();
       
   446             $ldap->rename($this->_dn, $this->_newDn, $recursive, false);
       
   447             foreach ($this->_newDn->getRdn() as $key => $value) {
       
   448                 if (array_key_exists($key, $changedData)) {
       
   449                     unset($changedData[$key]);
       
   450                 }
       
   451             }
       
   452             $this->_dn = $this->_newDn;
       
   453             $this->_newDn = null;
       
   454         }
       
   455         if (count($changedData) > 0) {
       
   456             $ldap->update($this->_getDn(), $changedData);
       
   457         }
       
   458         $this->_originalData = $this->_currentData;
       
   459         return $this;
       
   460     }
       
   461 
       
   462     /**
       
   463      * Gets the DN of the current node as a Zend_Ldap_Dn.
       
   464      *
       
   465      * This is an offline method.
       
   466      *
       
   467      * @return Zend_Ldap_Dn
       
   468      */
       
   469     protected function _getDn()
       
   470     {
       
   471         return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn;
       
   472     }
       
   473 
       
   474     /**
       
   475      * Gets the current DN of the current node as a Zend_Ldap_Dn.
       
   476      * The method returns a clone of the node's DN to prohibit modification.
       
   477      *
       
   478      * This is an offline method.
       
   479      *
       
   480      * @return Zend_Ldap_Dn
       
   481      */
       
   482     public function getCurrentDn()
       
   483     {
       
   484         $dn = clone parent::_getDn();
       
   485         return $dn;
       
   486     }
       
   487 
       
   488     /**
       
   489      * Sets the new DN for this node
       
   490      *
       
   491      * This is an offline method.
       
   492      *
       
   493      * @param  Zend_Ldap_Dn|string|array $newDn
       
   494      * @throws Zend_Ldap_Exception
       
   495      * @return Zend_Ldap_Node Provides a fluid interface
       
   496      */
       
   497     public function setDn($newDn)
       
   498     {
       
   499         if ($newDn instanceof Zend_Ldap_Dn) {
       
   500             $this->_newDn = clone $newDn;
       
   501         } else {
       
   502             $this->_newDn = Zend_Ldap_Dn::factory($newDn);
       
   503         }
       
   504         $this->_ensureRdnAttributeValues();
       
   505         return $this;
       
   506     }
       
   507 
       
   508     /**
       
   509      * {@see setDn()}
       
   510      *
       
   511      * This is an offline method.
       
   512      *
       
   513      * @param  Zend_Ldap_Dn|string|array $newDn
       
   514      * @throws Zend_Ldap_Exception
       
   515      * @return Zend_Ldap_Node Provides a fluid interface
       
   516      */
       
   517     public function move($newDn)
       
   518     {
       
   519         return $this->setDn($newDn);
       
   520     }
       
   521 
       
   522     /**
       
   523      * {@see setDn()}
       
   524      *
       
   525      * This is an offline method.
       
   526      *
       
   527      * @param  Zend_Ldap_Dn|string|array $newDn
       
   528      * @throws Zend_Ldap_Exception
       
   529      * @return Zend_Ldap_Node Provides a fluid interface
       
   530      */
       
   531     public function rename($newDn)
       
   532     {
       
   533         return $this->setDn($newDn);
       
   534     }
       
   535 
       
   536     /**
       
   537      * Sets the objectClass.
       
   538      *
       
   539      * This is an offline method.
       
   540      *
       
   541      * @param  array|string $value
       
   542      * @return Zend_Ldap_Node Provides a fluid interface
       
   543      * @throws Zend_Ldap_Exception
       
   544      */
       
   545     public function setObjectClass($value)
       
   546     {
       
   547         $this->setAttribute('objectClass', $value);
       
   548         return $this;
       
   549     }
       
   550 
       
   551     /**
       
   552      * Appends to the objectClass.
       
   553      *
       
   554      * This is an offline method.
       
   555      *
       
   556      * @param  array|string $value
       
   557      * @return Zend_Ldap_Node Provides a fluid interface
       
   558      * @throws Zend_Ldap_Exception
       
   559      */
       
   560     public function appendObjectClass($value)
       
   561     {
       
   562         $this->appendToAttribute('objectClass', $value);
       
   563         return $this;
       
   564     }
       
   565 
       
   566     /**
       
   567      * Returns a LDIF representation of the current node
       
   568      *
       
   569      * @param  array $options Additional options used during encoding
       
   570      * @return string
       
   571      */
       
   572     public function toLdif(array $options = array())
       
   573     {
       
   574         $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false));
       
   575         /**
       
   576          * Zend_Ldap_Ldif_Encoder
       
   577          */
       
   578         require_once 'Zend/Ldap/Ldif/Encoder.php';
       
   579         return Zend_Ldap_Ldif_Encoder::encode($attributes, $options);
       
   580     }
       
   581 
       
   582     /**
       
   583      * Gets changed node data.
       
   584      *
       
   585      * The array contains all changed attributes.
       
   586      * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}.
       
   587      *
       
   588      * This is an offline method.
       
   589      *
       
   590      * @return array
       
   591      */
       
   592     public function getChangedData()
       
   593     {
       
   594         $changed = array();
       
   595         foreach ($this->_currentData as $key => $value) {
       
   596             if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
       
   597                 $changed[$key] = $value;
       
   598             } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
       
   599                 $changed[$key] = $value;
       
   600             }
       
   601         }
       
   602         return $changed;
       
   603     }
       
   604 
       
   605     /**
       
   606      * Returns all changes made.
       
   607      *
       
   608      * This is an offline method.
       
   609      *
       
   610      * @return array
       
   611      */
       
   612     public function getChanges()
       
   613     {
       
   614         $changes = array(
       
   615             'add'     => array(),
       
   616             'delete'  => array(),
       
   617             'replace' => array());
       
   618         foreach ($this->_currentData as $key => $value) {
       
   619             if (!array_key_exists($key, $this->_originalData) && !empty($value)) {
       
   620                 $changes['add'][$key] = $value;
       
   621             } else if (count($this->_originalData[$key]) === 0 && !empty($value)) {
       
   622                 $changes['add'][$key] = $value;
       
   623             } else if ($this->_originalData[$key] !== $this->_currentData[$key]) {
       
   624                 if (empty($value)) {
       
   625                     $changes['delete'][$key] = $value;
       
   626                 } else {
       
   627                     $changes['replace'][$key] = $value;
       
   628                 }
       
   629             }
       
   630         }
       
   631         return $changes;
       
   632     }
       
   633 
       
   634     /**
       
   635      * Sets a LDAP attribute.
       
   636      *
       
   637      * This is an offline method.
       
   638      *
       
   639      * @param  string $name
       
   640      * @param  mixed  $value
       
   641      * @return Zend_Ldap_Node Provides a fluid interface
       
   642      * @throws Zend_Ldap_Exception
       
   643      */
       
   644     public function setAttribute($name, $value)
       
   645     {
       
   646         $this->_setAttribute($name, $value, false);
       
   647         return $this;
       
   648     }
       
   649 
       
   650     /**
       
   651      * Appends to a LDAP attribute.
       
   652      *
       
   653      * This is an offline method.
       
   654      *
       
   655      * @param  string $name
       
   656      * @param  mixed  $value
       
   657      * @return Zend_Ldap_Node Provides a fluid interface
       
   658      * @throws Zend_Ldap_Exception
       
   659      */
       
   660     public function appendToAttribute($name, $value)
       
   661     {
       
   662         $this->_setAttribute($name, $value, true);
       
   663         return $this;
       
   664     }
       
   665 
       
   666     /**
       
   667      * Checks if the attribute can be set and sets it accordingly.
       
   668      *
       
   669      * @param  string  $name
       
   670      * @param  mixed   $value
       
   671      * @param  boolean $append
       
   672      * @throws Zend_Ldap_Exception
       
   673      */
       
   674     protected function _setAttribute($name, $value, $append)
       
   675     {
       
   676         $this->_assertChangeableAttribute($name);
       
   677         Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append);
       
   678     }
       
   679 
       
   680     /**
       
   681      * Sets a LDAP date/time attribute.
       
   682      *
       
   683      * This is an offline method.
       
   684      *
       
   685      * @param  string        $name
       
   686      * @param  integer|array $value
       
   687      * @param  boolean       $utc
       
   688      * @return Zend_Ldap_Node Provides a fluid interface
       
   689      * @throws Zend_Ldap_Exception
       
   690      */
       
   691     public function setDateTimeAttribute($name, $value, $utc = false)
       
   692     {
       
   693         $this->_setDateTimeAttribute($name, $value, $utc, false);
       
   694         return $this;
       
   695     }
       
   696 
       
   697     /**
       
   698      * Appends to a LDAP date/time attribute.
       
   699      *
       
   700      * This is an offline method.
       
   701      *
       
   702      * @param  string        $name
       
   703      * @param  integer|array $value
       
   704      * @param  boolean       $utc
       
   705      * @return Zend_Ldap_Node Provides a fluid interface
       
   706      * @throws Zend_Ldap_Exception
       
   707      */
       
   708     public function appendToDateTimeAttribute($name, $value, $utc = false)
       
   709     {
       
   710         $this->_setDateTimeAttribute($name, $value, $utc, true);
       
   711         return $this;
       
   712     }
       
   713 
       
   714     /**
       
   715      * Checks if the attribute can be set and sets it accordingly.
       
   716      *
       
   717      * @param  string        $name
       
   718      * @param  integer|array $value
       
   719      * @param  boolean       $utc
       
   720      * @param  boolean       $append
       
   721      * @throws Zend_Ldap_Exception
       
   722      */
       
   723     protected function _setDateTimeAttribute($name, $value, $utc, $append)
       
   724     {
       
   725         $this->_assertChangeableAttribute($name);
       
   726         Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append);
       
   727     }
       
   728 
       
   729     /**
       
   730      * Sets a LDAP password.
       
   731      *
       
   732      * @param  string $password
       
   733      * @param  string $hashType
       
   734      * @param  string $attribName
       
   735      * @return Zend_Ldap_Node Provides a fluid interface
       
   736      * @throws Zend_Ldap_Exception
       
   737      */
       
   738     public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5,
       
   739         $attribName = 'userPassword')
       
   740     {
       
   741         $this->_assertChangeableAttribute($attribName);
       
   742         Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName);
       
   743         return $this;
       
   744     }
       
   745 
       
   746     /**
       
   747      * Deletes a LDAP attribute.
       
   748      *
       
   749      * This method deletes the attribute.
       
   750      *
       
   751      * This is an offline method.
       
   752      *
       
   753      * @param  string $name
       
   754      * @return Zend_Ldap_Node Provides a fluid interface
       
   755      * @throws Zend_Ldap_Exception
       
   756      */
       
   757     public function deleteAttribute($name)
       
   758     {
       
   759         if ($this->existsAttribute($name, true)) {
       
   760             $this->_setAttribute($name, null, false);
       
   761         }
       
   762         return $this;
       
   763     }
       
   764 
       
   765     /**
       
   766      * Removes duplicate values from a LDAP attribute
       
   767      *
       
   768      * @param  string $attribName
       
   769      * @return void
       
   770      */
       
   771     public function removeDuplicatesFromAttribute($attribName)
       
   772     {
       
   773         Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName);
       
   774     }
       
   775 
       
   776     /**
       
   777      * Remove given values from a LDAP attribute
       
   778      *
       
   779      * @param  string      $attribName
       
   780      * @param  mixed|array $value
       
   781      * @return void
       
   782      */
       
   783     public function removeFromAttribute($attribName, $value)
       
   784     {
       
   785         Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value);
       
   786     }
       
   787 
       
   788     /**
       
   789      * @param  string $name
       
   790      * @return boolean
       
   791      * @throws Zend_Ldap_Exception
       
   792      */
       
   793     protected function _assertChangeableAttribute($name)
       
   794     {
       
   795         $name = strtolower($name);
       
   796         $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER);
       
   797         if ($name == 'dn') {
       
   798             /**
       
   799              * @see Zend_Ldap_Exception
       
   800              */
       
   801             require_once 'Zend/Ldap/Exception.php';
       
   802             throw new Zend_Ldap_Exception(null, 'DN cannot be changed.');
       
   803         }
       
   804         else if (array_key_exists($name, $rdn)) {
       
   805             /**
       
   806              * @see Zend_Ldap_Exception
       
   807              */
       
   808             require_once 'Zend/Ldap/Exception.php';
       
   809             throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN');
       
   810         } else if (in_array($name, self::$_systemAttributes)) {
       
   811             /**
       
   812              * @see Zend_Ldap_Exception
       
   813              */
       
   814             require_once 'Zend/Ldap/Exception.php';
       
   815             throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only');
       
   816         }
       
   817         else return true;
       
   818     }
       
   819 
       
   820     /**
       
   821      * Sets a LDAP attribute.
       
   822      *
       
   823      * This is an offline method.
       
   824      *
       
   825      * @param  string $name
       
   826      * @param  mixed  $value
       
   827      * @return null
       
   828      * @throws Zend_Ldap_Exception
       
   829      */
       
   830     public function __set($name, $value)
       
   831     {
       
   832         $this->setAttribute($name, $value);
       
   833     }
       
   834 
       
   835     /**
       
   836      * Deletes a LDAP attribute.
       
   837      *
       
   838      * This method deletes the attribute.
       
   839      *
       
   840      * This is an offline method.
       
   841      *
       
   842      * @param  string $name
       
   843      * @return null
       
   844      * @throws Zend_Ldap_Exception
       
   845      */
       
   846     public function __unset($name)
       
   847     {
       
   848         $this->deleteAttribute($name);
       
   849     }
       
   850 
       
   851     /**
       
   852      * Sets a LDAP attribute.
       
   853      * Implements ArrayAccess.
       
   854      *
       
   855      * This is an offline method.
       
   856      *
       
   857      * @param  string $name
       
   858      * @param  mixed  $value
       
   859      * @return null
       
   860      * @throws Zend_Ldap_Exception
       
   861      */
       
   862     public function offsetSet($name, $value)
       
   863     {
       
   864         $this->setAttribute($name, $value);
       
   865     }
       
   866 
       
   867     /**
       
   868      * Deletes a LDAP attribute.
       
   869      * Implements ArrayAccess.
       
   870      *
       
   871      * This method deletes the attribute.
       
   872      *
       
   873      * This is an offline method.
       
   874      *
       
   875      * @param  string $name
       
   876      * @return null
       
   877      * @throws Zend_Ldap_Exception
       
   878      */
       
   879     public function offsetUnset($name)
       
   880     {
       
   881         $this->deleteAttribute($name);
       
   882     }
       
   883 
       
   884     /**
       
   885      * Check if node exists on LDAP.
       
   886      *
       
   887      * This is an online method.
       
   888      *
       
   889      * @param  Zend_Ldap $ldap
       
   890      * @return boolean
       
   891      * @throws Zend_Ldap_Exception
       
   892      */
       
   893     public function exists(Zend_Ldap $ldap = null)
       
   894     {
       
   895         if ($ldap !== null) {
       
   896             $this->attachLdap($ldap);
       
   897         }
       
   898         $ldap = $this->getLdap();
       
   899         return $ldap->exists($this->_getDn());
       
   900     }
       
   901 
       
   902     /**
       
   903      * Reload node attributes from LDAP.
       
   904      *
       
   905      * This is an online method.
       
   906      *
       
   907      * @param  Zend_Ldap $ldap
       
   908      * @return Zend_Ldap_Node Provides a fluid interface
       
   909      * @throws Zend_Ldap_Exception
       
   910      */
       
   911     public function reload(Zend_Ldap $ldap = null)
       
   912     {
       
   913         if ($ldap !== null) {
       
   914             $this->attachLdap($ldap);
       
   915         }
       
   916         $ldap = $this->getLdap();
       
   917         parent::reload($ldap);
       
   918         return $this;
       
   919     }
       
   920 
       
   921     /**
       
   922      * Search current subtree with given options.
       
   923      *
       
   924      * This is an online method.
       
   925      *
       
   926      * @param  string|Zend_Ldap_Filter_Abstract $filter
       
   927      * @param  integer                          $scope
       
   928      * @param  string                           $sort
       
   929      * @return Zend_Ldap_Node_Collection
       
   930      * @throws Zend_Ldap_Exception
       
   931      */
       
   932     public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null)
       
   933     {
       
   934         /**
       
   935          * @see Zend_Ldap_Node_Collection
       
   936          */
       
   937         require_once 'Zend/Ldap/Node/Collection.php';
       
   938         return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort,
       
   939             'Zend_Ldap_Node_Collection');
       
   940     }
       
   941 
       
   942     /**
       
   943      * Count items in current subtree found by given filter.
       
   944      *
       
   945      * This is an online method.
       
   946      *
       
   947      * @param  string|Zend_Ldap_Filter_Abstract $filter
       
   948      * @param  integer                          $scope
       
   949      * @return integer
       
   950      * @throws Zend_Ldap_Exception
       
   951      */
       
   952     public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB)
       
   953     {
       
   954         return $this->getLdap()->count($filter, $this->_getDn(), $scope);
       
   955     }
       
   956 
       
   957     /**
       
   958      * Count children of current node.
       
   959      *
       
   960      * This is an online method.
       
   961      *
       
   962      * @return integer
       
   963      * @throws Zend_Ldap_Exception
       
   964      */
       
   965     public function countChildren()
       
   966     {
       
   967         return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE);
       
   968     }
       
   969 
       
   970     /**
       
   971      * Gets children of current node.
       
   972      *
       
   973      * This is an online method.
       
   974      *
       
   975      * @param  string|Zend_Ldap_Filter_Abstract $filter
       
   976      * @param  string                           $sort
       
   977      * @return Zend_Ldap_Node_Collection
       
   978      * @throws Zend_Ldap_Exception
       
   979      */
       
   980     public function searchChildren($filter, $sort = null)
       
   981     {
       
   982         return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort);
       
   983     }
       
   984 
       
   985     /**
       
   986      * Checks if current node has children.
       
   987      * Returns whether the current element has children.
       
   988      *
       
   989      * Can be used offline but returns false if children have not been retrieved yet.
       
   990      *
       
   991      * @return boolean
       
   992      * @throws Zend_Ldap_Exception
       
   993      */
       
   994     public function hasChildren()
       
   995     {
       
   996         if (!is_array($this->_children)) {
       
   997             if ($this->isAttached()) {
       
   998                 return ($this->countChildren() > 0);
       
   999             } else {
       
  1000                 return false;
       
  1001             }
       
  1002         } else {
       
  1003             return (count($this->_children) > 0);
       
  1004         }
       
  1005     }
       
  1006 
       
  1007     /**
       
  1008      * Returns the children for the current node.
       
  1009      *
       
  1010      * Can be used offline but returns an empty array if children have not been retrieved yet.
       
  1011      *
       
  1012      * @return Zend_Ldap_Node_ChildrenIterator
       
  1013      * @throws Zend_Ldap_Exception
       
  1014      */
       
  1015     public function getChildren()
       
  1016     {
       
  1017         if (!is_array($this->_children)) {
       
  1018             $this->_children = array();
       
  1019             if ($this->isAttached()) {
       
  1020                 $children = $this->searchChildren('(objectClass=*)', null);
       
  1021                 foreach ($children as $child) {
       
  1022                     $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child;
       
  1023                 }
       
  1024             }
       
  1025         }
       
  1026         /**
       
  1027          * @see Zend_Ldap_Node_ChildrenIterator
       
  1028          */
       
  1029         require_once 'Zend/Ldap/Node/ChildrenIterator.php';
       
  1030         return new Zend_Ldap_Node_ChildrenIterator($this->_children);
       
  1031     }
       
  1032 
       
  1033     /**
       
  1034      * Returns the parent of the current node.
       
  1035      *
       
  1036      * @param  Zend_Ldap $ldap
       
  1037      * @return Zend_Ldap_Node
       
  1038      * @throws Zend_Ldap_Exception
       
  1039      */
       
  1040     public function getParent(Zend_Ldap $ldap = null)
       
  1041     {
       
  1042         if ($ldap !== null) {
       
  1043             $this->attachLdap($ldap);
       
  1044         }
       
  1045         $ldap = $this->getLdap();
       
  1046         $parentDn = $this->_getDn()->getParentDn(1);
       
  1047         return self::fromLdap($parentDn, $ldap);
       
  1048     }
       
  1049 
       
  1050     /**
       
  1051      * Return the current attribute.
       
  1052      * Implements Iterator
       
  1053      *
       
  1054      * @return array
       
  1055      */
       
  1056     public function current()
       
  1057     {
       
  1058         return $this;
       
  1059     }
       
  1060 
       
  1061     /**
       
  1062      * Return the attribute name.
       
  1063      * Implements Iterator
       
  1064      *
       
  1065      * @return string
       
  1066      */
       
  1067     public function key()
       
  1068     {
       
  1069         return $this->getRdnString();
       
  1070     }
       
  1071 
       
  1072     /**
       
  1073      * Move forward to next attribute.
       
  1074      * Implements Iterator
       
  1075      */
       
  1076     public function next()
       
  1077     {
       
  1078         $this->_iteratorRewind = false;
       
  1079     }
       
  1080 
       
  1081     /**
       
  1082      * Rewind the Iterator to the first attribute.
       
  1083      * Implements Iterator
       
  1084      */
       
  1085     public function rewind()
       
  1086     {
       
  1087         $this->_iteratorRewind = true;
       
  1088     }
       
  1089 
       
  1090     /**
       
  1091      * Check if there is a current attribute
       
  1092      * after calls to rewind() or next().
       
  1093      * Implements Iterator
       
  1094      *
       
  1095      * @return boolean
       
  1096      */
       
  1097     public function valid()
       
  1098     {
       
  1099         return $this->_iteratorRewind;
       
  1100     }
       
  1101 }