Passed
Push — master ( 9df60a...0fb430 )
by Smoren
02:58 queued 01:09
created

ArrayView::offsetGet()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 11
c 3
b 1
f 0
dl 0
loc 21
rs 8.8333
cc 7
nc 6
nop 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A ArrayView::convertIndex() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Smoren\ArrayView\Views;
6
7
use Smoren\ArrayView\Exceptions\IndexError;
8
use Smoren\ArrayView\Exceptions\SizeError;
9
use Smoren\ArrayView\Exceptions\ReadonlyError;
10
use Smoren\ArrayView\Exceptions\ValueError;
11
use Smoren\ArrayView\Interfaces\ArrayViewInterface;
12
use Smoren\ArrayView\Interfaces\MaskSelectorInterface;
13
use Smoren\ArrayView\Selectors\MaskSelector;
14
use Smoren\ArrayView\Selectors\SliceSelector;
15
use Smoren\ArrayView\Traits\ArrayViewAccessTrait;
16
use Smoren\ArrayView\Util;
17
18
/**
19
 * @template T
20
 *
21
 * @implements ArrayViewInterface<T>
22
 */
23
class ArrayView implements ArrayViewInterface
24
{
25
    /**
26
     * @use ArrayViewAccessTrait<T>
27
     */
28
    use ArrayViewAccessTrait;
29
30
    /**
31
     * @var array<T>|ArrayViewInterface<T>
32
     */
33
    protected $source;
34
    /**
35
     * @var bool
36
     */
37
    protected bool $readonly;
38
    /**
39
     * @var ArrayViewInterface<T>|null
40
     */
41
    protected ?ArrayViewInterface $parentView;
42
43
    /**
44
     * {@inheritDoc}
45
     */
46
    public static function toView(&$source, ?bool $readonly = null): ArrayViewInterface
47
    {
48
        if (!($source instanceof ArrayViewInterface)) {
49
            return new ArrayView($source, $readonly);
50
        }
51
52
        if (!$source->isReadonly() && $readonly) {
53
            return new ArrayView($source, $readonly);
54
        }
55
56
        return $source;
57
    }
58
59
    /**
60
     * {@inheritDoc}
61
     */
62
    public static function toUnlinkedView($source, ?bool $readonly = null): ArrayViewInterface
63
    {
64
        return static::toView($source, $readonly);
65
    }
66
67
    /**
68
     * @param array<T>|ArrayViewInterface<T> $source
69
     * @param bool|null $readonly
70
     * @throws ReadonlyError
71
     */
72
    public function __construct(&$source, ?bool $readonly = null)
73
    {
74
        if (is_array($source) && !Util::isArraySequential($source)) {
75
            throw new ValueError('Cannot create view for non-sequential array.');
76
        }
77
78
        $this->source = &$source;
79
        $this->readonly = $readonly ?? (($source instanceof ArrayViewInterface) ? $source->isReadonly() : false);
0 ignored issues
show
introduced by
$source is always a sub-type of Smoren\ArrayView\Interfaces\ArrayViewInterface.
Loading history...
80
        $this->parentView = ($source instanceof ArrayViewInterface) ? $source : null;
0 ignored issues
show
introduced by
$source is always a sub-type of Smoren\ArrayView\Interfaces\ArrayViewInterface.
Loading history...
81
82
        if (($source instanceof ArrayViewInterface) && $source->isReadonly() && !$this->isReadonly()) {
83
            throw new ReadonlyError("Cannot create non-readonly view for readonly source.");
84
        }
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     */
90
    public function toArray(): array
91
    {
92
        return [...$this];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($this) returns the type array<integer,Smoren\ArrayView\Views\ArrayView> which is incompatible with the return type mandated by Smoren\ArrayView\Interfa...iewInterface::toArray() of Smoren\ArrayView\Interfaces\T[].

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98
    public function filter(callable $predicate): ArrayViewInterface
99
    {
100
        return $this->is($predicate)->select($this);
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106
    public function is(callable $predicate): MaskSelectorInterface
107
    {
108
        $data = $this->toArray();
109
        return new MaskSelector(array_map($predicate, $data, array_keys($data)));
110
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115
    public function subview($selector, bool $readonly = null): ArrayViewInterface
116
    {
117
        return is_string($selector)
118
            ? (new SliceSelector($selector))->select($this, $readonly)
119
            : $selector->select($this, $readonly);
120
    }
121
122
    /**
123
     * @return ArrayView<T>
124
     *
125
     * {@inheritDoc}
126
     */
127
    public function apply(callable $mapper): self
128
    {
129
        $size = \count($this);
130
        for ($i = 0; $i < $size; $i++) {
131
            /** @var T $item */
132
            $item = $this[$i];
133
            $this[$i] = $mapper($item, $i);
134
        }
135
        return $this;
136
    }
137
138
    /**
139
     * @template U
140
     *
141
     * @param array<U>|ArrayViewInterface<U> $data
142
     * @param callable(T, U, int): T $mapper
143
     *
144
     * @return ArrayView<T>
145
     *
146
     * {@inheritDoc}
147
     */
148
    public function applyWith($data, callable $mapper): self
149
    {
150
        [$dataSize, $thisSize] = [\count($data), \count($this)];
151
        if ($dataSize !== $thisSize) {
152
            throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize}).");
153
        }
154
155
        $dataView = ArrayView::toView($data);
156
157
        $size = \count($this);
158
        for ($i = 0; $i < $size; $i++) {
159
            /** @var T $lhs */
160
            $lhs = $this[$i];
161
            /** @var U $rhs */
162
            $rhs = $dataView[$i];
163
            $this[$i] = $mapper($lhs, $rhs, $i);
164
        }
165
166
        return $this;
167
    }
168
169
    /**
170
     * {@inheritDoc}
171
     *
172
     * @return ArrayView<T>
173
     */
174
    public function set($newValues): self
175
    {
176
        if (!\is_array($newValues) && !($newValues instanceof ArrayViewInterface)) {
177
            $size = \count($this);
178
            for ($i = 0; $i < $size; $i++) {
179
                $this[$i] = $newValues;
180
            }
181
            return $this;
182
        }
183
184
        [$dataSize, $thisSize] = [\count($newValues), \count($this)];
185
        if ($dataSize !== $thisSize) {
186
            throw new SizeError("Length of values array not equal to view length ({$dataSize} != {$thisSize}).");
187
        }
188
189
        $newValuesView = ArrayView::toView($newValues);
190
191
        $size = \count($this);
192
        for ($i = 0; $i < $size; $i++) {
193
            $this[$i] = $newValuesView[$i];
194
        }
195
196
        return $this;
197
    }
198
199
    /**
200
     * {@inheritDoc}
201
     */
202
    public function getIterator(): \Generator
203
    {
204
        $size = \count($this);
205
        for ($i = 0; $i < $size; $i++) {
206
            /** @var T $item */
207
            $item = $this[$i];
208
            yield $item;
209
        }
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215
    public function isReadonly(): bool
216
    {
217
        return $this->readonly;
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223
    public function count(): int
224
    {
225
        return $this->getParentSize();
226
    }
227
228
    /**
229
     * @return int
230
     */
231
    protected function getParentSize(): int
232
    {
233
        return ($this->parentView !== null)
234
            ? \count($this->parentView)
235
            : \count($this->source);
236
    }
237
238
    /**
239
     * @param int $i
240
     * @return int
241
     */
242
    protected function convertIndex(int $i): int
243
    {
244
        return Util::normalizeIndex($i, \count($this->source));
245
    }
246
247
    /**
248
     * @param numeric $offset
0 ignored issues
show
Bug introduced by
The type Smoren\ArrayView\Views\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...
249
     * @return bool
250
     */
251
    private function numericOffsetExists($offset): bool
0 ignored issues
show
Unused Code introduced by
The method numericOffsetExists() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
252
    {
253
        if (!\is_string($offset) && \is_numeric($offset) && (\is_nan($offset) || \is_infinite($offset))) {
0 ignored issues
show
introduced by
The condition is_numeric($offset) is always false.
Loading history...
introduced by
The condition is_string($offset) is always false.
Loading history...
254
            return false;
255
        }
256
257
        try {
258
            $index = $this->convertIndex(intval($offset));
259
        } catch (IndexError $e) {
260
            return false;
261
        }
262
        return \is_array($this->source)
263
            ? \array_key_exists($index, $this->source)
264
            : $this->source->offsetExists($index);
265
    }
266
}
267