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