ArrayViewAccessTrait   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 21
eloc 34
c 2
b 0
f 1
dl 0
loc 202
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A offsetGet() 0 11 3
A offsetExists() 0 10 3
A offsetSet() 0 17 4
A offsetUnset() 0 3 1
B toSelector() 0 26 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Smoren\ArrayView\Traits;
6
7
use Smoren\ArrayView\Exceptions\IndexError;
8
use Smoren\ArrayView\Exceptions\KeyError;
9
use Smoren\ArrayView\Exceptions\NotSupportedError;
10
use Smoren\ArrayView\Exceptions\ReadonlyError;
11
use Smoren\ArrayView\Interfaces\ArraySelectorInterface;
12
use Smoren\ArrayView\Interfaces\ArrayViewInterface;
13
use Smoren\ArrayView\Selectors\IndexListSelector;
14
use Smoren\ArrayView\Selectors\MaskSelector;
15
use Smoren\ArrayView\Selectors\SliceSelector;
16
use Smoren\ArrayView\Structs\Slice;
17
use Smoren\ArrayView\Util;
18
19
/**
20
 * Trait providing methods for accessing elements in ArrayView object.
21
 *
22
 * The trait implements methods for accessing, retrieving, setting,
23
 * and unsetting elements in the ArrayView object.
24
 *
25
 * @template T Type of ArrayView values.
26
 * @template S of string|array<int|bool>|ArrayViewInterface<int|bool>|ArraySelectorInterface Selector type.
27
 */
28
trait ArrayViewAccessTrait
29
{
30
    /**
31
     * Check if the specified offset exists in the ArrayView object.
32
     *
33
     * ```php
34
     * $source = [1, 2, 3, 4, 5];
35
     * $view = ArrayView::toView($source);
36
     *
37
     * isset($view[0]); // true
38
     * isset($view[-1]); // true
39
     * isset($view[10]); // false
40
     *
41
     * isset($view[new SliceSelector('::2')]); // true
42
     * isset($view[new IndexListSelector([0, 2, 4])]); // true
43
     * isset($view[new IndexListSelector([0, 2, 10])]); // false
44
     * isset($view[new MaskSelector([true, true, false, false, true])]); // true
45
     * isset($view[new MaskSelector([true, true, false, false, true, true])]); // false
46
     *
47
     * isset($view['::2']); // true
48
     * isset($view[[0, 2, 4]]); // true
49
     * isset($view[[0, 2, 10]]); // false
50
     * isset($view[[true, true, false, false, true]]); // true
51
     * isset($view[[true, true, false, false, true, true]]); // false
52
     * ```
53
     *
54
     * @param numeric|S $offset The offset to check.
0 ignored issues
show
Bug introduced by
The type Smoren\ArrayView\Traits\S was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type Smoren\ArrayView\Traits\numeric was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
55
     *
56
     * @return bool
57
     *
58
     * {@inheritDoc}
59
     */
60
    public function offsetExists($offset): bool
61
    {
62
        if (\is_numeric($offset)) {
0 ignored issues
show
introduced by
The condition is_numeric($offset) is always false.
Loading history...
63
            return $this->numericOffsetExists($offset);
64
        }
65
66
        try {
67
            return $this->toSelector($offset)->compatibleWith($this);
0 ignored issues
show
Bug introduced by
$this of type Smoren\ArrayView\Traits\ArrayViewAccessTrait is incompatible with the type Smoren\ArrayView\Interfaces\ArrayViewInterface expected by parameter $view of Smoren\ArrayView\Selecto...ector::compatibleWith(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
            return $this->toSelector($offset)->compatibleWith(/** @scrutinizer ignore-type */ $this);
Loading history...
Bug introduced by
$this of type Smoren\ArrayView\Traits\ArrayViewAccessTrait is incompatible with the type Smoren\ArrayView\Interfaces\ArrayViewInterface expected by parameter $view of Smoren\ArrayView\Selecto...ector::compatibleWith(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
            return $this->toSelector($offset)->compatibleWith(/** @scrutinizer ignore-type */ $this);
Loading history...
Bug introduced by
$this of type Smoren\ArrayView\Traits\ArrayViewAccessTrait is incompatible with the type Smoren\ArrayView\Interfaces\ArrayViewInterface expected by parameter $view of Smoren\ArrayView\Selecto...ector::compatibleWith(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
            return $this->toSelector($offset)->compatibleWith(/** @scrutinizer ignore-type */ $this);
Loading history...
68
        } catch (KeyError $e) {
69
            return false;
70
        }
71
    }
72
73
    /**
74
     * Get the value at the specified offset in the ArrayView object.
75
     *
76
     * ```php
77
     * $source = [1, 2, 3, 4, 5];
78
     * $view = ArrayView::toView($source);
79
     *
80
     * $view[0]; // 1
81
     * $view[-1]; // 5
82
     *
83
     * $view[new SliceSelector('::2')]; // [1, 3, 5]
84
     * $view[new IndexListSelector([0, 2, 4])]; // [1, 3, 5]
85
     * $view[new MaskSelector([true, true, false, false, true])]; // [1, 2, 5]
86
     *
87
     * $view['::2']; // [1, 3, 5]
88
     * $view[[0, 2, 4]]; // [1, 3, 5]
89
     * $view[[true, true, false, false, true]]; // [1, 2, 5]
90
     * ```
91
     *
92
     * @param numeric|S $offset The offset to get the value at.
93
     *
94
     * @return T|array<T> The value at the specified offset.
95
     *
96
     * @throws IndexError if the offset is out of range.
97
     * @throws KeyError if the key is invalid.
98
     *
99
     * {@inheritDoc}
100
     */
101
    #[\ReturnTypeWillChange]
102
    public function offsetGet($offset)
103
    {
104
        if (\is_numeric($offset)) {
0 ignored issues
show
introduced by
The condition is_numeric($offset) is always false.
Loading history...
105
            if (!$this->numericOffsetExists($offset)) {
106
                throw new IndexError("Index {$offset} is out of range.");
107
            }
108
            return $this->source[$this->convertIndex(\intval($offset))];
109
        }
110
111
        return $this->subview($this->toSelector($offset))->toArray();
0 ignored issues
show
Bug introduced by
It seems like subview() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
        return $this->/** @scrutinizer ignore-call */ subview($this->toSelector($offset))->toArray();
Loading history...
112
    }
113
114
    /**
115
     * Set the value at the specified offset in the ArrayView object.
116
     *
117
     * ```php
118
     * $source = [1, 2, 3, 4, 5];
119
     * $view = ArrayView::toView($source);
120
     *
121
     * $view[0] = 11;
122
     * $view[-1] = 55;
123
     *
124
     * $source; // [11, 2, 3, 4, 55]
125
     *
126
     * $source = [1, 2, 3, 4, 5];
127
     * $view = ArrayView::toView($source);
128
     *
129
     * $view[new SliceSelector('::2')] = [11, 33, 55];
130
     * $source; // [11, 2, 33, 4, 55]
131
     *
132
     * $view[new IndexListSelector([1, 3])] = [22, 44];
133
     * $source; // [11, 22, 33, 44, 55]
134
     *
135
     * $view[new MaskSelector([true, false, false, false, true])] = [111, 555];
136
     * $source; // [111, 22, 33, 44, 555]
137
     *
138
     * $source = [1, 2, 3, 4, 5];
139
     * $view = ArrayView::toView($source);
140
     *
141
     * $view['::2'] = [11, 33, 55];
142
     * $source; // [11, 2, 33, 4, 55]
143
     *
144
     * $view[[1, 3]] = [22, 44];
145
     * $source; // [11, 22, 33, 44, 55]
146
     *
147
     * $view[[true, false, false, false, true]] = [111, 555];
148
     * $source; // [111, 22, 33, 44, 555]
149
     * ```
150
     *
151
     * @param numeric|S $offset The offset to set the value at.
152
     * @param T|array<T>|ArrayViewInterface<T> $value The value to set.
0 ignored issues
show
Bug introduced by
The type Smoren\ArrayView\Traits\T was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
153
     *
154
     * @return void
155
     *
156
     * @throws IndexError if the offset is out of range.
157
     * @throws KeyError if the key is invalid.
158
     * @throws ReadonlyError if the object is readonly.
159
     *
160
     * {@inheritDoc}
161
     */
162
    public function offsetSet($offset, $value): void
163
    {
164
        if ($this->isReadonly()) {
0 ignored issues
show
Bug introduced by
It seems like isReadonly() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        if ($this->/** @scrutinizer ignore-call */ isReadonly()) {
Loading history...
165
            throw new ReadonlyError("Cannot modify a readonly view.");
166
        }
167
168
        if (!\is_numeric($offset)) {
0 ignored issues
show
introduced by
The condition is_numeric($offset) is always false.
Loading history...
169
            $this->subview($this->toSelector($offset))->set($value);
170
            return;
171
        }
172
173
        if (!$this->numericOffsetExists($offset)) {
174
            throw new IndexError("Index {$offset} is out of range.");
175
        }
176
177
        // @phpstan-ignore-next-line
178
        $this->source[$this->convertIndex(\intval($offset))] = $value;
179
    }
180
181
    /**
182
     * Unset the value at the specified offset in the array-like object.
183
     *
184
     * @param numeric|S $offset The offset to unset the value at.
185
     *
186
     * @return void
187
     *
188
     * @throws NotSupportedError always.
189
     *
190
     * {@inheritDoc}
191
     */
192
    public function offsetUnset($offset): void
0 ignored issues
show
Unused Code introduced by
The parameter $offset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

192
    public function offsetUnset(/** @scrutinizer ignore-unused */ $offset): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
193
    {
194
        throw new NotSupportedError();
195
    }
196
197
    /**
198
     * Converts array to selector.
199
     *
200
     * @param S $input value to convert.
201
     *
202
     * @return ArraySelectorInterface
203
     */
204
    protected function toSelector($input): ArraySelectorInterface
205
    {
206
        if ($input instanceof ArraySelectorInterface) {
207
            return $input;
208
        }
209
210
        if (\is_string($input) && Slice::isSlice($input)) {
211
            return new SliceSelector($input);
212
        }
213
214
        if ($input instanceof ArrayViewInterface) {
215
            $input = $input->toArray();
216
        }
217
218
        if (!\is_array($input) || !Util::isArraySequential($input)) {
219
            $strOffset = \is_scalar($input) ? \strval($input) : \gettype($input);
220
            throw new KeyError("Invalid key: \"{$strOffset}\".");
221
        }
222
223
        if (\count($input) > 0 && \is_bool($input[0])) {
224
            /** @var array<bool> $input */
225
            return new MaskSelector($input);
226
        }
227
228
        /** @var array<int> $input */
229
        return new IndexListSelector($input);
230
    }
231
}
232