| Total Complexity | 45 | 
| Total Lines | 584 | 
| Duplicated Lines | 0 % | 
| Changes | 11 | ||
| Bugs | 4 | Features | 3 | 
Complex classes like ArrayView often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ArrayView, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 32 | class ArrayView implements ArrayViewInterface  | 
            ||
| 33 | { | 
            ||
| 34 | /**  | 
            ||
| 35 | * @use ArrayViewAccessTrait<T, string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface>  | 
            ||
| 36 | *  | 
            ||
| 37 | * for array access methods.  | 
            ||
| 38 | */  | 
            ||
| 39 | use ArrayViewAccessTrait;  | 
            ||
| 40 | |||
| 41 | /**  | 
            ||
| 42 | * @var array<T>|ArrayViewInterface<T> The source array or view.  | 
            ||
| 43 | */  | 
            ||
| 44 | protected $source;  | 
            ||
| 45 | /**  | 
            ||
| 46 | * @var bool Flag indicating if the view is readonly.  | 
            ||
| 47 | */  | 
            ||
| 48 | protected bool $readonly;  | 
            ||
| 49 | /**  | 
            ||
| 50 | * @var ArrayViewInterface<T>|null The parent view of the current view.  | 
            ||
| 51 | */  | 
            ||
| 52 | protected ?ArrayViewInterface $parentView;  | 
            ||
| 53 | |||
| 54 | /**  | 
            ||
| 55 | * Creates an ArrayView instance from the given source array or ArrayView.  | 
            ||
| 56 | *  | 
            ||
| 57 | * * If the source is not an ArrayView, a new ArrayView is created with the provided source.  | 
            ||
| 58 | * * If the source is an ArrayView and the `readonly` parameter is specified as `true`,  | 
            ||
| 59 | * a new readonly ArrayView is created.  | 
            ||
| 60 | * * If the source is an ArrayView and it is already readonly, the same ArrayView is returned.  | 
            ||
| 61 | *  | 
            ||
| 62 | * ##### Example  | 
            ||
| 63 | * ```php  | 
            ||
| 64 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 65 | * $view = ArrayView::toView($source);  | 
            ||
| 66 | *  | 
            ||
| 67 | * $view[0]; // 1  | 
            ||
| 68 | * $view['1::2']; // [2, 4]  | 
            ||
| 69 | * $view['1::2'] = [22, 44];  | 
            ||
| 70 | *  | 
            ||
| 71 | * $view->toArray(); // [1, 22, 3, 44, 5]  | 
            ||
| 72 | * $source; // [1, 22, 3, 44, 5]  | 
            ||
| 73 | * ```  | 
            ||
| 74 | *  | 
            ||
| 75 | * ##### Readonly example  | 
            ||
| 76 | * ```php  | 
            ||
| 77 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 78 | * $view = ArrayView::toView($source, true);  | 
            ||
| 79 | *  | 
            ||
| 80 | * $view['1::2']; // [2, 4]  | 
            ||
| 81 | * $view['1::2'] = [22, 44]; // throws ReadonlyError  | 
            ||
| 82 | * $view[0] = 11; // throws ReadonlyError  | 
            ||
| 83 | * ```  | 
            ||
| 84 | *  | 
            ||
| 85 | * @param array<T>|ArrayViewInterface<T> $source The source array or ArrayView to create a view from.  | 
            ||
| 86 | * @param bool|null $readonly Optional flag to indicate whether the view should be readonly.  | 
            ||
| 87 | *  | 
            ||
| 88 | * @return ArrayViewInterface<T> An ArrayView instance based on the source array or ArrayView.  | 
            ||
| 89 | *  | 
            ||
| 90 | * @throws ValueError if the array is not sequential.  | 
            ||
| 91 | * @throws ReadonlyError if the source is readonly and trying to create a non-readonly view.  | 
            ||
| 92 | */  | 
            ||
| 93 | public static function toView(&$source, ?bool $readonly = null): ArrayViewInterface  | 
            ||
| 94 |     { | 
            ||
| 95 |         if (!($source instanceof ArrayViewInterface)) { | 
            ||
| 96 | return new ArrayView($source, $readonly);  | 
            ||
| 97 | }  | 
            ||
| 98 | |||
| 99 |         if (!$source->isReadonly() && $readonly) { | 
            ||
| 100 | return new ArrayView($source, $readonly);  | 
            ||
| 101 | }  | 
            ||
| 102 | |||
| 103 | return $source;  | 
            ||
| 104 | }  | 
            ||
| 105 | |||
| 106 | /**  | 
            ||
| 107 |      * {@inheritDoc} | 
            ||
| 108 | *  | 
            ||
| 109 | * ##### Example:  | 
            ||
| 110 | * ```php  | 
            ||
| 111 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 112 | * $view = ArrayView::toUnlinkedView($source);  | 
            ||
| 113 | *  | 
            ||
| 114 | * $view[0]; // 1  | 
            ||
| 115 | * $view['1::2']; // [2, 4]  | 
            ||
| 116 | * $view['1::2'] = [22, 44];  | 
            ||
| 117 | *  | 
            ||
| 118 | * $view->toArray(); // [1, 22, 3, 44, 5]  | 
            ||
| 119 | * $source; // [1, 2, 3, 4, 5]  | 
            ||
| 120 | * ```  | 
            ||
| 121 | *  | 
            ||
| 122 | * ##### Readonly example:  | 
            ||
| 123 | * ```php  | 
            ||
| 124 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 125 | * $view = ArrayView::toUnlinkedView($source, true);  | 
            ||
| 126 | *  | 
            ||
| 127 | * $view['1::2']; // [2, 4]  | 
            ||
| 128 | * $view['1::2'] = [22, 44]; // throws ReadonlyError  | 
            ||
| 129 | * $view[0] = 11; // throws ReadonlyError  | 
            ||
| 130 | * ```  | 
            ||
| 131 | */  | 
            ||
| 132 | public static function toUnlinkedView($source, ?bool $readonly = null): ArrayViewInterface  | 
            ||
| 135 | }  | 
            ||
| 136 | |||
| 137 | /**  | 
            ||
| 138 | * Constructor to create a new ArrayView.  | 
            ||
| 139 | *  | 
            ||
| 140 | * * If the source is not an ArrayView, a new ArrayView is created with the provided source.  | 
            ||
| 141 | * * If the source is an ArrayView and the `readonly` parameter is specified as `true`,  | 
            ||
| 142 | * a new readonly ArrayView is created.  | 
            ||
| 143 | * * If the source is an ArrayView and it is already readonly, the same ArrayView is returned.  | 
            ||
| 144 | *  | 
            ||
| 145 | * @param array<T>|ArrayViewInterface<T> $source The source array or view.  | 
            ||
| 146 | * @param bool|null $readonly Flag indicating if the view is readonly.  | 
            ||
| 147 | *  | 
            ||
| 148 | * @throws ValueError if the array is not sequential.  | 
            ||
| 149 | * @throws ReadonlyError if the source is readonly and trying to create a non-readonly view.  | 
            ||
| 150 | *  | 
            ||
| 151 | * @see ArrayView::toView() for creating views.  | 
            ||
| 152 | */  | 
            ||
| 153 | public function __construct(&$source, ?bool $readonly = null)  | 
            ||
| 154 |     { | 
            ||
| 155 | $this->checkSequential($source);  | 
            ||
| 156 | |||
| 157 | $this->source = &$source;  | 
            ||
| 158 | $this->readonly = $readonly ?? (($source instanceof ArrayViewInterface) ? $source->isReadonly() : false);  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 159 | $this->parentView = ($source instanceof ArrayViewInterface) ? $source : null;  | 
            ||
| 160 | |||
| 161 |         if (($source instanceof ArrayViewInterface) && $source->isReadonly() && !$this->isReadonly()) { | 
            ||
| 162 |             throw new ReadonlyError("Cannot create non-readonly view for readonly source."); | 
            ||
| 163 | }  | 
            ||
| 164 | }  | 
            ||
| 165 | |||
| 166 | /**  | 
            ||
| 167 | * Returns the array representation of the view.  | 
            ||
| 168 | *  | 
            ||
| 169 | * ##### Example  | 
            ||
| 170 | * ```php  | 
            ||
| 171 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 172 | * $view = ArrayView::toView($source);  | 
            ||
| 173 | * $view->toArray(); // [1, 2, 3, 4, 5]  | 
            ||
| 174 | * ```  | 
            ||
| 175 | *  | 
            ||
| 176 | * @return array<T> The array representation of the view.  | 
            ||
| 177 | */  | 
            ||
| 178 | public function toArray(): array  | 
            ||
| 181 | }  | 
            ||
| 182 | |||
| 183 | /**  | 
            ||
| 184 | * Filters the elements in the view based on a predicate function.  | 
            ||
| 185 | *  | 
            ||
| 186 | * ##### Example  | 
            ||
| 187 | * ```php  | 
            ||
| 188 | * $source = [1, 2, 3, 4, 5, 6];  | 
            ||
| 189 | * $view = ArrayView::toView($source);  | 
            ||
| 190 | *  | 
            ||
| 191 | * $filtered = $view->filter(fn ($x) => $x % 2 === 0);  | 
            ||
| 192 | * $filtered->toArray(); // [2, 4, 6]  | 
            ||
| 193 | *  | 
            ||
| 194 | * $filtered[':'] = [20, 40, 60];  | 
            ||
| 195 | * $filtered->toArray(); // [20, 40, 60]  | 
            ||
| 196 | * $source; // [1, 20, 3, 40, 5, 60]  | 
            ||
| 197 | * ```  | 
            ||
| 198 | *  | 
            ||
| 199 | * @param callable(T, int): bool $predicate Function that returns a boolean value for each element.  | 
            ||
| 200 | *  | 
            ||
| 201 | * @return ArrayMaskView<T> A new view with elements that satisfy the predicate.  | 
            ||
| 202 | */  | 
            ||
| 203 | public function filter(callable $predicate): ArrayViewInterface  | 
            ||
| 206 | }  | 
            ||
| 207 | |||
| 208 | /**  | 
            ||
| 209 | * Checks if all elements in the view satisfy a given predicate function.  | 
            ||
| 210 | *  | 
            ||
| 211 | * ##### Example  | 
            ||
| 212 | * ```php  | 
            ||
| 213 | * $source = [1, 2, 3, 4, 5, 6];  | 
            ||
| 214 | * $view = ArrayView::toView($source);  | 
            ||
| 215 | *  | 
            ||
| 216 | * $mask = $view->is(fn ($x) => $x % 2 === 0);  | 
            ||
| 217 | * $mask->getValue(); // [false, true, false, true, false, true]  | 
            ||
| 218 | *  | 
            ||
| 219 | * $view->subview($mask)->toArray(); // [2, 4, 6]  | 
            ||
| 220 | * $view[$mask]; // [2, 4, 6]  | 
            ||
| 221 | *  | 
            ||
| 222 | * $view[$mask] = [20, 40, 60];  | 
            ||
| 223 | * $source; // [1, 20, 3, 40, 5, 60]  | 
            ||
| 224 | * ```  | 
            ||
| 225 | *  | 
            ||
| 226 | * @param callable(T, int): bool $predicate Function that returns a boolean value for each element.  | 
            ||
| 227 | *  | 
            ||
| 228 | * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate.  | 
            ||
| 229 | *  | 
            ||
| 230 | * @see ArrayViewInterface::match() Full synonim.  | 
            ||
| 231 | */  | 
            ||
| 232 | public function is(callable $predicate): MaskSelectorInterface  | 
            ||
| 233 |     { | 
            ||
| 234 | $data = $this->toArray();  | 
            ||
| 235 | return new MaskSelector(array_map($predicate, $data, array_keys($data)));  | 
            ||
| 236 | }  | 
            ||
| 237 | |||
| 238 | /**  | 
            ||
| 239 | * Checks if all elements in the view satisfy a given predicate function.  | 
            ||
| 240 | *  | 
            ||
| 241 | * ##### Example  | 
            ||
| 242 | * ```php  | 
            ||
| 243 | * $source = [1, 2, 3, 4, 5, 6];  | 
            ||
| 244 | * $view = ArrayView::toView($source);  | 
            ||
| 245 | *  | 
            ||
| 246 | * $mask = $view->is(fn ($x) => $x % 2 === 0);  | 
            ||
| 247 | * $mask->getValue(); // [false, true, false, true, false, true]  | 
            ||
| 248 | *  | 
            ||
| 249 | * $view->subview($mask)->toArray(); // [2, 4, 6]  | 
            ||
| 250 | * $view[$mask]; // [2, 4, 6]  | 
            ||
| 251 | *  | 
            ||
| 252 | * $view[$mask] = [20, 40, 60];  | 
            ||
| 253 | * $source; // [1, 20, 3, 40, 5, 60]  | 
            ||
| 254 | * ```  | 
            ||
| 255 | *  | 
            ||
| 256 | * @param callable(T, int): bool $predicate Function that returns a boolean value for each element.  | 
            ||
| 257 | *  | 
            ||
| 258 | * @return MaskSelector Boolean mask for selecting elements that satisfy the predicate.  | 
            ||
| 259 | *  | 
            ||
| 260 | * @see ArrayView::match() Full synonim.  | 
            ||
| 261 | */  | 
            ||
| 262 | public function match(callable $predicate): MaskSelectorInterface  | 
            ||
| 263 |     { | 
            ||
| 264 | return $this->is($predicate);  | 
            ||
| 265 | }  | 
            ||
| 266 | |||
| 267 | /**  | 
            ||
| 268 | * Compares the elements of the current ArrayView instance with another array or ArrayView  | 
            ||
| 269 | * using the provided comparator function.  | 
            ||
| 270 | *  | 
            ||
| 271 | * @template U The type of the elements in the array for comparison with.  | 
            ||
| 272 | *  | 
            ||
| 273 | * @param array<U>|ArrayViewInterface<U> $data The array or ArrayView to compare to.  | 
            ||
| 274 | * @param callable(T, U, int): bool $comparator Function that determines the comparison logic between the elements.  | 
            ||
| 275 | *  | 
            ||
| 276 | * @return MaskSelectorInterface A MaskSelector instance representing the results of the element comparisons.  | 
            ||
| 277 | *  | 
            ||
| 278 | * @throws ValueError if the $data is not sequential array.  | 
            ||
| 279 | * @throws SizeError if size of $data not equals to size of the view.  | 
            ||
| 280 | *  | 
            ||
| 281 | * @see ArrayView::is() Full synonim.  | 
            ||
| 282 | */  | 
            ||
| 283 | public function matchWith($data, callable $comparator): MaskSelectorInterface  | 
            ||
| 284 |     { | 
            ||
| 285 | $data = $data instanceof ArrayViewInterface ? $data->toArray() : $data;  | 
            ||
| 286 | return new MaskSelector(array_map($comparator, $this->toArray(), $data, array_keys($data)));  | 
            ||
| 287 | }  | 
            ||
| 288 | |||
| 289 | /**  | 
            ||
| 290 | * Returns a subview of this view based on a selector or string slice.  | 
            ||
| 291 | *  | 
            ||
| 292 | * ##### Example (using selector objects)  | 
            ||
| 293 | * ```php  | 
            ||
| 294 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 295 | *  | 
            ||
| 296 | * $subview = ArrayView::toView($source)  | 
            ||
| 297 |      *     ->subview(new SliceSelector('::2'))                          // [1, 3, 5, 7, 9] | 
            ||
| 298 | * ->subview(new MaskSelector([true, false, true, true, true])) // [1, 5, 7, 9]  | 
            ||
| 299 | * ->subview(new IndexListSelector([0, 1, 2])) // [1, 5, 7]  | 
            ||
| 300 |      *     ->subview(new SliceSelector('1:'));                          // [5, 7] | 
            ||
| 301 | *  | 
            ||
| 302 | * $subview[':'] = [55, 77];  | 
            ||
| 303 | * print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]  | 
            ||
| 304 | * ```  | 
            ||
| 305 | *  | 
            ||
| 306 | * ##### Example (using short objects)  | 
            ||
| 307 | * ```php  | 
            ||
| 308 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 309 | *  | 
            ||
| 310 | * $subview = ArrayView::toView($source)  | 
            ||
| 311 |      *     ->subview('::2')                           // [1, 3, 5, 7, 9] | 
            ||
| 312 | * ->subview([true, false, true, true, true]) // [1, 5, 7, 9]  | 
            ||
| 313 | * ->subview([0, 1, 2]) // [1, 5, 7]  | 
            ||
| 314 |      *     ->subview('1:');                           // [5, 7] | 
            ||
| 315 | *  | 
            ||
| 316 | * $subview[':'] = [55, 77];  | 
            ||
| 317 | * print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]  | 
            ||
| 318 | * ```  | 
            ||
| 319 | *  | 
            ||
| 320 | * ##### Readonly example  | 
            ||
| 321 | * ```php  | 
            ||
| 322 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 323 |      * $subview = ArrayView::toView($source)->subview('::2'); | 
            ||
| 324 | *  | 
            ||
| 325 | * $subview[':']; // [1, 3, 5, 7, 9]  | 
            ||
| 326 | * $subview[':'] = [11, 33, 55, 77, 99]; // throws ReadonlyError  | 
            ||
| 327 | * $subview[0] = [11]; // throws ReadonlyError  | 
            ||
| 328 | * ```  | 
            ||
| 329 | *  | 
            ||
| 330 | * @template S of string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface Selector type.  | 
            ||
| 331 | *  | 
            ||
| 332 | * @param S $selector The selector or string to filter the subview.  | 
            ||
| 333 | * @param bool|null $readonly Flag indicating if the subview should be read-only.  | 
            ||
| 334 | *  | 
            ||
| 335 | * @return ArrayViewInterface<T> A new view representing the subview of this view.  | 
            ||
| 336 | *  | 
            ||
| 337 | * @throws IndexError if the selector is IndexListSelector and some indexes are out of range.  | 
            ||
| 338 | * @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.  | 
            ||
| 339 | * @throws KeyError if the selector is not valid (e.g. non-sequential array).  | 
            ||
| 340 | */  | 
            ||
| 341 | public function subview($selector, bool $readonly = null): ArrayViewInterface  | 
            ||
| 342 |     { | 
            ||
| 343 | return $this->toSelector($selector)->select($this, $readonly);  | 
            ||
| 344 | }  | 
            ||
| 345 | |||
| 346 | /**  | 
            ||
| 347 | * Applies a transformation function to each element in the view.  | 
            ||
| 348 | *  | 
            ||
| 349 | * ##### Example  | 
            ||
| 350 | * ```php  | 
            ||
| 351 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 352 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] | 
            ||
| 353 | *  | 
            ||
| 354 | * $subview->apply(fn ($x) => $x * 10);  | 
            ||
| 355 | *  | 
            ||
| 356 | * $subview->toArray(); // [10, 30, 50, 70, 90]  | 
            ||
| 357 | * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10]  | 
            ||
| 358 | * ```  | 
            ||
| 359 | *  | 
            ||
| 360 | * @param callable(T, int): T $mapper Function to transform each element.  | 
            ||
| 361 | *  | 
            ||
| 362 | * @return ArrayView<T> this view.  | 
            ||
| 363 | */  | 
            ||
| 364 | public function apply(callable $mapper): self  | 
            ||
| 365 |     { | 
            ||
| 366 | $size = \count($this);  | 
            ||
| 367 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 368 | /** @var T $item */  | 
            ||
| 369 | $item = $this[$i];  | 
            ||
| 370 | $this[$i] = $mapper($item, $i);  | 
            ||
| 371 | }  | 
            ||
| 372 | return $this;  | 
            ||
| 373 | }  | 
            ||
| 374 | |||
| 375 | /**  | 
            ||
| 376 | * Sets new values for the elements in the view.  | 
            ||
| 377 | *  | 
            ||
| 378 | * ##### Example  | 
            ||
| 379 | * ```php  | 
            ||
| 380 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 381 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] | 
            ||
| 382 | *  | 
            ||
| 383 | * $data = [9, 27, 45, 63, 81];  | 
            ||
| 384 | *  | 
            ||
| 385 | * $subview->applyWith($data, fn ($lhs, $rhs) => $lhs * $rhs);  | 
            ||
| 386 | * $subview->toArray(); // [10, 30, 50, 70, 90]  | 
            ||
| 387 | *  | 
            ||
| 388 | * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10]  | 
            ||
| 389 | * ```  | 
            ||
| 390 | *  | 
            ||
| 391 | * @template U Type of $data items.  | 
            ||
| 392 | *  | 
            ||
| 393 | * @param array<U>|ArrayViewInterface<U> $data  | 
            ||
| 394 | * @param callable(T, U, int): T $mapper  | 
            ||
| 395 | *  | 
            ||
| 396 | * @return ArrayView<T> this view.  | 
            ||
| 397 | *  | 
            ||
| 398 | * @throws ValueError if the $data is not sequential array.  | 
            ||
| 399 | * @throws SizeError if size of $data not equals to size of the view.  | 
            ||
| 400 | */  | 
            ||
| 401 | public function applyWith($data, callable $mapper): self  | 
            ||
| 402 |     { | 
            ||
| 403 | $this->checkSequential($data);  | 
            ||
| 404 | |||
| 405 | [$dataSize, $thisSize] = [\count($data), \count($this)];  | 
            ||
| 406 |         if ($dataSize !== $thisSize) { | 
            ||
| 407 |             throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); | 
            ||
| 408 | }  | 
            ||
| 409 | |||
| 410 | $dataView = ArrayView::toView($data);  | 
            ||
| 411 | |||
| 412 | $size = \count($this);  | 
            ||
| 413 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 414 | /** @var T $lhs */  | 
            ||
| 415 | $lhs = $this[$i];  | 
            ||
| 416 | /** @var U $rhs */  | 
            ||
| 417 | $rhs = $dataView[$i];  | 
            ||
| 418 | $this[$i] = $mapper($lhs, $rhs, $i);  | 
            ||
| 419 | }  | 
            ||
| 420 | |||
| 421 | return $this;  | 
            ||
| 422 | }  | 
            ||
| 423 | |||
| 424 | /**  | 
            ||
| 425 | * Sets new values for the elements in the view.  | 
            ||
| 426 | *  | 
            ||
| 427 | * ##### Example  | 
            ||
| 428 | * ```php  | 
            ||
| 429 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 430 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 431 | *  | 
            ||
| 432 | * $subview->set([11, 33, 55]);  | 
            ||
| 433 | * $subview->toArray(); // [11, 33, 55]  | 
            ||
| 434 | *  | 
            ||
| 435 | * $source; // [11, 2, 33, 4, 55]  | 
            ||
| 436 | * ```  | 
            ||
| 437 | *  | 
            ||
| 438 | * @param array<T>|ArrayViewInterface<T>|T $newValues The new values to set.  | 
            ||
| 439 | *  | 
            ||
| 440 | * @return ArrayView<T> this view.  | 
            ||
| 441 | *  | 
            ||
| 442 | * @throws ValueError if the $newValues is not sequential array.  | 
            ||
| 443 | * @throws SizeError if size of $newValues not equals to size of the view.  | 
            ||
| 444 | */  | 
            ||
| 445 | public function set($newValues): self  | 
            ||
| 446 |     { | 
            ||
| 447 | $this->checkSequential($newValues);  | 
            ||
| 448 | |||
| 449 |         if (!\is_array($newValues) && !($newValues instanceof ArrayViewInterface)) { | 
            ||
| 450 | $size = \count($this);  | 
            ||
| 451 |             for ($i = 0; $i < $size; $i++) { | 
            ||
| 452 | $this[$i] = $newValues;  | 
            ||
| 453 | }  | 
            ||
| 454 | return $this;  | 
            ||
| 455 | }  | 
            ||
| 456 | |||
| 457 | [$dataSize, $thisSize] = [\count($newValues), \count($this)];  | 
            ||
| 458 |         if ($dataSize !== $thisSize) { | 
            ||
| 459 |             throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); | 
            ||
| 460 | }  | 
            ||
| 461 | |||
| 462 | $size = \count($this);  | 
            ||
| 463 | |||
| 464 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 465 | $this[$i] = $newValues[$i];  | 
            ||
| 466 | }  | 
            ||
| 467 | |||
| 468 | return $this;  | 
            ||
| 469 | }  | 
            ||
| 470 | |||
| 471 | /**  | 
            ||
| 472 | * Return iterator to iterate the view elements.  | 
            ||
| 473 | *  | 
            ||
| 474 | * ##### Example  | 
            ||
| 475 | * ```php  | 
            ||
| 476 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 477 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 478 | *  | 
            ||
| 479 |      * foreach ($subview as $item) { | 
            ||
| 480 | * // 1, 3, 5  | 
            ||
| 481 | * }  | 
            ||
| 482 | *  | 
            ||
| 483 | * print_r([...$subview]); // [1, 3, 5]  | 
            ||
| 484 | * ```  | 
            ||
| 485 | *  | 
            ||
| 486 | * @return \Generator<int, T>  | 
            ||
| 487 | */  | 
            ||
| 488 | public function getIterator(): \Generator  | 
            ||
| 489 |     { | 
            ||
| 490 | $size = \count($this);  | 
            ||
| 491 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 492 | /** @var T $item */  | 
            ||
| 493 | $item = $this[$i];  | 
            ||
| 494 | yield $item;  | 
            ||
| 495 | }  | 
            ||
| 496 | }  | 
            ||
| 497 | |||
| 498 | /**  | 
            ||
| 499 | * Return true if view is readonly, otherwise false.  | 
            ||
| 500 | *  | 
            ||
| 501 | * ##### Example  | 
            ||
| 502 | * ```php  | 
            ||
| 503 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 504 | *  | 
            ||
| 505 | * $readonlyView = ArrayView::toView($source, true);  | 
            ||
| 506 | * $readonlyView->isReadonly(); // true  | 
            ||
| 507 | *  | 
            ||
| 508 |      * $readonlySubview = ArrayView::toView($source)->subview('::2', true); | 
            ||
| 509 | * $readonlySubview->isReadonly(); // true  | 
            ||
| 510 | *  | 
            ||
| 511 | * $view = ArrayView::toView($source);  | 
            ||
| 512 | * $view->isReadonly(); // false  | 
            ||
| 513 | *  | 
            ||
| 514 |      * $subview = ArrayView::toView($source)->subview('::2'); | 
            ||
| 515 | * $subview->isReadonly(); // false  | 
            ||
| 516 | * ```  | 
            ||
| 517 | *  | 
            ||
| 518 | * @return bool  | 
            ||
| 519 | */  | 
            ||
| 520 | public function isReadonly(): bool  | 
            ||
| 521 |     { | 
            ||
| 522 | return $this->readonly;  | 
            ||
| 523 | }  | 
            ||
| 524 | |||
| 525 | /**  | 
            ||
| 526 | * Return size of the view.  | 
            ||
| 527 | *  | 
            ||
| 528 | * ##### Example  | 
            ||
| 529 | * ```php  | 
            ||
| 530 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 531 | *  | 
            ||
| 532 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 533 | * count($subview); // 3  | 
            ||
| 534 | * ```  | 
            ||
| 535 | *  | 
            ||
| 536 | * @return int  | 
            ||
| 537 | */  | 
            ||
| 538 | public function count(): int  | 
            ||
| 541 | }  | 
            ||
| 542 | |||
| 543 | /**  | 
            ||
| 544 | * Get the size of the parent view or source array.  | 
            ||
| 545 | *  | 
            ||
| 546 | * @return int The size of the parent view or source array.  | 
            ||
| 547 | */  | 
            ||
| 548 | protected function getParentSize(): int  | 
            ||
| 553 | }  | 
            ||
| 554 | |||
| 555 | /**  | 
            ||
| 556 | * Check if the given source array is sequential (indexed from 0 to n-1).  | 
            ||
| 557 | *  | 
            ||
| 558 | * If the array is not sequential, a ValueError is thrown indicating that  | 
            ||
| 559 | * a view cannot be created for a non-sequential array.  | 
            ||
| 560 | *  | 
            ||
| 561 | * @param mixed $source The source array to check for sequential indexing.  | 
            ||
| 562 | *  | 
            ||
| 563 | * @return void  | 
            ||
| 564 | *  | 
            ||
| 565 | * @throws ValueError if the source array is not sequential.  | 
            ||
| 566 | */  | 
            ||
| 567 | protected function checkSequential($source): void  | 
            ||
| 568 |     { | 
            ||
| 569 |         if (is_array($source) && !Util::isArraySequential($source)) { | 
            ||
| 570 |             throw new ValueError('Cannot create view for non-sequential array.'); | 
            ||
| 571 | }  | 
            ||
| 572 | }  | 
            ||
| 573 | |||
| 574 | /**  | 
            ||
| 575 | * Convert the given index to a valid index within the source array.  | 
            ||
| 576 | *  | 
            ||
| 577 | * @param int $i The index to convert.  | 
            ||
| 578 | *  | 
            ||
| 579 | * @return int The converted index within the source array.  | 
            ||
| 580 | *  | 
            ||
| 581 | * @throws IndexError if the index is out of range and $throwError is true.  | 
            ||
| 582 | */  | 
            ||
| 583 | protected function convertIndex(int $i): int  | 
            ||
| 584 |     { | 
            ||
| 585 | return Util::normalizeIndex($i, \count($this->source));  | 
            ||
| 586 | }  | 
            ||
| 587 | |||
| 588 | /**  | 
            ||
| 589 | * Check if a numeric offset exists in the source array.  | 
            ||
| 590 | *  | 
            ||
| 591 | * @param numeric $offset The numeric offset to check.  | 
            ||
| 592 | *  | 
            ||
| 593 | * @return bool Returns true if the numeric offset exists in the source, false otherwise.  | 
            ||
| 594 | */  | 
            ||
| 595 | private function numericOffsetExists($offset): bool  | 
            ||
| 616 | }  | 
            ||
| 617 | }  | 
            ||
| 618 |