File indexing completed on 2025-01-26 05:29:38
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 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Zend_Ldap_Dn provides an API for DN manipulation 0024 * 0025 * @category Zend 0026 * @package Zend_Ldap 0027 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0028 * @license http://framework.zend.com/license/new-bsd New BSD License 0029 */ 0030 class Zend_Ldap_Dn implements ArrayAccess 0031 { 0032 const ATTR_CASEFOLD_NONE = 'none'; 0033 const ATTR_CASEFOLD_UPPER = 'upper'; 0034 const ATTR_CASEFOLD_LOWER = 'lower'; 0035 0036 /** 0037 * The default case fold to use 0038 * 0039 * @var string 0040 */ 0041 protected static $_defaultCaseFold = self::ATTR_CASEFOLD_NONE; 0042 0043 /** 0044 * The case fold used for this instance 0045 * 0046 * @var string 0047 */ 0048 protected $_caseFold; 0049 0050 /** 0051 * The DN data 0052 * 0053 * @var array 0054 */ 0055 protected $_dn; 0056 0057 /** 0058 * Creates a DN from an array or a string 0059 * 0060 * @param string|array $dn 0061 * @param string|null $caseFold 0062 * @return Zend_Ldap_Dn 0063 * @throws Zend_Ldap_Exception 0064 */ 0065 public static function factory($dn, $caseFold = null) 0066 { 0067 if (is_array($dn)) { 0068 return self::fromArray($dn, $caseFold); 0069 } else if (is_string($dn)) { 0070 return self::fromString($dn, $caseFold); 0071 } else { 0072 /** 0073 * Zend_Ldap_Exception 0074 */ 0075 // require_once 'Zend/Ldap/Exception.php'; 0076 throw new Zend_Ldap_Exception(null, 'Invalid argument type for $dn'); 0077 } 0078 } 0079 0080 /** 0081 * Creates a DN from a string 0082 * 0083 * @param string $dn 0084 * @param string|null $caseFold 0085 * @return Zend_Ldap_Dn 0086 * @throws Zend_Ldap_Exception 0087 */ 0088 public static function fromString($dn, $caseFold = null) 0089 { 0090 $dn = trim($dn); 0091 if (empty($dn)) { 0092 $dnArray = array(); 0093 } else { 0094 $dnArray = self::explodeDn((string)$dn); 0095 } 0096 return new self($dnArray, $caseFold); 0097 } 0098 0099 /** 0100 * Creates a DN from an array 0101 * 0102 * @param array $dn 0103 * @param string|null $caseFold 0104 * @return Zend_Ldap_Dn 0105 * @throws Zend_Ldap_Exception 0106 */ 0107 public static function fromArray(array $dn, $caseFold = null) 0108 { 0109 return new self($dn, $caseFold); 0110 } 0111 0112 /** 0113 * Constructor 0114 * 0115 * @param array $dn 0116 * @param string|null $caseFold 0117 */ 0118 protected function __construct(array $dn, $caseFold) 0119 { 0120 $this->_dn = $dn; 0121 $this->setCaseFold($caseFold); 0122 } 0123 0124 /** 0125 * Gets the RDN of the current DN 0126 * 0127 * @param string $caseFold 0128 * @return array 0129 * @throws Zend_Ldap_Exception if DN has no RDN (empty array) 0130 */ 0131 public function getRdn($caseFold = null) 0132 { 0133 $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold); 0134 return self::_caseFoldRdn($this->get(0, 1, $caseFold), null); 0135 } 0136 0137 /** 0138 * Gets the RDN of the current DN as a string 0139 * 0140 * @param string $caseFold 0141 * @return string 0142 * @throws Zend_Ldap_Exception if DN has no RDN (empty array) 0143 */ 0144 public function getRdnString($caseFold = null) 0145 { 0146 $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold); 0147 return self::implodeRdn($this->getRdn(), $caseFold); 0148 } 0149 0150 /** 0151 * Get the parent DN $levelUp levels up the tree 0152 * 0153 * @param int $levelUp 0154 * @return Zend_Ldap_Dn 0155 */ 0156 public function getParentDn($levelUp = 1) 0157 { 0158 $levelUp = (int)$levelUp; 0159 if ($levelUp < 1 || $levelUp >= count($this->_dn)) { 0160 /** 0161 * Zend_Ldap_Exception 0162 */ 0163 // require_once 'Zend/Ldap/Exception.php'; 0164 throw new Zend_Ldap_Exception(null, 'Cannot retrieve parent DN with given $levelUp'); 0165 } 0166 $newDn = array_slice($this->_dn, $levelUp); 0167 return new self($newDn, $this->_caseFold); 0168 } 0169 0170 /** 0171 * Get a DN part 0172 * 0173 * @param int $index 0174 * @param int $length 0175 * @param string $caseFold 0176 * @return array 0177 * @throws Zend_Ldap_Exception if index is illegal 0178 */ 0179 public function get($index, $length = 1, $caseFold = null) 0180 { 0181 $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold); 0182 $this->_assertIndex($index); 0183 $length = (int)$length; 0184 if ($length <= 0) { 0185 $length = 1; 0186 } 0187 if ($length === 1) { 0188 return self::_caseFoldRdn($this->_dn[$index], $caseFold); 0189 } 0190 else { 0191 return self::_caseFoldDn(array_slice($this->_dn, $index, $length, false), $caseFold); 0192 } 0193 } 0194 0195 /** 0196 * Set a DN part 0197 * 0198 * @param int $index 0199 * @param array $value 0200 * @return Zend_Ldap_Dn Provides a fluent interface 0201 * @throws Zend_Ldap_Exception if index is illegal 0202 */ 0203 public function set($index, array $value) 0204 { 0205 $this->_assertIndex($index); 0206 self::_assertRdn($value); 0207 $this->_dn[$index] = $value; 0208 return $this; 0209 } 0210 0211 /** 0212 * Remove a DN part 0213 * 0214 * @param int $index 0215 * @param int $length 0216 * @return Zend_Ldap_Dn Provides a fluent interface 0217 * @throws Zend_Ldap_Exception if index is illegal 0218 */ 0219 public function remove($index, $length = 1) 0220 { 0221 $this->_assertIndex($index); 0222 $length = (int)$length; 0223 if ($length <= 0) { 0224 $length = 1; 0225 } 0226 array_splice($this->_dn, $index, $length, null); 0227 return $this; 0228 } 0229 0230 /** 0231 * Append a DN part 0232 * 0233 * @param array $value 0234 * @return Zend_Ldap_Dn Provides a fluent interface 0235 */ 0236 public function append(array $value) 0237 { 0238 self::_assertRdn($value); 0239 $this->_dn[] = $value; 0240 return $this; 0241 } 0242 0243 /** 0244 * Prepend a DN part 0245 * 0246 * @param array $value 0247 * @return Zend_Ldap_Dn Provides a fluent interface 0248 */ 0249 public function prepend(array $value) 0250 { 0251 self::_assertRdn($value); 0252 array_unshift($this->_dn, $value); 0253 return $this; 0254 } 0255 0256 /** 0257 * Insert a DN part 0258 * 0259 * @param int $index 0260 * @param array $value 0261 * @return Zend_Ldap_Dn Provides a fluent interface 0262 * @throws Zend_Ldap_Exception if index is illegal 0263 */ 0264 public function insert($index, array $value) 0265 { 0266 $this->_assertIndex($index); 0267 self::_assertRdn($value); 0268 $first = array_slice($this->_dn, 0, $index + 1); 0269 $second = array_slice($this->_dn, $index + 1); 0270 $this->_dn = array_merge($first, array($value), $second); 0271 return $this; 0272 } 0273 0274 /** 0275 * Assert index is correct and usable 0276 * 0277 * @param mixed $index 0278 * @return boolean 0279 * @throws Zend_Ldap_Exception 0280 */ 0281 protected function _assertIndex($index) 0282 { 0283 if (!is_int($index)) { 0284 /** 0285 * Zend_Ldap_Exception 0286 */ 0287 // require_once 'Zend/Ldap/Exception.php'; 0288 throw new Zend_Ldap_Exception(null, 'Parameter $index must be an integer'); 0289 } 0290 if ($index < 0 || $index >= count($this->_dn)) { 0291 /** 0292 * Zend_Ldap_Exception 0293 */ 0294 // require_once 'Zend/Ldap/Exception.php'; 0295 throw new Zend_Ldap_Exception(null, 'Parameter $index out of bounds'); 0296 } 0297 return true; 0298 } 0299 0300 /** 0301 * Assert if value is in a correct RDN format 0302 * 0303 * @param array $value 0304 * @return boolean 0305 * @throws Zend_Ldap_Exception 0306 */ 0307 protected static function _assertRdn(array $value) 0308 { 0309 if (count($value)<1) { 0310 /** 0311 * Zend_Ldap_Exception 0312 */ 0313 // require_once 'Zend/Ldap/Exception.php'; 0314 throw new Zend_Ldap_Exception(null, 'RDN Array is malformed: it must have at least one item'); 0315 } 0316 0317 foreach (array_keys($value) as $key) { 0318 if (!is_string($key)) { 0319 /** 0320 * Zend_Ldap_Exception 0321 */ 0322 // require_once 'Zend/Ldap/Exception.php'; 0323 throw new Zend_Ldap_Exception(null, 'RDN Array is malformed: it must use string keys'); 0324 } 0325 } 0326 } 0327 0328 /** 0329 * Sets the case fold 0330 * 0331 * @param string|null $caseFold 0332 */ 0333 public function setCaseFold($caseFold) 0334 { 0335 $this->_caseFold = self::_sanitizeCaseFold($caseFold, self::$_defaultCaseFold); 0336 } 0337 0338 /** 0339 * Return DN as a string 0340 * 0341 * @param string $caseFold 0342 * @return string 0343 * @throws Zend_Ldap_Exception 0344 */ 0345 public function toString($caseFold = null) 0346 { 0347 $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold); 0348 return self::implodeDn($this->_dn, $caseFold); 0349 } 0350 0351 /** 0352 * Return DN as an array 0353 * 0354 * @param string $caseFold 0355 * @return array 0356 */ 0357 public function toArray($caseFold = null) 0358 { 0359 $caseFold = self::_sanitizeCaseFold($caseFold, $this->_caseFold); 0360 0361 if ($caseFold === self::ATTR_CASEFOLD_NONE) { 0362 return $this->_dn; 0363 } else { 0364 return self::_caseFoldDn($this->_dn, $caseFold); 0365 } 0366 } 0367 0368 /** 0369 * Do a case folding on a RDN 0370 * 0371 * @param array $part 0372 * @param string $caseFold 0373 * @return array 0374 */ 0375 protected static function _caseFoldRdn(array $part, $caseFold) 0376 { 0377 switch ($caseFold) { 0378 case self::ATTR_CASEFOLD_UPPER: 0379 return array_change_key_case($part, CASE_UPPER); 0380 case self::ATTR_CASEFOLD_LOWER: 0381 return array_change_key_case($part, CASE_LOWER); 0382 case self::ATTR_CASEFOLD_NONE: 0383 default: 0384 return $part; 0385 } 0386 } 0387 0388 /** 0389 * Do a case folding on a DN ort part of it 0390 * 0391 * @param array $dn 0392 * @param string $caseFold 0393 * @return array 0394 */ 0395 protected static function _caseFoldDn(array $dn, $caseFold) 0396 { 0397 $return = array(); 0398 foreach ($dn as $part) { 0399 $return[] = self::_caseFoldRdn($part, $caseFold); 0400 } 0401 return $return; 0402 } 0403 0404 /** 0405 * Cast to string representation {@see toString()} 0406 * 0407 * @return string 0408 */ 0409 public function __toString() 0410 { 0411 return $this->toString(); 0412 } 0413 0414 /** 0415 * Required by the ArrayAccess implementation 0416 * 0417 * @param int $offset 0418 * @return boolean 0419 */ 0420 public function offsetExists($offset) 0421 { 0422 $offset = (int)$offset; 0423 if ($offset < 0 || $offset >= count($this->_dn)) { 0424 return false; 0425 } else { 0426 return true; 0427 } 0428 } 0429 0430 /** 0431 * Proxy to {@see get()} 0432 * Required by the ArrayAccess implementation 0433 * 0434 * @param int $offset 0435 * @return array 0436 */ 0437 public function offsetGet($offset) 0438 { 0439 return $this->get($offset, 1, null); 0440 } 0441 0442 /** 0443 * Proxy to {@see set()} 0444 * Required by the ArrayAccess implementation 0445 * 0446 * @param int $offset 0447 * @param array $value 0448 */ 0449 public function offsetSet($offset, $value) 0450 { 0451 $this->set($offset, $value); 0452 } 0453 0454 /** 0455 * Proxy to {@see remove()} 0456 * Required by the ArrayAccess implementation 0457 * 0458 * @param int $offset 0459 */ 0460 public function offsetUnset($offset) 0461 { 0462 $this->remove($offset, 1); 0463 } 0464 0465 /** 0466 * Sets the default case fold 0467 * 0468 * @param string $caseFold 0469 */ 0470 public static function setDefaultCaseFold($caseFold) 0471 { 0472 self::$_defaultCaseFold = self::_sanitizeCaseFold($caseFold, self::ATTR_CASEFOLD_NONE); 0473 } 0474 0475 /** 0476 * Sanitizes the case fold 0477 * 0478 * @param string $caseFold 0479 * @return string 0480 */ 0481 protected static function _sanitizeCaseFold($caseFold, $default) 0482 { 0483 switch ($caseFold) { 0484 case self::ATTR_CASEFOLD_NONE: 0485 case self::ATTR_CASEFOLD_UPPER: 0486 case self::ATTR_CASEFOLD_LOWER: 0487 return $caseFold; 0488 break; 0489 default: 0490 return $default; 0491 break; 0492 } 0493 } 0494 0495 /** 0496 * Escapes a DN value according to RFC 2253 0497 * 0498 * Escapes the given VALUES according to RFC 2253 so that they can be safely used in LDAP DNs. 0499 * The characters ",", "+", """, "\", "<", ">", ";", "#", " = " with a special meaning in RFC 2252 0500 * are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair. 0501 * Finally all leading and trailing spaces are converted to sequences of \20. 0502 * @see Net_LDAP2_Util::escape_dn_value() from Benedikt Hallinger <beni@php.net> 0503 * @link http://pear.php.net/package/Net_LDAP2 0504 * @author Benedikt Hallinger <beni@php.net> 0505 * 0506 * @param string|array $values An array containing the DN values that should be escaped 0507 * @return array The array $values, but escaped 0508 */ 0509 public static function escapeValue($values = array()) 0510 { 0511 /** 0512 * @see Zend_Ldap_Converter 0513 */ 0514 // require_once 'Zend/Ldap/Converter.php'; 0515 0516 if (!is_array($values)) $values = array($values); 0517 foreach ($values as $key => $val) { 0518 // Escaping of filter meta characters 0519 $val = str_replace(array('\\', ',', '+', '"', '<', '>', ';', '#', '=', ), 0520 array('\\\\', '\,', '\+', '\"', '\<', '\>', '\;', '\#', '\='), $val); 0521 $val = Zend_Ldap_Converter::ascToHex32($val); 0522 0523 // Convert all leading and trailing spaces to sequences of \20. 0524 if (preg_match('/^(\s*)(.+?)(\s*)$/', $val, $matches)) { 0525 $val = $matches[2]; 0526 for ($i = 0; $i<strlen($matches[1]); $i++) { 0527 $val = '\20' . $val; 0528 } 0529 for ($i = 0; $i<strlen($matches[3]); $i++) { 0530 $val = $val . '\20'; 0531 } 0532 } 0533 if (null === $val) $val = '\0'; // apply escaped "null" if string is empty 0534 $values[$key] = $val; 0535 } 0536 return (count($values) == 1) ? $values[0] : $values; 0537 } 0538 0539 /** 0540 * Undoes the conversion done by {@link escapeValue()}. 0541 * 0542 * Any escape sequence starting with a baskslash - hexpair or special character - 0543 * will be transformed back to the corresponding character. 0544 * @see Net_LDAP2_Util::escape_dn_value() from Benedikt Hallinger <beni@php.net> 0545 * @link http://pear.php.net/package/Net_LDAP2 0546 * @author Benedikt Hallinger <beni@php.net> 0547 * 0548 * @param string|array $values Array of DN Values 0549 * @return array Same as $values, but unescaped 0550 */ 0551 public static function unescapeValue($values = array()) 0552 { 0553 /** 0554 * @see Zend_Ldap_Converter 0555 */ 0556 // require_once 'Zend/Ldap/Converter.php'; 0557 0558 if (!is_array($values)) $values = array($values); 0559 foreach ($values as $key => $val) { 0560 // strip slashes from special chars 0561 $val = str_replace(array('\\\\', '\,', '\+', '\"', '\<', '\>', '\;', '\#', '\='), 0562 array('\\', ',', '+', '"', '<', '>', ';', '#', '=', ), $val); 0563 $values[$key] = Zend_Ldap_Converter::hex32ToAsc($val); 0564 } 0565 return (count($values) == 1) ? $values[0] : $values; 0566 } 0567 0568 /** 0569 * Creates an array containing all parts of the given DN. 0570 * 0571 * Array will be of type 0572 * array( 0573 * array("cn" => "name1", "uid" => "user"), 0574 * array("cn" => "name2"), 0575 * array("dc" => "example"), 0576 * array("dc" => "org") 0577 * ) 0578 * for a DN of cn=name1+uid=user,cn=name2,dc=example,dc=org. 0579 * 0580 * @param string $dn 0581 * @param array $keys An optional array to receive DN keys (e.g. CN, OU, DC, ...) 0582 * @param array $vals An optional array to receive DN values 0583 * @param string $caseFold 0584 * @return array 0585 * @throws Zend_Ldap_Exception 0586 */ 0587 public static function explodeDn($dn, array &$keys = null, array &$vals = null, 0588 $caseFold = self::ATTR_CASEFOLD_NONE) 0589 { 0590 $k = array(); 0591 $v = array(); 0592 if (!self::checkDn($dn, $k, $v, $caseFold)) { 0593 /** 0594 * Zend_Ldap_Exception 0595 */ 0596 // require_once 'Zend/Ldap/Exception.php'; 0597 throw new Zend_Ldap_Exception(null, 'DN is malformed'); 0598 } 0599 $ret = array(); 0600 for ($i = 0; $i < count($k); $i++) { 0601 if (is_array($k[$i]) && is_array($v[$i]) && (count($k[$i]) === count($v[$i]))) { 0602 $multi = array(); 0603 for ($j = 0; $j < count($k[$i]); $j++) { 0604 $key=$k[$i][$j]; 0605 $val=$v[$i][$j]; 0606 $multi[$key] = $val; 0607 } 0608 $ret[] = $multi; 0609 } else if (is_string($k[$i]) && is_string($v[$i])) { 0610 $ret[] = array($k[$i] => $v[$i]); 0611 } 0612 } 0613 if ($keys !== null) $keys = $k; 0614 if ($vals !== null) $vals = $v; 0615 return $ret; 0616 } 0617 0618 /** 0619 * @param string $dn The DN to parse 0620 * @param array $keys An optional array to receive DN keys (e.g. CN, OU, DC, ...) 0621 * @param array $vals An optional array to receive DN values 0622 * @param string $caseFold 0623 * @return boolean True if the DN was successfully parsed or false if the string is not a valid DN. 0624 */ 0625 public static function checkDn($dn, array &$keys = null, array &$vals = null, 0626 $caseFold = self::ATTR_CASEFOLD_NONE) 0627 { 0628 /* This is a classic state machine parser. Each iteration of the 0629 * loop processes one character. State 1 collects the key. When equals ( = ) 0630 * is encountered the state changes to 2 where the value is collected 0631 * until a comma (,) or semicolon (;) is encountered after which we switch back 0632 * to state 1. If a backslash (\) is encountered, state 3 is used to collect the 0633 * following character without engaging the logic of other states. 0634 */ 0635 $key = null; 0636 $value = null; 0637 $slen = strlen($dn); 0638 $state = 1; 0639 $ko = $vo = 0; 0640 $multi = false; 0641 $ka = array(); 0642 $va = array(); 0643 for ($di = 0; $di <= $slen; $di++) { 0644 $ch = ($di == $slen) ? 0 : $dn[$di]; 0645 switch ($state) { 0646 case 1: // collect key 0647 if ($ch === '=') { 0648 $key = trim(substr($dn, $ko, $di - $ko)); 0649 if ($caseFold == self::ATTR_CASEFOLD_LOWER) $key = strtolower($key); 0650 else if ($caseFold == self::ATTR_CASEFOLD_UPPER) $key = strtoupper($key); 0651 if (is_array($multi)) { 0652 $keyId = strtolower($key); 0653 if (in_array($keyId, $multi)) { 0654 return false; 0655 } 0656 $ka[count($ka)-1][] = $key; 0657 $multi[] = $keyId; 0658 } else { 0659 $ka[] = $key; 0660 } 0661 $state = 2; 0662 $vo = $di + 1; 0663 } else if ($ch === ',' || $ch === ';' || $ch === '+') { 0664 return false; 0665 } 0666 break; 0667 case 2: // collect value 0668 if ($ch === '\\') { 0669 $state = 3; 0670 } else if ($ch === ',' || $ch === ';' || $ch === 0 || $ch === '+') { 0671 $value = self::unescapeValue(trim(substr($dn, $vo, $di - $vo))); 0672 if (is_array($multi)) { 0673 $va[count($va)-1][] = $value; 0674 } else { 0675 $va[] = $value; 0676 } 0677 $state = 1; 0678 $ko = $di + 1; 0679 if ($ch === '+' && $multi === false) { 0680 $lastKey = array_pop($ka); 0681 $lastVal = array_pop($va); 0682 $ka[] = array($lastKey); 0683 $va[] = array($lastVal); 0684 $multi = array(strtolower($lastKey)); 0685 } else if ($ch === ','|| $ch === ';' || $ch === 0) { 0686 $multi = false; 0687 } 0688 } else if ($ch === '=') { 0689 return false; 0690 } 0691 break; 0692 case 3: // escaped 0693 $state = 2; 0694 break; 0695 } 0696 } 0697 0698 if ($keys !== null) { 0699 $keys = $ka; 0700 } 0701 if ($vals !== null) { 0702 $vals = $va; 0703 } 0704 0705 return ($state === 1 && $ko > 0); 0706 } 0707 0708 /** 0709 * Returns a DN part in the form $attribute = $value 0710 * 0711 * This method supports the creation of multi-valued RDNs 0712 * $part must contain an even number of elemets. 0713 * 0714 * @param array $attribute 0715 * @param string $caseFold 0716 * @return string 0717 * @throws Zend_Ldap_Exception 0718 */ 0719 public static function implodeRdn(array $part, $caseFold = null) 0720 { 0721 self::_assertRdn($part); 0722 $part = self::_caseFoldRdn($part, $caseFold); 0723 $rdnParts = array(); 0724 foreach ($part as $key => $value) { 0725 $value = self::escapeValue($value); 0726 $keyId = strtolower($key); 0727 $rdnParts[$keyId] = implode('=', array($key, $value)); 0728 } 0729 ksort($rdnParts, SORT_STRING); 0730 return implode('+', $rdnParts); 0731 } 0732 0733 /** 0734 * Implodes an array in the form delivered by {@link explodeDn()} 0735 * to a DN string. 0736 * 0737 * $dnArray must be of type 0738 * array( 0739 * array("cn" => "name1", "uid" => "user"), 0740 * array("cn" => "name2"), 0741 * array("dc" => "example"), 0742 * array("dc" => "org") 0743 * ) 0744 * 0745 * @param array $dnArray 0746 * @param string $caseFold 0747 * @param string $separator 0748 * @return string 0749 * @throws Zend_Ldap_Exception 0750 */ 0751 public static function implodeDn(array $dnArray, $caseFold = null, $separator = ',') 0752 { 0753 $parts = array(); 0754 foreach ($dnArray as $p) { 0755 $parts[] = self::implodeRdn($p, $caseFold); 0756 } 0757 return implode($separator, $parts); 0758 } 0759 0760 /** 0761 * Checks if given $childDn is beneath $parentDn subtree. 0762 * 0763 * @param string|Zend_Ldap_Dn $childDn 0764 * @param string|Zend_Ldap_Dn $parentDn 0765 * @return boolean 0766 */ 0767 public static function isChildOf($childDn, $parentDn) 0768 { 0769 try { 0770 $keys = array(); 0771 $vals = array(); 0772 if ($childDn instanceof Zend_Ldap_Dn) { 0773 $cdn = $childDn->toArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER); 0774 } else { 0775 $cdn = self::explodeDn($childDn, $keys, $vals, Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER); 0776 } 0777 if ($parentDn instanceof Zend_Ldap_Dn) { 0778 $pdn = $parentDn->toArray(Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER); 0779 } else { 0780 $pdn = self::explodeDn($parentDn, $keys, $vals, Zend_Ldap_Dn::ATTR_CASEFOLD_LOWER); 0781 } 0782 } 0783 catch (Zend_Ldap_Exception $e) { 0784 return false; 0785 } 0786 0787 $startIndex = count($cdn)-count($pdn); 0788 if ($startIndex<0) return false; 0789 for ($i = 0; $i<count($pdn); $i++) { 0790 if ($cdn[$i+$startIndex] != $pdn[$i]) return false; 0791 } 0792 return true; 0793 } 0794 }