File indexing completed on 2024-12-22 05:36:49
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Ldap 0017 * @subpackage Node 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * @see Zend_Ldap 0025 */ 0026 // require_once 'Zend/Ldap.php'; 0027 /** 0028 * @see Zend_Ldap_Node_Abstract 0029 */ 0030 // require_once 'Zend/Ldap/Node/Abstract.php'; 0031 0032 /** 0033 * Zend_Ldap_Node provides an object oriented view into a LDAP node. 0034 * 0035 * @category Zend 0036 * @package Zend_Ldap 0037 * @subpackage Node 0038 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0039 * @license http://framework.zend.com/license/new-bsd New BSD License 0040 */ 0041 class Zend_Ldap_Node extends Zend_Ldap_Node_Abstract implements Iterator, RecursiveIterator 0042 { 0043 /** 0044 * Holds the node's new DN if node is renamed. 0045 * 0046 * @var Zend_Ldap_Dn 0047 */ 0048 protected $_newDn; 0049 /** 0050 * Holds the node's orginal attributes (as loaded). 0051 * 0052 * @var array 0053 */ 0054 protected $_originalData; 0055 /** 0056 * This node will be added 0057 * 0058 * @var boolean 0059 */ 0060 protected $_new; 0061 /** 0062 * This node will be deleted 0063 * 0064 * @var boolean 0065 */ 0066 protected $_delete; 0067 /** 0068 * Holds the connection to the LDAP server if in connected mode. 0069 * 0070 * @var Zend_Ldap 0071 */ 0072 protected $_ldap; 0073 0074 /** 0075 * Holds an array of the current node's children. 0076 * 0077 * @var array 0078 */ 0079 protected $_children; 0080 0081 /** 0082 * Controls iteration status 0083 * 0084 * @var boolean 0085 */ 0086 private $_iteratorRewind = false; 0087 0088 /** 0089 * Constructor. 0090 * 0091 * Constructor is protected to enforce the use of factory methods. 0092 * 0093 * @param Zend_Ldap_Dn $dn 0094 * @param array $data 0095 * @param boolean $fromDataSource 0096 * @param Zend_Ldap $ldap 0097 * @throws Zend_Ldap_Exception 0098 */ 0099 protected function __construct(Zend_Ldap_Dn $dn, array $data, $fromDataSource, Zend_Ldap $ldap = null) 0100 { 0101 parent::__construct($dn, $data, $fromDataSource); 0102 if ($ldap !== null) $this->attachLdap($ldap); 0103 else $this->detachLdap(); 0104 } 0105 0106 /** 0107 * Serialization callback 0108 * 0109 * Only DN and attributes will be serialized. 0110 * 0111 * @return array 0112 */ 0113 public function __sleep() 0114 { 0115 return array('_dn', '_currentData', '_newDn', '_originalData', 0116 '_new', '_delete', '_children'); 0117 } 0118 0119 /** 0120 * Deserialization callback 0121 * 0122 * Enforces a detached node. 0123 * 0124 * @return null 0125 */ 0126 public function __wakeup() 0127 { 0128 $this->detachLdap(); 0129 } 0130 0131 /** 0132 * Gets the current LDAP connection. 0133 * 0134 * @return Zend_Ldap 0135 * @throws Zend_Ldap_Exception 0136 */ 0137 public function getLdap() 0138 { 0139 if ($this->_ldap === null) { 0140 /** 0141 * @see Zend_Ldap_Exception 0142 */ 0143 // require_once 'Zend/Ldap/Exception.php'; 0144 throw new Zend_Ldap_Exception(null, 'No LDAP connection specified.', Zend_Ldap_Exception::LDAP_OTHER); 0145 } 0146 else return $this->_ldap; 0147 } 0148 0149 /** 0150 * Attach node to an LDAP connection 0151 * 0152 * This is an offline method. 0153 * 0154 * @uses Zend_Ldap_Dn::isChildOf() 0155 * @param Zend_Ldap $ldap 0156 * @return Zend_Ldap_Node Provides a fluent interface 0157 * @throws Zend_Ldap_Exception 0158 */ 0159 public function attachLdap(Zend_Ldap $ldap) 0160 { 0161 if (!Zend_Ldap_Dn::isChildOf($this->_getDn(), $ldap->getBaseDn())) { 0162 /** 0163 * @see Zend_Ldap_Exception 0164 */ 0165 // require_once 'Zend/Ldap/Exception.php'; 0166 throw new Zend_Ldap_Exception(null, 'LDAP connection is not responsible for given node.', 0167 Zend_Ldap_Exception::LDAP_OTHER); 0168 } 0169 0170 if ($ldap !== $this->_ldap) { 0171 $this->_ldap = $ldap; 0172 if (is_array($this->_children)) { 0173 foreach ($this->_children as $child) { 0174 /* @var Zend_Ldap_Node $child */ 0175 $child->attachLdap($ldap); 0176 } 0177 } 0178 } 0179 return $this; 0180 } 0181 0182 /** 0183 * Detach node from LDAP connection 0184 * 0185 * This is an offline method. 0186 * 0187 * @return Zend_Ldap_Node Provides a fluent interface 0188 */ 0189 public function detachLdap() 0190 { 0191 $this->_ldap = null; 0192 if (is_array($this->_children)) { 0193 foreach ($this->_children as $child) { 0194 /* @var Zend_Ldap_Node $child */ 0195 $child->detachLdap(); 0196 } 0197 } 0198 return $this; 0199 } 0200 0201 /** 0202 * Checks if the current node is attached to a LDAP server. 0203 * 0204 * This is an offline method. 0205 * 0206 * @return boolean 0207 */ 0208 public function isAttached() 0209 { 0210 return ($this->_ldap !== null); 0211 } 0212 0213 /** 0214 * @param array $data 0215 * @param boolean $fromDataSource 0216 * @throws Zend_Ldap_Exception 0217 */ 0218 protected function _loadData(array $data, $fromDataSource) 0219 { 0220 parent::_loadData($data, $fromDataSource); 0221 if ($fromDataSource === true) { 0222 $this->_originalData = $data; 0223 } else { 0224 $this->_originalData = array(); 0225 } 0226 $this->_children = null; 0227 $this->_markAsNew(($fromDataSource === true) ? false : true); 0228 $this->_markAsToBeDeleted(false); 0229 } 0230 0231 /** 0232 * Factory method to create a new detached Zend_Ldap_Node for a given DN. 0233 * 0234 * @param string|array|Zend_Ldap_Dn $dn 0235 * @param array $objectClass 0236 * @return Zend_Ldap_Node 0237 * @throws Zend_Ldap_Exception 0238 */ 0239 public static function create($dn, array $objectClass = array()) 0240 { 0241 if (is_string($dn) || is_array($dn)) { 0242 $dn = Zend_Ldap_Dn::factory($dn); 0243 } else if ($dn instanceof Zend_Ldap_Dn) { 0244 $dn = clone $dn; 0245 } else { 0246 /** 0247 * @see Zend_Ldap_Exception 0248 */ 0249 // require_once 'Zend/Ldap/Exception.php'; 0250 throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.'); 0251 } 0252 $new = new self($dn, array(), false, null); 0253 $new->_ensureRdnAttributeValues(); 0254 $new->setAttribute('objectClass', $objectClass); 0255 return $new; 0256 } 0257 0258 /** 0259 * Factory method to create an attached Zend_Ldap_Node for a given DN. 0260 * 0261 * @param string|array|Zend_Ldap_Dn $dn 0262 * @param Zend_Ldap $ldap 0263 * @return Zend_Ldap_Node|null 0264 * @throws Zend_Ldap_Exception 0265 */ 0266 public static function fromLdap($dn, Zend_Ldap $ldap) 0267 { 0268 if (is_string($dn) || is_array($dn)) { 0269 $dn = Zend_Ldap_Dn::factory($dn); 0270 } else if ($dn instanceof Zend_Ldap_Dn) { 0271 $dn = clone $dn; 0272 } else { 0273 /** 0274 * @see Zend_Ldap_Exception 0275 */ 0276 // require_once 'Zend/Ldap/Exception.php'; 0277 throw new Zend_Ldap_Exception(null, '$dn is of a wrong data type.'); 0278 } 0279 $data = $ldap->getEntry($dn, array('*', '+'), true); 0280 if ($data === null) { 0281 return null; 0282 } 0283 $entry = new self($dn, $data, true, $ldap); 0284 return $entry; 0285 } 0286 0287 /** 0288 * Factory method to create a detached Zend_Ldap_Node from array data. 0289 * 0290 * @param array $data 0291 * @param boolean $fromDataSource 0292 * @return Zend_Ldap_Node 0293 * @throws Zend_Ldap_Exception 0294 */ 0295 public static function fromArray(array $data, $fromDataSource = false) 0296 { 0297 if (!array_key_exists('dn', $data)) { 0298 /** 0299 * @see Zend_Ldap_Exception 0300 */ 0301 // require_once 'Zend/Ldap/Exception.php'; 0302 throw new Zend_Ldap_Exception(null, '\'dn\' key is missing in array.'); 0303 } 0304 if (is_string($data['dn']) || is_array($data['dn'])) { 0305 $dn = Zend_Ldap_Dn::factory($data['dn']); 0306 } else if ($data['dn'] instanceof Zend_Ldap_Dn) { 0307 $dn = clone $data['dn']; 0308 } else { 0309 /** 0310 * @see Zend_Ldap_Exception 0311 */ 0312 // require_once 'Zend/Ldap/Exception.php'; 0313 throw new Zend_Ldap_Exception(null, '\'dn\' key is of a wrong data type.'); 0314 } 0315 $fromDataSource = ($fromDataSource === true) ? true : false; 0316 $new = new self($dn, $data, $fromDataSource, null); 0317 $new->_ensureRdnAttributeValues(); 0318 return $new; 0319 } 0320 0321 /** 0322 * Ensures that teh RDN attributes are correctly set. 0323 * 0324 * @param boolean $overwrite True to overwrite the RDN attributes 0325 * @return void 0326 */ 0327 protected function _ensureRdnAttributeValues($overwrite = false) 0328 { 0329 foreach ($this->getRdnArray() as $key => $value) { 0330 if (!array_key_exists($key, $this->_currentData) || $overwrite) { 0331 Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, false); 0332 } else if (!in_array($value, $this->_currentData[$key])) { 0333 Zend_Ldap_Attribute::setAttribute($this->_currentData, $key, $value, true); 0334 } 0335 } 0336 } 0337 0338 /** 0339 * Marks this node as new. 0340 * 0341 * Node will be added (instead of updated) on calling update() if $new is true. 0342 * 0343 * @param boolean $new 0344 */ 0345 protected function _markAsNew($new) 0346 { 0347 $this->_new = ($new === false) ? false : true; 0348 } 0349 0350 /** 0351 * Tells if the node is consiedered as new (not present on the server) 0352 * 0353 * Please note, that this doesn't tell you if the node is present on the server. 0354 * Use {@link exits()} to see if a node is already there. 0355 * 0356 * @return boolean 0357 */ 0358 public function isNew() 0359 { 0360 return $this->_new; 0361 } 0362 0363 /** 0364 * Marks this node as to be deleted. 0365 * 0366 * Node will be deleted on calling update() if $delete is true. 0367 * 0368 * @param boolean $delete 0369 */ 0370 protected function _markAsToBeDeleted($delete) 0371 { 0372 $this->_delete = ($delete === true) ? true : false; 0373 } 0374 0375 0376 /** 0377 * Is this node going to be deleted once update() is called? 0378 * 0379 * @return boolean 0380 */ 0381 public function willBeDeleted() 0382 { 0383 return $this->_delete; 0384 } 0385 0386 /** 0387 * Marks this node as to be deleted 0388 * 0389 * Node will be deleted on calling update() if $delete is true. 0390 * 0391 * @return Zend_Ldap_Node Provides a fluent interface 0392 */ 0393 public function delete() 0394 { 0395 $this->_markAsToBeDeleted(true); 0396 return $this; 0397 } 0398 0399 /** 0400 * Is this node going to be moved once update() is called? 0401 * 0402 * @return boolean 0403 */ 0404 public function willBeMoved() 0405 { 0406 if ($this->isNew() || $this->willBeDeleted()) { 0407 return false; 0408 } else if ($this->_newDn !== null) { 0409 return ($this->_dn != $this->_newDn); 0410 } else { 0411 return false; 0412 } 0413 } 0414 0415 /** 0416 * Sends all pending changes to the LDAP server 0417 * 0418 * @param Zend_Ldap $ldap 0419 * @return Zend_Ldap_Node Provides a fluent interface 0420 * @throws Zend_Ldap_Exception 0421 */ 0422 public function update(Zend_Ldap $ldap = null) 0423 { 0424 if ($ldap !== null) { 0425 $this->attachLdap($ldap); 0426 } 0427 $ldap = $this->getLdap(); 0428 if (!($ldap instanceof Zend_Ldap)) { 0429 /** 0430 * @see Zend_Ldap_Exception 0431 */ 0432 // require_once 'Zend/Ldap/Exception.php'; 0433 throw new Zend_Ldap_Exception(null, 'No LDAP connection available'); 0434 } 0435 0436 if ($this->willBeDeleted()) { 0437 if ($ldap->exists($this->_dn)) { 0438 $this->_preDelete(); 0439 $ldap->delete($this->_dn); 0440 $this->_postDelete(); 0441 } 0442 return $this; 0443 } 0444 0445 if ($this->isNew()) { 0446 $this->_preAdd(); 0447 $data = $this->getData(); 0448 $ldap->add($this->_getDn(), $data); 0449 $this->_loadData($data, true); 0450 $this->_postAdd(); 0451 return $this; 0452 } 0453 0454 $changedData = $this->getChangedData(); 0455 if ($this->willBeMoved()) { 0456 $this->_preRename(); 0457 $recursive = $this->hasChildren(); 0458 $ldap->rename($this->_dn, $this->_newDn, $recursive, false); 0459 foreach ($this->_newDn->getRdn() as $key => $value) { 0460 if (array_key_exists($key, $changedData)) { 0461 unset($changedData[$key]); 0462 } 0463 } 0464 $this->_dn = $this->_newDn; 0465 $this->_newDn = null; 0466 $this->_postRename(); 0467 } 0468 if (count($changedData) > 0) { 0469 $this->_preUpdate(); 0470 $ldap->update($this->_getDn(), $changedData); 0471 $this->_postUpdate(); 0472 } 0473 $this->_originalData = $this->_currentData; 0474 return $this; 0475 } 0476 0477 /** 0478 * Gets the DN of the current node as a Zend_Ldap_Dn. 0479 * 0480 * This is an offline method. 0481 * 0482 * @return Zend_Ldap_Dn 0483 */ 0484 protected function _getDn() 0485 { 0486 return ($this->_newDn === null) ? parent::_getDn() : $this->_newDn; 0487 } 0488 0489 /** 0490 * Gets the current DN of the current node as a Zend_Ldap_Dn. 0491 * The method returns a clone of the node's DN to prohibit modification. 0492 * 0493 * This is an offline method. 0494 * 0495 * @return Zend_Ldap_Dn 0496 */ 0497 public function getCurrentDn() 0498 { 0499 $dn = clone parent::_getDn(); 0500 return $dn; 0501 } 0502 0503 /** 0504 * Sets the new DN for this node 0505 * 0506 * This is an offline method. 0507 * 0508 * @param Zend_Ldap_Dn|string|array $newDn 0509 * @throws Zend_Ldap_Exception 0510 * @return Zend_Ldap_Node Provides a fluent interface 0511 */ 0512 public function setDn($newDn) 0513 { 0514 if ($newDn instanceof Zend_Ldap_Dn) { 0515 $this->_newDn = clone $newDn; 0516 } else { 0517 $this->_newDn = Zend_Ldap_Dn::factory($newDn); 0518 } 0519 $this->_ensureRdnAttributeValues(true); 0520 return $this; 0521 } 0522 0523 /** 0524 * {@see setDn()} 0525 * 0526 * This is an offline method. 0527 * 0528 * @param Zend_Ldap_Dn|string|array $newDn 0529 * @throws Zend_Ldap_Exception 0530 * @return Zend_Ldap_Node Provides a fluent interface 0531 */ 0532 public function move($newDn) 0533 { 0534 return $this->setDn($newDn); 0535 } 0536 0537 /** 0538 * {@see setDn()} 0539 * 0540 * This is an offline method. 0541 * 0542 * @param Zend_Ldap_Dn|string|array $newDn 0543 * @throws Zend_Ldap_Exception 0544 * @return Zend_Ldap_Node Provides a fluent interface 0545 */ 0546 public function rename($newDn) 0547 { 0548 return $this->setDn($newDn); 0549 } 0550 0551 /** 0552 * Sets the objectClass. 0553 * 0554 * This is an offline method. 0555 * 0556 * @param array|string $value 0557 * @return Zend_Ldap_Node Provides a fluent interface 0558 * @throws Zend_Ldap_Exception 0559 */ 0560 public function setObjectClass($value) 0561 { 0562 $this->setAttribute('objectClass', $value); 0563 return $this; 0564 } 0565 0566 /** 0567 * Appends to the objectClass. 0568 * 0569 * This is an offline method. 0570 * 0571 * @param array|string $value 0572 * @return Zend_Ldap_Node Provides a fluent interface 0573 * @throws Zend_Ldap_Exception 0574 */ 0575 public function appendObjectClass($value) 0576 { 0577 $this->appendToAttribute('objectClass', $value); 0578 return $this; 0579 } 0580 0581 /** 0582 * Returns a LDIF representation of the current node 0583 * 0584 * @param array $options Additional options used during encoding 0585 * @return string 0586 */ 0587 public function toLdif(array $options = array()) 0588 { 0589 $attributes = array_merge(array('dn' => $this->getDnString()), $this->getData(false)); 0590 /** 0591 * Zend_Ldap_Ldif_Encoder 0592 */ 0593 // require_once 'Zend/Ldap/Ldif/Encoder.php'; 0594 return Zend_Ldap_Ldif_Encoder::encode($attributes, $options); 0595 } 0596 0597 /** 0598 * Gets changed node data. 0599 * 0600 * The array contains all changed attributes. 0601 * This format can be used in {@link Zend_Ldap::add()} and {@link Zend_Ldap::update()}. 0602 * 0603 * This is an offline method. 0604 * 0605 * @return array 0606 */ 0607 public function getChangedData() 0608 { 0609 $changed = array(); 0610 foreach ($this->_currentData as $key => $value) { 0611 if (!array_key_exists($key, $this->_originalData) && !empty($value)) { 0612 $changed[$key] = $value; 0613 } else if ($this->_originalData[$key] !== $this->_currentData[$key]) { 0614 $changed[$key] = $value; 0615 } 0616 } 0617 return $changed; 0618 } 0619 0620 /** 0621 * Returns all changes made. 0622 * 0623 * This is an offline method. 0624 * 0625 * @return array 0626 */ 0627 public function getChanges() 0628 { 0629 $changes = array( 0630 'add' => array(), 0631 'delete' => array(), 0632 'replace' => array()); 0633 foreach ($this->_currentData as $key => $value) { 0634 if (!array_key_exists($key, $this->_originalData) && !empty($value)) { 0635 $changes['add'][$key] = $value; 0636 } else if (count($this->_originalData[$key]) === 0 && !empty($value)) { 0637 $changes['add'][$key] = $value; 0638 } else if ($this->_originalData[$key] !== $this->_currentData[$key]) { 0639 if (empty($value)) { 0640 $changes['delete'][$key] = $value; 0641 } else { 0642 $changes['replace'][$key] = $value; 0643 } 0644 } 0645 } 0646 return $changes; 0647 } 0648 0649 /** 0650 * Sets a LDAP attribute. 0651 * 0652 * This is an offline method. 0653 * 0654 * @param string $name 0655 * @param mixed $value 0656 * @return Zend_Ldap_Node Provides a fluent interface 0657 * @throws Zend_Ldap_Exception 0658 */ 0659 public function setAttribute($name, $value) 0660 { 0661 $this->_setAttribute($name, $value, false); 0662 return $this; 0663 } 0664 0665 /** 0666 * Appends to a LDAP attribute. 0667 * 0668 * This is an offline method. 0669 * 0670 * @param string $name 0671 * @param mixed $value 0672 * @return Zend_Ldap_Node Provides a fluent interface 0673 * @throws Zend_Ldap_Exception 0674 */ 0675 public function appendToAttribute($name, $value) 0676 { 0677 $this->_setAttribute($name, $value, true); 0678 return $this; 0679 } 0680 0681 /** 0682 * Checks if the attribute can be set and sets it accordingly. 0683 * 0684 * @param string $name 0685 * @param mixed $value 0686 * @param boolean $append 0687 * @throws Zend_Ldap_Exception 0688 */ 0689 protected function _setAttribute($name, $value, $append) 0690 { 0691 $this->_assertChangeableAttribute($name); 0692 Zend_Ldap_Attribute::setAttribute($this->_currentData, $name, $value, $append); 0693 } 0694 0695 /** 0696 * Sets a LDAP date/time attribute. 0697 * 0698 * This is an offline method. 0699 * 0700 * @param string $name 0701 * @param integer|array $value 0702 * @param boolean $utc 0703 * @return Zend_Ldap_Node Provides a fluent interface 0704 * @throws Zend_Ldap_Exception 0705 */ 0706 public function setDateTimeAttribute($name, $value, $utc = false) 0707 { 0708 $this->_setDateTimeAttribute($name, $value, $utc, false); 0709 return $this; 0710 } 0711 0712 /** 0713 * Appends to a LDAP date/time attribute. 0714 * 0715 * This is an offline method. 0716 * 0717 * @param string $name 0718 * @param integer|array $value 0719 * @param boolean $utc 0720 * @return Zend_Ldap_Node Provides a fluent interface 0721 * @throws Zend_Ldap_Exception 0722 */ 0723 public function appendToDateTimeAttribute($name, $value, $utc = false) 0724 { 0725 $this->_setDateTimeAttribute($name, $value, $utc, true); 0726 return $this; 0727 } 0728 0729 /** 0730 * Checks if the attribute can be set and sets it accordingly. 0731 * 0732 * @param string $name 0733 * @param integer|array $value 0734 * @param boolean $utc 0735 * @param boolean $append 0736 * @throws Zend_Ldap_Exception 0737 */ 0738 protected function _setDateTimeAttribute($name, $value, $utc, $append) 0739 { 0740 $this->_assertChangeableAttribute($name); 0741 Zend_Ldap_Attribute::setDateTimeAttribute($this->_currentData, $name, $value, $utc, $append); 0742 } 0743 0744 /** 0745 * Sets a LDAP password. 0746 * 0747 * @param string $password 0748 * @param string $hashType 0749 * @param string $attribName 0750 * @return Zend_Ldap_Node Provides a fluent interface 0751 * @throws Zend_Ldap_Exception 0752 */ 0753 public function setPasswordAttribute($password, $hashType = Zend_Ldap_Attribute::PASSWORD_HASH_MD5, 0754 $attribName = 'userPassword') 0755 { 0756 $this->_assertChangeableAttribute($attribName); 0757 Zend_Ldap_Attribute::setPassword($this->_currentData, $password, $hashType, $attribName); 0758 return $this; 0759 } 0760 0761 /** 0762 * Deletes a LDAP attribute. 0763 * 0764 * This method deletes the attribute. 0765 * 0766 * This is an offline method. 0767 * 0768 * @param string $name 0769 * @return Zend_Ldap_Node Provides a fluent interface 0770 * @throws Zend_Ldap_Exception 0771 */ 0772 public function deleteAttribute($name) 0773 { 0774 if ($this->existsAttribute($name, true)) { 0775 $this->_setAttribute($name, null, false); 0776 } 0777 return $this; 0778 } 0779 0780 /** 0781 * Removes duplicate values from a LDAP attribute 0782 * 0783 * @param string $attribName 0784 * @return void 0785 */ 0786 public function removeDuplicatesFromAttribute($attribName) 0787 { 0788 Zend_Ldap_Attribute::removeDuplicatesFromAttribute($this->_currentData, $attribName); 0789 } 0790 0791 /** 0792 * Remove given values from a LDAP attribute 0793 * 0794 * @param string $attribName 0795 * @param mixed|array $value 0796 * @return void 0797 */ 0798 public function removeFromAttribute($attribName, $value) 0799 { 0800 Zend_Ldap_Attribute::removeFromAttribute($this->_currentData, $attribName, $value); 0801 } 0802 0803 /** 0804 * @param string $name 0805 * @return boolean 0806 * @throws Zend_Ldap_Exception 0807 */ 0808 protected function _assertChangeableAttribute($name) 0809 { 0810 $name = strtolower($name); 0811 $rdn = $this->getRdnArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER); 0812 if ($name == 'dn') { 0813 /** 0814 * @see Zend_Ldap_Exception 0815 */ 0816 // require_once 'Zend/Ldap/Exception.php'; 0817 throw new Zend_Ldap_Exception(null, 'DN cannot be changed.'); 0818 } 0819 else if (array_key_exists($name, $rdn)) { 0820 /** 0821 * @see Zend_Ldap_Exception 0822 */ 0823 // require_once 'Zend/Ldap/Exception.php'; 0824 throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s part of the RDN'); 0825 } else if (in_array($name, self::$_systemAttributes)) { 0826 /** 0827 * @see Zend_Ldap_Exception 0828 */ 0829 // require_once 'Zend/Ldap/Exception.php'; 0830 throw new Zend_Ldap_Exception(null, 'Cannot change attribute because it\'s read-only'); 0831 } 0832 else return true; 0833 } 0834 0835 /** 0836 * Sets a LDAP attribute. 0837 * 0838 * This is an offline method. 0839 * 0840 * @param string $name 0841 * @param mixed $value 0842 * @return null 0843 * @throws Zend_Ldap_Exception 0844 */ 0845 public function __set($name, $value) 0846 { 0847 $this->setAttribute($name, $value); 0848 } 0849 0850 /** 0851 * Deletes a LDAP attribute. 0852 * 0853 * This method deletes the attribute. 0854 * 0855 * This is an offline method. 0856 * 0857 * @param string $name 0858 * @return null 0859 * @throws Zend_Ldap_Exception 0860 */ 0861 public function __unset($name) 0862 { 0863 $this->deleteAttribute($name); 0864 } 0865 0866 /** 0867 * Sets a LDAP attribute. 0868 * Implements ArrayAccess. 0869 * 0870 * This is an offline method. 0871 * 0872 * @param string $name 0873 * @param mixed $value 0874 * @return null 0875 * @throws Zend_Ldap_Exception 0876 */ 0877 public function offsetSet($name, $value) 0878 { 0879 $this->setAttribute($name, $value); 0880 } 0881 0882 /** 0883 * Deletes a LDAP attribute. 0884 * Implements ArrayAccess. 0885 * 0886 * This method deletes the attribute. 0887 * 0888 * This is an offline method. 0889 * 0890 * @param string $name 0891 * @return null 0892 * @throws Zend_Ldap_Exception 0893 */ 0894 public function offsetUnset($name) 0895 { 0896 $this->deleteAttribute($name); 0897 } 0898 0899 /** 0900 * Check if node exists on LDAP. 0901 * 0902 * This is an online method. 0903 * 0904 * @param Zend_Ldap $ldap 0905 * @return boolean 0906 * @throws Zend_Ldap_Exception 0907 */ 0908 public function exists(Zend_Ldap $ldap = null) 0909 { 0910 if ($ldap !== null) { 0911 $this->attachLdap($ldap); 0912 } 0913 $ldap = $this->getLdap(); 0914 return $ldap->exists($this->_getDn()); 0915 } 0916 0917 /** 0918 * Reload node attributes from LDAP. 0919 * 0920 * This is an online method. 0921 * 0922 * @param Zend_Ldap $ldap 0923 * @return Zend_Ldap_Node Provides a fluent interface 0924 * @throws Zend_Ldap_Exception 0925 */ 0926 public function reload(Zend_Ldap $ldap = null) 0927 { 0928 if ($ldap !== null) { 0929 $this->attachLdap($ldap); 0930 } 0931 $ldap = $this->getLdap(); 0932 parent::reload($ldap); 0933 return $this; 0934 } 0935 0936 /** 0937 * Search current subtree with given options. 0938 * 0939 * This is an online method. 0940 * 0941 * @param string|Zend_Ldap_Filter_Abstract $filter 0942 * @param integer $scope 0943 * @param string $sort 0944 * @return Zend_Ldap_Node_Collection 0945 * @throws Zend_Ldap_Exception 0946 */ 0947 public function searchSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB, $sort = null) 0948 { 0949 /** 0950 * @see Zend_Ldap_Node_Collection 0951 */ 0952 // require_once 'Zend/Ldap/Node/Collection.php'; 0953 return $this->getLdap()->search($filter, $this->_getDn(), $scope, array('*', '+'), $sort, 0954 'Zend_Ldap_Node_Collection'); 0955 } 0956 0957 /** 0958 * Count items in current subtree found by given filter. 0959 * 0960 * This is an online method. 0961 * 0962 * @param string|Zend_Ldap_Filter_Abstract $filter 0963 * @param integer $scope 0964 * @return integer 0965 * @throws Zend_Ldap_Exception 0966 */ 0967 public function countSubtree($filter, $scope = Zend_Ldap::SEARCH_SCOPE_SUB) 0968 { 0969 return $this->getLdap()->count($filter, $this->_getDn(), $scope); 0970 } 0971 0972 /** 0973 * Count children of current node. 0974 * 0975 * This is an online method. 0976 * 0977 * @return integer 0978 * @throws Zend_Ldap_Exception 0979 */ 0980 public function countChildren() 0981 { 0982 return $this->countSubtree('(objectClass=*)', Zend_Ldap::SEARCH_SCOPE_ONE); 0983 } 0984 0985 /** 0986 * Gets children of current node. 0987 * 0988 * This is an online method. 0989 * 0990 * @param string|Zend_Ldap_Filter_Abstract $filter 0991 * @param string $sort 0992 * @return Zend_Ldap_Node_Collection 0993 * @throws Zend_Ldap_Exception 0994 */ 0995 public function searchChildren($filter, $sort = null) 0996 { 0997 return $this->searchSubtree($filter, Zend_Ldap::SEARCH_SCOPE_ONE, $sort); 0998 } 0999 1000 /** 1001 * Checks if current node has children. 1002 * Returns whether the current element has children. 1003 * 1004 * Can be used offline but returns false if children have not been retrieved yet. 1005 * 1006 * @return boolean 1007 * @throws Zend_Ldap_Exception 1008 */ 1009 public function hasChildren() 1010 { 1011 if (!is_array($this->_children)) { 1012 if ($this->isAttached()) { 1013 return ($this->countChildren() > 0); 1014 } else { 1015 return false; 1016 } 1017 } else { 1018 return (count($this->_children) > 0); 1019 } 1020 } 1021 1022 /** 1023 * Returns the children for the current node. 1024 * 1025 * Can be used offline but returns an empty array if children have not been retrieved yet. 1026 * 1027 * @return Zend_Ldap_Node_ChildrenIterator 1028 * @throws Zend_Ldap_Exception 1029 */ 1030 public function getChildren() 1031 { 1032 if (!is_array($this->_children)) { 1033 $this->_children = array(); 1034 if ($this->isAttached()) { 1035 $children = $this->searchChildren('(objectClass=*)', null); 1036 foreach ($children as $child) { 1037 /* @var Zend_Ldap_Node $child */ 1038 $this->_children[$child->getRdnString(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER)] = $child; 1039 } 1040 } 1041 } 1042 /** 1043 * @see Zend_Ldap_Node_ChildrenIterator 1044 */ 1045 // require_once 'Zend/Ldap/Node/ChildrenIterator.php'; 1046 return new Zend_Ldap_Node_ChildrenIterator($this->_children); 1047 } 1048 1049 /** 1050 * Returns the parent of the current node. 1051 * 1052 * @param Zend_Ldap $ldap 1053 * @return Zend_Ldap_Node 1054 * @throws Zend_Ldap_Exception 1055 */ 1056 public function getParent(Zend_Ldap $ldap = null) 1057 { 1058 if ($ldap !== null) { 1059 $this->attachLdap($ldap); 1060 } 1061 $ldap = $this->getLdap(); 1062 $parentDn = $this->_getDn()->getParentDn(1); 1063 return self::fromLdap($parentDn, $ldap); 1064 } 1065 1066 /** 1067 * Return the current attribute. 1068 * Implements Iterator 1069 * 1070 * @return array 1071 */ 1072 public function current() 1073 { 1074 return $this; 1075 } 1076 1077 /** 1078 * Return the attribute name. 1079 * Implements Iterator 1080 * 1081 * @return string 1082 */ 1083 public function key() 1084 { 1085 return $this->getRdnString(); 1086 } 1087 1088 /** 1089 * Move forward to next attribute. 1090 * Implements Iterator 1091 */ 1092 public function next() 1093 { 1094 $this->_iteratorRewind = false; 1095 } 1096 1097 /** 1098 * Rewind the Iterator to the first attribute. 1099 * Implements Iterator 1100 */ 1101 public function rewind() 1102 { 1103 $this->_iteratorRewind = true; 1104 } 1105 1106 /** 1107 * Check if there is a current attribute 1108 * after calls to rewind() or next(). 1109 * Implements Iterator 1110 * 1111 * @return boolean 1112 */ 1113 public function valid() 1114 { 1115 return $this->_iteratorRewind; 1116 } 1117 1118 #################################################### 1119 # Empty method bodies for overriding in subclasses # 1120 #################################################### 1121 1122 /** 1123 * Allows pre-delete logic to be applied to node. 1124 * Subclasses may override this method. 1125 * 1126 * @return void 1127 */ 1128 protected function _preDelete() { } 1129 1130 /** 1131 * Allows post-delete logic to be applied to node. 1132 * Subclasses may override this method. 1133 * 1134 * @return void 1135 */ 1136 protected function _postDelete() { } 1137 1138 /** 1139 * Allows pre-add logic to be applied to node. 1140 * Subclasses may override this method. 1141 * 1142 * @return void 1143 */ 1144 protected function _preAdd() { } 1145 1146 /** 1147 * Allows post-add logic to be applied to node. 1148 * Subclasses may override this method. 1149 * 1150 * @return void 1151 */ 1152 protected function _postAdd() { } 1153 1154 /** 1155 * Allows pre-rename logic to be applied to node. 1156 * Subclasses may override this method. 1157 * 1158 * @return void 1159 */ 1160 protected function _preRename() { } 1161 1162 /** 1163 * Allows post-rename logic to be applied to node. 1164 * Subclasses may override this method. 1165 * 1166 * @return void 1167 */ 1168 protected function _postRename() { } 1169 1170 /** 1171 * Allows pre-update logic to be applied to node. 1172 * Subclasses may override this method. 1173 * 1174 * @return void 1175 */ 1176 protected function _preUpdate() { } 1177 1178 /** 1179 * Allows post-update logic to be applied to node. 1180 * Subclasses may override this method. 1181 * 1182 * @return void 1183 */ 1184 protected function _postUpdate() { } 1185 }