| Total Complexity | 42 | 
| Total Lines | 532 | 
| Duplicated Lines | 0 % | 
| Changes | 8 | ||
| Bugs | 2 | Features | 2 | 
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<mixed>|ArrayViewInterface<mixed>|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  | 
            ||
| 133 |     { | 
            ||
| 134 | return static::toView($source, $readonly);  | 
            ||
| 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  | 
            ||
| 179 |     { | 
            ||
| 180 | return [...$this];  | 
            ||
| 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  | 
            ||
| 204 |     { | 
            ||
| 205 | return $this->is($predicate)->select($this);  | 
            ||
| 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 | public function is(callable $predicate): MaskSelectorInterface  | 
            ||
| 231 |     { | 
            ||
| 232 | $data = $this->toArray();  | 
            ||
| 233 | return new MaskSelector(array_map($predicate, $data, array_keys($data)));  | 
            ||
| 234 | }  | 
            ||
| 235 | |||
| 236 | /**  | 
            ||
| 237 | * Returns a subview of this view based on a selector or string slice.  | 
            ||
| 238 | *  | 
            ||
| 239 | * ##### Example (using selector objects)  | 
            ||
| 240 | * ```  | 
            ||
| 241 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 242 | *  | 
            ||
| 243 | * $subview = ArrayView::toView($source)  | 
            ||
| 244 |      *     ->subview(new SliceSelector('::2'))                          // [1, 3, 5, 7, 9] | 
            ||
| 245 | * ->subview(new MaskSelector([true, false, true, true, true])) // [1, 5, 7, 9]  | 
            ||
| 246 | * ->subview(new IndexListSelector([0, 1, 2])) // [1, 5, 7]  | 
            ||
| 247 |      *     ->subview(new SliceSelector('1:'));                          // [5, 7] | 
            ||
| 248 | *  | 
            ||
| 249 | * $subview[':'] = [55, 77];  | 
            ||
| 250 | * print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]  | 
            ||
| 251 | * ```  | 
            ||
| 252 | *  | 
            ||
| 253 | * ##### Example (using short objects)  | 
            ||
| 254 | * ```  | 
            ||
| 255 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 256 | *  | 
            ||
| 257 | * $subview = ArrayView::toView($source)  | 
            ||
| 258 |      *     ->subview('::2')                           // [1, 3, 5, 7, 9] | 
            ||
| 259 | * ->subview([true, false, true, true, true]) // [1, 5, 7, 9]  | 
            ||
| 260 | * ->subview([0, 1, 2]) // [1, 5, 7]  | 
            ||
| 261 |      *     ->subview('1:');                           // [5, 7] | 
            ||
| 262 | *  | 
            ||
| 263 | * $subview[':'] = [55, 77];  | 
            ||
| 264 | * print_r($source); // [1, 2, 3, 4, 55, 6, 77, 8, 9, 10]  | 
            ||
| 265 | * ```  | 
            ||
| 266 | *  | 
            ||
| 267 | * ##### Readonly example  | 
            ||
| 268 | * ```  | 
            ||
| 269 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 270 |      * $subview = ArrayView::toView($source)->subview('::2'); | 
            ||
| 271 | *  | 
            ||
| 272 | * $subview[':']; // [1, 3, 5, 7, 9]  | 
            ||
| 273 | * $subview[':'] = [11, 33, 55, 77, 99]; // throws ReadonlyError  | 
            ||
| 274 | * $subview[0] = [11]; // throws ReadonlyError  | 
            ||
| 275 | * ```  | 
            ||
| 276 | *  | 
            ||
| 277 | * @template S of string|array<mixed>|ArrayViewInterface<mixed>|ArraySelectorInterface  | 
            ||
| 278 | *  | 
            ||
| 279 | * @param S $selector The selector or string to filter the subview.  | 
            ||
| 280 | * @param bool|null $readonly Flag indicating if the subview should be read-only.  | 
            ||
| 281 | *  | 
            ||
| 282 | * @return ArrayViewInterface<T> A new view representing the subview of this view.  | 
            ||
| 283 | *  | 
            ||
| 284 | * @throws IndexError if the selector is IndexListSelector and some indexes are out of range.  | 
            ||
| 285 | * @throws SizeError if the selector is MaskSelector and size of the mask not equals to size of the view.  | 
            ||
| 286 | * @throws KeyError if the selector is not valid (e.g. non-sequential array).  | 
            ||
| 287 | */  | 
            ||
| 288 | public function subview($selector, bool $readonly = null): ArrayViewInterface  | 
            ||
| 289 |     { | 
            ||
| 290 | return $this->toSelector($selector)->select($this, $readonly);  | 
            ||
| 291 | }  | 
            ||
| 292 | |||
| 293 | /**  | 
            ||
| 294 | * Applies a transformation function to each element in the view.  | 
            ||
| 295 | *  | 
            ||
| 296 | * ##### Example  | 
            ||
| 297 | * ```php  | 
            ||
| 298 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 299 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] | 
            ||
| 300 | *  | 
            ||
| 301 | * $subview->apply(fn ($x) => $x * 10);  | 
            ||
| 302 | *  | 
            ||
| 303 | * $subview->toArray(); // [10, 30, 50, 70, 90]  | 
            ||
| 304 | * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10]  | 
            ||
| 305 | * ```  | 
            ||
| 306 | *  | 
            ||
| 307 | * @param callable(T, int): T $mapper Function to transform each element.  | 
            ||
| 308 | *  | 
            ||
| 309 | * @return ArrayView<T> this view.  | 
            ||
| 310 | */  | 
            ||
| 311 | public function apply(callable $mapper): self  | 
            ||
| 312 |     { | 
            ||
| 313 | $size = \count($this);  | 
            ||
| 314 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 315 | /** @var T $item */  | 
            ||
| 316 | $item = $this[$i];  | 
            ||
| 317 | $this[$i] = $mapper($item, $i);  | 
            ||
| 318 | }  | 
            ||
| 319 | return $this;  | 
            ||
| 320 | }  | 
            ||
| 321 | |||
| 322 | /**  | 
            ||
| 323 | * Sets new values for the elements in the view.  | 
            ||
| 324 | *  | 
            ||
| 325 | * ##### Example  | 
            ||
| 326 | * ```php  | 
            ||
| 327 | * $source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];  | 
            ||
| 328 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5, 7, 9] | 
            ||
| 329 | *  | 
            ||
| 330 | * $data = [9, 27, 45, 63, 81];  | 
            ||
| 331 | *  | 
            ||
| 332 | * $subview->applyWith($data, fn ($lhs, $rhs) => $lhs * $rhs);  | 
            ||
| 333 | * $subview->toArray(); // [10, 30, 50, 70, 90]  | 
            ||
| 334 | *  | 
            ||
| 335 | * $source; // [10, 2, 30, 4, 50, 6, 70, 8, 90, 10]  | 
            ||
| 336 | * ```  | 
            ||
| 337 | *  | 
            ||
| 338 | * @template U Type of $data items.  | 
            ||
| 339 | *  | 
            ||
| 340 | * @param array<U>|ArrayViewInterface<U> $data  | 
            ||
| 341 | * @param callable(T, U, int): T $mapper  | 
            ||
| 342 | *  | 
            ||
| 343 | * @return ArrayView<T> this view.  | 
            ||
| 344 | *  | 
            ||
| 345 | * @throws ValueError if the $data is not sequential array.  | 
            ||
| 346 | * @throws SizeError if size of $data not equals to size of the view.  | 
            ||
| 347 | */  | 
            ||
| 348 | public function applyWith($data, callable $mapper): self  | 
            ||
| 349 |     { | 
            ||
| 350 | $this->checkSequential($data);  | 
            ||
| 351 | |||
| 352 | [$dataSize, $thisSize] = [\count($data), \count($this)];  | 
            ||
| 353 |         if ($dataSize !== $thisSize) { | 
            ||
| 354 |             throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); | 
            ||
| 355 | }  | 
            ||
| 356 | |||
| 357 | $dataView = ArrayView::toView($data);  | 
            ||
| 358 | |||
| 359 | $size = \count($this);  | 
            ||
| 360 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 361 | /** @var T $lhs */  | 
            ||
| 362 | $lhs = $this[$i];  | 
            ||
| 363 | /** @var U $rhs */  | 
            ||
| 364 | $rhs = $dataView[$i];  | 
            ||
| 365 | $this[$i] = $mapper($lhs, $rhs, $i);  | 
            ||
| 366 | }  | 
            ||
| 367 | |||
| 368 | return $this;  | 
            ||
| 369 | }  | 
            ||
| 370 | |||
| 371 | /**  | 
            ||
| 372 | * Sets new values for the elements in the view.  | 
            ||
| 373 | *  | 
            ||
| 374 | * ##### Example  | 
            ||
| 375 | * ```php  | 
            ||
| 376 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 377 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 378 | *  | 
            ||
| 379 | * $subview->set([11, 33, 55]);  | 
            ||
| 380 | * $subview->toArray(); // [11, 33, 55]  | 
            ||
| 381 | *  | 
            ||
| 382 | * $source; // [11, 2, 33, 4, 55]  | 
            ||
| 383 | * ```  | 
            ||
| 384 | *  | 
            ||
| 385 | * @param array<T>|ArrayViewInterface<T>|T $newValues The new values to set.  | 
            ||
| 386 | *  | 
            ||
| 387 | * @return ArrayView<T> this view.  | 
            ||
| 388 | *  | 
            ||
| 389 | * @throws ValueError if the $newValues is not sequential array.  | 
            ||
| 390 | * @throws SizeError if size of $newValues not equals to size of the view.  | 
            ||
| 391 | */  | 
            ||
| 392 | public function set($newValues): self  | 
            ||
| 393 |     { | 
            ||
| 394 | $this->checkSequential($newValues);  | 
            ||
| 395 | |||
| 396 |         if (!\is_array($newValues) && !($newValues instanceof ArrayViewInterface)) { | 
            ||
| 397 | $size = \count($this);  | 
            ||
| 398 |             for ($i = 0; $i < $size; $i++) { | 
            ||
| 399 | $this[$i] = $newValues;  | 
            ||
| 400 | }  | 
            ||
| 401 | return $this;  | 
            ||
| 402 | }  | 
            ||
| 403 | |||
| 404 | [$dataSize, $thisSize] = [\count($newValues), \count($this)];  | 
            ||
| 405 |         if ($dataSize !== $thisSize) { | 
            ||
| 406 |             throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize})."); | 
            ||
| 407 | }  | 
            ||
| 408 | |||
| 409 | $newValuesView = ArrayView::toView($newValues);  | 
            ||
| 410 | |||
| 411 | $size = \count($this);  | 
            ||
| 412 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 413 | $this[$i] = $newValuesView[$i];  | 
            ||
| 414 | }  | 
            ||
| 415 | |||
| 416 | return $this;  | 
            ||
| 417 | }  | 
            ||
| 418 | |||
| 419 | /**  | 
            ||
| 420 | * Return iterator to iterate the view elements.  | 
            ||
| 421 | *  | 
            ||
| 422 | * ##### Example  | 
            ||
| 423 | * ```php  | 
            ||
| 424 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 425 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 426 | *  | 
            ||
| 427 |      * foreach ($subview as $item) { | 
            ||
| 428 | * // 1, 3, 5  | 
            ||
| 429 | * }  | 
            ||
| 430 | *  | 
            ||
| 431 | * print_r([...$subview]); // [1, 3, 5]  | 
            ||
| 432 | * ```  | 
            ||
| 433 | *  | 
            ||
| 434 | * @return \Generator<int, T>  | 
            ||
| 435 | */  | 
            ||
| 436 | public function getIterator(): \Generator  | 
            ||
| 437 |     { | 
            ||
| 438 | $size = \count($this);  | 
            ||
| 439 |         for ($i = 0; $i < $size; $i++) { | 
            ||
| 440 | /** @var T $item */  | 
            ||
| 441 | $item = $this[$i];  | 
            ||
| 442 | yield $item;  | 
            ||
| 443 | }  | 
            ||
| 444 | }  | 
            ||
| 445 | |||
| 446 | /**  | 
            ||
| 447 | * Return true if view is readonly, otherwise false.  | 
            ||
| 448 | *  | 
            ||
| 449 | * ##### Example  | 
            ||
| 450 | * ```php  | 
            ||
| 451 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 452 | *  | 
            ||
| 453 | * $readonlyView = ArrayView::toView($source, true);  | 
            ||
| 454 | * $readonlyView->isReadonly(); // true  | 
            ||
| 455 | *  | 
            ||
| 456 |      * $readonlySubview = ArrayView::toView($source)->subview('::2', true); | 
            ||
| 457 | * $readonlySubview->isReadonly(); // true  | 
            ||
| 458 | *  | 
            ||
| 459 | * $view = ArrayView::toView($source);  | 
            ||
| 460 | * $view->isReadonly(); // false  | 
            ||
| 461 | *  | 
            ||
| 462 |      * $subview = ArrayView::toView($source)->subview('::2'); | 
            ||
| 463 | * $subview->isReadonly(); // false  | 
            ||
| 464 | * ```  | 
            ||
| 465 | *  | 
            ||
| 466 | * @return bool  | 
            ||
| 467 | */  | 
            ||
| 468 | public function isReadonly(): bool  | 
            ||
| 469 |     { | 
            ||
| 470 | return $this->readonly;  | 
            ||
| 471 | }  | 
            ||
| 472 | |||
| 473 | /**  | 
            ||
| 474 | * Return size of the view.  | 
            ||
| 475 | *  | 
            ||
| 476 | * ##### Example  | 
            ||
| 477 | * ```php  | 
            ||
| 478 | * $source = [1, 2, 3, 4, 5];  | 
            ||
| 479 | *  | 
            ||
| 480 |      * $subview = ArrayView::toView($source)->subview('::2'); // [1, 3, 5] | 
            ||
| 481 | * count($subview); // 3  | 
            ||
| 482 | * ```  | 
            ||
| 483 | *  | 
            ||
| 484 | * @return int  | 
            ||
| 485 | */  | 
            ||
| 486 | public function count(): int  | 
            ||
| 489 | }  | 
            ||
| 490 | |||
| 491 | /**  | 
            ||
| 492 | * Get the size of the parent view or source array.  | 
            ||
| 493 | *  | 
            ||
| 494 | * @return int The size of the parent view or source array.  | 
            ||
| 495 | */  | 
            ||
| 496 | protected function getParentSize(): int  | 
            ||
| 501 | }  | 
            ||
| 502 | |||
| 503 | /**  | 
            ||
| 504 | * Check if the given source array is sequential (indexed from 0 to n-1).  | 
            ||
| 505 | *  | 
            ||
| 506 | * If the array is not sequential, a ValueError is thrown indicating that  | 
            ||
| 507 | * a view cannot be created for a non-sequential array.  | 
            ||
| 508 | *  | 
            ||
| 509 | * @param mixed $source The source array to check for sequential indexing.  | 
            ||
| 510 | *  | 
            ||
| 511 | * @return void  | 
            ||
| 512 | *  | 
            ||
| 513 | * @throws ValueError if the source array is not sequential.  | 
            ||
| 514 | */  | 
            ||
| 515 | protected function checkSequential($source): void  | 
            ||
| 516 |     { | 
            ||
| 517 |         if (is_array($source) && !Util::isArraySequential($source)) { | 
            ||
| 518 |             throw new ValueError('Cannot create view for non-sequential array.'); | 
            ||
| 519 | }  | 
            ||
| 520 | }  | 
            ||
| 521 | |||
| 522 | /**  | 
            ||
| 523 | * Convert the given index to a valid index within the source array.  | 
            ||
| 524 | *  | 
            ||
| 525 | * @param int $i The index to convert.  | 
            ||
| 526 | *  | 
            ||
| 527 | * @return int The converted index within the source array.  | 
            ||
| 528 | *  | 
            ||
| 529 | * @throws IndexError if the index is out of range and $throwError is true.  | 
            ||
| 530 | */  | 
            ||
| 531 | protected function convertIndex(int $i): int  | 
            ||
| 532 |     { | 
            ||
| 533 | return Util::normalizeIndex($i, \count($this->source));  | 
            ||
| 534 | }  | 
            ||
| 535 | |||
| 536 | /**  | 
            ||
| 537 | * Check if a numeric offset exists in the source array.  | 
            ||
| 538 | *  | 
            ||
| 539 | * @param numeric $offset The numeric offset to check.  | 
            ||
| 540 | *  | 
            ||
| 541 | * @return bool Returns true if the numeric offset exists in the source, false otherwise.  | 
            ||
| 542 | */  | 
            ||
| 543 | private function numericOffsetExists($offset): bool  | 
            ||
| 564 | }  | 
            ||
| 565 | }  | 
            ||
| 566 |