Passed
Push — master ( c6a124...535cb5 )
by Paul
09:05 queued 01:21
created

BaseType::serializeProperty()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 12
c 1
b 1
f 0
dl 0
loc 19
ccs 0
cts 19
cp 0
rs 8.8333
cc 7
nc 17
nop 1
crap 56
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Schema;
4
5
use ArrayAccess;
6
use DateTime;
7
use DateTimeInterface;
8
use GeminiLabs\SiteReviews\Helper;
9
use GeminiLabs\SiteReviews\Helpers\Arr;
10
use GeminiLabs\SiteReviews\Modules\Schema\Exceptions\InvalidProperty;
11
use JsonSerializable;
12
use ReflectionClass;
13
14
abstract class BaseType implements ArrayAccess, JsonSerializable, Type
15
{
16
    /**
17
     * @var array
18
     */
19
    public $allowed = [];
20
21
    /**
22
     * @var array
23
     */
24
    public $parents = [];
25
26
    /**
27
     * @var array
28
     */
29
    protected $properties = [];
30
31
    /**
32
     * @var string
33
     */
34
    protected $type;
35
36
    /**
37
     * @param string $method
38
     * @return static
39
     */
40
    public function __call($method, array $arguments)
41
    {
42
        return $this->setProperty($method, Arr::get($arguments, 0));
43
    }
44
45
    /**
46
     * @param string $type
47
     */
48
    public function __construct($type = null)
49
    {
50
        $this->type = !is_string($type)
51
            ? (new ReflectionClass($this))->getShortName()
52
            : $type;
53
        $this->setAllowedProperties();
54
    }
55
56
    /**
57
     * @return string
58
     */
59
    public function __toString()
60
    {
61
        return $this->toScript();
62
    }
63
64
    /**
65
     * @return static
66
     */
67
    public function addProperties(array $properties)
68
    {
69
        foreach ($properties as $property => $value) {
70
            $this->setProperty($property, $value);
71
        }
72
        return $this;
73
    }
74
75
    /**
76
     * @return string
77
     */
78
    public function getContext()
79
    {
80
        return 'https://schema.org';
81
    }
82
83
    /**
84
     * @return array
85
     */
86
    public function getProperties()
87
    {
88
        return $this->properties;
89
    }
90
91
    /**
92
     * @param string $property
93
     * @param mixed $default
94
     * @return mixed
95
     */
96
    public function getProperty($property, $default = null)
97
    {
98
        return Arr::get($this->properties, $property, $default);
99
    }
100
101
    /**
102
     * @return string
103
     */
104
    public function getType()
105
    {
106
        return $this->type;
107
    }
108
109
    /**
110
     * @param bool $condition
111
     * @param mixed $callback
112
     * @return static
113
     */
114
    public function doIf($condition, $callback)
115
    {
116
        if ($condition) {
117
            $callback($this);
118
        }
119
        return $this;
120
    }
121
122
    /**
123
     * @return array
124
     */
125
    public function jsonSerialize()
126
    {
127
        return $this->toArray();
128
    }
129
130
    /**
131
     * @param mixed $offset
132
     * @return bool
133
     */
134
    public function offsetExists($offset)
135
    {
136
        return array_key_exists($offset, $this->properties);
137
    }
138
139
    /**
140
     * @param string $offset
141
     * @return mixed
142
     */
143
    public function offsetGet($offset)
144
    {
145
        return $this->getProperty($offset);
146
    }
147
148
    /**
149
     * @param string $offset
150
     * @param mixed $value
151
     * @return void
152
     */
153
    public function offsetSet($offset, $value)
154
    {
155
        $this->setProperty($offset, $value);
156
    }
157
158
    /**
159
     * @param string $offset
160
     * @return void
161
     */
162
    public function offsetUnset($offset)
163
    {
164
        unset($this->properties[$offset]);
165
    }
166
167
    /**
168
     * @param string $property
169
     * @param mixed $value
170
     * @return static
171
     */
172
    public function setProperty($property, $value)
173
    {
174
        if (!in_array($property, $this->allowed)
175
            && 'UnknownType' != (new ReflectionClass($this))->getShortName()) {
176
            glsr_log()->warning($this->getType().' does not allow the "'.$property.'" property');
177
            return $this;
178
        }
179
        if (!Helper::isEmpty($value)) {
180
            $this->properties[$property] = $value;
181
        }
182
        return $this;
183
    }
184
185
    /**
186
     * @return array
187
     */
188
    public function toArray()
189
    {
190
        $this->serializeIdentifier();
191
        $properties = $this->serializeProperty($this->getProperties());
192
        $array = [
193
            '@context' => $this->getContext(),
194
            '@type' => $this->getType(),
195
        ];
196
        return is_array($properties)
197
            ? $array + $properties
198
            : $array;
199
    }
200
201
    /**
202
     * @return string
203
     */
204
    public function toScript()
205
    {
206
        return sprintf('<script type="application/ld+json">%s</script>',
207
            json_encode($this->toArray(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
208
        );
209
    }
210
211
    /**
212
     * @param array|null $parents
213
     * @return array
214
     */
215
    protected function getParents($parents = null)
216
    {
217
        if (!isset($parents)) {
218
            $parents = $this->parents;
219
        }
220
        $newParents = $parents;
221
        foreach ($parents as $parent) {
222
            $parentClass = Helper::buildClassName($parent, __NAMESPACE__);
223
            if (!class_exists($parentClass)) {
224
                continue;
225
            }
226
            $newParents = array_merge($newParents, $this->getParents((new $parentClass())->parents));
227
        }
228
        return array_values(array_unique($newParents));
229
    }
230
231
    /**
232
     * @return void
233
     */
234
    protected function setAllowedProperties()
235
    {
236
        $parents = $this->getParents();
237
        foreach ($parents as $parent) {
238
            $parentClass = Helper::buildClassName($parent, __NAMESPACE__);
239
            if (!class_exists($parentClass)) {
240
                continue;
241
            }
242
            $this->allowed = array_values(array_unique(array_merge((new $parentClass())->allowed, $this->allowed)));
243
        }
244
    }
245
246
    /**
247
     * @return void
248
     */
249
    protected function serializeIdentifier()
250
    {
251
        if (isset($this['identifier'])) {
252
            $this->setProperty('@id', $this['identifier']);
253
            unset($this['identifier']);
254
        }
255
    }
256
257
    /**
258
     * @param mixed $property
259
     * @return array|string
260
     */
261
    protected function serializeProperty($property)
262
    {
263
        if (is_array($property)) {
264
            return array_map([$this, 'serializeProperty'], $property);
265
        }
266
        if ($property instanceof Type) {
267
            $property = $property->toArray();
268
            unset($property['@context']);
269
        }
270
        if ($property instanceof DateTimeInterface) {
271
            $property = $property->format(DateTime::ATOM);
272
        }
273
        if (is_object($property) && method_exists($property, '__toString')) {
274
            $property = (string) $property;
275
        }
276
        if (is_object($property)) {
277
            throw new InvalidProperty();
278
        }
279
        return $property;
280
    }
281
}
282