Valable::make()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Bavix\Geo\Value;
4
5
abstract class Valable implements \JsonSerializable
6
{
7
8
    const READ_ONLY = 1;
9
    const WRITE = 2;
10
11
    /**
12
     * @var string
13
     */
14
    protected $objectId;
15
16
    /**
17
     * @var array
18
     */
19
    protected $properties;
20
21
    /**
22
     * @var array
23
     */
24
    protected $extends;
25
26
    /**
27
     * @var array
28
     */
29
    private $data = [];
30
31
    /**
32
     * Object constructor.
33
     *
34
     * @param array|null $data
35
     */
36
    public function __construct(array $data = null)
37
    {
38
        $this->objectId = \spl_object_id($this);
39
40
        if ($this->extends) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->extends of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
41
            $this->properties = \array_merge_recursive(
42
                $this->properties,
43
                $this->extends
44
            );
45
        }
46
47
        foreach ((array)$data as $key => $value) {
48
            $this->$key = $value;
49
        }
50
    }
51
52
    /**
53
     * @return string
54
     */
55
    public function objectId(): string
56
    {
57
        return $this->objectId;
58
    }
59
60
    /**
61
     * @param array|null $data
62
     *
63
     * @return static
64
     */
65
    public static function make(array $data = null): self
66
    {
67
        return new static($data);
68
    }
69
70
    /**
71
     * @param string $name
72
     * @param array  $arguments
73
     *
74
     * @return static
75
     */
76
    public static function __callStatic(string $name, array $arguments): self
77
    {
78
        $name = \preg_replace('~([A-Z]{1})~', '_$1', $name, 1);
79
        list($method, $property) = \explode('_', $name, 2);
80
81
        if ($method !== 'from') {
82
            throw new \InvalidArgumentException('Method ' . $method . ' not found');
83
        }
84
85
        return static::make([
86
            \lcfirst($property) => $arguments[0] ?? 0
87
        ]);
88
    }
89
90
    /**
91
     * @return static
92
     */
93
    public function proxy(): self
94
    {
95
        static $proxies = [];
96
97
        if (empty($proxies[$this->objectId()])) {
98
            $proxies[$this->objectId()] = $self = clone $this;
99
            foreach ((array)$this->properties as $key => $value) {
100
                $self->properties[$key]['type'] = self::READ_ONLY;
101
            }
102
        }
103
104
        return $proxies[$this->objectId()];
105
    }
106
107
    /**
108
     * @param string $name
109
     */
110
    protected function propertyValidate(string $name)
111
    {
112
        if (empty($this->properties[$name])) {
113
            throw new \InvalidArgumentException('Property not exists');
114
        }
115
    }
116
117
    /**
118
     * @param string $name
119
     * @param        $value
120
     */
121
    protected function modifies(string $name, $value)
122
    {
123
        $this->data[$name] = $value;
124
125
        if (isset($this->properties[$name]['modify'])) {
126
            $this->modify($name, $this->properties[$name]['modify']);
127
        }
128
    }
129
130
    /**
131
     * @param string $name
132
     * @param array  $props
133
     */
134
    protected function modify(string $name, array $props)
135
    {
136
        foreach ($props as $prop => $callback) {
137
            if (!function_exists($callback) && class_exists($callback)) {
138
                $this->dataSet($prop, $this->classModify($callback, $name, $prop));
139
                continue;
140
            }
141
142
            $this->dataSet($prop, $this->$callback($this->data[$name], $name, $prop));
143
        }
144
    }
145
146
    /**
147
     * @param string $prop
148
     * @param $mixed
149
     */
150
    protected function dataSet(string $prop, $mixed)
151
    {
152
        if ($mixed !== null) {
153
            $this->data[$prop] = $mixed;
154
        }
155
    }
156
157
    /**
158
     * @param string $class
159
     * @param string $name
160
     * @param string $prop
161
     *
162
     * @return mixed
163
     */
164
    protected function classModify(string $class, string $name, string $prop)
165
    {
166
        $object = new $class();
167
168
        if (!($object instanceof ValueInterface)) {
169
            throw new \InvalidArgumentException('Interface ' . ValueInterface::class . ' not found');
170
        }
171
172
        return $object::modify($this, $name, $prop);
173
    }
174
175
    /**
176
     * @param string $name
177
     *
178
     * @return mixed
179
     */
180
    public function __get(string $name)
181
    {
182
        $this->propertyValidate($name);
183
184
        if ($this->__isset($name)) {
185
            return $this->data[$name];
186
        }
187
188
        return null;
189
    }
190
191
    /**
192
     * @param string $name
193
     * @param        $value
194
     */
195
    public function __set(string $name, $value)
196
    {
197
        $this->propertyValidate($name);
198
199
        if (empty($this->properties[$name]['type'])) {
200
            throw new \InvalidArgumentException('Type not found');
201
        }
202
203
        if ($this->properties[$name]['type'] !== self::WRITE) {
204
            throw new \InvalidArgumentException('Magic __set(\'' . $name . '\') is not installed');
205
        }
206
207
        $this->modifies($name, $value);
208
    }
209
210
    /**
211
     * @param string $name
212
     *
213
     * @return bool
214
     */
215
    public function __isset(string $name): bool
216
    {
217
        return
218
            \array_key_exists($name, $this->data) ||
219
            isset($this->properties[$name]);
220
    }
221
222
    /**
223
     * @return array
224
     */
225
    public function jsonSerialize(): array
226
    {
227
        return $this->data;
228
    }
229
230
}
231