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