Completed
Pull Request — master (#56)
by
unknown
13:39
created

Validation::validate()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 12
cts 12
cp 1
rs 8.8657
c 0
b 0
f 0
cc 6
nc 7
nop 3
crap 6
1
<?php
2
3
namespace DavidePastore\Slim\Validation;
4
5
use Psr\Http\Message\ResponseInterface;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr\Http\Server\RequestHandlerInterface;
8
use Respect\Validation\Exceptions\NestedValidationException;
9
use Slim\Routing\RouteContext;
10
11
/**
12
 * Validation for Slim.
13
 */
14
class Validation
15
{
16
    /**
17
     * Validators.
18
     *
19
     * @var array
20
     */
21
    protected $validators = [];
22
23
    /**
24
     * Options.
25
     *
26
     * @var array
27
     */
28
    protected $options = [
29
        'useTemplate' => false,
30
    ];
31
32
    /**
33
     * The translator to use for the exception message.
34
     *
35
     * @var array
36
     */
37
    protected $translator = null;
38
39
    /**
40
     * Errors from the validation.
41
     *
42
     * @var array
43
     */
44
    protected $errors = [];
45
46
    /**
47
     * The 'errors' attribute name.
48
     *
49
     * @var string
50
     */
51
    protected $errors_name = 'errors';
52
53
    /**
54
     * The 'has_error' attribute name.
55
     *
56
     * @var string
57
     */
58
    protected $has_errors_name = 'has_errors';
59
60
    /**
61
     * The 'validators' attribute name.
62
     *
63
     * @var string
64
     */
65
    protected $validators_name = 'validators';
66
67
    /**
68
     * The 'translator' attribute name.
69
     *
70
     * @var string
71
     */
72
    protected $translator_name = 'translator';
73
74
    /**
75
     * Create new Validator service provider.
76 25
     *
77
     * @param null|array|ArrayAccess $validators
78
     * @param null|array             $translator
79 25
     * @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...
80 24
     */
81 25
    public function __construct($validators = null, $translator = null, $options = [])
82 1
    {
83 1
        // Set the validators
84 25
        if (is_array($validators) || $validators instanceof \ArrayAccess) {
85 25
            $this->validators = $validators;
86 25
        } elseif (is_null($validators)) {
87
            $this->validators = [];
88
        }
89
        $this->translator = $translator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $translator can be null. However, the property $translator is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
90
        $this->options = array_merge($this->options, $options);
91
    }
92
93
    /**
94
     * Validation middleware invokable class.
95
     *
96
     * @param \Psr\Http\Message\ServerRequestInterface $request  PSR7 request
97 25
     * @param \Psr\Http\Server\RequestHandlerInterface $response PSR7 response
0 ignored issues
show
Bug introduced by
There is no parameter named $response. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
98
     *
99 25
     * @return \Psr\Http\Message\ResponseInterface
100 25
     */
101 25
    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
102 25
    {
103
        $this->errors = [];
104 25
        $params = $request->getParsedBody();
105 25
106 25
        $routeContext = RouteContext::fromRequest($request);
107 25
        $route = $routeContext->getRoute();
108
        $arguments = $route->getArguments();
109 25
110
        $queryParams = $request->getQueryParams();
111
112
        $params = array_merge((array) $arguments, (array) $params, (array) $queryParams);
113
114
        $this->validate($params, $this->validators);
115
116
        $request = $request->withAttribute($this->errors_name, $this->getErrors());
117
        $request = $request->withAttribute($this->has_errors_name, $this->hasErrors());
118
        $request = $request->withAttribute($this->validators_name, $this->getValidators());
119
        $request = $request->withAttribute($this->translator_name, $this->getTranslator());
120 25
121
        return $handler->handle($request);
122
    }
123 25
124 24
    /**
125 24
     * Validate the parameters by the given params, validators and actual keys.
126 24
     * This method populates the $errors attribute.
127 9
     *
128 9
     * @param array $params     The array of parameters.
129
     * @param array $validators The array of validators.
130 24
     * @param array $actualKeys An array that will save all the keys of the tree to retrieve the correct value.
131 24
     */
132 14
    private function validate($params = [], $validators = [], $actualKeys = [])
133 2
    {
134 2
        //Validate every parameters in the validators array
135 14
        foreach ($validators as $key => $validator) {
136
            $actualKeys[] = $key;
137
            $param = $this->getNestedParam($params, $actualKeys);
138
            if (is_array($validator)) {
139
                $this->validate($params, $validator, $actualKeys);
140 24
            } else {
141 25
                try {
142 25
                    $validator->assert($param);
143
                } catch (NestedValidationException $exception) {
144
                    if ($this->translator) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->translator of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
145
                        $this->translator = $exception->getMessages($this->translator);
146
                    }
147
                    if ($this->options['useTemplate']) {
148
                        $this->errors[implode('.', $actualKeys)] = [$exception->getFullMessage()];
149
                    } else {
150
                        $this->errors[implode('.', $actualKeys)] = $exception->getMessages();
151
                    }
152 24
                }
153
            }
154 24
155 22
            //Remove the key added in this foreach
156
            array_pop($actualKeys);
157 24
        }
158 24
    }
159 22
160 22
    /**
161
     * Get the nested parameter value.
162 22
     *
163 14
     * @param array $params An array that represents the values of the parameters.
164 3
     * @param array $keys   An array that represents the tree of keys to use.
165
     *
166
     * @return mixed The nested parameter value by the given params and tree of keys.
167
     */
168
    private function getNestedParam($params = [], $keys = [])
169
    {
170
        if (empty($keys)) {
171
            return $params;
172
        } else {
173
            $firstKey = array_shift($keys);
174
            if ($this->isArrayLike($params) && array_key_exists($firstKey, $params)) {
175
                $params = (array) $params;
176 24
                $paramValue = $params[$firstKey];
177
178 24
                return $this->getNestedParam($paramValue, $keys);
179
            } else {
180
                return;
181
            }
182
        }
183
    }
184
185
    /**
186 25
     * Check if the given $params is an array like variable.
187
     *
188 25
     * @param array $params The variable to check.
189
     *
190
     * @return bool Returns true if the given $params parameter is array like.
191
     */
192
    private function isArrayLike($params)
193
    {
194
        return is_array($params) || $params instanceof \SimpleXMLElement;
195
    }
196 25
197
    /**
198 25
     * Check if there are any errors.
199
     *
200
     * @return bool
201
     */
202
    public function hasErrors()
203
    {
204
        return !empty($this->errors);
205
    }
206 25
207
    /**
208 25
     * Get errors.
209
     *
210
     * @return array The errors array.
211
     */
212
    public function getErrors()
213
    {
214
        return $this->errors;
215
    }
216 1
217
    /**
218 1
     * Get validators.
219 1
     *
220
     * @return array The validators array.
221
     */
222
    public function getValidators()
223
    {
224
        return $this->validators;
225
    }
226 25
227
    /**
228 25
     * Set validators.
229
     *
230
     * @param array $validators The validators array.
231
     */
232
    public function setValidators($validators)
233
    {
234
        $this->validators = $validators;
235
    }
236 1
237
    /**
238 1
     * Get translator.
239 1
     *
240
     * @return callable The translator.
241
     */
242
    public function getTranslator()
243
    {
244
        return $this->translator;
245
    }
246
247
    /**
248
     * Set translator.
249
     *
250
     * @param callable $translator The translator.
251
     */
252
    public function setTranslator($translator)
253
    {
254
        $this->translator = $translator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $translator of type callable is incompatible with the declared type array of property $translator.

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...
255
    }
256
}
257