Completed
Push — master ( c28d6e...fb506f )
by Davide
10s
created

Validation::__invoke()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 10
cts 10
cp 1
rs 9.7998
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
namespace DavidePastore\Slim\Validation;
4
5
use Respect\Validation\Exceptions\NestedValidationException;
6
7
/**
8
 * Validation for Slim.
9
 */
10
class Validation
11
{
12
    /**
13
     * Validators.
14
     *
15
     * @var array
16
     */
17
    protected $validators = [];
18
19
    /**
20
     * Options.
21
     *
22
     * @var array
23
     */
24
    protected $options = [
25
      'useTemplate' => false,
26
    ];
27
28
    /**
29
     * The translator to use fro the exception message.
30
     *
31
     * @var callable
32
     */
33
    protected $translator = null;
34
35
    /**
36
     * Errors from the validation.
37
     *
38
     * @var array
39
     */
40
    protected $errors = [];
41
42
    /**
43
     * The 'errors' attribute name.
44
     *
45
     * @var string
46
     */
47
    protected $errors_name = 'errors';
48
49
    /**
50
     * The 'has_error' attribute name.
51
     *
52
     * @var string
53
     */
54
    protected $has_errors_name = 'has_errors';
55
56
    /**
57
     * The 'validators' attribute name.
58
     *
59
     * @var string
60
     */
61
    protected $validators_name = 'validators';
62
63
    /**
64
     * The 'translator' attribute name.
65
     *
66
     * @var string
67
     */
68
    protected $translator_name = 'translator';
69
70
    /**
71
     * Create new Validator service provider.
72
     *
73
     * @param null|array|ArrayAccess $validators
74
     * @param null|callable          $translator
75
     * @param []|array               $options
0 ignored issues
show
Documentation introduced by
The doc-type []|array could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
76
     */
77 26
    public function __construct($validators = null, $translator = null, $options = [])
78
    {
79
        // Set the validators
80 26
        if (is_array($validators) || $validators instanceof \ArrayAccess) {
81 25
            $this->validators = $validators;
82 1
        } elseif (is_null($validators)) {
83 1
            $this->validators = [];
84
        }
85 26
        $this->translator = $translator;
86 26
        $this->options = array_merge($this->options, $options);
87 26
    }
88
89
    /**
90
     * Validation middleware invokable class.
91
     *
92
     * @param \Psr\Http\Message\ServerRequestInterface $request  PSR7 request
93
     * @param \Psr\Http\Message\ResponseInterface      $response PSR7 response
94
     * @param callable                                 $next     Next middleware
95
     *
96
     * @return \Psr\Http\Message\ResponseInterface
97
     */
98 26
    public function __invoke($request, $response, $next)
99
    {
100 26
        $this->errors = [];
101 26
        $params = $request->getParams();
102 26
        $params = array_merge((array) $request->getAttribute('routeInfo')[2], $params);
103 26
        $this->validate($params, $this->validators);
104
105 26
        $request = $request->withAttribute($this->errors_name, $this->getErrors());
106 26
        $request = $request->withAttribute($this->has_errors_name, $this->hasErrors());
107 26
        $request = $request->withAttribute($this->validators_name, $this->getValidators());
108 26
        $request = $request->withAttribute($this->translator_name, $this->getTranslator());
109
110 26
        return $next($request, $response);
111
    }
112
113
    /**
114
     * Validate the parameters by the given params, validators and actual keys.
115
     * This method populates the $errors attribute.
116
     *
117
     * @param array $params     The array of parameters.
118
     * @param array $validators The array of validators.
119
     * @param array $actualKeys An array that will save all the keys of the tree to retrieve the correct value.
120
     */
121 26
    private function validate($params = [], $validators = [], $actualKeys = [])
122
    {
123
        //Validate every parameters in the validators array
124 26
        foreach ($validators as $key => $validator) {
125 25
            $actualKeys[] = $key;
126 25
            $param = $this->getNestedParam($params, $actualKeys);
127 25
            if (is_array($validator)) {
128 9
                $this->validate($params, $validator, $actualKeys);
129
            } else {
130
                try {
131 25
                    $validator->assert($param);
132 15
                } catch (NestedValidationException $exception) {
133 15
                    if ($this->translator) {
134 2
                        $exception->setParam('translator', $this->translator);
135
                    }
136 15
                    if ($this->options['useTemplate']) {
137 1
                        $this->errors[implode('.', $actualKeys)] = [$exception->getMainMessage()];
138
                    } else {
139 14
                        $this->errors[implode('.', $actualKeys)] = $exception->getMessages();
140
                    }
141
                }
142
            }
143
144
            //Remove the key added in this foreach
145 25
            array_pop($actualKeys);
146
        }
147 26
    }
148
149
    /**
150
     * Get the nested parameter value.
151
     *
152
     * @param array $params An array that represents the values of the parameters.
153
     * @param array $keys   An array that represents the tree of keys to use.
154
     *
155
     * @return mixed The nested parameter value by the given params and tree of keys.
156
     */
157 25
    private function getNestedParam($params = [], $keys = [])
158
    {
159 25
        if (empty($keys)) {
160 23
            return $params;
161
        } else {
162 25
            $firstKey = array_shift($keys);
163 25
            if ($this->isArrayLike($params) && array_key_exists($firstKey, $params)) {
164 23
                $params = (array) $params;
165 23
                $paramValue = $params[$firstKey];
166
167 23
                return $this->getNestedParam($paramValue, $keys);
168
            } else {
169 3
                return;
170
            }
171
        }
172
    }
173
174
    /**
175
     * Check if the given $params is an array like variable.
176
     *
177
     * @param array $params The variable to check.
178
     *
179
     * @return boolean Returns true if the given $params parameter is array like.
180
     */
181 25
    private function isArrayLike($params)
182
    {
183 25
        return is_array($params) || $params instanceof \SimpleXMLElement;
184
    }
185
186
    /**
187
     * Check if there are any errors.
188
     *
189
     * @return bool
190
     */
191 26
    public function hasErrors()
192
    {
193 26
        return !empty($this->errors);
194
    }
195
196
    /**
197
     * Get errors.
198
     *
199
     * @return array The errors array.
200
     */
201 26
    public function getErrors()
202
    {
203 26
        return $this->errors;
204
    }
205
206
    /**
207
     * Get validators.
208
     *
209
     * @return array The validators array.
210
     */
211 26
    public function getValidators()
212
    {
213 26
        return $this->validators;
214
    }
215
216
    /**
217
     * Set validators.
218
     *
219
     * @param array $validators The validators array.
220
     */
221 1
    public function setValidators($validators)
222
    {
223 1
        $this->validators = $validators;
224 1
    }
225
226
    /**
227
     * Get translator.
228
     *
229
     * @return callable The translator.
230
     */
231 26
    public function getTranslator()
232
    {
233 26
        return $this->translator;
234
    }
235
236
    /**
237
     * Set translator.
238
     *
239
     * @param callable $translator The translator.
240
     */
241 1
    public function setTranslator($translator)
242
    {
243 1
        $this->translator = $translator;
244 1
    }
245
}
246