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 is_array; 20 21/** 22 * Represents an Entry in LDAP. 23 * 24 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 25 */ 26class Entry implements IteratorAggregate, Countable 27{ 28 /** 29 * @var Attribute[] 30 */ 31 protected $attributes; 32 33 /** 34 * @var Dn 35 */ 36 protected $dn; 37 38 /** 39 * @var Changes 40 */ 41 protected $changes; 42 43 /** 44 * @param string|Dn $dn 45 * @param Attribute ...$attributes 46 */ 47 public function __construct($dn, Attribute ...$attributes) 48 { 49 $this->dn = $dn instanceof Dn ? $dn : new Dn($dn); 50 $this->attributes = $attributes; 51 $this->changes = new Changes(); 52 } 53 54 /** 55 * Add an attribute and its values. 56 * 57 * @param string|Attribute $attribute 58 * @param string ...$values 59 * @return $this 60 */ 61 public function add($attribute, ...$values) 62 { 63 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute, ...$values); 64 65 if (($exists = $this->get($attribute, true)) !== null) { 66 $exists->add(...$attribute->getValues()); 67 } else { 68 $this->attributes[] = $attribute; 69 } 70 $this->changes->add(Change::add(clone $attribute)); 71 72 return $this; 73 } 74 75 /** 76 * Remove an attribute's value(s). 77 * 78 * @param string|Attribute $attribute 79 * @param mixed|string ...$values 80 * @return $this 81 */ 82 public function remove($attribute, ...$values) 83 { 84 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute, ...$values); 85 86 if (count($attribute->getValues()) !== 0) { 87 if (($exists = $this->get($attribute, true)) !== null) { 88 $exists->remove(...$attribute->getValues()); 89 } 90 $this->changes->add(Change::delete(clone $attribute)); 91 } 92 93 return $this; 94 } 95 96 /** 97 * Reset an attribute, which removes any values it may have. 98 * 99 * @param string|Attribute ...$attributes 100 * @return $this 101 */ 102 public function reset(...$attributes) 103 { 104 foreach ($attributes as $attribute) { 105 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute); 106 foreach ($this->attributes as $i => $attr) { 107 if ($attr->equals($attribute, true)) { 108 unset($this->attributes[$i]); 109 break; 110 } 111 } 112 $this->changes()->add(Change::reset(clone $attribute)); 113 } 114 115 return $this; 116 } 117 118 /** 119 * Set an attribute on the entry, replacing any value(s) that may exist on it. 120 * 121 * @param string|Attribute $attribute 122 * @param mixed ...$values 123 * @return $this 124 */ 125 public function set($attribute, ...$values) 126 { 127 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute, ...$values); 128 129 $exists = false; 130 foreach ($this->attributes as $i => $attr) { 131 if ($attr->equals($attribute, true)) { 132 $exists = true; 133 $this->attributes[$i] = $attribute; 134 break; 135 } 136 } 137 if (!$exists) { 138 $this->attributes[] = $attribute; 139 } 140 $this->changes->add(Change::replace(clone $attribute)); 141 142 return $this; 143 } 144 145 /** 146 * Get a specific attribute by name (or Attribute object). 147 * 148 * @param string|Attribute $attribute 149 * @param bool $strict If set to true, then options on the attribute must also match. 150 * @return null|Attribute 151 */ 152 public function get($attribute, bool $strict = false): ?Attribute 153 { 154 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute); 155 156 foreach ($this->attributes as $attr) { 157 if ($attr->equals($attribute, $strict)) { 158 return $attr; 159 } 160 } 161 162 return null; 163 } 164 165 /** 166 * Check if a specific attribute exists on the entry. 167 * 168 * @param string|Attribute $attribute 169 * @param bool $strict 170 * @return bool 171 */ 172 public function has($attribute, bool $strict = false): bool 173 { 174 $attribute = $attribute instanceof Attribute ? $attribute : new Attribute($attribute); 175 176 return (bool) $this->get($attribute, $strict); 177 } 178 179 /** 180 * @return Attribute[] 181 */ 182 public function getAttributes(): array 183 { 184 return $this->attributes; 185 } 186 187 /** 188 * @return Dn 189 */ 190 public function getDn(): Dn 191 { 192 return $this->dn; 193 } 194 195 /** 196 * Get the changes accumulated for this entry. 197 * 198 * @return Changes 199 */ 200 public function changes(): Changes 201 { 202 return $this->changes; 203 } 204 205 /** 206 * Get the entry representation as an associative array. 207 * 208 * @return array 209 */ 210 public function toArray(): array 211 { 212 $attributes = []; 213 214 foreach ($this->attributes as $attribute) { 215 $attributes[$attribute->getDescription()] = $attribute->getValues(); 216 } 217 218 return $attributes; 219 } 220 221 /** 222 * @inheritDoc 223 * @psalm-return \ArrayIterator<array-key, Attribute> 224 */ 225 public function getIterator(): Traversable 226 { 227 return new ArrayIterator($this->attributes); 228 } 229 230 /** 231 * @return int 232 * @psalm-return 0|positive-int 233 */ 234 public function count(): int 235 { 236 return count($this->attributes); 237 } 238 239 public function __toString(): string 240 { 241 return $this->dn->toString(); 242 } 243 244 public function __get(string $name): ?Attribute 245 { 246 return $this->get($name); 247 } 248 249 /** 250 * @param string[]|string $value 251 */ 252 public function __set(string $name, $value): void 253 { 254 $this->set($name, ...(is_array($value) ? $value : [$value])); 255 } 256 257 public function __isset(string $name): bool 258 { 259 return $this->has($name); 260 } 261 262 public function __unset(string $name): void 263 { 264 $this->reset($name); 265 } 266 267 /** 268 * An alias of fromArray(). 269 * 270 * @param string $dn 271 * @param array $attributes 272 * @return Entry 273 */ 274 public static function create(string $dn, array $attributes = []): Entry 275 { 276 return self::fromArray($dn, $attributes); 277 } 278 279 /** 280 * Construct an entry from an associative array. 281 * 282 * @param string $dn 283 * @param array $attributes 284 * @return Entry 285 */ 286 public static function fromArray(string $dn, array $attributes = []): Entry 287 { 288 /** @var Attribute[] $entryAttr */ 289 $entryAttr = []; 290 291 foreach ($attributes as $attribute => $value) { 292 $entryAttr[] = new Attribute($attribute, ...(is_array($value) ? $value : [$value])); 293 } 294 295 return new self($dn, ...$entryAttr); 296 } 297} 298