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 140b3fd2d3SAndreas Gohruse FreeDSx\Ldap\Exception\InvalidArgumentException; 15*dad993c5SAndreas Gohruse function array_keys; 16*dad993c5SAndreas Gohruse function array_values; 17*dad993c5SAndreas Gohruse function count; 18*dad993c5SAndreas Gohruse function explode; 19*dad993c5SAndreas Gohruse function preg_split; 20*dad993c5SAndreas Gohruse function str_replace; 21*dad993c5SAndreas Gohruse function substr; 22*dad993c5SAndreas Gohruse function substr_replace; 230b3fd2d3SAndreas Gohr 240b3fd2d3SAndreas Gohr/** 250b3fd2d3SAndreas Gohr * Represents a Relative Distinguished Name. 260b3fd2d3SAndreas Gohr * 270b3fd2d3SAndreas Gohr * @author Chad Sikorra <Chad.Sikorra@gmail.com> 280b3fd2d3SAndreas Gohr */ 290b3fd2d3SAndreas Gohrclass Rdn 300b3fd2d3SAndreas Gohr{ 310b3fd2d3SAndreas Gohr use EscapeTrait; 320b3fd2d3SAndreas Gohr 330b3fd2d3SAndreas Gohr public const ESCAPE_MAP = [ 340b3fd2d3SAndreas Gohr '\\' => '\\5c', 350b3fd2d3SAndreas Gohr '"' => '\\22', 360b3fd2d3SAndreas Gohr '+' => '\\2b', 370b3fd2d3SAndreas Gohr ',' => '\\2c', 380b3fd2d3SAndreas Gohr ';' => '\\3b', 390b3fd2d3SAndreas Gohr '<' => '\\3c', 400b3fd2d3SAndreas Gohr '>' => '\\3e', 410b3fd2d3SAndreas Gohr ]; 420b3fd2d3SAndreas Gohr 430b3fd2d3SAndreas Gohr /** 440b3fd2d3SAndreas Gohr * @var string 450b3fd2d3SAndreas Gohr */ 460b3fd2d3SAndreas Gohr protected $name; 470b3fd2d3SAndreas Gohr 480b3fd2d3SAndreas Gohr /** 490b3fd2d3SAndreas Gohr * @var string 500b3fd2d3SAndreas Gohr */ 510b3fd2d3SAndreas Gohr protected $value; 520b3fd2d3SAndreas Gohr 530b3fd2d3SAndreas Gohr /** 540b3fd2d3SAndreas Gohr * @var Rdn[] 550b3fd2d3SAndreas Gohr */ 560b3fd2d3SAndreas Gohr protected $additional = []; 570b3fd2d3SAndreas Gohr 580b3fd2d3SAndreas Gohr /** 590b3fd2d3SAndreas Gohr * @param string $name 600b3fd2d3SAndreas Gohr * @param string $value 610b3fd2d3SAndreas Gohr */ 620b3fd2d3SAndreas Gohr public function __construct(string $name, string $value) 630b3fd2d3SAndreas Gohr { 640b3fd2d3SAndreas Gohr $this->name = $name; 650b3fd2d3SAndreas Gohr $this->value = $value; 660b3fd2d3SAndreas Gohr } 670b3fd2d3SAndreas Gohr 680b3fd2d3SAndreas Gohr /** 690b3fd2d3SAndreas Gohr * @return string 700b3fd2d3SAndreas Gohr */ 710b3fd2d3SAndreas Gohr public function getName(): string 720b3fd2d3SAndreas Gohr { 730b3fd2d3SAndreas Gohr return $this->name; 740b3fd2d3SAndreas Gohr } 750b3fd2d3SAndreas Gohr 760b3fd2d3SAndreas Gohr /** 770b3fd2d3SAndreas Gohr * @return string 780b3fd2d3SAndreas Gohr */ 790b3fd2d3SAndreas Gohr public function getValue(): string 800b3fd2d3SAndreas Gohr { 810b3fd2d3SAndreas Gohr return $this->value; 820b3fd2d3SAndreas Gohr } 830b3fd2d3SAndreas Gohr 840b3fd2d3SAndreas Gohr /** 850b3fd2d3SAndreas Gohr * @return bool 860b3fd2d3SAndreas Gohr */ 870b3fd2d3SAndreas Gohr public function isMultivalued(): bool 880b3fd2d3SAndreas Gohr { 89*dad993c5SAndreas Gohr return count($this->additional) !== 0; 900b3fd2d3SAndreas Gohr } 910b3fd2d3SAndreas Gohr 920b3fd2d3SAndreas Gohr /** 930b3fd2d3SAndreas Gohr * @return string 940b3fd2d3SAndreas Gohr */ 950b3fd2d3SAndreas Gohr public function toString(): string 960b3fd2d3SAndreas Gohr { 970b3fd2d3SAndreas Gohr $rdn = $this->name . '=' . $this->value; 980b3fd2d3SAndreas Gohr 990b3fd2d3SAndreas Gohr foreach ($this->additional as $additional) { 1000b3fd2d3SAndreas Gohr $rdn .= '+' . $additional->getName() . '=' . $additional->getValue(); 1010b3fd2d3SAndreas Gohr } 1020b3fd2d3SAndreas Gohr 1030b3fd2d3SAndreas Gohr return $rdn; 1040b3fd2d3SAndreas Gohr } 1050b3fd2d3SAndreas Gohr 1060b3fd2d3SAndreas Gohr /** 1070b3fd2d3SAndreas Gohr * @return string 1080b3fd2d3SAndreas Gohr */ 1090b3fd2d3SAndreas Gohr public function __toString() 1100b3fd2d3SAndreas Gohr { 1110b3fd2d3SAndreas Gohr return $this->toString(); 1120b3fd2d3SAndreas Gohr } 1130b3fd2d3SAndreas Gohr 1140b3fd2d3SAndreas Gohr /** 1150b3fd2d3SAndreas Gohr * @param string $rdn 1160b3fd2d3SAndreas Gohr * @return Rdn 117*dad993c5SAndreas Gohr * @throws InvalidArgumentException 1180b3fd2d3SAndreas Gohr */ 1190b3fd2d3SAndreas Gohr public static function create(string $rdn): Rdn 1200b3fd2d3SAndreas Gohr { 121*dad993c5SAndreas Gohr $pieces = preg_split('/(?<!\\\\)\+/', $rdn); 1220b3fd2d3SAndreas Gohr if ($pieces === false) { 1230b3fd2d3SAndreas Gohr throw new InvalidArgumentException(sprintf('The RDN "%s" is invalid.', $rdn)); 1240b3fd2d3SAndreas Gohr } 1250b3fd2d3SAndreas Gohr 1260b3fd2d3SAndreas Gohr // @todo Simplify this logic somehow? 1270b3fd2d3SAndreas Gohr $obj = null; 1280b3fd2d3SAndreas Gohr foreach ($pieces as $piece) { 129*dad993c5SAndreas Gohr $parts = explode('=', $piece, 2); 130*dad993c5SAndreas Gohr if (count($parts) !== 2) { 1310b3fd2d3SAndreas Gohr throw new InvalidArgumentException(sprintf('The RDN "%s" is invalid.', $piece)); 1320b3fd2d3SAndreas Gohr } 1330b3fd2d3SAndreas Gohr if ($obj === null) { 1340b3fd2d3SAndreas Gohr $obj = new self($parts[0], $parts[1]); 1350b3fd2d3SAndreas Gohr } else { 1360b3fd2d3SAndreas Gohr /** @var Rdn $obj */ 1370b3fd2d3SAndreas Gohr $obj->additional[] = new self($parts[0], $parts[1]); 1380b3fd2d3SAndreas Gohr } 1390b3fd2d3SAndreas Gohr } 1400b3fd2d3SAndreas Gohr 1410b3fd2d3SAndreas Gohr if ($obj === null) { 1420b3fd2d3SAndreas Gohr throw new InvalidArgumentException(sprintf("The RDN '%s' is not valid.", $rdn)); 1430b3fd2d3SAndreas Gohr } 1440b3fd2d3SAndreas Gohr 1450b3fd2d3SAndreas Gohr return $obj; 1460b3fd2d3SAndreas Gohr } 1470b3fd2d3SAndreas Gohr 1480b3fd2d3SAndreas Gohr /** 1490b3fd2d3SAndreas Gohr * Escape an RDN value. 1500b3fd2d3SAndreas Gohr * 1510b3fd2d3SAndreas Gohr * @param string $value 1520b3fd2d3SAndreas Gohr * @return string 1530b3fd2d3SAndreas Gohr */ 1540b3fd2d3SAndreas Gohr public static function escape(string $value): string 1550b3fd2d3SAndreas Gohr { 1560b3fd2d3SAndreas Gohr if (self::shouldNotEscape($value)) { 1570b3fd2d3SAndreas Gohr return $value; 1580b3fd2d3SAndreas Gohr } 159*dad993c5SAndreas Gohr $value = str_replace(array_keys(self::ESCAPE_MAP), array_values(self::ESCAPE_MAP), $value); 1600b3fd2d3SAndreas Gohr 1610b3fd2d3SAndreas Gohr if ($value[0] === '#' || $value[0] === ' ') { 162*dad993c5SAndreas Gohr $value = ($value[0] === '#' ? '\23' : '\20') . substr($value, 1); 1630b3fd2d3SAndreas Gohr } 1640b3fd2d3SAndreas Gohr if ($value[-1] === ' ') { 165*dad993c5SAndreas Gohr $value = substr_replace($value, '\20', -1, 1); 1660b3fd2d3SAndreas Gohr } 1670b3fd2d3SAndreas Gohr 1680b3fd2d3SAndreas Gohr return self::escapeNonPrintable($value); 1690b3fd2d3SAndreas Gohr } 1700b3fd2d3SAndreas Gohr} 171