Completed
Push — master ( c220de...8dca8c )
by Simone
02:01
created

MagicResource::validate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * This file is part of sensorario/resources repository
5
 *
6
 * (c) Simone Gentili <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sensorario\Resources;
13
14
use Sensorario\Resources\Configurator;
15
use Sensorario\Resources\Validators\ResourcesValidator;
16
17
/**
18
 * @method array defaults() returns resource's default values
19
 * @method array rules() returns resource's rules
20
 */
21
abstract class MagicResource
22
{
23
    protected $properties = [];
24
25
    protected $validator;
26
27
    private static $methodWhiteList = [
28
        'box',
29
        'allowedValues'
30
    ];
31
32 3
    public function __call($functionName, $arguments)
33
    {
34 3
        $propertyName = strtolower($functionName);
35
36 3
        if ($this->hasProperty($propertyName)) {
37 1
            return $this->get($propertyName);
38
        }
39
40 2
        if (isset($this->defaults()[$propertyName])) {
41 1
            return $this->defaults()[$propertyName];
42
        }
43
44 1
        throw new \Sensorario\Resources\Exceptions\UndefinedMethodException(
45 1
            'Method `' . get_class($this)
46 1
            . '::' . $functionName 
47 1
            . '()` is not yet implemented'
48
        );
49
    }
50
51
    abstract public function applyConfiguration(Configurator $configurator);
52
53 39
    public function __construct(
54
        array $properties,
55
        ResourcesValidator $validator,
56
        Configurator $configuration = null
57
    ) {
58 39
        $this->properties    = $properties;
59 39
        $this->validator     = $validator;
60 39
        $this->configuration = $configuration;
0 ignored issues
show
Bug introduced by
The property configuration does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
61
62 39
        if ($configuration) {
63 21
            $this->applyConfiguration(
64 21
                $configuration
65
            );
66
        }
67
68 39
        $this->init();
69 24
    }
70
71 39
    private function init()
72
    {
73 39
        $this->ensurePropertiesConsistency();
74 38
        $this->validate();
75 24
    }
76
77 39
    public static function __callStatic($methodName, array $args)
78
    {
79 39
        $isMethodAllowed = in_array(
80 39
            $methodName,
81 39
            self::$methodWhiteList
82
        );
83
84 39
        $configuration = null;
85
86
        if (
87 39
            isset($args[1])
88 22
            && 'Sensorario\Resources\Configurator' == get_class($args[1])
89
        ) {
90 21
            $configuration = new Configurator(
91 21
                $args[1]->resourceName(),
92 21
                $args[1]->container()
93
            );
94
        }
95
96 39
        if ($isMethodAllowed) {
97 38
            return new static(
98 38
                $args[0] ?? [],
99 38
                new ResourcesValidator(),
100 38
                $configuration
101
            );
102
        }
103
104 1
        throw new \Sensorario\Resources\Exceptions\FactoryMethodException(
105
            'Invalid factory method '
106 1
            . '`' . $methodName . '`'
107
        );
108
    }
109
110 31
    final public function hasProperty($propertyName)
111
    {
112
        return isset(
113 31
            $this->properties[$propertyName]
114
        );
115
    }
116
117 1
    final public function set($propertyName, $propertValue) 
118
    {
119 1
        $this->properties[$propertyName] = $propertValue;
120 1
    }
121
122 24
    final public function get($propertyName)
123
    {
124 24
        if ('' == $propertyName) {
125 1
            throw new \Sensorario\Resources\Exceptions\PropertyNameEmptyException(
126 1
                'Oops! Property name requested is empty string!!'
127
            );
128
        }
129
130 23
        if ($this->hasNotProperty($propertyName)) {
131 3
            if ($prop = $this->defaults()[$propertyName] ?? false) {
132 2
                return $prop;
133
            }
134
135 1
            throw new \Sensorario\Resources\Exceptions\NoValuesException(
136
                'No value nor method `'
137 1
                . $propertyName
138 1
                . '` found in this resource'
139
            );
140
        }
141
142 20
        return $this->properties[$propertyName];
143
    }
144
145 29
    final public function hasNotProperty($propertyName)
146
    {
147 29
        return !$this->hasProperty($propertyName);
148
    }
149
150 2
    final public function hasProperties(array $properties)
151
    {
152 2
        foreach ($properties as $property) {
153 2
            if ($this->hasNotProperty($property)) {
154 1
                return false;
155
            }
156
        }
157
158 1
        return true;
159
    }
160
161 38
    final public function properties()
162
    {
163 38
        $properties = $this->properties;
164
165 38
        foreach ($properties as $k => $v) {
166 30
            if ('object' === gettype($v)) {
167 5
                if (!isset($this->rules()[$k]['object'])) {
168 1
                    throw new \Sensorario\Resources\Exceptions\PropertyWithoutRuleException(
169 1
                        'Property ' . $k . ' is an object but is not defined in rules'
170
                    );
171
                }
172
173 4
                if ($this->rules()[$k]['object'] === '\\Sensorario\\Resources\\Resource') {
174 1
                    $properties[$k] = $v->properties();
175
                }
176
177 4
                if ($this->rules()[$k]['object'] === 'array') {
178
                    $properties[$k] = (array) $v;
179
                }
180
            }
181
        }
182
183 37
        return $properties;
184
    }
185
186 38
    public function validate()
187
    {
188 38
        $this->validator->validate($this);
0 ignored issues
show
Compatibility introduced by
$this of type object<Sensorario\Resources\MagicResource> is not a sub-type of object<Sensorario\Resources\Resource>. It seems like you assume a child class of the class Sensorario\Resources\MagicResource to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
189 24
    }
190
191 39
    public function ensurePropertiesConsistency()
192
    {
193 39
        foreach ($this->properties as $k => $v) {
194 31
            if ('object' === gettype($v) && !isset($this->rules()[$k])) {
195 1
                throw new \Sensorario\Resources\Exceptions\PropertyWithoutRuleException(
196 1
                    'When property `' . $k . '` is an object class, must be defined in Resources::rules()' .
197 1
                    ' but rules here are equals to ' . var_export($this->rules(), true)
198 1
                    . ' And properties are ' . var_export($this->properties, true)
199
                );
200
            }
201
        }
202 38
    }
203
}
204