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 (#907)
by Henrique
03:44 queued 01:15
created

Factory::setDefaultInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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