Completed
Push — master ( 24c4aa...872463 )
by Ivannis Suárez
02:16
created

Validator::registerValidator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
/**
3
 * This file is part of the Cubiche package.
4
 *
5
 * Copyright (c) Cubiche
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Cubiche\Core\Validator;
12
13
use Cubiche\Core\Validator\Exception\ValidationException;
14
use Cubiche\Core\Validator\Mapping\ClassMetadata;
15
use Cubiche\Core\Validator\Mapping\Driver\StaticDriver;
16
use Metadata\Driver\DriverChain;
17
use Metadata\MetadataFactory;
18
use Metadata\MetadataFactoryInterface;
19
use Respect\Validation\Exceptions\NestedValidationException;
20
21
/**
22
 * Validator class.
23
 *
24
 * @author Ivannis Suárez Jerez <[email protected]>
25
 */
26
class Validator implements ValidatorInterface
27
{
28
    /**
29
     * @var array
30
     */
31
    protected $constraints = array();
32
33
    /**
34
     * @var string
35
     */
36
    protected $defaultGroup;
37
38
    /**
39
     * @var MetadataFactoryInterface
40
     */
41
    protected $metadataFactory;
42
43
    /**
44
     * @var Validator
45
     */
46
    private static $instance = null;
47
48
    /**
49
     * Validator constructor.
50
     *
51
     * @param MetadataFactoryInterface $metadataFactory
52
     * @param string                   $defaultGroup
53
     */
54
    private function __construct(MetadataFactoryInterface $metadataFactory, $defaultGroup = Assert::DEFAULT_GROUP)
55
    {
56
        $this->metadataFactory = $metadataFactory;
57
        $this->defaultGroup = $defaultGroup;
58
    }
59
60
    /**
61
     * @param MetadataFactoryInterface $metadataFactory
62
     */
63
    public static function setMetadataFactory(MetadataFactoryInterface $metadataFactory)
64
    {
65
        static::create()->metadataFactory = $metadataFactory;
66
    }
67
68
    /**
69
     * @param $defaultGroup
70
     */
71
    public static function setDefaultGroup($defaultGroup)
72
    {
73
        static::create()->defaultGroup = $defaultGroup;
74
    }
75
76
    /**
77
     * @return Validator
78
     */
79
    public static function create()
80
    {
81
        if (static::$instance === null) {
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
82
            static::$instance = new static(
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
83
                new MetadataFactory(new DriverChain(array(new StaticDriver())))
84
            );
85
        }
86
87
        return static::$instance;
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
88
    }
89
90
    /**
91
     * @param Assert $assert
92
     * @param string $className
93
     * @param string $group
94
     *
95
     * @return $this
96
     */
97
    protected function addConstraint(Assert $assert, $className = null, $group = null)
98
    {
99
        $className = $this->normalizeClassName($className);
100
        $group = $this->normalizeGroup($group);
101
102
        if (!isset($this->constraints[$className])) {
103
            $this->constraints[$className] = array();
104
        }
105
106
        if (!isset($this->constraints[$className][$group])) {
107
            $this->constraints[$className][$group] = Assert::create();
108
        }
109
110
        $this->constraints[$className][$group]->addRules($assert->getRules());
111
112
        return $this;
113
    }
114
115
    /**
116
     * @param string $className
117
     * @param string $group
118
     *
119
     * @return Assert
120
     */
121
    protected function getConstraintsByGroup($className = null, $group = null)
122
    {
123
        $className = $this->normalizeClassName($className);
124
        $group = $this->normalizeGroup($group);
125
126
        if (!isset($this->constraints[$className])) {
127
            return Assert::create()->alwaysValid();
128
        }
129
130
        if (!isset($this->constraints[$className][$group])) {
131
            return Assert::create()->alwaysValid();
132
        }
133
134
        return $this->constraints[$className][$group];
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public static function assert($value, $constraints = null, $group = null)
141
    {
142
        return static::create()->assertConstraints($value, $constraints, $group);
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    protected function assertConstraints($value, $constraints = null, $group = null)
149
    {
150
        $group = $this->normalizeGroup($group);
151
152
        // If explicit constraints are passed, validate the value against
153
        // those constraints
154
        if (null !== $constraints) {
155
            if (!is_array($constraints)) {
156
                $constraints = array($constraints);
157
            }
158
159
            foreach ($constraints as $constraint) {
160
                $this->addConstraint($constraint, null, $group);
161
            }
162
163
            $constraints = $this->getConstraintsByGroup(null, $group);
164
165
            try {
166
                $returnValue = $constraints->assert($value);
167
            } catch (NestedValidationException $e) {
168
                throw new ValidationException(
169
                    $e->getMainMessage(),
170
                    $e->getMessages(),
171
                    $e->getCode(),
172
                    $e->getPrevious()
173
                );
174
            }
175
176
            return $returnValue;
177
        }
178
179
        // If an object is passed without explicit constraints, validate that
180
        // object against the constraints defined for the object's class
181
        if (is_object($value)) {
182
            $this->addObjectConstraints($value);
183
184
            $constraints = $this->getConstraintsByGroup(get_class($value), $group);
185
186
            try {
187
                $returnValue = $constraints->assert($value);
188
            } catch (NestedValidationException $e) {
189
                throw new ValidationException(
190
                    $e->getMainMessage(),
191
                    $e->getMessages(),
192
                    $e->getCode(),
193
                    $e->getPrevious()
194
                );
195
            }
196
197
            return $returnValue;
198
        }
199
200
        // If an array is passed without explicit constraints, validate each
201
        // object in the array
202
        if (is_array($value)) {
203
            $this->addArrayConstraints($value);
204
205
            $returnValue = true;
206
            foreach ($value as $item) {
207
                $constraints = $this->getConstraintsByGroup(is_object($item) ? get_class($item) : null, $group);
208
209
                try {
210
                    $returnValue = $returnValue && $constraints->assert($item);
211
                } catch (NestedValidationException $e) {
212
                    throw new ValidationException(
213
                        $e->getMainMessage(),
214
                        $e->getMessages(),
215
                        $e->getCode(),
216
                        $e->getPrevious()
217
                    );
218
                }
219
            }
220
221
            return $returnValue;
222
        }
223
224
        throw new \RuntimeException(sprintf(
225
            'Cannot validate values of type "%s" automatically. Please '.
226
            'provide a constraint.',
227
            gettype($value)
228
        ));
229
    }
230
231
    /**
232
     * {@inheritdoc}
233
     */
234
    public static function validate($value, $constraints = null, $group = null)
235
    {
236
        return static::create()->validateConstraints($value, $constraints, $group);
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    protected function validateConstraints($value, $constraints = null, $group = null)
243
    {
244
        $group = $this->normalizeGroup($group);
245
246
        // If explicit constraints are passed, validate the value against
247
        // those constraints
248
        if (null !== $constraints) {
249
            if (!is_array($constraints)) {
250
                $constraints = array($constraints);
251
            }
252
253
            foreach ($constraints as $constraint) {
254
                $this->addConstraint($constraint, null, $group);
255
            }
256
257
            return $this->getConstraintsByGroup(null, $group)->validate($value);
258
        }
259
260
        // If an object is passed without explicit constraints, validate that
261
        // object against the constraints defined for the object's class
262
        if (is_object($value)) {
263
            $this->addObjectConstraints($value);
264
265
            return $this->getConstraintsByGroup(get_class($value), $group)->validate($value);
266
        }
267
268
        // If an array is passed without explicit constraints, validate each
269
        // object in the array
270
        if (is_array($value)) {
271
            $this->addArrayConstraints($value);
272
273
            $returnValue = true;
274
            foreach ($value as $item) {
275
                $constraints = $this->getConstraintsByGroup(is_object($item) ? get_class($item) : null, $group);
276
                $returnValue = $returnValue && $constraints->validate($item);
277
            }
278
279
            return $returnValue;
280
        }
281
282
        throw new \RuntimeException(sprintf(
283
            'Cannot validate values of type "%s" automatically. Please '.
284
            'provide a constraint.',
285
            gettype($value)
286
        ));
287
    }
288
289
    /**
290
     * @param object $object
291
     */
292
    protected function addObjectConstraints($object)
293
    {
294
        $metadata = $this->metadataFactory->getMetadataForClass(get_class($object));
295
        if ($metadata !== null) {
296
            /** @var ClassMetadata $classMetadata */
297
            $classMetadata = $metadata->getRootClassMetadata();
298
299
            foreach ($classMetadata->getPropertiesMetadata() as $propertyMetadata) {
300
                foreach ($propertyMetadata->getConstraints() as $group => $constraints) {
301
                    $allOf = Assert::create();
302
                    foreach ($constraints as $constraint) {
303
                        $allOf->addRules($constraint->getRules());
304
                    }
305
306
                    $this->addConstraint(
307
                        Assert::create()->attribute($propertyMetadata->getPropertyName(), $allOf),
0 ignored issues
show
Compatibility introduced by
\Cubiche\Core\Validator\...PropertyName(), $allOf) of type object<Respect\Validation\Validator> is not a sub-type of object<Cubiche\Core\Validator\Assert>. It seems like you assume a child class of the class Respect\Validation\Validator 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...
308
                        get_class($object),
309
                        $group
310
                    );
311
                }
312
            }
313
        }
314
    }
315
316
    /**
317
     * @param string $className
318
     *
319
     * @return ClassMetadata|null
320
     */
321
    public static function getMetadataForClass($className)
322
    {
323
        return self::create()->metadataFactory->getMetadataForClass($className);
324
    }
325
326
    /**
327
     * @param string $namespace
328
     * @param bool   $prepend
329
     */
330
    public static function registerValidator($namespace, $prepend = false)
331
    {
332
        Assert::registerValidator($namespace, $prepend);
333
    }
334
335
    /**
336
     * @param mixed $array
337
     */
338
    protected function addArrayConstraints($array)
339
    {
340
        foreach ($array as $key => $value) {
341
            if (is_array($value)) {
342
                $this->addArrayConstraints($value);
343
344
                continue;
345
            }
346
347
            // Scalar and null values in the collection are ignored
348
            if (is_object($value)) {
349
                $this->addObjectConstraints($value);
350
            } else {
351
                throw new \RuntimeException(sprintf(
352
                    'Cannot validate values of type "%s" automatically. Please '.
353
                    'provide a constraint.',
354
                    gettype($value)
355
                ));
356
            }
357
        }
358
    }
359
360
    /**
361
     * Normalizes the given group.
362
     *
363
     * @param string $group
364
     *
365
     * @return string
366
     */
367
    protected function normalizeGroup($group = null)
368
    {
369
        return $group === null || empty($group)  ? $this->defaultGroup : $group;
370
    }
371
372
    /**
373
     * Normalizes the given className.
374
     *
375
     * @param string $className
376
     *
377
     * @return string
378
     */
379
    protected function normalizeClassName($className = null)
380
    {
381
        return $className !== null ? $className : self::class;
382
    }
383
}
384