Completed
Push — master ( d45ca1...554243 )
by Adrian
01:42
created

RuleFactory   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 88.14%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 1
dl 0
loc 232
ccs 52
cts 59
cp 0.8814
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 14 4
A __construct() 0 4 1
B registerDefaultRules() 0 61 2
A createRule() 0 18 6
A setMessages() 0 11 3
A getSuggestedMessageTemplate() 0 11 6
B construcRuleByNameAndOptions() 0 31 8
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Validation;
5
6
use Sirius\Validation\Rule\Callback as CallbackRule;
7
8
class RuleFactory
9
{
10
    /**
11
     * Validator map allows for flexibility when creating a validation rule
12
     * You can use 'required' instead of 'required' for the name of the rule
13
     * or 'minLength'/'minlength' instead of 'MinLength'
14
     *
15
     * @var array
16
     */
17
    protected $validatorsMap = [];
18
19
    /**
20
     * @var array
21
     */
22
    protected $errorMessages = [];
23
24
    /**
25
     * @var array
26
     */
27
    protected $labeledErrorMessages = [];
28
29
    /**
30
     * Constructor
31
     */
32 29
    public function __construct()
33
    {
34 29
        $this->registerDefaultRules();
35 29
    }
36
37
    /**
38
     * Set up the default rules that come with the library
39
     */
40 29
    protected function registerDefaultRules()
41
    {
42
        $rulesClasses = [
43 29
            'Alpha',
44
            'AlphaNumeric',
45
            'AlphaNumHyphen',
46
            'ArrayLength',
47
            'ArrayMaxLength',
48
            'ArrayMinLength',
49
            'Between',
50
            'Callback',
51
            'Date',
52
            'DateTime',
53
            'Email',
54
            'EmailDomain',
55
            'Equal',
56
            'FullName',
57
            'GreaterThan',
58
            'InList',
59
            'Integer',
60
            'IpAddress',
61
            'Length',
62
            'LessThan',
63
            'Match',
64
            'MaxLength',
65
            'MinLength',
66
            'NotEqual',
67
            'NotInList',
68
            'NotMatch',
69
            'NotRegex',
70
            'Number',
71
            'Regex',
72
            'Required',
73
            'RequiredWhen',
74
            'RequiredWith',
75
            'RequiredWithout',
76
            'Time',
77
            'Url',
78
            'Website',
79
            'File\Extension',
80
            'File\Image',
81
            'File\ImageHeight',
82
            'File\ImageRatio',
83
            'File\ImageWidth',
84
            'File\Size',
85
            'Upload\Required',
86
            'Upload\Extension',
87
            'Upload\Image',
88
            'Upload\ImageHeight',
89
            'Upload\ImageRatio',
90
            'Upload\ImageWidth',
91
            'Upload\Size',
92
        ];
93 29
        foreach ($rulesClasses as $class) {
94 29
            $fullClassName       = '\\' . __NAMESPACE__ . '\Rule\\' . $class;
95 29
            $name                = strtolower(str_replace('\\', '', $class));
96 29
            $errorMessage        = constant($fullClassName . '::MESSAGE');
97 29
            $labeledErrorMessage = constant($fullClassName . '::LABELED_MESSAGE');
98 29
            $this->register($name, $fullClassName, $errorMessage, $labeledErrorMessage);
99
        }
100 29
    }
101
102
103
    /**
104
     * Register a class to be used when creating validation rules
105
     *
106
     * @param string $name
107
     * @param string $class
108
     *
109
     * @return \Sirius\Validation\RuleFactory
110
     */
111 29
    public function register($name, $class, $errorMessage = '', $labeledErrorMessage = '')
112
    {
113 29
        if (is_subclass_of($class, '\Sirius\Validation\Rule\AbstractRule')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
114 29
            $this->validatorsMap[$name] = $class;
115
        }
116 29
        if ($errorMessage) {
117 29
            $this->errorMessages[$name] = $errorMessage;
118
        }
119 29
        if ($labeledErrorMessage) {
120 29
            $this->labeledErrorMessages[$name] = $labeledErrorMessage;
121
        }
122
123 29
        return $this;
124
    }
125
126
    /**
127
     * Factory method to construct a validator based on options that are used most of the times
128
     *
129
     * @param string|callable $name
130
     *            name of a validator class or a callable object/function
131
     * @param string|array $options
132
     *            validator options (an array, JSON string or QUERY string)
133
     * @param string $messageTemplate
134
     *            error message template
135
     * @param string $label
136
     *            label of the form input field or model attribute
137
     *
138
     * @throws \InvalidArgumentException
139
     * @return \Sirius\Validation\Rule\AbstractValidator
140
     */
141 26
    public function createRule($name, $options = null, $messageTemplate = null, $label = null)
142
    {
143 26
        $validator = $this->construcRuleByNameAndOptions($name, $options);
144
145
        // no message template, try to get it from the registry
146 24
        if (!$messageTemplate) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageTemplate of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
147 21
            $messageTemplate = $this->getSuggestedMessageTemplate($name, !!$label);
0 ignored issues
show
Documentation introduced by
$name is of type callable, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
148
        }
149
150 24
        if (is_string($messageTemplate) && $messageTemplate !== '') {
151 21
            $validator->setMessageTemplate($messageTemplate);
152
        }
153 24
        if (is_string($label) && $label !== '') {
154 8
            $validator->setOption('label', $label);
155
        }
156
157 24
        return $validator;
158
    }
159
160
    /**
161
     * Set default error message for a rule
162
     *
163
     * @param string $rule
164
     * @param string|null $messageWithoutLabel
165
     * @param string|null $messageWithLabel
166
     *
167
     * @return $this
168
     */
169
    public function setMessages($rule, $messageWithoutLabel = null, $messageWithLabel = null)
170
    {
171
        if ($messageWithoutLabel) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageWithoutLabel of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
172
            $this->errorMessages[$rule] = $messageWithoutLabel;
173
        }
174
        if ($messageWithLabel) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $messageWithLabel of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
175
            $this->labeledErrorMessages[$rule] = $messageWithLabel;
176
        }
177
178
        return $this;
179
    }
180
181
    /**
182
     * Get the error message saved in the registry for a rule, where the message
183
     * is with or without a the label
184
     *
185
     * @param string $name name of the rule
186
     * @param bool $withLabel
187
     *
188
     * @return string|NULL
189
     */
190 21
    protected function getSuggestedMessageTemplate($name, $withLabel)
191
    {
192 21
        $noLabelMessage = is_string($name) && isset($this->errorMessages[$name]) ? $this->errorMessages[$name] : null;
193 21
        if ($withLabel) {
194 3
            return is_string($name) && isset($this->labeledErrorMessages[$name]) ?
195 3
                $this->labeledErrorMessages[$name] :
196 3
                $noLabelMessage;
197
        }
198
199 20
        return $noLabelMessage;
200
    }
201
202
    /**
203
     * @param $name
204
     * @param $options
205
     *
206
     * @return CallbackRule
207
     */
208 26
    protected function construcRuleByNameAndOptions($name, $options)
209
    {
210 26
        if (is_callable($name)) {
211 1
            $validator = new CallbackRule([
212 1
                'callback'  => $name,
213 1
                'arguments' => $options
214
            ]);
215 25
        } elseif (is_string($name)) {
216 25
            $name = trim($name);
217
            // use the validator map
218 25
            if (isset($this->validatorsMap[strtolower($name)])) {
219 24
                $name = $this->validatorsMap[strtolower($name)];
220
            }
221
            // try if the validator is the name of a class in the package
222 25
            if (class_exists('\Sirius\Validation\Rule\\' . $name, false)) {
223
                $name = '\Sirius\Validation\Rule\\' . $name;
224
            }
225
            // at this point we should have a class that can be instanciated
226 25
            if (class_exists($name) && is_subclass_of($name, '\Sirius\Validation\Rule\AbstractRule')) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
227 24
                $validator = new $name($options);
228
            }
229
        }
230
231 25
        if (!isset($validator)) {
232 1
            throw new \InvalidArgumentException(
233 1
                sprintf('Impossible to determine the validator based on the name: %s', (string) $name)
234
            );
235
        }
236
237 24
        return $validator;
238
    }
239
}
240