Test Failed
Push — master ( 33bfdc...47f867 )
by Kirill
02:32
created

Map::set()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Frontend;
11
12
/**
13
 * Class Map
14
 */
15
class Map implements \IteratorAggregate, \Countable, \ArrayAccess
16
{
17
    /**
18
     * @var array|object[]
19
     */
20
    private $keys = [];
21
22
    /**
23
     * @var array|object[]
24
     */
25
    private $values = [];
26
27
    /**
28
     * @var \Closure
29
     */
30
    private $toString;
31
32
    /**
33
     * Map constructor.
34
     * @param \Closure|null $hash
35
     */
36
    public function __construct(\Closure $hash = null)
37
    {
38
        $this->toString = $hash ?? [$this, 'hash'];
0 ignored issues
show
Documentation Bug introduced by
It seems like $hash ?? array($this, 'hash') can also be of type array<integer,this<Railt...d\\Map>","1":"string"}>. However, the property $toString is declared as type object<Closure>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
39
    }
40
41
    /**
42
     * @param mixed $key
43
     * @param mixed $value
44
     * @return Map
45
     */
46
    public function set($key, $value): Map
47
    {
48
        $id = $this->key($key);
49
50
        $this->keys[$id] = $key;
51
        $this->values[$id] = $value;
52
53
        return $this;
54
    }
55
56
    /**
57
     * @param mixed $key
58
     * @param null $default
59
     * @return mixed|null|object
60
     */
61
    public function get($key, $default = null)
62
    {
63
        if ($this->has($key)) {
64
            return $this->values[$this->key($key)];
65
        }
66
67
        return $default instanceof \Closure ? $default() : $default;
68
    }
69
70
    /**
71
     * @param mixed $key
72
     * @return bool
73
     */
74
    public function has($key): bool
75
    {
76
        $id = $this->key($key);
77
78
        return isset($this->keys[$id]) || \array_key_exists($id, $this->keys);
79
    }
80
81
    /**
82
     * @param mixed $key
83
     * @return string
84
     */
85
    public function key($key): string
86
    {
87
        return ($this->toString)($key);
88
    }
89
90
    /**
91
     * @param mixed ...$keys
92
     */
93
    public function delete(...$keys): void
94
    {
95
        foreach ($keys as $key) {
96
            $id = $this->key($key);
97
98
            unset($this->keys[$id], $this->values[$id]);
99
        }
100
    }
101
102
    /**
103
     * @param mixed $value
104
     * @return string
105
     */
106
    protected function hash($value): string
107
    {
108
        switch (true) {
109
            case $value === null:
110
                return 'null';
111
112
            case \is_object($value):
113
                if (\method_exists($value, '__toString')) {
114
                    return 'o:' . $value;
115
                }
116
117
                return 'h:' . \spl_object_hash($value);
118
119
            case \is_numeric($value):
120
                return 'n:' . (int)$value;
121
122
            case \is_bool($value):
123
                return 'b:' . (int)$value;
124
125
            case \is_string($value):
126
                return 's:' . $value;
127
128
            case \is_resource($value):
129
                return 'r:' . $value;
130
131
            case \is_array($value):
132
                return 'a:' . \md5(\serialize($value));
133
134
            default:
135
                return 'v:' . \gettype($value) . ':' . \serialize($value);
136
        }
137
    }
138
139
    /**
140
     * @return \Traversable
141
     */
142
    public function getIterator(): \Traversable
143
    {
144
        foreach ($this->keys as $id => $key) {
145
            yield $key => $this->values[$id];
146
        }
147
    }
148
149
    /**
150
     * @param mixed $offset
151
     * @return bool
152
     */
153
    public function offsetExists($offset): bool
154
    {
155
        return $this->has($offset);
156
    }
157
158
    /**
159
     * @param mixed $offset
160
     * @return mixed|null|object
161
     */
162
    public function offsetGet($offset)
163
    {
164
        return $this->get($offset);
165
    }
166
167
    /**
168
     * @param mixed $offset
169
     * @param mixed $value
170
     */
171
    public function offsetSet($offset, $value): void
172
    {
173
        $this->set($offset, $value);
174
    }
175
176
    /**
177
     * @param mixed $offset
178
     */
179
    public function offsetUnset($offset): void
180
    {
181
        $this->delete($offset);
182
    }
183
184
    /**
185
     * @return int
186
     */
187
    public function count(): int
188
    {
189
        return \count($this->keys);
190
    }
191
}
192