Passed
Push — master ( e0dbf5...75834f )
by Paul
04:31
created

BaseType::setProperty()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 8
ccs 0
cts 8
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Schema;
4
5
use ArrayAccess;
6
use DateTime;
7
use JsonSerializable;
8
use ReflectionClass;
9
use DateTimeInterface;
10
use GeminiLabs\SiteReviews\Helper;
11
use GeminiLabs\SiteReviews\Modules\Schema\Type;
12
use GeminiLabs\SiteReviews\Modules\Schema\Exceptions\InvalidProperty;
13
14
abstract class BaseType implements ArrayAccess, JsonSerializable, Type
15
{
16
	/**
17
	 * @var array
18
	 */
19
	protected $allowed = [];
20
21
	/**
22
	 * @var array
23
	 */
24
	protected $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, $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 $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 function $callback
0 ignored issues
show
Bug introduced by
The type GeminiLabs\SiteReviews\Modules\Schema\function 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...
112
	 * @return static
113
	 */
114
	public function if( $condition, $callback )
0 ignored issues
show
Coding Style introduced by
Possible parse error: non-abstract method defined as abstract
Loading history...
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
115
	{
116
		if( $condition ) {
117
			$callback( $this );
118
		}
119
		return $this;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
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
			glsr_log()->error( 'Invalid schema property ('.$property.') for '.$this->getType() );
176
			return $this;
177
		}
178
		$this->properties[$property] = $value;
179
		return $this;
180
	}
181
182
	/**
183
	 * @return array
184
	 */
185
	public function toArray()
186
	{
187
		return [
188
			'@context' => $this->getContext(),
189
			'@type' => $this->getType(),
190
		] + $this->serializeProperty( $this->getProperties() );
191
	}
192
193
	/**
194
	 * @return string
195
	 */
196
	public function toScript()
197
	{
198
		return sprintf( '<script type="application/ld+json">%s</script>',
199
			json_encode( $this->toArray(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES )
200
		);
201
	}
202
203
	/**
204
	 * @param null|array $parents
205
	 * @return void
206
	 */
207
	protected function getParents( $parents = null )
208
	{
209
		if( !isset( $parents )) {
210
			$parents = $this->parents;
211
		}
212
		$newParents = $parents;
213
		foreach( $parents as $parent ) {
214
			$parentClass = glsr( Helper::class )->buildClassName( $parent, __NAMESPACE__ );
215
			if( !class_exists( $parentClass ))continue;
216
			$newParents = array_merge( $newParents, $this->getParents( (new $parentClass)->parents ));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getParents(new $parentClass()->parents) targeting GeminiLabs\SiteReviews\M...\BaseType::getParents() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
$this->getParents(new $parentClass()->parents) of type void is incompatible with the type null|array expected by parameter $array2 of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

216
			$newParents = array_merge( $newParents, /** @scrutinizer ignore-type */ $this->getParents( (new $parentClass)->parents ));
Loading history...
217
		}
218
		return array_values( array_unique( $newParents ));
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_values(array_unique($newParents)) returns the type array which is incompatible with the documented return type void.
Loading history...
219
	}
220
221
	/**
222
	 * @return void
223
	 */
224
	protected function setAllowedProperties()
225
	{
226
		$parents = $this->getParents();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $parents is correct as $this->getParents() targeting GeminiLabs\SiteReviews\M...\BaseType::getParents() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
227
		foreach( $parents as $parent ) {
0 ignored issues
show
Bug introduced by
The expression $parents of type void is not traversable.
Loading history...
228
			$parentClass = glsr( Helper::class )->buildClassName( $parent, __NAMESPACE__ );
229
			if( !class_exists( $parentClass ))continue;
230
			$this->allowed = array_values( array_unique( array_merge( (new $parentClass)->allowed, $this->allowed )));
231
		}
232
	}
233
234
	/**
235
	 * @param mixed $property
236
	 * @return array|string
237
	 */
238
	protected function serializeProperty( $property )
239
	{
240
		if( is_array( $property )) {
241
			return array_map( [$this, 'serializeProperty'], $property );
242
		}
243
		if( $property instanceof Type ) {
244
			$property = $property->toArray();
245
			unset( $property['@context'] );
246
		}
247
		if( $property instanceof DateTimeInterface ) {
248
			$property = $property->format( DateTime::ATOM );
249
		}
250
		if( is_object( $property )) {
251
			throw new InvalidProperty();
252
		}
253
		return $property;
254
	}
255
}
256