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