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 (#678)
by Henrique
02:58
created

Factory::messages()   C

Complexity

Conditions 8
Paths 22

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 33
c 0
b 0
f 0
ccs 0
cts 19
cp 0
rs 5.3846
cc 8
eloc 19
nc 22
nop 2
crap 72
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
namespace Respect\Validation;
13
14
use Doctrine\Common\Annotations\Reader;
15
use Doctrine\Common\Annotations\SimpleAnnotationReader;
16
use ReflectionClass;
17
use Respect\Validation\Annotations\Template;
18
use Respect\Validation\Annotations\Templates;
19
use Respect\Validation\Exceptions\ComponentException;
20
use Respect\Validation\Exceptions\ExceptionNotFoundException;
21
use Respect\Validation\Exceptions\InvalidClassException;
22
use Respect\Validation\Exceptions\InvalidRuleExceptionException;
23
use Respect\Validation\Exceptions\RuleNotFoundException;
24
use Respect\Validation\Exceptions\ValidationException;
25
26
/**
27
 * Factory to create rules.
28
 *
29
 * @author Henrique Moody <[email protected]>
30
 *
31
 * @since 0.8.0
32
 */
33
final class Factory
34
{
35
    /**
36
     * @var string[]
37
     */
38
    private $namespaces;
39
40
    /**
41
     * @var ReflectionClass[]
42
     */
43
    private $reflections;
44
45
    /**
46
     * @var Reader
47
     */
48
    private $annotationReader;
49
50
    /**
51
     * @var self
52
     */
53
    private static $defaultInstance;
54
55
    /**
56
     * Initializes the rule with the defined namespaces.
57
     *
58
     * If the default namespace is not in the array, it will be add to the end
59
     * of the array.
60
     *
61
     * @param array $namespaces
62
     */
63 11
    public function __construct(array $namespaces = [])
64
    {
65 11
        if (!in_array(__NAMESPACE__, $namespaces)) {
66 9
            $namespaces[] = __NAMESPACE__;
67
        }
68
69 11
        $this->namespaces = $namespaces;
70 11
        $this->annotationReader = new SimpleAnnotationReader();
71 11
        foreach ($namespaces as $namespace) {
72 11
            $this->annotationReader->addNamespace(rtrim($namespace, '\\').'\\Annotations');
73
        }
74 11
    }
75
76
    /**
77
     * Defines the default instance of the factory.
78
     *
79
     * @param Factory $factory
80
     */
81 1
    public static function setDefaultInstance(self $factory)
82
    {
83 1
        self::$defaultInstance = $factory;
0 ignored issues
show
Documentation Bug introduced by
It seems like $factory of type object<self> is incompatible with the declared type object<Respect\Validation\Factory> of property $defaultInstance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
84 1
    }
85
86
    /**
87
     * Returns the default instance of the factory.
88
     *
89
     * @return self
90
     */
91 2
    public static function getDefaultInstance(): self
92
    {
93 2
        if (!self::$defaultInstance instanceof self) {
94 1
            self::$defaultInstance = new self();
95
        }
96
97 2
        return self::$defaultInstance;
98
    }
99
100
    /**
101
     * Returns a list of namespaces.
102
     *
103
     * @return array
104
     */
105 9
    public function getNamespaces(): array
106
    {
107 9
        return $this->namespaces;
108
    }
109
110
    /**
111
     * Creates a rule based on its name with the defined arguments.
112
     *
113
     * @param string $ruleName
114
     * @param array  $arguments
115
     *
116
     * @throws ComponentException When the rule cannot be created
117
     *
118
     * @return Rule
119
     */
120 6
    public function rule(string $ruleName, array $arguments = []): Rule
121
    {
122 6
        return $this->getRuleReflection($ruleName)->newInstanceArgs($arguments);
123
    }
124
125
    public function exception(Result $result): ValidationException
126
    {
127
        $message = $this->message($result);
128
        $reflection = $this->getExceptionReflection($message->getRuleName());
129
130
        return $reflection->newInstance($message->__toString());
131
    }
132
133
    public function message(Result $result, string $locale = 'en'): Message
134
    {
135
        return current($this->messages($result, $locale));
136
    }
137
138
    public function messages(Result $result, string $locale = 'en'): array
139
    {
140
        $key = spl_object_hash($result);
141
        $message = $this->createMessage($result, $locale);
142
        $messages = [$key => $message];
143
        foreach ($result as $childKey => $childResult) {
144
            if (!is_string($childKey)) {
145
                $childKey = spl_object_hash($childResult);
146
            }
147
148
            $childMessage = $this->createMessage($childResult, $locale);
149
150
            if (!$childResult->hasChildren()) {
151
                $messages[$childKey] = $childMessage;
152
                continue;
153
            }
154
155
            $messages[$childKey] = $this->messages($childResult);
156
            if (count($messages[$childKey]) == 1) {
157
                $messages[$childKey] = current($messages[$childKey]);
158
            }
159
160
            if ($childMessage->isIgnorable()) {
161
                continue;
162
            }
163
        }
164
165
        if ($message->isIgnorable() && count($messages) > 1) {
166
            unset($messages[$key]);
167
        }
168
169
        return $messages;
170
    }
171
172
    private function createMessage(Result $result, string $locale = 'en'): Message
0 ignored issues
show
Unused Code introduced by
The parameter $locale is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
173
    {
174
        $ruleName = $this->getRuleName($result->getRule());
175
        $properties = $result->getProperties();
176
        $reflection = $this->getExceptionReflection($ruleName);
177
178
        /* @var Templates $templates */
179
        $templates = $this->annotationReader->getClassAnnotation($reflection, Templates::class);
180
181
        /* @var Template $template */
182
        $template = $this->chooseTemplate(
183
            $templates,
184
            $result->isInverted(),
185
            $properties['templateId'] ?? Template::STANDARD_ID
186
        );
187
188
        return new Message($ruleName, $template->message, $result->getInput(), $properties, $templates->isIgnorable);
189
    }
190
191
    private function chooseTemplate(Templates $templates, bool $isInverted, string $templateId): Template
192
    {
193
        $templatesList = $templates->regular;
194
        if ($isInverted) {
195
            $templatesList = $templates->inverted;
196
        }
197
198
        foreach ($templatesList as $template) {
199
            if ($template->id != $templateId) {
200
                continue;
201
            }
202
203
            return $template;
204
        }
205
206
        return current($templatesList);
207
    }
208
209
    private function getRuleName(Rule $rule): string
210
    {
211
        $reflection = $this->getReflection(get_class($rule));
0 ignored issues
show
Bug introduced by
The call to getReflection() misses a required argument $parentClassName.

This check looks for function calls that miss required arguments.

Loading history...
212
213
        return $reflection->getShortName();
214
    }
215
216
    /**
217
     * Returns a reflection of a rule based on its name.
218
     *
219
     * @throws RuleNotFoundException
220
     *
221
     * @param string $ruleName
222
     *
223
     * @return ReflectionClass
224
     */
225 6
    private function getRuleReflection(string $ruleName): ReflectionClass
226
    {
227 6
        foreach ($this->getNamespaces() as $namespace) {
228 6
            $className = rtrim($namespace, '\\').'\\Rules\\'.ucfirst($ruleName);
229 6
            if (!class_exists($className)) {
230 1
                continue;
231
            }
232
233 5
            return $this->getReflection($className, Rule::class);
234
        }
235
236 1
        throw new RuleNotFoundException(sprintf('Could not find "%s" rule', $ruleName));
237
    }
238
239
    /**
240
     * Returns a reflection of an exception based on a rule name.
241
     *
242
     * @throws ExceptionNotFoundException
243
     *
244
     * @param string $ruleName
245
     *
246
     * @return ReflectionClass
247
     */
248
    private function getExceptionReflection(string $ruleName): ReflectionClass
249
    {
250
        foreach ($this->getNamespaces() as $namespace) {
251
            $className = sprintf('%s\\Exceptions\\%sException', rtrim($namespace, '\\'), $ruleName);
252
            if (!class_exists($className)) {
253
                continue;
254
            }
255
256
            return $this->getReflection($className, ValidationException::class);
257
        }
258
259
        throw new ExceptionNotFoundException(sprintf('Could not find "%s" exception', $ruleName));
260
    }
261
262
    /**
263
     * Creates a ReflectionClass object based on a class name.
264
     *
265
     * This method always return the same object for a given class name in order
266
     * to improve performance. Also checks if the reflection is child of $parentClass
267
     * and if it is instantiable.
268
     *
269
     * @param string $className
270
     * @param string $parentClassName
271
     *
272
     * @return ReflectionClass
273
     *
274
     * @throws InvalidClassException When not valid.
275
     */
276 5
    private function getReflection(string $className, string $parentClassName): ReflectionClass
277
    {
278 5
        if (!isset($this->reflections[$className])) {
279 5
            $this->reflections[$className] = new ReflectionClass($className);
280
281 5
            if (!$this->reflections[$className]->isInstantiable()) {
282 1
                throw new InvalidClassException(sprintf('"%s" is not instantiable', $className));
283
            }
284
        }
285
286 4
        if (!$this->reflections[$className]->isSubclassOf($parentClassName)) {
287 1
            throw new InvalidClassException(sprintf('"%s" is not subclass of "%s"', $className, $parentClassName));
288
        }
289
290 3
        return $this->reflections[$className];
291
    }
292
}
293