1<?php
2
3/**
4 * This file is part of the FreeDSx LDAP package.
5 *
6 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace FreeDSx\Ldap\Entry;
13
14use function preg_match;
15use function strlen;
16use function strtolower;
17use function substr;
18
19/**
20 * Represents an attribute option. Described in RFC 4512, Section 2.5.2.
21 *
22 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
23 */
24class Option
25{
26    protected const MATCH_RANGE = '/range=(\d+)-(.*)/';
27
28    /**
29     * @var string
30     */
31    protected $option;
32
33    /**
34     * @var string
35     */
36    protected $lcOption;
37
38    /**
39     * @param string $option
40     */
41    public function __construct(string $option)
42    {
43        $this->option = $option;
44    }
45
46    /**
47     * @return bool
48     */
49    public function isLanguageTag(): bool
50    {
51        return $this->startsWith('lang-');
52    }
53
54    /**
55     * @return bool
56     */
57    public function isRange(): bool
58    {
59        return $this->startsWith('range=');
60    }
61
62    /**
63     * A convenience method to get the high value of a range option.
64     *
65     * @see https://msdn.microsoft.com/en-us/library/cc223242.aspx
66     *
67     * @return null|string
68     */
69    public function getHighRange(): ?string
70    {
71        if (!$this->isRange()) {
72            return '';
73        }
74        preg_match(self::MATCH_RANGE, $this->option, $match);
75
76        return $match[2] ?? null;
77    }
78
79    /**
80     * A convenience method to get the low value of a range option.
81     *
82     * @see https://msdn.microsoft.com/en-us/library/cc223242.aspx
83     * @return string|null
84     */
85    public function getLowRange(): ?string
86    {
87        if (!$this->isRange()) {
88            return null;
89        }
90        preg_match(self::MATCH_RANGE, $this->option, $match);
91
92        return $match[1] ?? null;
93    }
94
95    /**
96     * @param string $option
97     * @return bool
98     */
99    public function startsWith(string $option): bool
100    {
101        if ($this->lcOption === null) {
102            $this->lcOption = strtolower($this->option);
103        }
104        $option = strtolower($option);
105
106        return substr($this->lcOption, 0, strlen($option)) === $option;
107    }
108
109    /**
110     * Options are case insensitive, so use this to optimize case-insensitive checks.
111     *
112     * @param Option $option
113     * @return bool
114     */
115    public function equals(Option $option): bool
116    {
117        if ($this->lcOption === null) {
118            $this->lcOption = strtolower($this->option);
119        }
120        if ($option->lcOption === null) {
121            $option->lcOption = strtolower($option->option);
122        }
123
124        return $this->lcOption === $option->lcOption;
125    }
126
127    /**
128     * @param bool $lowercase forces the string representation to lowercase.
129     * @return string
130     */
131    public function toString(bool $lowercase = false): string
132    {
133        if ($lowercase) {
134            if ($this->lcOption === null) {
135                $this->lcOption = strtolower($this->option);
136            }
137
138            return $this->lcOption;
139        }
140
141        return $this->option;
142    }
143
144    /**
145     * @return string
146     */
147    public function __toString()
148    {
149        return $this->option;
150    }
151
152    /**
153     * Convenience factory method for creating a range option.
154     *
155     * @see https://msdn.microsoft.com/en-us/library/cc223242.aspx
156     * @param string $startAt
157     * @param string $endAt
158     * @return Option
159     */
160    public static function fromRange(string $startAt, string $endAt = '*')
161    {
162        return new self('range=' . $startAt . '-' . $endAt);
163    }
164}
165