Passed
Push — master ( 499aad...9b53e7 )
by Iman
04:34
created

VideoStreamer::setHeader()   C

Complexity

Conditions 12
Paths 38

Size

Total Lines 58
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 41
dl 0
loc 58
rs 6.9666
c 3
b 1
f 0
cc 12
nc 38
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Iman\Streamer;
4
5
class VideoStreamer
6
{
7
    private $path = '';
8
9
    private $stream = '';
10
11
    private $buffer = 102400;
12
13
    private $start = -1;
14
15
    private $end = -1;
16
17
    private $size = 0;
18
19
    private $mime = '';
20
21
    public function __construct($filePath = '', $stream)
22
    {
23
        $this->path = $filePath;
24
        $this->stream = $stream;
25
        $this->mime = mime_content_type($filePath);
26
    }
27
28
    /**
29
     * Set proper header to serve the video content.
30
     */
31
    private function setHeader()
32
    {
33
        ob_get_clean();
34
        header('Content-Type: '.$this->mime);
35
        header('Cache-Control: max-age=2592000, public');
36
        header('Expires: '.gmdate('D, d M Y H:i:s', time() + 2592000).' GMT');
37
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', @filemtime($this->path)).' GMT');
38
        $this->start = 0;
39
        $this->size = filesize($this->path);
40
        $this->end = $this->size - 1;
41
        header('Accept-Ranges: 0-'.$this->end);
42
43
        if (! isset($_SERVER['HTTP_RANGE'])) {
44
            header('Content-Length: '.$this->size);
45
46
            return;
47
        }
48
49
        $c_end = $this->end;
50
        [
51
            ,
52
            $range,
53
        ] = explode('=', $_SERVER['HTTP_RANGE'], 2);
54
55
56
        if (strpos($range, ',') !== false) {
57
            header('HTTP/1.1 416 Requested Range Not Satisfiable');
58
            header("Content-Range: bytes $this->start-$this->end/$this->size");
59
            exit;
60
        }
61
62
        if ($range == '-') {
63
            $c_start = $this->size - substr($range, 1);
64
        } else {
65
            $range = explode('-', $range);
66
            $c_start = $range[0];
67
68
            if (  isset($range[1]) && is_numeric($range[1]) ) {
69
                \Log::info('$range:'. $range[1]. gettype($range[1]));
70
            } else {
71
                \Log::info('default: '. $c_end);
72
            }
73
74
            $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
75
        }
76
        $c_end = ($c_end > $this->end) ? $this->end : $c_end;
77
        if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
78
            header('HTTP/1.1 416 Requested Range Not Satisfiable');
79
            header("Content-Range: bytes $this->start-$this->end/$this->size");
80
            exit;
81
        }
82
        $this->start = $c_start;
83
        $this->end = $c_end;
84
        $length = $this->end - $this->start + 1;
85
        fseek($this->stream, $this->start);
0 ignored issues
show
Bug introduced by
It seems like $this->start can also be of type string; however, parameter $offset of fseek() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

85
        fseek($this->stream, /** @scrutinizer ignore-type */ $this->start);
Loading history...
86
        header('HTTP/1.1 206 Partial Content');
87
        header('Content-Length: '.$length);
88
        header("Content-Range: bytes $this->start-$this->end/".$this->size);
89
    }
90
91
    /**
92
     * perform the streaming of calculated range.
93
     */
94
    private function stream()
95
    {
96
        $i = $this->start;
97
        set_time_limit(0);
98
        $readBytes = 0;
99
        while (! feof($this->stream) && $i <= $this->end && $readBytes < $this->buffer * 3) {
100
            $bytesToRead = $this->buffer;
101
            if (($i + $bytesToRead) > $this->end) {
102
                $bytesToRead = $this->end - $i + 1;
103
            }
104
            $data = fread($this->stream, $bytesToRead);
105
            echo $data;
106
            flush();
107
            $i += $bytesToRead;
108
            $readBytes += $bytesToRead;
109
        }
110
    }
111
112
    /**
113
     * Start streaming video content.
114
     */
115
    public function start()
116
    {
117
        $this->setHeader();
118
        $this->stream();
119
120
        /**
121
         * close curretly opened stream.
122
         */
123
        fclose($this->stream);
124
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
125
    }
126
127
    public static function streamFile($path)
128
    {
129
        $stream = fopen($path, 'rb');
130
        if (! $stream) {
0 ignored issues
show
introduced by
$stream is of type false|resource, thus it always evaluated to false.
Loading history...
131
            throw new \Exception('File not found in: '. $path, 6542);
132
        }
133
134
        (new static($path, $stream))->start();
135
    }
136
}
137