Completed
Push — master ( 7cf5e9...3f7edd )
by Carlos C
10s
created

TypedStruct::set()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 2
1
<?php
2
namespace GenericCollections;
3
4
use GenericCollections\Exceptions\TypePropertyException;
5
use GenericCollections\Utils\TypeProperty;
6
7
/**
8
 * Concrete TypedStruct implementation. This class is like a HHVM Shape.
9
 * All defined keys can be accessed as properties or array.
10
 * It will throw a TypePropertyException if you try to set a value that does not match the definition type
11
 *
12
 * You can use this as a direct object like:
13
 * ```php
14
 * $obj = new TypedStruct(
15
 *      ['id' => 'int', 'name' => 'string'],
16
 *      ['id' => 1, 'name' => 'Foo Bar]
17
 * );
18
 * echo $obj['name']; // Foo Bar
19
 * ```
20
 *
21
 * Or you can inherith from this class to your own definition
22
 * ```php
23
 * class UserData extends TypedStruct
24
 * {
25
 *      public function __construct(array $values = [])
26
 *      {
27
 *          $definitions = ['id' => 'int', 'name' => 'string'];
28
 *          parent::__construct($definitions, $values);
29
 *      }
30
 * }
31
 * $user = new UserData(['id' => 10]);
32
 * echo $obj['id']; // 10
33
 * ```
34
 *
35
 * @package GenericCollections
36
 */
37
class TypedStruct implements \ArrayAccess, \IteratorAggregate, \Countable
38
{
39
    /**
40
     * @var TypeProperty[] Definition of the (string) key => (string) type
41
     */
42
    protected $definitions = [];
43
44
    /**
45
     * @var array
46
     */
47
    protected $values = [];
48
49
    /**
50
     * TypedStruct constructor.
51
     * @param string[] $definitions
52
     * @param array $values
53
     */
54 17
    public function __construct(array $definitions, array $values = [])
55
    {
56 17
        foreach ($definitions as $key => $type) {
57 16
            if (is_int($key)) {
58 1
                $key = $type;
59 1
                $type = 'mixed';
60
            }
61 16
            if (! is_string($key)) {
62 1
                throw new \InvalidArgumentException('Invalid definition key found');
63
            }
64 15
            if (! is_string($type)) {
65 1
                throw new \InvalidArgumentException('Invalid definition type found');
66
            }
67 14
            $this->definitions[$key] = new TypeProperty($type, true);
68 14
            $this->values[$key] = null;
69
        }
70 15
        $this->setValues($values);
71 14
    }
72
73
    /**
74
     * @return TypeProperty[] Get an array with all definitions
75
     */
76 2
    public function getDefinitions()
77
    {
78 2
        return $this->definitions;
79
    }
80
81
    /**
82
     * Retrieve a definition object
83
     * @param string $key
84
     * @return TypeProperty
85
     * @throws \InvalidArgumentException if key is not found
86
     */
87 12
    public function getDefinition($key)
88
    {
89 12
        $this->checkDefinitionExists($key);
90 11
        return $this->definitions[$key];
91
    }
92
93
    /**
94
     * Set all the values from the array to the local object
95
     * If the key is not found then it is just ignored
96
     *
97
     * @param array $values
98
     */
99 15
    public function setValues(array $values)
100
    {
101 15
        foreach ($values as $key => $value) {
102 11
            if ($this->exists($key)) {
103 11
                $this->set($key, $value);
104
            }
105
        }
106 14
    }
107
108
    /**
109
     * Set the value for a key, this will check that the value is the same type of the definition
110
     *
111
     * @param string $key
112
     * @param mixed $value
113
     * @throws \InvalidArgumentException if key is not found
114
     * @throws TypePropertyException if the value does not match the defined type
115
     */
116 12
    public function set($key, $value)
117
    {
118 12
        $definition = $this->getDefinition($key);
119 11
        if (! $definition->check($value)) {
120 1
            throw new TypePropertyException("Definition '$key' only allows type '{$definition->getType()}'");
121
        }
122 10
        $this->values[$key] = $value;
123 10
    }
124
125
    /**
126
     * Return TRUE if the key exists
127
     *
128
     * @param string $key
129
     * @return bool
130
     */
131 11
    public function exists($key)
132
    {
133 11
        return array_key_exists($key, $this->definitions);
134
    }
135
136
    /**
137
     * Return the value of a key
138
     *
139
     * @param string $key
140
     * @return mixed
141
     * @throws \InvalidArgumentException if key is not found
142
     */
143 8
    public function get($key)
144
    {
145 8
        $this->checkDefinitionExists($key);
146 7
        return $this->values[$key];
147
    }
148
149
    /**
150
     * Return an array with the key and values
151
     *
152
     * @return array
153
     */
154 1
    public function getValues()
155
    {
156 1
        return $this->values;
157
    }
158
159
    /**
160
     * Utility function that throws an exception if the key is not found
161
     * @param string $key
162
     */
163 14
    protected function checkDefinitionExists($key)
164
    {
165 14
        if (! array_key_exists($key, $this->definitions)) {
166 2
            throw new \InvalidArgumentException("Definition '$key' does not exists");
167
        }
168 12
    }
169
170 3
    public function __get($name)
171
    {
172 3
        return $this->get($name);
173
    }
174
175 1
    public function __set($name, $value)
176
    {
177 1
        $this->set($name, $value);
178 1
    }
179
180 2
    public function __isset($name)
181
    {
182 2
        return isset($this->values[$name]);
183
    }
184
185 1
    public function __unset($name)
186
    {
187 1
        $this->set($name, null);
188 1
    }
189
190 2
    public function offsetExists($offset)
191
    {
192 2
        return isset($this->values[$offset]);
193
    }
194
195 3
    public function offsetGet($offset)
196
    {
197 3
        return $this->get($offset);
198
    }
199
200 1
    public function offsetSet($offset, $value)
201
    {
202 1
        $this->set($offset, $value);
203 1
    }
204
205 1
    public function offsetUnset($offset)
206
    {
207 1
        $this->set($offset, null);
208 1
    }
209
210 1
    public function getIterator()
211
    {
212 1
        return new \ArrayIterator($this->values);
213
    }
214
215 2
    public function count()
216
    {
217 2
        return count($this->values);
218
    }
219
}
220