ArrayValue::map()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Conia\Boiler;
6
7
use ArrayAccess;
8
use Conia\Boiler\Exception\OutOfBoundsException;
9
use Conia\Boiler\Exception\RuntimeException;
10
use Conia\Boiler\Exception\UnexpectedValueException;
11
use Countable;
12
use Iterator;
13
14
/**
15
 * @psalm-api
16
 *
17
 * @psalm-type ArrayCallable = callable(mixed, mixed):int
18
 *
19
 * @template-implements ArrayAccess<array-key, mixed>
20
 * @template-implements Iterator<mixed>
21
 *
22
 * @psalm-suppress MixedArrayOffset -- ArrayValue is meant to hold mixed values accessed by mixed keys
23
 */
24
class ArrayValue implements ArrayAccess, Iterator, Countable, ValueInterface
25
{
26
    private int $position;
27
    private array $keys;
28
29 27
    public function __construct(private array $array)
30
    {
31 27
        $this->array = $array;
32 27
        $this->keys = array_keys($array);
33 27
        $this->position = 0;
34
    }
35
36 10
    public function unwrap(): array
37
    {
38 10
        return $this->array;
39
    }
40
41 4
    public function rewind(): void
42
    {
43 4
        $this->position = 0;
44
    }
45
46 4
    public function current(): mixed
47
    {
48 4
        return Wrapper::wrap($this->array[$this->key()]);
49
    }
50
51 4
    public function key(): mixed
52
    {
53 4
        return $this->keys[$this->position];
54
    }
55
56 4
    public function next(): void
57
    {
58 4
        $this->position++;
59
    }
60
61 4
    public function valid(): bool
62
    {
63 4
        return isset($this->keys[$this->position]);
64
    }
65
66
    /** @param array-key $offset */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array-key at position 0 could not be parsed: Unknown type name 'array-key' at position 0 in array-key.
Loading history...
67 7
    public function offsetExists(mixed $offset): bool
68
    {
69
        // isset is significantly faster than array_key_exists but
70
        // returns false when the value exists but is null.
71 7
        return isset($this->array[$offset]) || array_key_exists($offset, $this->array);
72
    }
73
74
    /** @param array-key $offset */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array-key at position 0 could not be parsed: Unknown type name 'array-key' at position 0 in array-key.
Loading history...
75 7
    public function offsetGet(mixed $offset): mixed
76
    {
77 7
        if ($this->offsetExists($offset)) {
78 5
            return Wrapper::wrap($this->array[$offset]);
79
        }
80 2
        if (is_numeric($offset)) {
81 1
            $key = (string)$offset;
82
        } else {
83 1
            $key = "'{$offset}'";
84
        }
85
86 2
        throw new OutOfBoundsException("Undefined array key {$key}");
87
    }
88
89 1
    public function offsetSet(mixed $offset, mixed $value): void
90
    {
91 1
        if ($offset) {
92 1
            $this->array[$offset] = $value;
93
        } else {
94 1
            $this->array[] = $value;
95
        }
96
    }
97
98 1
    public function offsetUnset(mixed $offset): void
99
    {
100 1
        unset($this->array[$offset]);
101
    }
102
103 2
    public function count(): int
104
    {
105 2
        return count($this->array);
106
    }
107
108
    /** @param array-key $key */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array-key at position 0 could not be parsed: Unknown type name 'array-key' at position 0 in array-key.
Loading history...
109 1
    public function exists(mixed $key): bool
110
    {
111 1
        return array_key_exists($key, $this->array);
112
    }
113
114 1
    public function merge(array|self $array): self
115
    {
116 1
        return new self(array_merge(
117 1
            $this->array,
118 1
            $array instanceof self ? $array->unwrap() : $array
0 ignored issues
show
introduced by
$array is never a sub-type of self.
Loading history...
119 1
        ));
120
    }
121
122
    /** @psalm-param ArrayCallable $callable */
123 1
    public function map(callable $callable): self
124
    {
125 1
        return new self(array_map($callable, $this->array));
126
    }
127
128
    /** @psalm-param ArrayCallable $callable */
129 1
    public function filter(callable $callable): self
130
    {
131 1
        return new self(array_filter($this->array, $callable));
132
    }
133
134
    /** @psalm-param ArrayCallable $callable */
135 1
    public function reduce(callable $callable, mixed $initial = null): mixed
136
    {
137 1
        return Wrapper::wrap(array_reduce($this->array, $callable, $initial));
138
    }
139
140
    /** @psalm-param ArrayCallable $callable */
141 5
    public function sorted(string $mode = '', ?callable $callable = null): self
142
    {
143 5
        $mode = strtolower(trim($mode));
144
145 5
        if (str_starts_with($mode, 'u')) {
146 3
            if (empty($callable)) {
147 1
                throw new RuntimeException('No callable provided for user defined sorting');
148
            }
149
150 2
            return $this->usort($this->array, $mode, $callable);
151
        }
152
153 2
        return $this->sort($this->array, $mode);
154
    }
155
156 2
    protected function sort(array $array, string $mode): self
157
    {
158 2
        match ($mode) {
159 2
            '' => sort($array),
160 2
            'ar' => arsort($array),
161 2
            'a' => asort($array),
162 2
            'kr' => krsort($array),
163 2
            'k' => ksort($array),
164 2
            'r' => rsort($array),
165 2
            default => throw new UnexpectedValueException("Sort mode '{$mode}' not supported"),
166 2
        };
167
168 1
        return new self($array);
169
    }
170
171
    /** @psalm-param ArrayCallable $callable */
172 2
    protected function usort(array $array, string $mode, callable $callable): self
173
    {
174 2
        match ($mode) {
175 2
            'ua' => uasort($array, $callable),
176 2
            'u' => usort($array, $callable),
177 2
            default => throw new UnexpectedValueException("Sort mode '{$mode}' not supported"),
178 2
        };
179
180 1
        return new self($array);
181
    }
182
}
183