GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — 3.x (#2037)
by Chad
02:13
created

App::head()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
/**
3
 * Slim Framework (http://slimframework.com)
4
 *
5
 * @link      https://github.com/slimphp/Slim
6
 * @copyright Copyright (c) 2011-2016 Josh Lockhart
7
 * @license   https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
8
 */
9
namespace Slim;
10
11
use Exception;
12
use Throwable;
13
use Closure;
14
use InvalidArgumentException;
15
use Psr\Http\Message\RequestInterface;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Interop\Container\ContainerInterface;
19
use FastRoute\Dispatcher;
20
use Slim\Exception\SlimException;
21
use Slim\Exception\MethodNotAllowedException;
22
use Slim\Exception\NotFoundException;
23
use Slim\Http\Uri;
24
use Slim\Http\Headers;
25
use Slim\Http\Body;
26
use Slim\Http\Request;
27
use Slim\Interfaces\Http\EnvironmentInterface;
28
use Slim\Interfaces\RouteGroupInterface;
29
use Slim\Interfaces\RouteInterface;
30
use Slim\Interfaces\RouterInterface;
31
32
/**
33
 * App
34
 *
35
 * This is the primary class with which you instantiate,
36
 * configure, and run a Slim Framework application.
37
 * The \Slim\App class also accepts Slim Framework middleware.
38
 *
39
 * @property-read callable $errorHandler
40
 * @property-read callable $phpErrorHandler
41
 * @property-read callable $notFoundHandler function($request, $response)
42
 * @property-read callable $notAllowedHandler function($request, $response, $allowedHttpMethods)
43
 */
44
class App
45
{
46
    use MiddlewareAwareTrait;
47
48
    /**
49
     * Current version
50
     *
51
     * @var string
52
     */
53
    const VERSION = '3.6.0-dev';
54
55
    /**
56
     * Container
57
     *
58
     * @var ContainerInterface
59
     */
60
    private $container;
61
62
    /********************************************************************************
63
     * Constructor
64
     *******************************************************************************/
65
66
    /**
67
     * Create new application
68
     *
69
     * @param ContainerInterface|array $container Either a ContainerInterface or an associative array of app settings
70
     * @throws InvalidArgumentException when no container is provided that implements ContainerInterface
71
     */
72
    public function __construct($container = [])
73
    {
74
        if (is_array($container)) {
75
            $container = new Container($container);
76
        }
77
        if (!$container instanceof ContainerInterface) {
78
            throw new InvalidArgumentException('Expected a ContainerInterface');
79
        }
80
        $this->container = $container;
81
    }
82
83
    /**
84
     * Enable access to the DI container by consumers of $app
85
     *
86
     * @return ContainerInterface
87
     */
88
    public function getContainer()
89
    {
90
        return $this->container;
91
    }
92
93
    /**
94
     * Add middleware
95
     *
96
     * This method prepends new middleware to the app's middleware stack.
97
     *
98
     * @param  callable|string    $callable The callback routine
99
     *
100
     * @return static
101
     */
102
    public function add($callable)
103
    {
104
        return $this->addMiddleware(new DeferredCallable($callable, $this->container));
105
    }
106
107
    /**
108
     * Calling a non-existant method on App checks to see if there's an item
109
     * in the container that is callable and if so, calls it.
110
     *
111
     * @param  string $method
112
     * @param  array $args
113
     * @return mixed
114
     */
115
    public function __call($method, $args)
116
    {
117
        if ($this->container->has($method)) {
118
            $obj = $this->container->get($method);
119
            if (is_callable($obj)) {
120
                return call_user_func_array($obj, $args);
121
            }
122
        }
123
124
        throw new \BadMethodCallException("Method $method is not a valid method");
125
    }
126
127
    /********************************************************************************
128
     * Router proxy methods
129
     *******************************************************************************/
130
131
    /**
132
     * Add GET route
133
     *
134
     * @param  string $pattern  The route URI pattern
135
     * @param  callable|string  $callable The route callback routine
136
     *
137
     * @return \Slim\Interfaces\RouteInterface
138
     */
139
    public function get($pattern, $callable)
140
    {
141
        return $this->map(['GET'], $pattern, $callable);
142
    }
143
144
    /**
145
     * Add HEAD route
146
     *
147
     * @param string $pattern  The route URI pattern
148
     * @param callable|string  $callable The route callback routine
149
     *
150
     * @return \Slim\Interfaces\RouteInterface
151
     */
152
    public function head($pattern, $callable)
153
    {
154
        return $this->map(['HEAD'], $pattern, $callable);
155
    }
156
157
    /**
158
     * Add POST route
159
     *
160
     * @param  string $pattern  The route URI pattern
161
     * @param  callable|string  $callable The route callback routine
162
     *
163
     * @return \Slim\Interfaces\RouteInterface
164
     */
165
    public function post($pattern, $callable)
166
    {
167
        return $this->map(['POST'], $pattern, $callable);
168
    }
169
170
    /**
171
     * Add PUT route
172
     *
173
     * @param  string $pattern  The route URI pattern
174
     * @param  callable|string  $callable The route callback routine
175
     *
176
     * @return \Slim\Interfaces\RouteInterface
177
     */
178
    public function put($pattern, $callable)
179
    {
180
        return $this->map(['PUT'], $pattern, $callable);
181
    }
182
183
    /**
184
     * Add PATCH route
185
     *
186
     * @param  string $pattern  The route URI pattern
187
     * @param  callable|string  $callable The route callback routine
188
     *
189
     * @return \Slim\Interfaces\RouteInterface
190
     */
191
    public function patch($pattern, $callable)
192
    {
193
        return $this->map(['PATCH'], $pattern, $callable);
194
    }
195
196
    /**
197
     * Add DELETE route
198
     *
199
     * @param  string $pattern  The route URI pattern
200
     * @param  callable|string  $callable The route callback routine
201
     *
202
     * @return \Slim\Interfaces\RouteInterface
203
     */
204
    public function delete($pattern, $callable)
205
    {
206
        return $this->map(['DELETE'], $pattern, $callable);
207
    }
208
209
    /**
210
     * Add OPTIONS route
211
     *
212
     * @param  string $pattern  The route URI pattern
213
     * @param  callable|string  $callable The route callback routine
214
     *
215
     * @return \Slim\Interfaces\RouteInterface
216
     */
217
    public function options($pattern, $callable)
218
    {
219
        return $this->map(['OPTIONS'], $pattern, $callable);
220
    }
221
222
    /**
223
     * Add route for any HTTP method
224
     *
225
     * @param  string $pattern  The route URI pattern
226
     * @param  callable|string  $callable The route callback routine
227
     *
228
     * @return \Slim\Interfaces\RouteInterface
229
     */
230
    public function any($pattern, $callable)
231
    {
232
        return $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $pattern, $callable);
233
    }
234
235
    /**
236
     * Add route with multiple methods
237
     *
238
     * @param  string[] $methods  Numeric array of HTTP method names
239
     * @param  string   $pattern  The route URI pattern
240
     * @param  callable|string    $callable The route callback routine
241
     *
242
     * @return RouteInterface
243
     */
244
    public function map(array $methods, $pattern, $callable)
245
    {
246
        if ($callable instanceof Closure) {
247
            $callable = $callable->bindTo($this->container);
248
        }
249
250
        $route = $this->container->get('router')->map($methods, $pattern, $callable);
251
        if (is_callable([$route, 'setContainer'])) {
252
            $route->setContainer($this->container);
253
        }
254
255
        if (is_callable([$route, 'setOutputBuffering'])) {
256
            $route->setOutputBuffering($this->container->get('settings')['outputBuffering']);
257
        }
258
259
        return $route;
260
    }
261
262
    /**
263
     * Route Groups
264
     *
265
     * This method accepts a route pattern and a callback. All route
266
     * declarations in the callback will be prepended by the group(s)
267
     * that it is in.
268
     *
269
     * @param string   $pattern
270
     * @param callable $callable
271
     *
272
     * @return RouteGroupInterface
273
     */
274
    public function group($pattern, $callable)
275
    {
276
        /** @var RouteGroup $group */
277
        $group = $this->container->get('router')->pushGroup($pattern, $callable);
278
        $group->setContainer($this->container);
279
        $group($this);
280
        $this->container->get('router')->popGroup();
281
        return $group;
282
    }
283
284
    /********************************************************************************
285
     * Runner
286
     *******************************************************************************/
287
288
    /**
289
     * Run application
290
     *
291
     * This method traverses the application middleware stack and then sends the
292
     * resultant Response object to the HTTP client.
293
     *
294
     * @param bool|false $silent
295
     * @return ResponseInterface
296
     *
297
     * @throws Exception
298
     * @throws MethodNotAllowedException
299
     * @throws NotFoundException
300
     */
301
    public function run($silent = false)
302
    {
303
        $request = $this->container->get('request');
304
        $response = $this->container->get('response');
305
306
        $response = $this->process($request, $response);
307
308
        if (!$silent) {
309
            $this->respond($response);
310
        }
311
312
        return $response;
313
    }
314
315
    /**
316
     * Process a request
317
     *
318
     * This method traverses the application middleware stack and then returns the
319
     * resultant Response object.
320
     *
321
     * @param ServerRequestInterface $request
322
     * @param ResponseInterface $response
323
     * @return ResponseInterface
324
     *
325
     * @throws Exception
326
     * @throws MethodNotAllowedException
327
     * @throws NotFoundException
328
     */
329
    public function process(ServerRequestInterface $request, ResponseInterface $response)
330
    {
331
        // Ensure basePath is set
332
        $router = $this->container->get('router');
333
        if (is_callable([$request->getUri(), 'getBasePath']) && is_callable([$router, 'setBasePath'])) {
334
            $router->setBasePath($request->getUri()->getBasePath());
335
        }
336
337
        // Dispatch the Router first if the setting for this is on
338
        if ($this->container->get('settings')['determineRouteBeforeAppMiddleware'] === true) {
339
            // Dispatch router (note: you won't be able to alter routes after this)
340
            $request = $this->dispatchRouterAndPrepareRoute($request, $router);
341
        }
342
343
        // Traverse middleware stack
344
        try {
345
            $response = $this->callMiddlewareStack($request, $response);
346
        } catch (Exception $e) {
347
            $response = $this->handleException($e, $request, $response);
348
        } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
349
            $response = $this->handlePhpError($e, $request, $response);
350
        }
351
352
        $response = $this->finalize($response);
353
354
        return $response;
355
    }
356
357
    /**
358
     * Send the response the client
359
     *
360
     * @param ResponseInterface $response
361
     */
362
    public function respond(ResponseInterface $response)
363
    {
364
        // Send response
365
        if (!headers_sent()) {
366
            // Status
367
            header(sprintf(
368
                'HTTP/%s %s %s',
369
                $response->getProtocolVersion(),
370
                $response->getStatusCode(),
371
                $response->getReasonPhrase()
372
            ));
373
374
            // Headers
375
            foreach ($response->getHeaders() as $name => $values) {
376
                foreach ($values as $value) {
377
                    header(sprintf('%s: %s', $name, $value), false);
378
                }
379
            }
380
        }
381
382
        // Body
383
        if (!$this->isEmptyResponse($response)) {
384
            $body = $response->getBody();
385
            if ($body->isSeekable()) {
386
                $body->rewind();
387
            }
388
            $settings       = $this->container->get('settings');
389
            $chunkSize      = $settings['responseChunkSize'];
390
391
            $contentLength  = $response->getHeaderLine('Content-Length');
392
            if (!$contentLength) {
393
                $contentLength = $body->getSize();
394
            }
395
396
397
            if (isset($contentLength)) {
398
                $amountToRead = $contentLength;
399
                while ($amountToRead > 0 && !$body->eof()) {
400
                    $data = $body->read(min($chunkSize, $amountToRead));
401
                    echo $data;
402
                    
403
                    $amountToRead -= strlen($data);
404
                                        
405
                    if (connection_status() != CONNECTION_NORMAL) {
406
                        break;
407
                    }
408
                }
409
            } else {
410
                while (!$body->eof()) {
411
                    echo $body->read($chunkSize);
412
                    if (connection_status() != CONNECTION_NORMAL) {
413
                        break;
414
                    }
415
                }
416
            }
417
        }
418
    }
419
420
    /**
421
     * Invoke application
422
     *
423
     * This method implements the middleware interface. It receives
424
     * Request and Response objects, and it returns a Response object
425
     * after compiling the routes registered in the Router and dispatching
426
     * the Request object to the appropriate Route callback routine.
427
     *
428
     * @param  ServerRequestInterface $request  The most recent Request object
429
     * @param  ResponseInterface      $response The most recent Response object
430
     *
431
     * @return ResponseInterface
432
     * @throws MethodNotAllowedException
433
     * @throws NotFoundException
434
     */
435
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
436
    {
437
        // Get the route info
438
        $routeInfo = $request->getAttribute('routeInfo');
439
440
        /** @var \Slim\Interfaces\RouterInterface $router */
441
        $router = $this->container->get('router');
442
443
        // If router hasn't been dispatched or the URI changed then dispatch
444
        if (null === $routeInfo || ($routeInfo['request'] !== [$request->getMethod(), (string) $request->getUri()])) {
445
            $request = $this->dispatchRouterAndPrepareRoute($request, $router);
446
            $routeInfo = $request->getAttribute('routeInfo');
447
        }
448
449
        if ($routeInfo[0] === Dispatcher::FOUND) {
450
            $route = $router->lookupRoute($routeInfo[1]);
451
            return $route->run($request, $response);
452
        } elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) {
453
            if (!$this->container->has('notAllowedHandler')) {
454
                throw new MethodNotAllowedException($request, $response, $routeInfo[1]);
455
            }
456
            /** @var callable $notAllowedHandler */
457
            $notAllowedHandler = $this->container->get('notAllowedHandler');
458
            return $notAllowedHandler($request, $response, $routeInfo[1]);
459
        }
460
461
        if (!$this->container->has('notFoundHandler')) {
462
            throw new NotFoundException($request, $response);
463
        }
464
        /** @var callable $notFoundHandler */
465
        $notFoundHandler = $this->container->get('notFoundHandler');
466
        return $notFoundHandler($request, $response);
467
    }
468
469
    /**
470
     * Perform a sub-request from within an application route
471
     *
472
     * This method allows you to prepare and initiate a sub-request, run within
473
     * the context of the current request. This WILL NOT issue a remote HTTP
474
     * request. Instead, it will route the provided URL, method, headers,
475
     * cookies, body, and server variables against the set of registered
476
     * application routes. The result response object is returned.
477
     *
478
     * @param  string            $method      The request method (e.g., GET, POST, PUT, etc.)
479
     * @param  string            $path        The request URI path
480
     * @param  string            $query       The request URI query string
481
     * @param  array             $headers     The request headers (key-value array)
482
     * @param  array             $cookies     The request cookies (key-value array)
483
     * @param  string            $bodyContent The request body
484
     * @param  ResponseInterface $response     The response object (optional)
485
     * @return ResponseInterface
486
     */
487
    public function subRequest(
488
        $method,
489
        $path,
490
        $query = '',
491
        array $headers = [],
492
        array $cookies = [],
493
        $bodyContent = '',
494
        ResponseInterface $response = null
495
    ) {
496
        $env = $this->container->get('environment');
497
        $uri = Uri::createFromEnvironment($env)->withPath($path)->withQuery($query);
498
        $headers = new Headers($headers);
499
        $serverParams = $env->all();
500
        $body = new Body(fopen('php://temp', 'r+'));
501
        $body->write($bodyContent);
502
        $body->rewind();
503
        $request = new Request($method, $uri, $headers, $cookies, $serverParams, $body);
504
505
        if (!$response) {
506
            $response = $this->container->get('response');
507
        }
508
509
        return $this($request, $response);
510
    }
511
512
    /**
513
     * Dispatch the router to find the route. Prepare the route for use.
514
     *
515
     * @param ServerRequestInterface $request
516
     * @param RouterInterface        $router
517
     * @return ServerRequestInterface
518
     */
519
    protected function dispatchRouterAndPrepareRoute(ServerRequestInterface $request, RouterInterface $router)
520
    {
521
        $routeInfo = $router->dispatch($request);
522
523
        if ($routeInfo[0] === Dispatcher::FOUND) {
524
            $routeArguments = [];
525
            foreach ($routeInfo[2] as $k => $v) {
526
                $routeArguments[$k] = urldecode($v);
527
            }
528
529
            $route = $router->lookupRoute($routeInfo[1]);
530
            $route->prepare($request, $routeArguments);
531
532
            // add route to the request's attributes in case a middleware or handler needs access to the route
533
            $request = $request->withAttribute('route', $route);
534
        }
535
536
        $routeInfo['request'] = [$request->getMethod(), (string) $request->getUri()];
537
538
        return $request->withAttribute('routeInfo', $routeInfo);
539
    }
540
541
    /**
542
     * Finalize response
543
     *
544
     * @param ResponseInterface $response
545
     * @return ResponseInterface
546
     */
547
    protected function finalize(ResponseInterface $response)
548
    {
549
        // stop PHP sending a Content-Type automatically
550
        ini_set('default_mimetype', '');
551
552
        if ($this->isEmptyResponse($response)) {
553
            return $response->withoutHeader('Content-Type')->withoutHeader('Content-Length');
554
        }
555
556
        // Add Content-Length header if `addContentLengthHeader` setting is set
557
        if (isset($this->container->get('settings')['addContentLengthHeader']) &&
558
            $this->container->get('settings')['addContentLengthHeader'] == true) {
559
            if (ob_get_length() > 0) {
560
                throw new \RuntimeException("Unexpected data in output buffer. " .
561
                    "Maybe you have characters before an opening <?php tag?");
562
            }
563
            $size = $response->getBody()->getSize();
564
            if ($size !== null && !$response->hasHeader('Content-Length')) {
565
                $response = $response->withHeader('Content-Length', (string) $size);
566
            }
567
        }
568
569
        return $response;
570
    }
571
572
    /**
573
     * Helper method, which returns true if the provided response must not output a body and false
574
     * if the response could have a body.
575
     *
576
     * @see https://tools.ietf.org/html/rfc7231
577
     *
578
     * @param ResponseInterface $response
579
     * @return bool
580
     */
581
    protected function isEmptyResponse(ResponseInterface $response)
582
    {
583
        if (method_exists($response, 'isEmpty')) {
584
            return $response->isEmpty();
585
        }
586
587
        return in_array($response->getStatusCode(), [204, 205, 304]);
588
    }
589
590
    /**
591
     * Call relevant handler from the Container if needed. If it doesn't exist,
592
     * then just re-throw.
593
     *
594
     * @param  Exception $e
595
     * @param  ServerRequestInterface $request
596
     * @param  ResponseInterface $response
597
     *
598
     * @return ResponseInterface
599
     * @throws Exception if a handler is needed and not found
600
     */
601
    protected function handleException(Exception $e, ServerRequestInterface $request, ResponseInterface $response)
602
    {
603
        if ($e instanceof MethodNotAllowedException) {
604
            $handler = 'notAllowedHandler';
605
            $params = [$e->getRequest(), $e->getResponse(), $e->getAllowedMethods()];
606
        } elseif ($e instanceof NotFoundException) {
607
            $handler = 'notFoundHandler';
608
            $params = [$e->getRequest(), $e->getResponse()];
609
        } elseif ($e instanceof SlimException) {
610
            // This is a Stop exception and contains the response
611
            return $e->getResponse();
612
        } else {
613
            // Other exception, use $request and $response params
614
            $handler = 'errorHandler';
615
            $params = [$request, $response, $e];
616
        }
617
618 View Code Duplication
        if ($this->container->has($handler)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
619
            $callable = $this->container->get($handler);
620
            // Call the registered handler
621
            return call_user_func_array($callable, $params);
622
        }
623
624
        // No handlers found, so just throw the exception
625
        throw $e;
626
    }
627
628
    /**
629
     * Call relevant handler from the Container if needed. If it doesn't exist,
630
     * then just re-throw.
631
     *
632
     * @param  Throwable $e
633
     * @param  ServerRequestInterface $request
634
     * @param  ResponseInterface $response
635
     * @return ResponseInterface
636
     * @throws Throwable
637
     */
638
    protected function handlePhpError(Throwable $e, ServerRequestInterface $request, ResponseInterface $response)
639
    {
640
        $handler = 'phpErrorHandler';
641
        $params = [$request, $response, $e];
642
643 View Code Duplication
        if ($this->container->has($handler)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
644
            $callable = $this->container->get($handler);
645
            // Call the registered handler
646
            return call_user_func_array($callable, $params);
647
        }
648
649
        // No handlers found, so just throw the exception
650
        throw $e;
651
    }
652
}
653