10b3fd2d3SAndreas Gohr<?php 2*dad993c5SAndreas Gohr 30b3fd2d3SAndreas Gohr/** 40b3fd2d3SAndreas Gohr * This file is part of the FreeDSx LDAP package. 50b3fd2d3SAndreas Gohr * 60b3fd2d3SAndreas Gohr * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 70b3fd2d3SAndreas Gohr * 80b3fd2d3SAndreas Gohr * For the full copyright and license information, please view the LICENSE 90b3fd2d3SAndreas Gohr * file that was distributed with this source code. 100b3fd2d3SAndreas Gohr */ 110b3fd2d3SAndreas Gohr 120b3fd2d3SAndreas Gohrnamespace FreeDSx\Ldap\Entry; 130b3fd2d3SAndreas Gohr 14*dad993c5SAndreas Gohruse ArrayIterator; 15*dad993c5SAndreas Gohruse Countable; 160b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\InvalidArgumentException; 170b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\UnexpectedValueException; 18*dad993c5SAndreas Gohruse IteratorAggregate; 19*dad993c5SAndreas Gohruse Traversable; 20*dad993c5SAndreas Gohruse function array_slice; 21*dad993c5SAndreas Gohruse function count; 22*dad993c5SAndreas Gohruse function implode; 23*dad993c5SAndreas Gohruse function ltrim; 24*dad993c5SAndreas Gohruse function preg_split; 250b3fd2d3SAndreas Gohr 260b3fd2d3SAndreas Gohr/** 270b3fd2d3SAndreas Gohr * Represents a Distinguished Name. 280b3fd2d3SAndreas Gohr * 290b3fd2d3SAndreas Gohr * @author Chad Sikorra <Chad.Sikorra@gmail.com> 300b3fd2d3SAndreas Gohr */ 31*dad993c5SAndreas Gohrclass Dn implements IteratorAggregate, Countable 320b3fd2d3SAndreas Gohr{ 330b3fd2d3SAndreas Gohr /** 340b3fd2d3SAndreas Gohr * @var string 350b3fd2d3SAndreas Gohr */ 360b3fd2d3SAndreas Gohr protected $dn; 370b3fd2d3SAndreas Gohr 380b3fd2d3SAndreas Gohr /** 390b3fd2d3SAndreas Gohr * @var null|Rdn[] 400b3fd2d3SAndreas Gohr */ 410b3fd2d3SAndreas Gohr protected $pieces; 420b3fd2d3SAndreas Gohr 430b3fd2d3SAndreas Gohr /** 440b3fd2d3SAndreas Gohr * @param string $dn 450b3fd2d3SAndreas Gohr */ 460b3fd2d3SAndreas Gohr public function __construct(string $dn) 470b3fd2d3SAndreas Gohr { 480b3fd2d3SAndreas Gohr $this->dn = $dn; 490b3fd2d3SAndreas Gohr } 500b3fd2d3SAndreas Gohr 510b3fd2d3SAndreas Gohr /** 520b3fd2d3SAndreas Gohr * @return Rdn 53*dad993c5SAndreas Gohr * @throws UnexpectedValueException 540b3fd2d3SAndreas Gohr */ 550b3fd2d3SAndreas Gohr public function getRdn(): Rdn 560b3fd2d3SAndreas Gohr { 570b3fd2d3SAndreas Gohr if ($this->pieces === null) { 580b3fd2d3SAndreas Gohr $this->parse(); 590b3fd2d3SAndreas Gohr } 600b3fd2d3SAndreas Gohr if (!isset($this->pieces[0])) { 610b3fd2d3SAndreas Gohr throw new UnexpectedValueException('The DN has no RDN.'); 620b3fd2d3SAndreas Gohr } 630b3fd2d3SAndreas Gohr 640b3fd2d3SAndreas Gohr return $this->pieces[0]; 650b3fd2d3SAndreas Gohr } 660b3fd2d3SAndreas Gohr 670b3fd2d3SAndreas Gohr /** 680b3fd2d3SAndreas Gohr * @return null|Dn 69*dad993c5SAndreas Gohr * @throws UnexpectedValueException 700b3fd2d3SAndreas Gohr */ 710b3fd2d3SAndreas Gohr public function getParent(): ?Dn 720b3fd2d3SAndreas Gohr { 730b3fd2d3SAndreas Gohr if ($this->pieces === null) { 740b3fd2d3SAndreas Gohr $this->parse(); 750b3fd2d3SAndreas Gohr } 76*dad993c5SAndreas Gohr if (count((array) $this->pieces) < 2) { 770b3fd2d3SAndreas Gohr return null; 780b3fd2d3SAndreas Gohr } 790b3fd2d3SAndreas Gohr 80*dad993c5SAndreas Gohr return new Dn(implode(',', array_slice((array) $this->pieces, 1))); 810b3fd2d3SAndreas Gohr } 820b3fd2d3SAndreas Gohr 830b3fd2d3SAndreas Gohr /** 84*dad993c5SAndreas Gohr * @inheritDoc 85*dad993c5SAndreas Gohr * @@psalm-return \ArrayIterator<array-key, Rdn> 86*dad993c5SAndreas Gohr * @throws UnexpectedValueException 870b3fd2d3SAndreas Gohr */ 88*dad993c5SAndreas Gohr public function getIterator(): Traversable 890b3fd2d3SAndreas Gohr { 90*dad993c5SAndreas Gohr return new ArrayIterator($this->toArray()); 910b3fd2d3SAndreas Gohr } 920b3fd2d3SAndreas Gohr 930b3fd2d3SAndreas Gohr /** 940b3fd2d3SAndreas Gohr * @return string 950b3fd2d3SAndreas Gohr */ 960b3fd2d3SAndreas Gohr public function toString(): string 970b3fd2d3SAndreas Gohr { 980b3fd2d3SAndreas Gohr return $this->dn; 990b3fd2d3SAndreas Gohr } 1000b3fd2d3SAndreas Gohr 1010b3fd2d3SAndreas Gohr /** 102*dad993c5SAndreas Gohr * @inheritDoc 103*dad993c5SAndreas Gohr * @psalm-return 0|positive-int 104*dad993c5SAndreas Gohr * @throws UnexpectedValueException 1050b3fd2d3SAndreas Gohr */ 106*dad993c5SAndreas Gohr public function count(): int 1070b3fd2d3SAndreas Gohr { 1080b3fd2d3SAndreas Gohr if ($this->pieces === null) { 1090b3fd2d3SAndreas Gohr $this->parse(); 1100b3fd2d3SAndreas Gohr } 1110b3fd2d3SAndreas Gohr 112*dad993c5SAndreas Gohr return count((array) $this->pieces); 1130b3fd2d3SAndreas Gohr } 1140b3fd2d3SAndreas Gohr 1150b3fd2d3SAndreas Gohr /** 1160b3fd2d3SAndreas Gohr * @return string 1170b3fd2d3SAndreas Gohr */ 1180b3fd2d3SAndreas Gohr public function __toString() 1190b3fd2d3SAndreas Gohr { 1200b3fd2d3SAndreas Gohr return $this->dn; 1210b3fd2d3SAndreas Gohr } 1220b3fd2d3SAndreas Gohr 1230b3fd2d3SAndreas Gohr /** 1240b3fd2d3SAndreas Gohr * @return Rdn[] 125*dad993c5SAndreas Gohr * @throws UnexpectedValueException 1260b3fd2d3SAndreas Gohr */ 1270b3fd2d3SAndreas Gohr public function toArray(): array 1280b3fd2d3SAndreas Gohr { 1290b3fd2d3SAndreas Gohr if ($this->pieces !== null) { 1300b3fd2d3SAndreas Gohr return $this->pieces; 1310b3fd2d3SAndreas Gohr } 1320b3fd2d3SAndreas Gohr $this->parse(); 1330b3fd2d3SAndreas Gohr 1340b3fd2d3SAndreas Gohr return ($this->pieces === null) ? [] : $this->pieces; 1350b3fd2d3SAndreas Gohr } 1360b3fd2d3SAndreas Gohr 1370b3fd2d3SAndreas Gohr /** 1380b3fd2d3SAndreas Gohr * @param string $dn 1390b3fd2d3SAndreas Gohr * @return bool 1400b3fd2d3SAndreas Gohr */ 1410b3fd2d3SAndreas Gohr public static function isValid(string $dn): bool 1420b3fd2d3SAndreas Gohr { 1430b3fd2d3SAndreas Gohr try { 1440b3fd2d3SAndreas Gohr (new self($dn))->toArray(); 1450b3fd2d3SAndreas Gohr 1460b3fd2d3SAndreas Gohr return true; 1470b3fd2d3SAndreas Gohr } catch (UnexpectedValueException | InvalidArgumentException $e) { 1480b3fd2d3SAndreas Gohr return false; 1490b3fd2d3SAndreas Gohr } 1500b3fd2d3SAndreas Gohr } 1510b3fd2d3SAndreas Gohr 1520b3fd2d3SAndreas Gohr /** 1530b3fd2d3SAndreas Gohr * @todo This needs proper handling. But the regex would probably be rather crazy. 154*dad993c5SAndreas Gohr * 155*dad993c5SAndreas Gohr * @throws UnexpectedValueException 1560b3fd2d3SAndreas Gohr */ 1570b3fd2d3SAndreas Gohr protected function parse(): void 1580b3fd2d3SAndreas Gohr { 1590b3fd2d3SAndreas Gohr if ($this->dn === '') { 1600b3fd2d3SAndreas Gohr $this->pieces = []; 1610b3fd2d3SAndreas Gohr 1620b3fd2d3SAndreas Gohr return; 1630b3fd2d3SAndreas Gohr } 164*dad993c5SAndreas Gohr $pieces = preg_split('/(?<!\\\\),/', $this->dn); 1650b3fd2d3SAndreas Gohr $pieces = ($pieces === false) ? [] : $pieces; 1660b3fd2d3SAndreas Gohr 167*dad993c5SAndreas Gohr if (count($pieces) === 0) { 1680b3fd2d3SAndreas Gohr throw new UnexpectedValueException(sprintf( 1690b3fd2d3SAndreas Gohr 'The DN value "%s" is not valid.', 1700b3fd2d3SAndreas Gohr $this->dn 1710b3fd2d3SAndreas Gohr )); 1720b3fd2d3SAndreas Gohr } 1730b3fd2d3SAndreas Gohr 1740b3fd2d3SAndreas Gohr $rdns = []; 1750b3fd2d3SAndreas Gohr foreach ($pieces as $i => $piece) { 176*dad993c5SAndreas Gohr $rdns[$i] = Rdn::create(ltrim($piece)); 1770b3fd2d3SAndreas Gohr } 1780b3fd2d3SAndreas Gohr 1790b3fd2d3SAndreas Gohr $this->pieces = $rdns; 1800b3fd2d3SAndreas Gohr } 1810b3fd2d3SAndreas Gohr} 182