xref: /plugin/pureldap/vendor/freedsx/ldap/src/FreeDSx/Ldap/Entry/Rdn.php (revision a1fd61bad19af47d542046b33c29833512eb7d62)
1<?php
2/**
3 * This file is part of the FreeDSx LDAP package.
4 *
5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace FreeDSx\Ldap\Entry;
12
13use FreeDSx\Ldap\Exception\InvalidArgumentException;
14
15/**
16 * Represents a Relative Distinguished Name.
17 *
18 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
19 */
20class Rdn
21{
22    use EscapeTrait;
23
24    public const ESCAPE_MAP = [
25        '\\' => '\\5c',
26        '"' => '\\22',
27        '+' => '\\2b',
28        ',' => '\\2c',
29        ';' => '\\3b',
30        '<' => '\\3c',
31        '>' => '\\3e',
32    ];
33
34    /**
35     * @var string
36     */
37    protected $name;
38
39    /**
40     * @var string
41     */
42    protected $value;
43
44    /**
45     * @var Rdn[]
46     */
47    protected $additional = [];
48
49    /**
50     * @param string $name
51     * @param string $value
52     */
53    public function __construct(string $name, string $value)
54    {
55        $this->name = $name;
56        $this->value = $value;
57    }
58
59    /**
60     * @return string
61     */
62    public function getName(): string
63    {
64        return $this->name;
65    }
66
67    /**
68     * @return string
69     */
70    public function getValue(): string
71    {
72        return $this->value;
73    }
74
75    /**
76     * @return bool
77     */
78    public function isMultivalued(): bool
79    {
80        return \count($this->additional) !== 0;
81    }
82
83    /**
84     * @return string
85     */
86    public function toString(): string
87    {
88        $rdn = $this->name . '=' . $this->value;
89
90        foreach ($this->additional as $additional) {
91            $rdn .= '+' . $additional->getName() . '=' . $additional->getValue();
92        }
93
94        return $rdn;
95    }
96
97    /**
98     * @return string
99     */
100    public function __toString()
101    {
102        return $this->toString();
103    }
104
105    /**
106     * @param string $rdn
107     * @return Rdn
108     */
109    public static function create(string $rdn): Rdn
110    {
111        $pieces = \preg_split('/(?<!\\\\)\+/', $rdn);
112        if ($pieces === false) {
113            throw new InvalidArgumentException(sprintf('The RDN "%s" is invalid.', $rdn));
114        }
115
116        // @todo Simplify this logic somehow?
117        $obj = null;
118        foreach ($pieces as $piece) {
119            $parts = \explode('=', $piece, 2);
120            if (\count($parts) !== 2) {
121                throw new InvalidArgumentException(sprintf('The RDN "%s" is invalid.', $piece));
122            }
123            if ($obj === null) {
124                $obj = new self($parts[0], $parts[1]);
125            } else {
126                /** @var Rdn $obj */
127                $obj->additional[] = new self($parts[0], $parts[1]);
128            }
129        }
130
131        if ($obj === null) {
132            throw new InvalidArgumentException(sprintf("The RDN '%s' is not valid.", $rdn));
133        }
134
135        return $obj;
136    }
137
138    /**
139     * Escape an RDN value.
140     *
141     * @param string $value
142     * @return string
143     */
144    public static function escape(string $value): string
145    {
146        if (self::shouldNotEscape($value)) {
147            return $value;
148        }
149        $value = \str_replace(\array_keys(self::ESCAPE_MAP), \array_values(self::ESCAPE_MAP), $value);
150
151        if ($value[0] === '#' || $value[0] === ' ') {
152            $value = ($value[0] === '#' ? '\23' : '\20') . \substr($value, 1);
153        }
154        if ($value[-1] === ' ') {
155            $value = \substr_replace($value, '\20', -1, 1);
156        }
157
158        return self::escapeNonPrintable($value);
159    }
160}
161