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 ArrayIterator;
15use Countable;
16use IteratorAggregate;
17use Traversable;
18use function count;
19use function sort;
20
21/**
22 * Represents a collection of attribute options.
23 *
24 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
25 */
26class Options implements Countable, IteratorAggregate
27{
28    /**
29     * @var Option[]
30     */
31    protected $options;
32
33    /**
34     * @param string|Option ...$options
35     */
36    public function __construct(...$options)
37    {
38        $this->set(...$options);
39    }
40
41    /**
42     * @param string|Option ...$options
43     * @return $this
44     */
45    public function add(...$options)
46    {
47        foreach ($options as $option) {
48            $this->options[] = $option instanceof Option ? $option : new Option($option);
49        }
50
51        return $this;
52    }
53
54    /**
55     * @param string|Option ...$options
56     * @return $this
57     */
58    public function set(...$options)
59    {
60        $this->options = [];
61        foreach ($options as $i => $option) {
62            if ($option instanceof Option) {
63                $this->options[] = $option;
64            } else {
65                $this->options[] = new Option($option);
66            }
67        }
68
69        return $this;
70    }
71
72    /**
73     * @param string|Option $option
74     * @return bool
75     */
76    public function has($option): bool
77    {
78        $option = $option instanceof Option ? $option : new Option($option);
79
80        foreach ($this->options as $opt) {
81            if ($opt->equals($option)) {
82                return true;
83            }
84        }
85
86        return false;
87    }
88
89    /**
90     * @param string|Option ...$options
91     * @return $this
92     */
93    public function remove(...$options)
94    {
95        foreach ($options as $option) {
96            $option = $option instanceof Option ? $option : new Option($option);
97            foreach ($this->options as $i => $opt) {
98                if ($opt->equals($option)) {
99                    unset($this->options[$i]);
100                }
101            }
102        }
103
104        return $this;
105    }
106
107    /**
108     * Retrieve the first option, if it exists.
109     *
110     * @return Option|null
111     */
112    public function first(): ?Option
113    {
114        $option = reset($this->options);
115
116        return $option === false ? null : $option;
117    }
118
119    /**
120     * Retrieve the last option, if it exists.
121     *
122     * @return Option|null
123     */
124    public function last(): ?Option
125    {
126        $option = end($this->options);
127        reset($this->options);
128
129        return $option === false ? null : $option;
130    }
131
132    /**
133     * @param bool $sortedlc Used for comparison, as both case and order of options are irrelevant for options equality.
134     * @return string
135     */
136    public function toString(bool $sortedlc = false): string
137    {
138        $opts = $this->options;
139        if ($sortedlc) {
140            sort($opts);
141        }
142
143        $options = '';
144        foreach ($opts as $option) {
145            $options .= ($options === '') ? $option->toString($sortedlc) : ';' . $option->toString($sortedlc);
146        }
147
148        return $options;
149    }
150
151    /**
152     * @return Option[]
153     * @psalm-return array<array-key, Option>
154     */
155    public function toArray(): array
156    {
157        return $this->options;
158    }
159
160    /**
161     * @return string
162     */
163    public function __toString()
164    {
165        return $this->toString();
166    }
167
168    /**
169     * @inheritDoc
170     * @psalm-return ArrayIterator<array-key, Option>
171     */
172    public function getIterator(): Traversable
173    {
174        return new ArrayIterator($this->options);
175    }
176
177    /**
178     * @inheritDoc
179     * @psalm-return 0|positive-int
180     */
181    public function count(): int
182    {
183        return count($this->options);
184    }
185}
186