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

Validation::validate()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6.0852

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 13
cts 15
cp 0.8667
rs 8.8657
c 0
b 0
f 0
cc 6
nc 7
nop 3
crap 6.0852
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
     *
77
     * @param null|array|ArrayAccess $validators
78
     * @param null|array             $translator
79
     * @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
     */
81 2
    public function __construct($validators = null, $translator = null, $options = [])
82
    {
83
        // Set the validators
84 2
        if (is_array($validators) || $validators instanceof \ArrayAccess) {
85 2
            $this->validators = $validators;
86
        } elseif (is_null($validators)) {
87
            $this->validators = [];
88
        }
89 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...
90 2
        $this->options = array_merge($this->options, $options);
91 2
    }
92
93
    /**
94
     * Validation middleware invokable class.
95
     *
96
     * @param \Psr\Http\Message\ServerRequestInterface $request  PSR7 request
97
     * @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
     * @return \Psr\Http\Message\ResponseInterface
100
     */
101 2
    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
102
    {
103 2
        $this->errors = [];
104 2
        $params = $request->getParsedBody();
105
106 2
        $routeContext = RouteContext::fromRequest($request);
107 2
        $route = $routeContext->getRoute();
108 2
        $arguments = $route->getArguments();
109
110 2
        $queryParams = $request->getQueryParams();
111
112 2
        $params = array_merge((array) $arguments, (array) $params, (array) $queryParams);
113
114 2
        $this->validate($params, $this->validators);
115
116 2
        $request = $request->withAttribute($this->errors_name, $this->getErrors());
117 2
        $request = $request->withAttribute($this->has_errors_name, $this->hasErrors());
118 2
        $request = $request->withAttribute($this->validators_name, $this->getValidators());
119 2
        $request = $request->withAttribute($this->translator_name, $this->getTranslator());
120
121 2
        return $handler->handle($request);
122
    }
123
124
    /**
125
     * Validate the parameters by the given params, validators and actual keys.
126
     * This method populates the $errors attribute.
127
     *
128
     * @param array $params     The array of parameters.
129
     * @param array $validators The array of validators.
130
     * @param array $actualKeys An array that will save all the keys of the tree to retrieve the correct value.
131
     */
132 2
    private function validate($params = [], $validators = [], $actualKeys = [])
133
    {
134
        //Validate every parameters in the validators array
135 2
        foreach ($validators as $key => $validator) {
136 2
            $actualKeys[] = $key;
137 2
            $param = $this->getNestedParam($params, $actualKeys);
138 2
            if (is_array($validator)) {
139
                $this->validate($params, $validator, $actualKeys);
140
            } else {
141
                try {
142 2
                    $validator->assert($param);
143 2
                } catch (NestedValidationException $exception) {
144 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...
145 1
                        $this->translator = $exception->getMessages($this->translator);
146
                    }
147 2
                    if ($this->options['useTemplate']) {
148
                        $this->errors[implode('.', $actualKeys)] = [$exception->getFullMessage()];
149
                    } else {
150 2
                        $this->errors[implode('.', $actualKeys)] = $exception->getMessages();
151
                    }
152
                }
153
            }
154
155
            //Remove the key added in this foreach
156 2
            array_pop($actualKeys);
157
        }
158 2
    }
159
160
    /**
161
     * Get the nested parameter value.
162
     *
163
     * @param array $params An array that represents the values of the parameters.
164
     * @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 2
    private function getNestedParam($params = [], $keys = [])
169
    {
170 2
        if (empty($keys)) {
171 2
            return $params;
172
        } else {
173 2
            $firstKey = array_shift($keys);
174 2
            if ($this->isArrayLike($params) && array_key_exists($firstKey, $params)) {
175 2
                $params = (array) $params;
176 2
                $paramValue = $params[$firstKey];
177
178 2
                return $this->getNestedParam($paramValue, $keys);
179
            } else {
180
                return;
181
            }
182
        }
183
    }
184
185
    /**
186
     * Check if the given $params is an array like variable.
187
     *
188
     * @param array $params The variable to check.
189
     *
190
     * @return bool Returns true if the given $params parameter is array like.
191
     */
192 2
    private function isArrayLike($params)
193
    {
194 2
        return is_array($params) || $params instanceof \SimpleXMLElement;
195
    }
196
197
    /**
198
     * Check if there are any errors.
199
     *
200
     * @return bool
201
     */
202 2
    public function hasErrors()
203
    {
204 2
        return !empty($this->errors);
205
    }
206
207
    /**
208
     * Get errors.
209
     *
210
     * @return array The errors array.
211
     */
212 2
    public function getErrors()
213
    {
214 2
        return $this->errors;
215
    }
216
217
    /**
218
     * Get validators.
219
     *
220
     * @return array The validators array.
221
     */
222 2
    public function getValidators()
223
    {
224 2
        return $this->validators;
225
    }
226
227
    /**
228
     * Set validators.
229
     *
230
     * @param array $validators The validators array.
231
     */
232 1
    public function setValidators($validators)
233
    {
234 1
        $this->validators = $validators;
235 1
    }
236
237
    /**
238
     * Get translator.
239
     *
240
     * @return callable The translator.
241
     */
242 2
    public function getTranslator()
243
    {
244 2
        return $this->translator;
245
    }
246
247
    /**
248
     * Set translator.
249
     *
250
     * @param callable $translator The translator.
251
     */
252 1
    public function setTranslator($translator)
253
    {
254 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...
255 1
    }
256
}
257