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\Search\Filter;
13
14use ArrayIterator;
15use FreeDSx\Asn1\Asn1;
16use FreeDSx\Asn1\Exception\EncoderException;
17use FreeDSx\Asn1\Type\AbstractType;
18use FreeDSx\Asn1\Type\IncompleteType;
19use FreeDSx\Asn1\Type\SetType;
20use FreeDSx\Ldap\Exception\ProtocolException;
21use FreeDSx\Ldap\Exception\RuntimeException;
22use FreeDSx\Ldap\Protocol\Factory\FilterFactory;
23use FreeDSx\Ldap\Protocol\LdapEncoder;
24use Traversable;
25
26/**
27 * Methods needed to implement the filter container interface.
28 *
29 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
30 */
31trait FilterContainerTrait
32{
33    /**
34     * @var FilterInterface[]
35     */
36    protected $filters = [];
37
38    /**
39     * @param FilterInterface ...$filters
40     */
41    public function __construct(FilterInterface ...$filters)
42    {
43        $this->filters = $filters;
44    }
45
46    /**
47     * @param FilterInterface ...$filters
48     * @return $this
49     */
50    public function add(FilterInterface ...$filters)
51    {
52        foreach ($filters as $filter) {
53            $this->filters[] = $filter;
54        }
55
56        return $this;
57    }
58
59    /**
60     * @param FilterInterface $filter
61     * @return bool
62     */
63    public function has(FilterInterface $filter): bool
64    {
65        return array_search($filter, $this->filters, true) !== false;
66    }
67
68    /**
69     * @param FilterInterface ...$filters
70     * @return $this
71     */
72    public function remove(FilterInterface ...$filters)
73    {
74        foreach ($filters as $filter) {
75            if (($i = array_search($filter, $this->filters, true)) !== false) {
76                unset($this->filters[$i]);
77            }
78        }
79
80        return $this;
81    }
82
83    /**
84     * @param FilterInterface ...$filters
85     * @return $this
86     */
87    public function set(FilterInterface ...$filters)
88    {
89        $this->filters = $filters;
90
91        return $this;
92    }
93
94    /**
95     * @return FilterInterface[]
96     */
97    public function get(): array
98    {
99        return $this->filters;
100    }
101
102    /**
103     * {@inheritdoc}
104     */
105    public function toAsn1(): AbstractType
106    {
107        return Asn1::context(self::CHOICE_TAG, Asn1::setOf(
108            ...array_map(function ($filter) {
109                /** @var FilterInterface $filter */
110                return $filter->toAsn1();
111            }, $this->filters)
112        ));
113    }
114
115    /**
116     * {@inheritdoc}
117     */
118    public function toString(): string
119    {
120        return self::PAREN_LEFT
121            . self::FILTER_OPERATOR
122            . implode('', array_map(function ($filter) {
123                /** @var FilterInterface $filter */
124                return $filter->toString();
125            }, $this->filters))
126            . self::PAREN_RIGHT;
127    }
128
129    /**
130     * @inheritDoc
131     * @psalm-return ArrayIterator<array-key, FilterInterface>
132     * @throws RuntimeException
133     */
134    public function getIterator(): Traversable
135    {
136        return new ArrayIterator($this->filters);
137    }
138
139    /**
140     * @inheritDoc
141     * @psalm-return 0|positive-int
142     */
143    public function count(): int
144    {
145        return count($this->filters);
146    }
147
148    /**
149     * @return string
150     */
151    public function __toString()
152    {
153        return $this->toString();
154    }
155
156    /**
157     * {@inheritDoc}
158     * @param AbstractType $type
159     * @return self
160     * @throws EncoderException
161     * @throws ProtocolException
162     */
163    public static function fromAsn1(AbstractType $type)
164    {
165        $type = $type instanceof IncompleteType ? (new LdapEncoder())->complete($type, AbstractType::TAG_TYPE_SET) : $type;
166        if (!($type instanceof SetType)) {
167            throw new ProtocolException('The filter is malformed');
168        }
169
170        $filters = [];
171        foreach ($type->getChildren() as $child) {
172            $filters[] = FilterFactory::get($child);
173        }
174
175        return new self(...$filters);
176    }
177}
178