This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Created by PhpStorm. |
||
4 | * User: harry |
||
5 | * Date: 2/14/18 |
||
6 | * Time: 11:58 AM |
||
7 | */ |
||
8 | |||
9 | namespace PhpRestfulApiResponse; |
||
10 | |||
11 | use League\Fractal\Manager; |
||
12 | use League\Fractal\Pagination\Cursor; |
||
13 | use League\Fractal\Resource\Collection; |
||
14 | use League\Fractal\Resource\Item; |
||
15 | use League\Fractal\TransformerAbstract; |
||
16 | use PhpRestfulApiResponse\Contracts\PhpRestfulApiResponse; |
||
17 | use Zend\Diactoros\MessageTrait; |
||
18 | use InvalidArgumentException; |
||
19 | use Zend\Diactoros\Response\InjectContentTypeTrait; |
||
20 | use Zend\Diactoros\Response\JsonResponse; |
||
21 | |||
22 | class Response implements PhpRestfulApiResponse |
||
23 | { |
||
24 | use MessageTrait, InjectContentTypeTrait; |
||
25 | |||
26 | const MIN_STATUS_CODE_VALUE = 100; |
||
27 | const MAX_STATUS_CODE_VALUE = 599; |
||
28 | |||
29 | /** |
||
30 | * Map of standard HTTP status code/reason phrases |
||
31 | * |
||
32 | * @var array |
||
33 | */ |
||
34 | protected $phrases = [ |
||
35 | // INFORMATIONAL CODES |
||
36 | 100 => 'Continue', |
||
37 | 101 => 'Switching Protocols', |
||
38 | 102 => 'Processing', |
||
39 | 103 => 'Early Hints', |
||
40 | // SUCCESS CODES |
||
41 | 200 => 'OK', |
||
42 | 201 => 'Created', |
||
43 | 202 => 'Accepted', |
||
44 | 203 => 'Non-Authoritative Information', |
||
45 | 204 => 'No Content', |
||
46 | 205 => 'Reset Content', |
||
47 | 206 => 'Partial Content', |
||
48 | 207 => 'Multi-Status', |
||
49 | 208 => 'Already Reported', |
||
50 | 226 => 'IM Used', |
||
51 | // REDIRECTION CODES |
||
52 | 300 => 'Multiple Choices', |
||
53 | 301 => 'Moved Permanently', |
||
54 | 302 => 'Found', |
||
55 | 303 => 'See Other', |
||
56 | 304 => 'Not Modified', |
||
57 | 305 => 'Use Proxy', |
||
58 | 306 => 'Switch Proxy', // Deprecated to 306 => '(Unused)' |
||
59 | 307 => 'Temporary Redirect', |
||
60 | 308 => 'Permanent Redirect', |
||
61 | // CLIENT ERROR |
||
62 | 400 => 'Bad Request', |
||
63 | 401 => 'Unauthorized', |
||
64 | 402 => 'Payment Required', |
||
65 | 403 => 'Forbidden', |
||
66 | 404 => 'Not Found', |
||
67 | 405 => 'Method Not Allowed', |
||
68 | 406 => 'Not Acceptable', |
||
69 | 407 => 'Proxy Authentication Required', |
||
70 | 408 => 'Request Timeout', |
||
71 | 409 => 'Conflict', |
||
72 | 410 => 'Gone', |
||
73 | 411 => 'Length Required', |
||
74 | 412 => 'Precondition Failed', |
||
75 | 413 => 'Payload Too Large', |
||
76 | 414 => 'URI Too Long', |
||
77 | 415 => 'Unsupported Media Type', |
||
78 | 416 => 'Range Not Satisfiable', |
||
79 | 417 => 'Expectation Failed', |
||
80 | 418 => 'I\'m a teapot', |
||
81 | 421 => 'Misdirected Request', |
||
82 | 422 => 'Unprocessable Entity', |
||
83 | 423 => 'Locked', |
||
84 | 424 => 'Failed Dependency', |
||
85 | 425 => 'Unordered Collection', |
||
86 | 426 => 'Upgrade Required', |
||
87 | 428 => 'Precondition Required', |
||
88 | 429 => 'Too Many Requests', |
||
89 | 431 => 'Request Header Fields Too Large', |
||
90 | 444 => 'Connection Closed Without Response', |
||
91 | 451 => 'Unavailable For Legal Reasons', |
||
92 | // SERVER ERROR |
||
93 | 499 => 'Client Closed Request', |
||
94 | 500 => 'Internal Server Error', |
||
95 | 501 => 'Not Implemented', |
||
96 | 502 => 'Bad Gateway', |
||
97 | 503 => 'Service Unavailable', |
||
98 | 504 => 'Gateway Timeout', |
||
99 | 505 => 'HTTP Version Not Supported', |
||
100 | 506 => 'Variant Also Negotiates', |
||
101 | 507 => 'Insufficient Storage', |
||
102 | 508 => 'Loop Detected', |
||
103 | 510 => 'Not Extended', |
||
104 | 511 => 'Network Authentication Required', |
||
105 | 599 => 'Network Connect Timeout Error', |
||
106 | ]; |
||
107 | |||
108 | /** |
||
109 | * @var string |
||
110 | */ |
||
111 | protected $reasonPhrase = ''; |
||
112 | |||
113 | /** |
||
114 | * @var int |
||
115 | */ |
||
116 | protected $statusCode; |
||
117 | |||
118 | /** |
||
119 | * @var int|string |
||
120 | */ |
||
121 | protected $errorCode; |
||
122 | |||
123 | /** |
||
124 | * Response constructor. |
||
125 | * @param string $body |
||
126 | * @param int $status |
||
127 | * @param int $errorCode |
||
128 | * @param array $headers |
||
129 | */ |
||
130 | 27 | public function __construct($body = 'php://memory', int $status = 200, $errorCode = null, array $headers = []) |
|
131 | { |
||
132 | 27 | $this->setStatusCode($status); |
|
133 | 27 | $this->setErrorCode($errorCode); |
|
134 | 27 | $this->stream = $this->getStream($body, 'wb+'); |
|
135 | 27 | $headers = $this->injectContentType('application/json', $headers); |
|
136 | 27 | $this->setHeaders($headers); |
|
137 | 27 | } |
|
138 | |||
139 | /** |
||
140 | * {@inheritdoc} |
||
141 | */ |
||
142 | 24 | public function getStatusCode() |
|
143 | { |
||
144 | 24 | return $this->statusCode; |
|
145 | } |
||
146 | |||
147 | /** |
||
148 | * {@inheritdoc} |
||
149 | */ |
||
150 | 20 | public function getReasonPhrase() |
|
151 | { |
||
152 | 20 | if (! $this->reasonPhrase |
|
153 | 20 | && isset($this->phrases[$this->statusCode]) |
|
154 | ) { |
||
155 | 20 | $this->reasonPhrase = $this->phrases[$this->statusCode]; |
|
156 | } |
||
157 | |||
158 | 20 | return $this->reasonPhrase; |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * {@inheritdoc} |
||
163 | */ |
||
164 | 1 | public function withStatus($code, $reasonPhrase = '') |
|
165 | { |
||
166 | 1 | $new = clone $this; |
|
167 | 1 | $new->setStatusCode($code); |
|
168 | 1 | $new->reasonPhrase = $reasonPhrase; |
|
169 | 1 | return $new; |
|
170 | } |
||
171 | |||
172 | /** |
||
173 | * @param array|null $data |
||
174 | * @param $code |
||
175 | * @param array $headers |
||
176 | * @return Response |
||
177 | */ |
||
178 | 3 | public function withArray($data, $code = 200, array $headers = []) |
|
179 | { |
||
180 | 3 | $new = clone $this; |
|
181 | 3 | $new->setStatusCode($code); |
|
182 | 3 | $new->getBody()->write($this->jsonEncode($data)); |
|
183 | 3 | $new->headers = array_merge($new->headers, $headers); |
|
184 | 3 | return $new; |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * @param $data |
||
189 | * @param TransformerAbstract|callable $transformer |
||
190 | * @param int $code |
||
191 | * @param null $resourceKey |
||
192 | * @param array $meta |
||
193 | * @param array $headers |
||
194 | * @return Response |
||
195 | */ |
||
196 | 1 | public function withItem($data, $transformer, $code = 200, $resourceKey = null, $meta = [], array $headers = []) |
|
197 | { |
||
198 | 1 | $resource = new Item($data, $transformer, $resourceKey); |
|
199 | |||
200 | 1 | foreach ($meta as $metaKey => $metaValue) { |
|
201 | $resource->setMetaValue($metaKey, $metaValue); |
||
202 | } |
||
203 | |||
204 | 1 | $manager = new Manager(); |
|
205 | |||
206 | 1 | $rootScope = $manager->createData($resource); |
|
207 | |||
208 | 1 | return $this->withArray($rootScope->toArray(), $code, $headers); |
|
0 ignored issues
–
show
|
|||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @param $data |
||
213 | * @param TransformerAbstract|callable $transformer |
||
214 | * @param int $code |
||
215 | * @param null $resourceKey |
||
216 | * @param Cursor|null $cursor |
||
217 | * @param array $meta |
||
218 | * @param array $headers |
||
219 | * @return Response |
||
220 | */ |
||
221 | 1 | public function withCollection($data, $transformer, $code = 200, $resourceKey = null, Cursor $cursor = null, $meta = [], array $headers = []) |
|
222 | { |
||
223 | 1 | $resource = new Collection($data, $transformer, $resourceKey); |
|
224 | |||
225 | 1 | foreach ($meta as $metaKey => $metaValue) { |
|
226 | $resource->setMetaValue($metaKey, $metaValue); |
||
227 | } |
||
228 | |||
229 | 1 | if (!is_null($cursor)) { |
|
230 | $resource->setCursor($cursor); |
||
231 | } |
||
232 | |||
233 | 1 | $manager = new Manager(); |
|
234 | |||
235 | 1 | $rootScope = $manager->createData($resource); |
|
236 | |||
237 | 1 | return $this->withArray($rootScope->toArray(), $code, $headers); |
|
0 ignored issues
–
show
The return type of
return $this->withArray(...ay(), $code, $headers); (PhpRestfulApiResponse\Response ) is incompatible with the return type declared by the interface PhpRestfulApiResponse\Co...esponse::withCollection of type Zend\Diactoros\Response .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
238 | } |
||
239 | |||
240 | /** |
||
241 | * Response for errors |
||
242 | * |
||
243 | * @param string|array $message |
||
244 | * @param int $statusCode |
||
245 | * @param int|string $errorCode |
||
246 | * @param array $headers |
||
247 | * @return mixed |
||
248 | */ |
||
249 | 19 | public function withError($message, int $statusCode, $errorCode = null, array $headers = []) |
|
250 | { |
||
251 | 19 | $new = clone $this; |
|
252 | 19 | $new->setStatusCode($statusCode); |
|
253 | 19 | $new->setErrorCode($errorCode); |
|
254 | 19 | $new->getBody()->write( |
|
255 | 19 | $this->jsonEncode( |
|
256 | [ |
||
257 | 19 | 'error' => array_filter([ |
|
258 | 19 | 'http_code' => $new->statusCode, |
|
259 | 19 | 'code' => $errorCode, |
|
260 | 19 | 'phrase' => $new->getReasonPhrase(), |
|
261 | 19 | 'message' => $message |
|
262 | ]) |
||
263 | ] |
||
264 | ) |
||
265 | ); |
||
266 | 19 | $new->headers = array_merge($new->headers, $headers); |
|
267 | 19 | return $new; |
|
268 | } |
||
269 | |||
270 | /** |
||
271 | * Generates a response with a 403 HTTP header and a given message. |
||
272 | * |
||
273 | * @param string $message |
||
274 | * @param int|string $errorCode |
||
275 | * @param array $headers |
||
276 | * @return mixed |
||
277 | */ |
||
278 | 2 | public function errorForbidden(string $message = '', $errorCode = null, array $headers = []) |
|
279 | { |
||
280 | 2 | return $this->withError($message, 403, $errorCode, $headers); |
|
281 | } |
||
282 | |||
283 | /** |
||
284 | * Generates a response with a 500 HTTP header and a given message. |
||
285 | * |
||
286 | * @param string $message |
||
287 | * @param int|string $errorCode |
||
288 | * @param array $headers |
||
289 | * @return mixed |
||
290 | */ |
||
291 | 2 | public function errorInternalError(string $message = '', $errorCode = null, array $headers = []) |
|
292 | { |
||
293 | 2 | return $this->withError($message, 500, $errorCode, $headers); |
|
294 | } |
||
295 | |||
296 | /** |
||
297 | * Generates a response with a 404 HTTP header and a given message. |
||
298 | * |
||
299 | * @param string $message |
||
300 | * @param int|string $errorCode |
||
301 | * @param array $headers |
||
302 | * @return mixed |
||
303 | */ |
||
304 | 2 | public function errorNotFound(string $message = '', $errorCode = null, array $headers = []) |
|
305 | { |
||
306 | 2 | return $this->withError($message, 404, $errorCode, $headers); |
|
307 | } |
||
308 | |||
309 | /** |
||
310 | * Generates a response with a 401 HTTP header and a given message. |
||
311 | * |
||
312 | * @param string $message |
||
313 | * @param int|string $errorCode |
||
314 | * @param array $headers |
||
315 | * @return mixed |
||
316 | */ |
||
317 | 2 | public function errorUnauthorized(string $message = '', $errorCode = null, array $headers = []) |
|
318 | { |
||
319 | 2 | return $this->withError($message, 401, $errorCode, $headers); |
|
320 | } |
||
321 | |||
322 | /** |
||
323 | * Generates a response with a 400 HTTP header and a given message. |
||
324 | * |
||
325 | * @param array $message |
||
326 | * @param int|array $errorCode |
||
327 | * @param array $headers |
||
328 | * @return mixed |
||
329 | */ |
||
330 | 1 | public function errorWrongArgs(array $message, $errorCode = null, array $headers = []) |
|
331 | { |
||
332 | 1 | return $this->withError($message, 400, $errorCode, $headers); |
|
0 ignored issues
–
show
It seems like
$errorCode defined by parameter $errorCode on line 330 can also be of type array ; however, PhpRestfulApiResponse\Response::withError() does only seem to accept integer|string|null , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble. ![]() |
|||
333 | } |
||
334 | |||
335 | /** |
||
336 | * Generates a response with a 410 HTTP header and a given message. |
||
337 | * |
||
338 | * @param string $message |
||
339 | * @param int|string $errorCode |
||
340 | * @param array $headers |
||
341 | * @return mixed |
||
342 | */ |
||
343 | 2 | public function errorGone(string $message = '', $errorCode = null, array $headers = []) |
|
344 | { |
||
345 | 2 | return $this->withError($message, 410, $errorCode, $headers); |
|
346 | } |
||
347 | |||
348 | /** |
||
349 | * Generates a response with a 405 HTTP header and a given message. |
||
350 | * |
||
351 | * @param string $message |
||
352 | * @param int|string $errorCode |
||
353 | * @param array $headers |
||
354 | * @return mixed |
||
355 | */ |
||
356 | 2 | public function errorMethodNotAllowed(string $message = '', $errorCode = null, array $headers = []) |
|
357 | { |
||
358 | 2 | return $this->withError($message, 405, $errorCode, $headers); |
|
359 | } |
||
360 | |||
361 | /** |
||
362 | * Generates a Response with a 431 HTTP header and a given message. |
||
363 | * |
||
364 | * @param string $message |
||
365 | * @param int|string $errorCode |
||
366 | * @param array $headers |
||
367 | * @return mixed |
||
368 | */ |
||
369 | 2 | public function errorUnwillingToProcess(string $message = '', $errorCode = null, array $headers = []) |
|
370 | { |
||
371 | 2 | return $this->withError($message, 431, $errorCode, $headers); |
|
372 | } |
||
373 | |||
374 | /** |
||
375 | * Generates a Response with a 422 HTTP header and a given message. |
||
376 | * |
||
377 | * @param string $message |
||
378 | * @param int|string $errorCode |
||
379 | * @param array $headers |
||
380 | * @return mixed |
||
381 | */ |
||
382 | 2 | public function errorUnprocessable(string $message = '', $errorCode = null, array $headers = []) |
|
383 | { |
||
384 | 2 | return $this->withError($message, 422, $errorCode, $headers); |
|
385 | } |
||
386 | |||
387 | /** |
||
388 | * @return int|string |
||
389 | */ |
||
390 | 20 | public function getErrorCode() |
|
391 | { |
||
392 | 20 | return $this->errorCode; |
|
393 | } |
||
394 | |||
395 | /** |
||
396 | * @param $errorCode |
||
397 | * @return $this |
||
398 | */ |
||
399 | 27 | public function setErrorCode($errorCode) |
|
400 | { |
||
401 | 27 | $this->errorCode = $errorCode; |
|
402 | 27 | } |
|
403 | |||
404 | /** |
||
405 | * Set a valid status code. |
||
406 | * |
||
407 | * @param int $statusCode |
||
408 | * @throws InvalidArgumentException on an invalid status code. |
||
409 | */ |
||
410 | 27 | protected function setStatusCode(int $statusCode) |
|
411 | { |
||
412 | 27 | if ($statusCode < static::MIN_STATUS_CODE_VALUE |
|
413 | 27 | || $statusCode > static::MAX_STATUS_CODE_VALUE |
|
414 | ) { |
||
415 | 2 | throw new InvalidArgumentException(sprintf( |
|
416 | 2 | 'Invalid status code "%s"; must be an integer between %d and %d, inclusive', |
|
417 | 2 | (is_scalar($statusCode) ? $statusCode : gettype($statusCode)), |
|
418 | 2 | static::MIN_STATUS_CODE_VALUE, |
|
419 | 2 | static::MAX_STATUS_CODE_VALUE |
|
420 | )); |
||
421 | } |
||
422 | 27 | $this->statusCode = $statusCode; |
|
423 | 27 | } |
|
424 | |||
425 | /** |
||
426 | * Encode the provided data to JSON. |
||
427 | * |
||
428 | * @param mixed $data |
||
429 | * @return string |
||
430 | * @throws InvalidArgumentException if unable to encode the $data to JSON. |
||
431 | */ |
||
432 | 22 | protected function jsonEncode($data) |
|
433 | { |
||
434 | 22 | if (is_resource($data)) { |
|
435 | throw new InvalidArgumentException('Cannot JSON encode resources'); |
||
436 | } |
||
437 | |||
438 | // Clear json_last_error() |
||
439 | 22 | json_encode(null); |
|
440 | |||
441 | 22 | $json = json_encode($data, JsonResponse::DEFAULT_JSON_FLAGS); |
|
442 | |||
443 | 22 | if (JSON_ERROR_NONE !== json_last_error()) { |
|
444 | throw new InvalidArgumentException(sprintf( |
||
445 | 'Unable to encode data to JSON in %s: %s', |
||
446 | __CLASS__, |
||
447 | json_last_error_msg() |
||
448 | )); |
||
449 | } |
||
450 | |||
451 | 22 | return $json; |
|
452 | } |
||
453 | } |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.