Completed
Push — master ( b2e200...1332b8 )
by Oscar
04:48
created

ReadResponse::__invoke()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 32
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 1 Features 2
Metric Value
c 9
b 1
f 2
dl 0
loc 32
rs 8.439
cc 6
eloc 14
nc 5
nop 3
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Utils;
6
use Psr7Middlewares\Middleware;
7
use Psr\Http\Message\RequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Middleware to read the response.
12
 */
13
class ReadResponse
14
{
15
    use Utils\FileTrait;
16
17
    /**
18
     * Execute the middleware.
19
     *
20
     * @param RequestInterface  $request
21
     * @param ResponseInterface $response
22
     * @param callable          $next
23
     *
24
     * @return ResponseInterface
25
     */
26
    public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
27
    {
28
        //If basePath does not match
29
        if (!$this->testBasePath($request->getUri()->getPath())) {
30
            return $next($request, $response);
31
        }
32
33
        //If the method is not allowed
34
        if ($request->getMethod() !== 'GET') {
35
            return $response->withStatus(405);
36
        }
37
38
        $file = $this->getFilename($request);
39
40
        //If the file does not exists, check if is gzipped
41
        if (!is_file($file)) {
42
            $file .= '.gz';
43
44
            if (EncodingNegotiator::getEncoding($request) !== 'gzip' || !is_file($file)) {
0 ignored issues
show
Compatibility introduced by
$request of type object<Psr\Http\Message\RequestInterface> is not a sub-type of object<Psr\Http\Message\ServerRequestInterface>. It seems like you assume a child interface of the interface Psr\Http\Message\RequestInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
45
                return $response->withStatus(404);
46
            }
47
48
            $response = $response->withHeader('Content-Encoding', 'gzip');
49
        }
50
51
        $response = $response->withBody(Middleware::createStream($file));
52
53
        //Handle range header
54
        $response = $this->range($request, $response);
55
56
        return $next($request, $response);
57
    }
58
59
    private static function range(RequestInterface $request, ResponseInterface $response)
60
    {
61
        $response = $response->withHeader('Accept-Ranges', 'bytes');
62
63
        $range = $request->getHeaderLine('Range');
64
65
        if (empty($range) || !($range = self::parseRangeHeader($range))) {
66
            return $response;
67
        }
68
69
        list($first, $last) = $range;
70
        $size = $response->getBody()->getSize();
71
72
        if ($last ===  null) {
73
            $last = $size - 1;
74
        }
75
76
        return $response
77
            ->withStatus(206)
78
            ->withHeader('Content-Length', (string) ($last - $first + 1))
79
            ->withHeader('Content-Range', sprintf('bytes %d-%d/%d', $first, $last,  $size));
80
    }
81
82
    /**
83
     * Parses a range header, for example: bytes=500-999.
84
     *
85
     * @param string $header
86
     *
87
     * @return false|array [first, last]
88
     */
89
    private static function parseRangeHeader($header)
90
    {
91
        if (preg_match('/bytes=(?P<first>\d+)-(?P<last>\d+)?/', $header, $matches)) {
92
            return [
93
                (int) $matches['first'],
94
                isset($matches['last']) ? (int) $matches['last'] : null,
95
            ];
96
        }
97
98
        return false;
99
    }
100
}
101