Completed
Push — master ( 77b530...acb106 )
by Jonathan
02:05
created

Registry::aliasDefinition()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 6
nop 1
crap 5
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Caridea
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2015-2018 LibreWorks contributors
19
 * @license   Apache-2.0
20
 */
21
namespace Caridea\Validate;
22
23
/**
24
 * A container for validation rules.
25
 */
26
class Registry
27
{
28
    /**
29
     * @var array<string,callable> Associative array of definition name to function callback
30
     */
31
    private $definitions = [];
32
    /**
33
     * @var \Caridea\Validate\Parser The parser
34
     */
35
    private $parser;
36
37
    /**
38
     * @var array<string,callable> Associative array of definition name to function callback
39
     */
40
    private static $defaultDefinitions = [
41
        'required'         => ['Caridea\Validate\Rule\Blank', 'required'],
42
        'not_empty'        => ['Caridea\Validate\Rule\Blank', 'notEmpty'],
43
        'not_empty_list'   => ['Caridea\Validate\Rule\Blank', 'notEmptyList'],
44
        'eq'               => ['Caridea\Validate\Rule\Compare', 'eq'],
45
        'one_of'           => ['Caridea\Validate\Rule\Compare', 'oneOf'],
46
        'min_length'       => ['Caridea\Validate\Rule\Length', 'min'],
47
        'max_length'       => ['Caridea\Validate\Rule\Length', 'max'],
48
        'length_equal'     => ['Caridea\Validate\Rule\Length', 'equal'],
49
        'length_between'   => ['Caridea\Validate\Rule\Length', 'between'],
50
        'like'             => ['Caridea\Validate\Rule\Match', 'like'],
51
        'integer'          => ['Caridea\Validate\Rule\Compare', 'integer'],
52
        'positive_integer' => ['Caridea\Validate\Rule\Compare', 'positiveInteger'],
53
        'decimal'          => ['Caridea\Validate\Rule\Compare', 'decimal'],
54
        'positive_decimal' => ['Caridea\Validate\Rule\Compare', 'positiveDecimal'],
55
        'min_number'       => ['Caridea\Validate\Rule\Compare', 'min'],
56
        'max_number'       => ['Caridea\Validate\Rule\Compare', 'max'],
57
        'number_between'   => ['Caridea\Validate\Rule\Compare', 'between'],
58
        'email'            => ['Caridea\Validate\Rule\Match', 'email'],
59
        'iso_date'         => ['Caridea\Validate\Rule\Match', 'isoDate'],
60
        'url'              => ['Caridea\Validate\Rule\Match', 'url'],
61
        'timezone'         => ['Caridea\Validate\Rule\Timezone', 'timezone'],
62
        'equal_to_field'   => ['Caridea\Validate\Rule\Compare', 'equalToField'],
63
        'nested_object'    => ['Caridea\Validate\Rule\Nested', 'nestedObject'],
64
        'list_of'          => ['Caridea\Validate\Rule\Nested', 'listOf'],
65
        'list_of_objects'  => ['Caridea\Validate\Rule\Nested', 'listOfObjects'],
66
        'list_of_different_objects' => ['Caridea\Validate\Rule\Nested', 'listOfDifferentObjects'],
67
    ];
68
69
    /**
70
     * Creates a new Validation rule registry.
71
     */
72 4
    public function __construct()
73
    {
74 4
        $this->definitions = array_merge([], self::$defaultDefinitions);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge(array(), self::$defaultDefinitions) of type array is incompatible with the declared type array<string,callable> of property $definitions.

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...
75 4
        $this->parser = new Parser($this);
76 4
    }
77
78
    /**
79
     * Registers rule definitions.
80
     *
81
     * ```php
82
     * $registry = new \Caridea\Validate\Registry();
83
     * $registry->register([
84
     *     'adult' => ['My\Validate\AgeRule', 'adult'],
85
     *     'credit_card' => function(){return new CreditCardRule();},
86
     *     'something' => 'my_function_that_can_be_called'
87
     * ]);
88
     * ```
89
     *
90
     * @param array<string,callable> $definitions Associative array of definition name to function callback
91
     * @return $this provides a fluent interface
92
     */
93 2
    public function register(array $definitions): self
94
    {
95 2
        foreach ($definitions as $name => $callback) {
96 2
            if (!is_callable($callback)) {
97 1
                throw new \InvalidArgumentException('Values passed to register must be callable');
98
            }
99 1
            $this->definitions[$name] = $callback;
100
        }
101 1
        return $this;
102
    }
103
104
    /**
105
     * Registers an alias for a ruleset.
106
     *
107
     * @param string $name The name of the alias
108
     * @param object|array $rules The ruleset to alias
109
     * @param string|null $error A custom error code to return, or `null` to use normal codes
110
     * @return $this provides a fluent interface
111
     */
112
    public function alias(string $name, $rules, ?string $error = null): self
113
    {
114 6
        $this->definitions[$name] = function () use ($rules, $error) {
115 6
            return $this->parser->parse($rules)->setError($error);
116
        };
117 6
        return $this;
118
    }
119
120
    /**
121
     * Registers an alias for a ruleset, using a LIVR-compliant definition.
122
     *
123
     * ```javascript
124
     * // alias.json
125
     * {
126
     *     "name": "valid_address",
127
     *     "rules": { "nested_object": {
128
     *         "country": "required",
129
     *         "city": "required",
130
     *         "zip": "positive_integer"
131
     *     }},
132
     *     error: "WRONG_ADDRESS"
133
     * }
134
     * ```
135
     * ```php
136
     * $registry->aliasDefinition(json_decode(file_get_contents('alias.json')));
137
     * ```
138
     *
139
     * @param array|object $definition The rule definition
140
     * @return $this provides a fluent interface
141
     * @throws \InvalidArgumentException if the definition is invalid
142
     */
143 4
    public function aliasDefinition($definition): self
144
    {
145 4
        if (is_object($definition)) {
146 2
            $definition = (array) $definition;
147
        }
148 4
        if (!is_array($definition)) {
149 1
            throw new \InvalidArgumentException("Invalid alias definition: must be an object or an associative array");
150
        }
151 3
        if (!isset($definition['name']) || !isset($definition['rules'])) {
152 1
            throw new \InvalidArgumentException("Invalid alias definition: must have 'name' and 'rules' fields");
153
        }
154 2
        return $this->alias($definition['name'], $definition['rules'], $definition['error'] ?? null);
155
    }
156
157
    /**
158
     * Constructs a validation rule.
159
     *
160
     * @param string $name A string name
161
     * @param mixed $arg Optional constructor argument, or an array of arguments
162
     * @return \Caridea\Validate\Rule The instantiated rule
163
     * @throws \InvalidArgumentException if the rule name is not registered
164
     * @throws \UnexpectedValueException if the factory returns a non-Rule
165
     */
166 3
    public function factory(string $name, $arg = null): Rule
167
    {
168 3
        if (!array_key_exists($name, $this->definitions)) {
169 1
            throw new \InvalidArgumentException("No rule registered with name: $name");
170
        }
171 2
        $vrule = is_array($arg) ?
172 2
            call_user_func_array($this->definitions[$name], $arg) :
173 2
            call_user_func($this->definitions[$name], $arg);
174 2
        if (!$vrule instanceof Rule) {
175 1
            throw new \UnexpectedValueException('Definitions must return Rule objects');
176
        }
177 1
        return $vrule;
178
    }
179
180
    /**
181
     * Creates a new Builder using this Repository.
182
     *
183
     * @return \Caridea\Validate\Builder The builder
184
     */
185 1
    public function builder(): Builder
186
    {
187 1
        return new Builder($this->parser);
188
    }
189
}
190