Validator   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 5
dl 0
loc 285
ccs 85
cts 85
cp 1
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A required() 0 4 1
A optional() 0 4 1
A validate() 0 22 3
A copyContext() 0 9 2
A context() 0 8 1
A output() 0 14 3
A overwriteMessages() 0 5 1
A overwriteDefaultMessages() 0 5 1
A getChain() 0 12 2
A buildChain() 0 4 1
A copyChains() 0 11 3
A runChainCallback() 0 8 2
A getMessageStack() 0 4 1
A addMessageStack() 0 6 1
A getMergedMessageStack() 0 10 2
1
<?php
2
/**
3
 * Particle.
4
 *
5
 * @link      http://github.com/particle-php for the canonical source repository
6
 * @copyright Copyright (c) 2005-2016 Particle (http://particle-php.com)
7
 * @license   https://github.com/particle-php/validator/blob/master/LICENSE New BSD License
8
 */
9
namespace Particle\Validator;
10
11
use Particle\Validator\Value\Container;
12
13
class Validator
14
{
15
    /**
16
     * The default context (if no context is currently active).
17
     */
18
    const DEFAULT_CONTEXT = 'default';
19
20
    /**
21
     * Contains an array of context => Chain objects.
22
     *
23
     * @var array
24
     */
25
    protected $chains = [
26
        self::DEFAULT_CONTEXT => [],
27
    ];
28
29
    /**
30
     * Contains an array of context => MessageStack.
31
     *
32
     * @var MessageStack[]
33
     */
34
    protected $messageStacks = [];
35
36
    /**
37
     * Contains the name of the current context.
38
     *
39
     * @var string
40
     */
41
    protected $context;
42
43
    /**
44
     * Construct the validator.
45
     */
46 265
    public function __construct()
47
    {
48 265
        $this->context = self::DEFAULT_CONTEXT;
49 265
        $this->messageStacks[$this->context] = new MessageStack();
50 265
    }
51
52
    /**
53
     * Creates a new required Validation Chain for the key $key.
54
     *
55
     * @param string $key
56
     * @param string|null $name
57
     * @param bool $allowEmpty
58
     * @return Chain
59
     */
60 254
    public function required($key, $name = null, $allowEmpty = false)
61
    {
62 254
        return $this->getChain($key, $name, true, $allowEmpty);
63
    }
64
65
    /**
66
     * Creates a new optional Validation Chain for the key $key.
67
     *
68
     * @param string $key
69
     * @param string|null $name
70
     * @param bool $allowEmpty
71
     * @return Chain
72
     */
73 14
    public function optional($key, $name = null, $allowEmpty = true)
74
    {
75 14
        return $this->getChain($key, $name, false, $allowEmpty);
76
    }
77
78
    /**
79
     * Validates the values in the $values array and returns a ValidationResult.
80
     *
81
     * @param array $values
82
     * @param string $context
83
     * @return ValidationResult
84
     */
85 260
    public function validate(array $values, $context = self::DEFAULT_CONTEXT)
86
    {
87 260
        $isValid = true;
88 260
        $output = new Container();
89 260
        $input = new Container($values);
90 260
        $stack = $this->getMergedMessageStack($context);
91
92 260
        foreach ($this->chains[$context] as $chain) {
93
            /** @var Chain $chain */
94 259
            $isValid = $chain->validate($stack, $input, $output) && $isValid;
95 260
        }
96
97 260
        $result = new ValidationResult(
98 260
            $isValid,
99 260
            $stack->getFailures(),
100 260
            $output->getArrayCopy()
101 260
        );
102
103 260
        $stack->reset();
104
105 260
        return $result;
106
    }
107
108
    /**
109
     * Copy the rules and messages of the context $otherContext to the current context.
110
     *
111
     * @param string $otherContext
112
     * @param callable|null $callback
113
     * @return $this
114
     */
115 3
    public function copyContext($otherContext, callable $callback = null)
116
    {
117 3
        $this->copyChains($otherContext, $callback);
118 3
        if ($otherContext !== self::DEFAULT_CONTEXT) {
119 3
            $this->getMessageStack($this->context)->merge($this->getMessageStack($otherContext));
120 3
        }
121
122 3
        return $this;
123
    }
124
125
    /**
126
     * Create a new validation context with the callback $callback.
127
     *
128
     * @param string $name
129
     * @param callable $callback
130
     */
131 6
    public function context($name, callable $callback)
132
    {
133 6
        $this->addMessageStack($name);
134
135 6
        $this->context = $name;
136 6
        call_user_func($callback, $this);
137 6
        $this->context = self::DEFAULT_CONTEXT;
138 6
    }
139
140
    /**
141
     * Output the structure of the Validator by calling the $output callable with a representation of Validators'
142
     * internal structure.
143
     *
144
     * @param callable $output
145
     * @param string $context
146
     * @return mixed
147
     */
148 2
    public function output(callable $output, $context = self::DEFAULT_CONTEXT)
149
    {
150 2
        $stack = $this->getMessageStack($context);
151
152 2
        $structure = new Output\Structure();
153 2
        if (array_key_exists($context, $this->chains)) {
154
            /* @var Chain $chain */
155 2
            foreach ($this->chains[$context] as $chain) {
156 2
                $chain->output($structure, $stack);
157 2
            }
158 2
        }
159
160 2
        return call_user_func($output, $structure);
161
    }
162
163
    /**
164
     * Overwrite the messages for specific keys.
165
     *
166
     * @param array $messages
167
     * @return $this
168
     */
169 7
    public function overwriteMessages(array $messages)
170
    {
171 7
        $this->getMessageStack($this->context)->overwriteMessages($messages);
172 7
        return $this;
173
    }
174
175
    /**
176
     * Overwrite the default messages with custom messages.
177
     *
178
     * @param array $messages
179
     * @return $this
180
     */
181 4
    public function overwriteDefaultMessages(array $messages)
182
    {
183 4
        $this->getMessageStack($this->context)->overwriteDefaultMessages($messages);
184 4
        return $this;
185
    }
186
187
    /**
188
     * Retrieves a Chain object, or builds one if it doesn't exist yet.
189
     *
190
     * @param string $key
191
     * @param string $name
192
     * @param bool $required
193
     * @param bool $allowEmpty
194
     * @return Chain
195
     */
196 264
    protected function getChain($key, $name, $required, $allowEmpty)
197
    {
198 264
        if (isset($this->chains[$this->context][$key])) {
199
            /** @var Chain $chain */
200 1
            $chain = $this->chains[$this->context][$key];
201 1
            $chain->required($required);
202 1
            $chain->allowEmpty($allowEmpty);
203
204 1
            return $chain;
205
        }
206 264
        return $this->chains[$this->context][$key] = $this->buildChain($key, $name, $required, $allowEmpty);
207
    }
208
209
    /**
210
     * Build a new Chain object and return it.
211
     *
212
     * @param string $key
213
     * @param string $name
214
     * @param bool $required
215
     * @param bool $allowEmpty
216
     * @return Chain
217
     */
218 264
    protected function buildChain($key, $name, $required, $allowEmpty)
219
    {
220 264
        return new Chain($key, $name, $required, $allowEmpty);
221
    }
222
223
    /**
224
     * Copies the chains of the context $otherContext to the current context.
225
     *
226
     * @param string $otherContext
227
     * @param callable|null $callback
228
     */
229 3
    protected function copyChains($otherContext, $callback)
230
    {
231 3
        if (isset($this->chains[$otherContext])) {
232 3
            $clonedChains = [];
233 3
            foreach ($this->chains[$otherContext] as $key => $chain) {
234 3
                $clonedChains[$key] = clone $chain;
235 3
            }
236
237 3
            $this->chains[$this->context] = $this->runChainCallback($clonedChains, $callback);
238 3
        }
239 3
    }
240
241
    /**
242
     * Executes the callback $callback and returns the resulting chains.
243
     *
244
     * @param Chain[] $chains
245
     * @param callable|null $callback
246
     * @return Chain[]
247
     */
248 3
    protected function runChainCallback($chains, $callback)
249
    {
250 3
        if ($callback !== null) {
251 2
            $callback($chains);
252 2
        }
253
254 3
        return $chains;
255
    }
256
257
    /**
258
     * Returns a message stack for the context $context.
259
     *
260
     * @param string $context
261
     * @return MessageStack
262
     */
263 262
    protected function getMessageStack($context)
264
    {
265 262
        return $this->messageStacks[$context];
266
    }
267
268
    /**
269
     * Adds a message stack.
270
     *
271
     * @param string $name
272
     */
273 6
    protected function addMessageStack($name)
274
    {
275 6
        $messageStack = new MessageStack();
276
277 6
        $this->messageStacks[$name] = $messageStack;
278 6
    }
279
280
    /**
281
     * Returns the message stack. If the context isn't the default context, it will merge the message of the default
282
     * context first.
283
     *
284
     * @param string $context
285
     * @return MessageStack
286
     */
287 260
    protected function getMergedMessageStack($context)
288
    {
289 260
        $stack = $this->getMessageStack($context);
290
291 260
        if ($context !== self::DEFAULT_CONTEXT) {
292 6
            $stack->merge($this->getMessageStack(self::DEFAULT_CONTEXT));
293 6
        }
294
295 260
        return $stack;
296
    }
297
}
298