Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — master ( 848c57...2069e7 )
by Henrique
02:36
created

Factory::createReflectionClass()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of Respect/Validation.
5
 *
6
 * (c) Alexandre Gomes Gaigalas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the "LICENSE.md"
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Respect\Validation;
15
16
use ReflectionClass;
17
use ReflectionObject;
18
use Respect\Validation\Exceptions\ComponentException;
19
use Respect\Validation\Exceptions\InvalidClassException;
20
use Respect\Validation\Exceptions\ValidationException;
21
use function array_map;
22
use function array_merge;
23
use function array_unique;
24
use function class_exists;
25
use function lcfirst;
26
use function Respect\Stringifier\stringify;
27
28
/**
29
 * Factory of objects.
30
 *
31
 * @author Henrique Moody <[email protected]>
32
 */
33
final class Factory
34
{
35
    private const DEFAULT_RULES_NAMESPACES = [
36
        'Respect\\Validation\\Rules',
37
        'Respect\\Validation\\Rules\\Locale',
38
    ];
39
40
    private const DEFAULT_EXCEPTIONS_NAMESPACES = [
41
        'Respect\\Validation\\Exceptions',
42
        'Respect\\Validation\\Exceptions\\Locale',
43
    ];
44
45
    /**
46
     * Default instance of the Factory.
47
     *
48
     * @var Factory
49
     */
50
    private static $defaultInstance;
51
52
    /**
53
     * @var string[]
54
     */
55
    private $rulesNamespaces = [];
56
57
    /**
58
     * @var string[]
59
     */
60
    private $exceptionsNamespaces = [];
61
62
    /**
63
     * Initializes the factory with the defined namespaces.
64
     *
65
     * If the default namespace is not in the array, it will be add to the end
66
     * of the array.
67
     *
68
     * @param string[] $rulesNamespaces
69
     * @param string[] $exceptionsNamespaces
70
     */
71 290
    public function __construct(array $rulesNamespaces, array $exceptionsNamespaces)
72
    {
73 290
        $this->rulesNamespaces = $this->filterNamespaces($rulesNamespaces, self::DEFAULT_RULES_NAMESPACES);
74 290
        $this->exceptionsNamespaces = $this->filterNamespaces($exceptionsNamespaces, self::DEFAULT_EXCEPTIONS_NAMESPACES);
75 290
    }
76
77
    /**
78
     * Define the default instance of the Factory.
79
     *
80
     * @param Factory $defaultInstance
81
     */
82 1
    public static function setDefaultInstance(self $defaultInstance): void
83
    {
84 1
        self::$defaultInstance = $defaultInstance;
85 1
    }
86
87
    /**
88
     * Returns the default instance of the Factory.
89
     *
90
     * @return Factory
91
     */
92 281
    public static function getDefaultInstance(): self
93
    {
94 281
        if (null === self::$defaultInstance) {
95 277
            self::$defaultInstance = new self(self::DEFAULT_RULES_NAMESPACES, self::DEFAULT_EXCEPTIONS_NAMESPACES);
96
        }
97
98 281
        return self::$defaultInstance;
99
    }
100
101
    /**
102
     * Creates a rule.
103
     *
104
     * @param string $ruleName
105
     * @param array $arguments
106
     *
107
     * @throws ComponentException
108
     *
109
     * @return Validatable
110
     */
111 285
    public function rule(string $ruleName, array $arguments = []): Validatable
112
    {
113 285
        foreach ($this->rulesNamespaces as $namespace) {
114 285
            $className = sprintf('%s\\%s', $namespace, ucfirst($ruleName));
115 285
            if (!class_exists($className)) {
116 3
                continue;
117
            }
118
119 283
            return $this->createReflectionClass($className, Validatable::class)->newInstanceArgs($arguments);
120
        }
121
122 2
        throw new ComponentException(sprintf('"%s" is not a valid rule name', $ruleName));
123
    }
124
125
    /**
126
     * Creates an exception.
127
     *
128
     *
129
     * @param Validatable $validatable
130
     * @param mixed $input
131
     * @param array $extraParams
132
     *
133
     * @throws ComponentException
134
     *
135
     * @return ValidationException
136
     */
137 217
    public function exception(Validatable $validatable, $input, array $extraParams = []): ValidationException
138
    {
139 217
        $reflection = new ReflectionObject($validatable);
140 217
        $ruleName = $reflection->getShortName();
141 217
        foreach ($this->exceptionsNamespaces as $namespace) {
142 217
            $exceptionName = sprintf('%s\\%sException', $namespace, $ruleName);
143 217
            if (!class_exists($exceptionName)) {
144 6
                continue;
145
            }
146
147 216
            $name = $validatable->getName() ?: stringify($input);
148 216
            $params = ['input' => $input] + $extraParams + $this->extractPropertiesValues($validatable, $reflection);
149
150 216
            return $this->createValidationException($exceptionName, $name, $params);
151
        }
152
153 1
        throw new ComponentException(sprintf('Cannot find exception for "%s" rule', lcfirst($ruleName)));
154
    }
155
156
    /**
157
     * Creates a reflection based on class name.
158
     *
159
     *
160
     * @param string $name
161
     * @param string $parentName
162
     *
163
     * @throws InvalidClassException
164
     *
165
     * @return ReflectionClass
166
     */
167 288
    private function createReflectionClass(string $name, string $parentName): ReflectionClass
168
    {
169 288
        $reflection = new ReflectionClass($name);
170 288
        if (!$reflection->isSubclassOf($parentName)) {
171 1
            throw new InvalidClassException(sprintf('"%s" must be an instance of "%s"', $name, $parentName));
172
        }
173
174 287
        if (!$reflection->isInstantiable()) {
175 1
            throw new InvalidClassException(sprintf('"%s" must be instantiable', $name));
176
        }
177
178 286
        return $reflection;
179
    }
180
181
    /**
182
     * Filters namespaces.
183
     *
184
     * Ensure namespaces are in the right format and contain the default namespaces.
185
     *
186
     * @param array $namespaces
187
     * @param array $defaultNamespaces
188
     *
189
     * @return array
190
     */
191
    private function filterNamespaces(array $namespaces, array $defaultNamespaces): array
192
    {
193 290
        $filter = function (string $namespace): string {
194 290
            return trim($namespace, '\\');
195 290
        };
196
197 290
        return array_unique(
198 290
            array_merge(
199 290
                array_map($filter, $namespaces),
200 290
                array_map($filter, $defaultNamespaces)
201
            )
202
        );
203
    }
204
205
    /**
206
     * Creates a Validation exception.
207
     *
208
     * @param string $exceptionName
209
     * @param mixed $name
210
     * @param array $params
211
     *
212
     * @return ValidationException
213
     */
214 216
    private function createValidationException(string $exceptionName, $name, array $params): ValidationException
215
    {
216 216
        $exception = $this->createReflectionClass($exceptionName, ValidationException::class)->newInstance();
217 216
        $exception->configure($name, $params);
218 216
        if (isset($params['template'])) {
219 5
            $exception->setTemplate($params['template']);
220
        }
221
222 216
        return $exception;
223
    }
224
225
    /**
226
     * @param Validatable $validatable
227
     * @param ReflectionClass $reflection
228
     *
229
     * @return array
230
     */
231 216
    private function extractPropertiesValues(Validatable $validatable, ReflectionClass $reflection): array
232
    {
233 216
        $values = [];
234 216
        foreach ($reflection->getProperties() as $property) {
235 216
            $property->setAccessible(true);
236
237 216
            $values[$property->getName()] = $property->getValue($validatable);
238
        }
239
240 216
        if (($parentReflection = $reflection->getParentClass())) {
241 216
            return $values + $this->extractPropertiesValues($validatable, $parentReflection);
242
        }
243
244 216
        return $values;
245
    }
246
}
247