1<?php
2
3declare(strict_types=1);
4
5/*
6 * This file is part of the league/commonmark package.
7 *
8 * (c) Colin O'Dell <colinodell@gmail.com>
9 *
10 * For the full copyright and license information, please view the LICENSE
11 * file that was distributed with this source code.
12 */
13
14namespace League\CommonMark\Util;
15
16/**
17 * Array collection
18 *
19 * Provides a wrapper around a standard PHP array.
20 *
21 * @internal
22 *
23 * @phpstan-template T
24 * @phpstan-implements \IteratorAggregate<int, T>
25 * @phpstan-implements \ArrayAccess<int, T>
26 */
27final class ArrayCollection implements \IteratorAggregate, \Countable, \ArrayAccess
28{
29    /**
30     * @var array<int, mixed>
31     * @phpstan-var array<int, T>
32     */
33    private array $elements;
34
35    /**
36     * Constructor
37     *
38     * @param array<int|string, mixed> $elements
39     *
40     * @phpstan-param array<int, T> $elements
41     */
42    public function __construct(array $elements = [])
43    {
44        $this->elements = $elements;
45    }
46
47    /**
48     * @return mixed|false
49     *
50     * @phpstan-return T|false
51     */
52    public function first()
53    {
54        return \reset($this->elements);
55    }
56
57    /**
58     * @return mixed|false
59     *
60     * @phpstan-return T|false
61     */
62    public function last()
63    {
64        return \end($this->elements);
65    }
66
67    /**
68     * Retrieve an external iterator
69     *
70     * @return \ArrayIterator<int, mixed>
71     *
72     * @phpstan-return \ArrayIterator<int, T>
73     */
74    #[\ReturnTypeWillChange]
75    public function getIterator(): \ArrayIterator
76    {
77        return new \ArrayIterator($this->elements);
78    }
79
80    /**
81     * Count elements of an object
82     *
83     * @return int The count as an integer.
84     */
85    public function count(): int
86    {
87        return \count($this->elements);
88    }
89
90    /**
91     * Whether an offset exists
92     *
93     * {@inheritDoc}
94     *
95     * @phpstan-param int $offset
96     */
97    public function offsetExists($offset): bool
98    {
99        return \array_key_exists($offset, $this->elements);
100    }
101
102    /**
103     * Offset to retrieve
104     *
105     * {@inheritDoc}
106     *
107     * @phpstan-param int $offset
108     *
109     * @phpstan-return T|null
110     */
111    #[\ReturnTypeWillChange]
112    public function offsetGet($offset)
113    {
114        return $this->elements[$offset] ?? null;
115    }
116
117    /**
118     * Offset to set
119     *
120     * {@inheritDoc}
121     *
122     * @phpstan-param int|null $offset
123     * @phpstan-param T        $value
124     */
125    #[\ReturnTypeWillChange]
126    public function offsetSet($offset, $value): void
127    {
128        if ($offset === null) {
129            $this->elements[] = $value;
130        } else {
131            $this->elements[$offset] = $value;
132        }
133    }
134
135    /**
136     * Offset to unset
137     *
138     * {@inheritDoc}
139     *
140     * @phpstan-param int $offset
141     */
142    #[\ReturnTypeWillChange]
143    public function offsetUnset($offset): void
144    {
145        if (! \array_key_exists($offset, $this->elements)) {
146            return;
147        }
148
149        unset($this->elements[$offset]);
150    }
151
152    /**
153     * Returns a subset of the array
154     *
155     * @return array<int, mixed>
156     *
157     * @phpstan-return array<int, T>
158     */
159    public function slice(int $offset, ?int $length = null): array
160    {
161        return \array_slice($this->elements, $offset, $length, true);
162    }
163
164    /**
165     * @return array<int, mixed>
166     *
167     * @phpstan-return array<int, T>
168     */
169    public function toArray(): array
170    {
171        return $this->elements;
172    }
173}
174