1 | <?php |
||
2 | |||
3 | /** |
||
4 | * PHP: Nelson Martell Library file |
||
5 | * |
||
6 | * Copyright © 2015-2021 Nelson Martell (http://nelson6e65.github.io) |
||
7 | * |
||
8 | * Licensed under The MIT License (MIT) |
||
9 | * For full copyright and license information, please see the LICENSE |
||
10 | * Redistributions of files must retain the above copyright notice. |
||
11 | * |
||
12 | * @copyright 2015-2021 Nelson Martell |
||
13 | * @link http://nelson6e65.github.io/php_nml/ |
||
14 | * @since 0.4.0 |
||
15 | * @license http://www.opensource.org/licenses/mit-license.php The MIT License (MIT) |
||
16 | * */ |
||
17 | |||
18 | namespace NelsonMartell\Collections; |
||
19 | |||
20 | use OutOfRangeException; |
||
21 | use NelsonMartell\Extensions\Text; |
||
22 | use NelsonMartell\StrictObject; |
||
23 | |||
24 | use function NelsonMartell\typeof; |
||
25 | |||
26 | /** |
||
27 | * Clase base de una colección de objetos, que provee una implementación |
||
28 | * predeterminada de la interfaz ICollection. |
||
29 | * |
||
30 | * @author Nelson Martell <[email protected]> |
||
31 | * @since 0.4.0 |
||
32 | * |
||
33 | * @property-read int $count Obtiene el número de elementos incluidos en la colección. Esta propiedad es de sólo |
||
34 | * lectura. |
||
35 | * */ |
||
36 | class Collection extends StrictObject implements ICollection |
||
37 | { |
||
38 | // Implementación de la interfaz Iterator. |
||
39 | use CollectionIterator; |
||
40 | |||
41 | public function __construct() |
||
42 | { |
||
43 | parent::__construct(); |
||
44 | } |
||
45 | |||
46 | final public function __invoke($index, $value = null) |
||
47 | { |
||
48 | if ($value == null) { |
||
49 | return $this->items[$index]; |
||
50 | } |
||
51 | |||
52 | $this->setItem($index, $value); |
||
53 | } |
||
54 | |||
55 | private $items = array(); |
||
56 | |||
57 | |||
58 | /** |
||
59 | * Inserta un nuevo elemento a la colección, en el índice especificado. |
||
60 | * |
||
61 | * @param int $index Índice del elemento a insertar. |
||
62 | * @param mixed $newItem Nuevo elemento a insertar a la colección. |
||
63 | * |
||
64 | * @return void |
||
65 | * */ |
||
66 | protected function insertItem($index, $newItem) |
||
67 | { |
||
68 | if ($index > $this->count || $index < 0) { |
||
69 | throw new OutOfRangeException(); |
||
70 | } |
||
71 | |||
72 | if ($index == $this->count) { |
||
73 | $this->items[$index] = null; |
||
74 | $this->count++; |
||
75 | } |
||
76 | |||
77 | $this->items[$index] = $newItem; |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * Quita todos los elementos de la colección. |
||
82 | * |
||
83 | * @return void |
||
84 | * */ |
||
85 | protected function clearItems() |
||
86 | { |
||
87 | $this->items = array(); |
||
88 | $this->count = 0; |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Establece un elemento en el índice especificado. |
||
93 | * |
||
94 | * @param int $index Índice del elemento a establecer. |
||
95 | * @param mixed $newItem Nuevo valor con el que se va a reemplazar. |
||
96 | * |
||
97 | * @return void |
||
98 | * */ |
||
99 | protected function setItem($index, $newItem) |
||
100 | { |
||
101 | if ($index >= $this->count || $index < 0) { |
||
102 | throw new OutOfRangeException(); |
||
103 | } |
||
104 | |||
105 | $this->items[$index] = $newItem; |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Obtiene el elemento almacenado en el índice especificado. |
||
110 | * Este método no lanza excepción en caso de indicar un índice fuera del |
||
111 | * rango; en cambio, devuelve NULL. |
||
112 | * El elemento obtenido es de sólo lectura. Para modificar el elemento |
||
113 | * dentro de la colección, tendría que utilizarse el método |
||
114 | * Collection::setItem una vez modificado. |
||
115 | * |
||
116 | * @param int $index Índice del elemento a obtener. |
||
117 | * |
||
118 | * @return mixed |
||
119 | * */ |
||
120 | protected function getItem($index) |
||
121 | { |
||
122 | if ($index >= $this->count || $index < 0) { |
||
123 | return null; |
||
124 | } |
||
125 | |||
126 | return $this->items[$index]; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Remove the item in specified index. |
||
131 | * |
||
132 | * @param int $index Index. |
||
133 | * |
||
134 | * @return void |
||
135 | */ |
||
136 | protected function removeItem($index) |
||
137 | { |
||
138 | if ($index >= $this->count || $index < 0) { |
||
139 | throw new OutOfRangeException(); |
||
140 | } |
||
141 | |||
142 | for ($i = $index; $i < $this->count - 1; $i++) { |
||
143 | $this->items[$i] = $this->items[$i + 1]; //Mueve los valores |
||
144 | } |
||
145 | |||
146 | unset($this->items[$this->count - 1]); //Des-asigna el último elemento |
||
147 | |||
148 | $this->count--; |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Gets the string representation of this object collection. |
||
153 | * |
||
154 | * You can format the output, by setting $format param to one of this |
||
155 | * options: |
||
156 | * - `R` or `r`: All items, separated by comma and space (`, `). |
||
157 | * This is the default format. |
||
158 | * - `L` or `l`: Same as `r` option, but enclosed in braces (`{`, `}`). |
||
159 | * - `g`: A full string, containing class name, items count and items |
||
160 | * list (this list, like `L` option). |
||
161 | * - `G`: Same as `g`, but using a full class name (including namespace). |
||
162 | * |
||
163 | * You can also use a custom format instead, using this placeholders: |
||
164 | * - `{class}`: Short class name (without namespace part); |
||
165 | * - `{nsclass}`: Full class name (included namespace); |
||
166 | * - `{count}`: Items count; and |
||
167 | * - `{items}`: List of items, using comma and space (`, `) as separator. |
||
168 | * |
||
169 | * Example: For a instance with 10 elements (numbers: 1-10), using: |
||
170 | * `Collection::ToString('My collection ({count} items): { {items} }');` |
||
171 | * Result: 'My collection (10 items): { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }' |
||
172 | * |
||
173 | * @param string $format String format (optional). By default, `r`. |
||
174 | * |
||
175 | * @return string |
||
176 | * @see Text::format() |
||
177 | * */ |
||
178 | public function toString($format = 'r') |
||
179 | { |
||
180 | static $defaultFormats = [ |
||
181 | 'r' => '{items}', |
||
182 | 'l' => '{ {items} }', |
||
183 | 'g' => '{class} ({count} items): { {items} }', |
||
184 | 'G' => '{nsclass} ({count} items): { {items} }', |
||
185 | ]; |
||
186 | |||
187 | if ($format == null || !is_string($format)) { |
||
188 | $format = 'r'; //Override if is not an string |
||
189 | } |
||
190 | |||
191 | $str = ''; |
||
192 | switch ($format) { |
||
193 | case 'r': |
||
194 | case 'l': |
||
195 | case 'g': |
||
196 | case 'G': |
||
197 | $str = $defaultFormats[$format]; |
||
198 | break; |
||
199 | |||
200 | default: |
||
201 | $str = $format; |
||
202 | } |
||
203 | |||
204 | $t = typeof($this); |
||
205 | |||
206 | $items = implode(', ', $this->items); |
||
207 | |||
208 | $placeHoldersValues = [ |
||
209 | 'class' => $t->shortName, |
||
210 | 'nsclass' => $t->name, |
||
211 | 'count' => $this->count, |
||
212 | 'items' => $items, |
||
213 | ]; |
||
214 | |||
215 | |||
216 | $s = Text::format($str, $placeHoldersValues); |
||
217 | |||
218 | return $s; |
||
219 | } |
||
220 | |||
221 | private $count = 0; |
||
222 | |||
223 | /** |
||
224 | * Getter for $count property. |
||
225 | * |
||
226 | * @return int |
||
227 | * */ |
||
228 | public function getCount() |
||
229 | { |
||
230 | return $this->count; |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Agrega un nuevo elemento al final de la colección. |
||
235 | * Nota para herederos: Para cambiar el comportamiento de este método, |
||
236 | * reemplazar más bien el método protegido 'insertItem'. |
||
237 | * |
||
238 | * @param mixed $item Elemento que se va a agregar a la colección. |
||
239 | * |
||
240 | * @return void |
||
241 | * */ |
||
242 | public function add($item) |
||
243 | { |
||
244 | $this->insertItem($this->count, $item); |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * Quita todos los elementos de la colección. |
||
249 | * Nota para herederos: Para cambiar el comportamiento de este método, |
||
250 | * reemplazar más bien el método protegido `Collection::clearItems`. |
||
251 | * |
||
252 | * @return void |
||
253 | * @see Collection::clearItems() |
||
254 | * */ |
||
255 | public function clear() |
||
256 | { |
||
257 | $this->clearItems(); |
||
258 | } |
||
259 | |||
260 | /** |
||
261 | * Determina si la colección contiene al elemento especificado. |
||
262 | * |
||
263 | * @param mixed $item Objeto que se va a buscar. |
||
264 | * |
||
265 | * @return bool `true` si $item se encuentra; en caso contrario, `false`. |
||
266 | * */ |
||
267 | public function contains($item) |
||
268 | { |
||
269 | foreach ($this->items as $i) { |
||
270 | if ($item === $i) { |
||
271 | return true; |
||
272 | } |
||
273 | } |
||
274 | |||
275 | return false; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Quita, si existe, la primera aparición de un objeto específico de la |
||
280 | * colección. |
||
281 | * |
||
282 | * @param mixed $item Objeto que se va a quitar. |
||
283 | * |
||
284 | * @return bool `true` si el elemento se ha quitado correctamente; en |
||
285 | * caso contrario, `false`. Este método también devuelve `false` si no |
||
286 | * se encontró. |
||
287 | * */ |
||
288 | public function remove($item) |
||
289 | { |
||
290 | for ($i = 0; $i < $this->count; $i++) { |
||
291 | if ($this->items[$i] === $item) { |
||
292 | $this->removeItem($i); |
||
293 | } |
||
294 | } |
||
295 | |||
296 | return false; |
||
297 | } |
||
298 | } |
||
299 |