• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHP-Alchemist / coreFiles / 13531756279

25 Feb 2025 09:58PM UTC coverage: 96.481% (+0.006%) from 96.475%
13531756279

push

github

druid628
Uses coverallsapp/github-actions@v2

521 of 540 relevant lines covered (96.48%)

4.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.45
/src/Abstracts/AbstractAssociativeArray.php
1
<?php
2

3
namespace PHPAlchemist\Abstracts;
4

5
use Exception;
6
use PHPAlchemist\Contracts\AssociativeArrayInterface;
7
use PHPAlchemist\Contracts\StringInterface;
8
use PHPAlchemist\Exceptions\HashTableFullException;
9
use PHPAlchemist\Exceptions\InvalidKeyTypeException;
10
use PHPAlchemist\Exceptions\ReadOnlyDataException;
11
use PHPAlchemist\Exceptions\UnmatchedClassException;
12
use PHPAlchemist\Exceptions\UnmatchedVersionException;
13
use PHPAlchemist\Traits\Array\OnClearTrait;
14
use PHPAlchemist\Traits\Array\OnInsertTrait;
15
use PHPAlchemist\Traits\Array\OnRemoveTrait;
16
use PHPAlchemist\Traits\Array\OnSetTrait;
17
use PHPAlchemist\Traits\ArrayTrait;
18
use PHPAlchemist\Types\Twine;
19

20
/**
21
 * Abstract class for Associative Array (Objectified Array Class).
22
 */
23
abstract class AbstractAssociativeArray implements AssociativeArrayInterface
24
{
25
    use ArrayTrait;
26
    use OnInsertTrait;
27
    use OnRemoveTrait;
28
    use OnClearTrait;
29
    use OnSetTrait;
30

31
    public static $serializeVersion = 1;
32

33
    /**
34
     * @var bool
35
     */
36
    protected $readOnly;
37

38
    /**
39
     * @var int position sentinel variable
40
     */
41
    protected $position;
42

43
    /**
44
     * @var array<string, mixed>
45
     */
46
    protected $data;
47

48
    /**
49
     * @var int locking a HashTable to a fixed size
50
     */
51
    protected $fixedSize;
52

53
    public function __construct(array $data = [], $readOnly = false, $fixedSize = null)
32✔
54
    {
55
        if (!$this->validateKeys($data)) {
32✔
56
            throw new InvalidKeyTypeException('Invalid Key type for HashTable');
1✔
57
        }
58

59
        if (is_int($fixedSize)) {
31✔
60
            if (count($data) < $fixedSize) {
2✔
61
                $data = array_pad($data, $fixedSize, 0);
1✔
62
            } elseif (count($data) > $fixedSize) {
1✔
63
                throw new Exception('HashTable data size is larger than defined size');
1✔
64
            }
65
        } elseif (is_bool($fixedSize) && $fixedSize) {
30✔
66
            $fixedSize = count($data);
1✔
67
        }
68

69
        $this->data      = $data;
30✔
70
        $this->position  = 0;
30✔
71
        $this->readOnly  = $readOnly;
30✔
72
        $this->fixedSize = $fixedSize;
30✔
73
    }
74

75
    public function __serialize() : array
1✔
76
    {
77
        return [
1✔
78
            'version' => static::$serializeVersion,
1✔
79
            'model'   => get_class($this),
1✔
80
            'data'    => $this->data,
1✔
81
        ];
1✔
82
    }
83

84
    public function __unserialize(array $data) : void
1✔
85
    {
86
        if ($data['model'] !== get_class($this)) {
1✔
87
            throw new UnmatchedClassException();
1✔
88
        }
89

90
        if ($data['version'] !== static::$serializeVersion) {
1✔
91
            throw new UnmatchedVersionException();
1✔
92
        }
93

94
        $this->setData($data);
1✔
95
    }
96

97
    /**
98
     * Add.
99
     *
100
     * @param mixed $key   key to add to array
101
     * @param mixed $value value to add to array
102
     *
103
     * @return $this
104
     */
105
    public function add(mixed $key, mixed $value) : AssociativeArrayInterface
8✔
106
    {
107
        $this->offsetSet($key, $value);
8✔
108

109
        return $this;
5✔
110
    }
111

112
    /**
113
     * @param $key
114
     *
115
     * @return mixed
116
     */
117
    public function get(mixed $key) : mixed
7✔
118
    {
119
        return $this->offsetGet($key);
7✔
120
    }
121

122
    /**
123
     * Get a count of the elements of the array.
124
     *
125
     * @return int
126
     */
127
    public function count() : int
4✔
128
    {
129
        return count($this->data);
4✔
130
    }
131

132
    public function delete(mixed $key) : void
4✔
133
    {
134
        if (array_key_exists($key, $this->data)) {
4✔
135
            $this->offsetUnset($key);
4✔
136
        }
137
    }
138

139
    /**
140
     * @param string $glue default: ' '
141
     *
142
     * @return StringInterface
143
     */
144
    public function implode($glue = ' ') : StringInterface
1✔
145
    {
146
        return new Twine(join($glue, $this->data));
1✔
147
    }
148

149
    /**
150
     * Move back to previous element.
151
     *
152
     * @return void Any returned value is ignored.
153
     */
154
    public function prev() : void
1✔
155
    {
156
        $this->position--;
1✔
157
    }
158

159
    // region Contractual Obligations
160

161
    /**
162
     * Whether a offset exists.
163
     *
164
     * @link   https://php.net/manual/en/arrayaccess.offsetexists.php
165
     *
166
     * @param mixed $offset <p>
167
     *                      An offset to check for.
168
     *                      </p>
169
     *
170
     * @return bool true on success or false on failure.
171
     *              </p>
172
     *              <p>
173
     *              The return value will be casted to boolean if non-boolean was returned.
174
     *
175
     * @since  5.0.0
176
     */
177
    public function offsetExists(mixed $offset) : bool
14✔
178
    {
179
        return isset($this->data[$offset]);
14✔
180
    }
181

182
    /**
183
     * Offset to retrieve.
184
     *
185
     * @link   https://php.net/manual/en/arrayaccess.offsetget.php
186
     *
187
     * @param mixed $offset <p>
188
     *                      The offset to retrieve.
189
     *                      </p>
190
     *
191
     * @return mixed Can return all value types.
192
     *
193
     * @since  5.0.0
194
     */
195
    public function offsetGet(mixed $offset) : mixed
11✔
196
    {
197
        if ($this->offsetExists($offset)) {
11✔
198
            return $this->data[$offset];
8✔
199
        }
200

201
        return false;
3✔
202
    }
203

204
    /**
205
     * Offset to set.
206
     *
207
     * @link https://php.net/manual/en/arrayaccess.offsetset.php
208
     *
209
     * @param mixed $offset The offset to assign the value to.
210
     * @param mixed $value  The value to set.
211
     *
212
     * @throws HashTableFullException
213
     * @throws InvalidKeyTypeException
214
     * @throws ReadOnlyDataException
215
     *
216
     * @return void
217
     *
218
     * @since  5.0.0
219
     */
220
    public function offsetSet(mixed $offset, mixed $value) : void
11✔
221
    {
222
        if ($this->isReadOnly()) {
11✔
223
            throw new ReadOnlyDataException('Invalid call to offsetSet on read-only '.__CLASS__.'.');
1✔
224
        }
225

226
        if (!$this->offsetExists($offset)
10✔
227
            && $this->isFixedSize()
10✔
228
            && $this->count() == $this->fixedSize
10✔
229
        ) {
230
            throw new HashTableFullException('Invalid call to offsetSet on '.__CLASS__.'where Size is Fixed and HashTable full.');
2✔
231
        }
232

233
        if (!$this->validateKey($offset)) {
8✔
234
            throw new InvalidKeyTypeException(sprintf('Invalid Key type (%s) for HashTable', gettype($offset)));
1✔
235
        }
236

237
        if (isset($this->onInsert) && is_callable($this->onInsert)) {
8✔
238
            $onInsert = $this->onInsert;
1✔
239
            [
1✔
240
                $offset,
1✔
241
                $value,
1✔
242
            ] = $onInsert($offset, $value);
1✔
243
        }
244

245
        $this->data[$offset] = $value;
8✔
246

247
        if (isset($this->onInsertComplete) && is_callable($this->onInsertComplete)) {
8✔
248
            $onInsertComplete = $this->onInsertComplete;
1✔
249
            $onInsertComplete($this->data);
1✔
250
        }
251
    }
252

253
    /**
254
     * Offset to unset.
255
     *
256
     * @link  https://php.net/manual/en/arrayaccess.offsetunset.php
257
     *
258
     * @param mixed $offset The offset to unset.
259
     *
260
     * @return void
261
     *
262
     * @since  5.0.0
263
     */
264
    public function offsetUnset(mixed $offset) : void
5✔
265
    {
266
        if (isset($this->onRemove) && is_callable($this->onRemove)) {
5✔
267
            $onRemove = $this->onRemove;
1✔
268
            $onRemove($offset, $this->data[$offset]);
1✔
269
        }
270

271
        unset($this->data[$offset]);
5✔
272

273
        if (isset($this->onRemoveComplete) && is_callable($this->onRemoveComplete)) {
5✔
274
            $onRemoveComplete = $this->onRemoveComplete;
1✔
275
            $onRemoveComplete($this->data);
1✔
276
        }
277
    }
278

279
    /**
280
     * Return the current element.
281
     *
282
     * @link https://php.net/manual/en/iterator.current.php
283
     *
284
     * @return mixed Can return any type.
285
     *
286
     * @since  5.0.0
287
     */
288
    public function current() : mixed
3✔
289
    {
290
        return ($this->valid()) ? array_values($this->data)[$this->position] : false;
3✔
291
    }
292

293
    /**
294
     * Move forward to next element.
295
     *
296
     * @link https://php.net/manual/en/iterator.next.php
297
     *
298
     * @return void Any returned value is ignored.
299
     *
300
     * @since  5.0.0
301
     */
302
    public function next() : void
4✔
303
    {
304
        $this->position++;
4✔
305
    }
306

307
    /**
308
     * Return the key of the current element.
309
     *
310
     * @link   https://php.net/manual/en/iterator.key.php
311
     *
312
     * @return mixed scalar on success, or null on failure.
313
     *
314
     * @since  5.0.0
315
     */
316
    public function key() : mixed
2✔
317
    {
318
        return array_keys($this->data)[$this->position];
2✔
319
    }
320

321
    /**
322
     * Checks if current position is valid.
323
     *
324
     * @link   https://php.net/manual/en/iterator.valid.php
325
     *
326
     * @return bool The return value will be casted to boolean and then evaluated.
327
     *              Returns true on success or false on failure.
328
     *
329
     * @since  5.0.0
330
     */
331
    public function valid() : bool
3✔
332
    {
333
        return isset(array_values($this->data)[$this->position]);
3✔
334
    }
335

336
    /**
337
     * Rewind the Iterator to the first element.
338
     *
339
     * @link   https://php.net/manual/en/iterator.rewind.php
340
     *
341
     * @return void Any returned value is ignored.
342
     *
343
     * @since  5.0.0
344
     */
345
    public function rewind() : void
3✔
346
    {
347
        $this->position = 0;
3✔
348
    }
349

350
    // endRegion
351

352
    /**
353
     * @return array
354
     */
355
    public function getData() : array
6✔
356
    {
357
        return $this->data;
6✔
358
    }
359

360
    /**
361
     * @param array $data
362
     */
363
    public function setData(array $data) : AssociativeArrayInterface
4✔
364
    {
365
        if (isset($this->onSet) && is_callable($this->onSet)) {
4✔
366
            $onSet = $this->onSet;
1✔
367
            $onSet($data);
1✔
368
        }
369

370
        $this->data = $data;
4✔
371

372
        if (isset($this->onSetComplete) && is_callable($this->onSetComplete)) {
4✔
373
            $onSetComplete = $this->onSetComplete;
1✔
374
            $onSetComplete($this->data);
1✔
375
        }
376

377
        return $this;
4✔
378
    }
379

380
    /**
381
     * Return an array of keys.
382
     *
383
     * @return array
384
     */
385
    public function getKeys() : array
1✔
386
    {
387
        return array_keys($this->data);
1✔
388
    }
389

390
    /**
391
     * Return an array of values.
392
     *
393
     * @return array
394
     */
395
    public function getValues() : array
1✔
396
    {
397
        return array_values($this->data);
1✔
398
    }
399

400
    /**
401
     * Is this HashTable readOnly?
402
     *
403
     * @return bool
404
     */
405
    public function isReadOnly() : bool
11✔
406
    {
407
        return $this->readOnly;
11✔
408
    }
409

410
    /**
411
     * Locks HashTable to current size.
412
     *
413
     * @return $this
414
     */
415
    public function lockSize()
1✔
416
    {
417
        $this->fixedSize = $this->count();
1✔
418

419
        return $this;
1✔
420
    }
421

422
    /**
423
     * Determine if HashTable is of a fixed size.
424
     *
425
     * @return bool
426
     */
427
    public function isFixedSize() : bool
10✔
428
    {
429
        return !is_null($this->fixedSize);
10✔
430
    }
431

432
    /**
433
     * @param array $dataSet
434
     *
435
     * @return bool
436
     */
437
    protected function validateKeys(array $dataSet) : bool
32✔
438
    {
439
        foreach (array_keys($dataSet) as $key) {
32✔
440
            if (!$this->validateKey($key)) {
24✔
441
                return false;
1✔
442
            }
443
        }
444

445
        return true;
31✔
446
    }
447

448
    /**
449
     * @param $key
450
     *
451
     * @return bool
452
     */
453
    protected function validateKey($key) : bool
28✔
454
    {
455
        return is_string($key);
28✔
456
    }
457

458
    /**
459
     * Get the value of a specified key and remove from
460
     * array.
461
     *
462
     * @param mixed $key
463
     *
464
     * @return mixed
465
     */
466
    public function extract(mixed $key) : mixed
1✔
467
    {
468
        $returnValue = $this->data[$key];
1✔
469
        $this->delete($key);
1✔
470

471
        return $returnValue;
1✔
472
    }
473

474
    public function clear() : void
2✔
475
    {
476
        if (isset($this->onClear) && is_callable($this->onClear)) {
2✔
477
            $onClear = $this->onClear;
1✔
478
            $onClear($this->data);
1✔
479
        }
480

481
        $this->data = [];
2✔
482
        $this->rewind();
2✔
483

484
        if (isset($this->onClearComplete) && is_callable($this->onClearComplete)) {
2✔
485
            $onClearComplete = $this->onClearComplete;
1✔
486
            $onClearComplete($this->data);
1✔
487
        }
488
    }
489

490
    /**
491
     * Find the key for $value.
492
     *
493
     * @param mixed $value the value to search the array for
494
     *
495
     * @return mixed
496
     */
497
    public function search(mixed $value) : mixed
2✔
498
    {
499
        return array_search($value, $this->data);
2✔
500
    }
501

502
    public function isEmpty() : bool
×
503
    {
504
        return empty($this->data);
×
505
    }
506
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc