Registry   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 3
dl 0
loc 167
ccs 32
cts 32
cp 1
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A register() 0 10 3
A alias() 0 7 1
B aliasDefinition() 0 13 5
A factory() 0 13 4
A builder() 0 4 1
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
        'any_object'       => ['Caridea\Validate\Rule\Type', 'anyObject'],
45
        'string'           => ['Caridea\Validate\Rule\Type', 'string'],
46
        'eq'               => ['Caridea\Validate\Rule\Compare', 'eq'],
47
        'one_of'           => ['Caridea\Validate\Rule\Compare', 'oneOf'],
48
        'min_length'       => ['Caridea\Validate\Rule\Length', 'min'],
49
        'max_length'       => ['Caridea\Validate\Rule\Length', 'max'],
50
        'length_equal'     => ['Caridea\Validate\Rule\Length', 'equal'],
51
        'length_between'   => ['Caridea\Validate\Rule\Length', 'between'],
52
        'like'             => ['Caridea\Validate\Rule\Match', 'like'],
53
        'integer'          => ['Caridea\Validate\Rule\Compare', 'integer'],
54
        'positive_integer' => ['Caridea\Validate\Rule\Compare', 'positiveInteger'],
55
        'decimal'          => ['Caridea\Validate\Rule\Compare', 'decimal'],
56
        'positive_decimal' => ['Caridea\Validate\Rule\Compare', 'positiveDecimal'],
57
        'min_number'       => ['Caridea\Validate\Rule\Compare', 'min'],
58
        'max_number'       => ['Caridea\Validate\Rule\Compare', 'max'],
59
        'number_between'   => ['Caridea\Validate\Rule\Compare', 'between'],
60
        'email'            => ['Caridea\Validate\Rule\Match', 'email'],
61
        'iso_date'         => ['Caridea\Validate\Rule\Match', 'isoDate'],
62
        'url'              => ['Caridea\Validate\Rule\Match', 'url'],
63
        'timezone'         => ['Caridea\Validate\Rule\Timezone', 'timezone'],
64
        'equal_to_field'   => ['Caridea\Validate\Rule\Compare', 'equalToField'],
65
        'nested_object'    => ['Caridea\Validate\Rule\Nested', 'nestedObject'],
66
        'variable_object'    => ['Caridea\Validate\Rule\Nested', 'variableObject'],
67
        'list_of'          => ['Caridea\Validate\Rule\Nested', 'listOf'],
68
        'list_of_objects'  => ['Caridea\Validate\Rule\Nested', 'listOfObjects'],
69
        'list_of_different_objects' => ['Caridea\Validate\Rule\Nested', 'listOfDifferentObjects'],
70
    ];
71
72
    /**
73
     * Creates a new Validation rule registry.
74
     */
75 4
    public function __construct()
76
    {
77 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...
78 4
        $this->parser = new Parser($this);
79 4
    }
80
81
    /**
82
     * Registers rule definitions.
83
     *
84
     * ```php
85
     * $registry = new \Caridea\Validate\Registry();
86
     * $registry->register([
87
     *     'adult' => ['My\Validate\AgeRule', 'adult'],
88
     *     'credit_card' => function(){return new CreditCardRule();},
89
     *     'something' => 'my_function_that_can_be_called'
90
     * ]);
91
     * ```
92
     *
93
     * @param array<string,callable> $definitions Associative array of definition name to function callback
94
     * @return $this provides a fluent interface
95
     */
96 2
    public function register(array $definitions): self
97
    {
98 2
        foreach ($definitions as $name => $callback) {
99 2
            if (!is_callable($callback)) {
100 1
                throw new \InvalidArgumentException('Values passed to register must be callable');
101
            }
102 1
            $this->definitions[$name] = $callback;
103
        }
104 1
        return $this;
105
    }
106
107
    /**
108
     * Registers an alias for a ruleset.
109
     *
110
     * @param string $name The name of the alias
111
     * @param object|array $rules The ruleset to alias
112
     * @param string|null $error A custom error code to return, or `null` to use normal codes
113
     * @return $this provides a fluent interface
114
     */
115
    public function alias(string $name, $rules, ?string $error = null): self
116
    {
117 6
        $this->definitions[$name] = function () use ($rules, $error) {
118 6
            return $this->parser->parse($rules)->setError($error);
119
        };
120 6
        return $this;
121
    }
122
123
    /**
124
     * Registers an alias for a ruleset, using a LIVR-compliant definition.
125
     *
126
     * ```javascript
127
     * // alias.json
128
     * {
129
     *     "name": "valid_address",
130
     *     "rules": { "nested_object": {
131
     *         "country": "required",
132
     *         "city": "required",
133
     *         "zip": "positive_integer"
134
     *     }},
135
     *     error: "WRONG_ADDRESS"
136
     * }
137
     * ```
138
     * ```php
139
     * $registry->aliasDefinition(json_decode(file_get_contents('alias.json')));
140
     * ```
141
     *
142
     * @param array|object $definition The rule definition
143
     * @return $this provides a fluent interface
144
     * @throws \InvalidArgumentException if the definition is invalid
145
     */
146 4
    public function aliasDefinition($definition): self
147
    {
148 4
        if (is_object($definition)) {
149 2
            $definition = (array) $definition;
150
        }
151 4
        if (!is_array($definition)) {
152 1
            throw new \InvalidArgumentException("Invalid alias definition: must be an object or an associative array");
153
        }
154 3
        if (!isset($definition['name']) || !isset($definition['rules'])) {
155 1
            throw new \InvalidArgumentException("Invalid alias definition: must have 'name' and 'rules' fields");
156
        }
157 2
        return $this->alias($definition['name'], $definition['rules'], $definition['error'] ?? null);
158
    }
159
160
    /**
161
     * Constructs a validation rule.
162
     *
163
     * @param string $name A string name
164
     * @param mixed $arg Optional constructor argument, or an array of arguments
165
     * @return \Caridea\Validate\Rule The instantiated rule
166
     * @throws \InvalidArgumentException if the rule name is not registered
167
     * @throws \UnexpectedValueException if the factory returns a non-Rule
168
     */
169 3
    public function factory(string $name, $arg = null): Rule
170
    {
171 3
        if (!array_key_exists($name, $this->definitions)) {
172 1
            throw new \InvalidArgumentException("No rule registered with name: $name");
173
        }
174 2
        $vrule = is_array($arg) ?
175 2
            call_user_func_array($this->definitions[$name], $arg) :
176 2
            call_user_func($this->definitions[$name], $arg);
177 2
        if (!$vrule instanceof Rule) {
178 1
            throw new \UnexpectedValueException('Definitions must return Rule objects');
179
        }
180 1
        return $vrule;
181
    }
182
183
    /**
184
     * Creates a new Builder using this Repository.
185
     *
186
     * @return \Caridea\Validate\Builder The builder
187
     */
188 1
    public function builder(): Builder
189
    {
190 1
        return new Builder($this->parser);
191
    }
192
}
193