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

Completed
Pull Request — master (#941)
by Henrique
03:04 queued 45s
created

Factory::getDefaultInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

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