Completed
Pull Request — master (#56)
by
unknown
01:34
created

Validation::validate()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.0113

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 12
cts 13
cp 0.9231
rs 9.2408
c 0
b 0
f 0
cc 5
nc 5
nop 3
crap 5.0113
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
30
    /**
31
     * The translator to use for the exception message.
32
     *
33
     * @var array
34
     */
35
    protected $translator = null;
36
37
    /**
38
     * Errors from the validation.
39
     *
40
     * @var array
41
     */
42
    protected $errors = [];
43
44
    /**
45
     * The 'errors' attribute name.
46
     *
47
     * @var string
48
     */
49
    protected $errors_name = 'errors';
50
51
    /**
52
     * The 'has_error' attribute name.
53
     *
54
     * @var string
55
     */
56
    protected $has_errors_name = 'has_errors';
57
58
    /**
59
     * The 'validators' attribute name.
60
     *
61
     * @var string
62
     */
63
    protected $validators_name = 'validators';
64
65
    /**
66
     * The 'translator' attribute name.
67
     *
68
     * @var string
69
     */
70
    protected $translator_name = 'translator';
71
72
    /**
73
     * Create new Validator service provider.
74
     *
75
     * @param null|array|ArrayAccess $validators
76
     * @param null|array             $translator
77
     * @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...
78
     */
79 2
    public function __construct($validators = null, $translator = null, $options = [])
80
    {
81
        // Set the validators
82 2
        if (is_array($validators) || $validators instanceof \ArrayAccess) {
83 2
            $this->validators = $validators;
84
        } elseif (is_null($validators)) {
85
            $this->validators = [];
86
        }
87 2
        $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...
88 2
        $this->options = array_merge($this->options, $options);
89 2
    }
90
91
    /**
92
     * Validation middleware invokable class.
93
     *
94
     * @param \Psr\Http\Message\ServerRequestInterface $request  PSR7 request
95
     * @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...
96
     *
97
     * @return \Psr\Http\Message\ResponseInterface
98
     */
99 2
    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
100
    {
101 2
        $this->errors = [];
102 2
        $params = $request->getParsedBody();
103
104 2
        $routeContext = RouteContext::fromRequest($request);
105 2
        $route = $routeContext->getRoute();
106 2
        $arguments = $route->getArguments();
107
108 2
        $queryParams = $request->getQueryParams();
109
110 2
        $params = array_merge((array) $arguments, (array) $params, (array) $queryParams);
111
112 2
        $this->validate($params, $this->validators);
113
114 2
        $request = $request->withAttribute($this->errors_name, $this->getErrors());
115 2
        $request = $request->withAttribute($this->has_errors_name, $this->hasErrors());
116 2
        $request = $request->withAttribute($this->validators_name, $this->getValidators());
117 2
        $request = $request->withAttribute($this->translator_name, $this->getTranslator());
118
119 2
        return $handler->handle($request);
120
    }
121
122
    /**
123
     * Validate the parameters by the given params, validators and actual keys.
124
     * This method populates the $errors attribute.
125
     *
126
     * @param array $params     The array of parameters.
127
     * @param array $validators The array of validators.
128
     * @param array $actualKeys An array that will save all the keys of the tree to retrieve the correct value.
129
     */
130 2
    private function validate($params = [], $validators = [], $actualKeys = [])
131
    {
132
        //Validate every parameters in the validators array
133 2
        foreach ($validators as $key => $validator) {
134 2
            $actualKeys[] = $key;
135 2
            $param = $this->getNestedParam($params, $actualKeys);
136 2
            if (is_array($validator)) {
137
                $this->validate($params, $validator, $actualKeys);
138
            } else {
139
                try {
140 2
                    $validator->assert($param);
141 2
                } catch (NestedValidationException $exception) {
142 2
                    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...
143 1
                        $this->translator = $exception->getMessages($this->translator);
144
                    }
145 2
                    $this->errors[implode('.', $actualKeys)] = $exception->getMessages();
146
                }
147
            }
148
149
            //Remove the key added in this foreach
150 2
            array_pop($actualKeys);
151
        }
152 2
    }
153
154
    /**
155
     * Get the nested parameter value.
156
     *
157
     * @param array $params An array that represents the values of the parameters.
158
     * @param array $keys   An array that represents the tree of keys to use.
159
     *
160
     * @return mixed The nested parameter value by the given params and tree of keys.
161
     */
162 2
    private function getNestedParam($params = [], $keys = [])
163
    {
164 2
        if (empty($keys)) {
165 2
            return $params;
166
        } else {
167 2
            $firstKey = array_shift($keys);
168 2
            if ($this->isArrayLike($params) && array_key_exists($firstKey, $params)) {
169 2
                $params = (array) $params;
170 2
                $paramValue = $params[$firstKey];
171
172 2
                return $this->getNestedParam($paramValue, $keys);
173
            } else {
174
                return;
175
            }
176
        }
177
    }
178
179
    /**
180
     * Check if the given $params is an array like variable.
181
     *
182
     * @param array $params The variable to check.
183
     *
184
     * @return bool Returns true if the given $params parameter is array like.
185
     */
186 2
    private function isArrayLike($params)
187
    {
188 2
        return is_array($params) || $params instanceof \SimpleXMLElement;
189
    }
190
191
    /**
192
     * Check if there are any errors.
193
     *
194
     * @return bool
195
     */
196 2
    public function hasErrors()
197
    {
198 2
        return !empty($this->errors);
199
    }
200
201
    /**
202
     * Get errors.
203
     *
204
     * @return array The errors array.
205
     */
206 2
    public function getErrors()
207
    {
208 2
        return $this->errors;
209
    }
210
211
    /**
212
     * Get validators.
213
     *
214
     * @return array The validators array.
215
     */
216 2
    public function getValidators()
217
    {
218 2
        return $this->validators;
219
    }
220
221
    /**
222
     * Set validators.
223
     *
224
     * @param array $validators The validators array.
225
     */
226 1
    public function setValidators($validators)
227
    {
228 1
        $this->validators = $validators;
229 1
    }
230
231
    /**
232
     * Get translator.
233
     *
234
     * @return callable The translator.
235
     */
236 2
    public function getTranslator()
237
    {
238 2
        return $this->translator;
239
    }
240
241
    /**
242
     * Set translator.
243
     *
244
     * @param callable $translator The translator.
245
     */
246 1
    public function setTranslator($translator)
247
    {
248 1
        $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...
249 1
    }
250
}
251