Passed
Pull Request — master (#41)
by kenny
06:25
created

PumpStream::pump()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 3
nop 1
dl 0
loc 11
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace One\Http;
4
5
/**
6
 * Provides a read only stream that pumps data from a PHP callable.
7
 *
8
 * When invoking the provided callable, the PumpStream will pass the amount of
9
 * data requested to read to the callable. The callable can choose to ignore
10
 * this value and return fewer or more bytes than requested. Any extra data
11
 * returned by the provided callable is buffered internally until drained using
12
 * the read() function of the PumpStream. The provided callable MUST return
13
 * false when there is no more data to read.
14
 */
15
class PumpStream implements \Psr\Http\Message\StreamInterface
16
{
17
    /** @var callable */
18
    private $source;
19
20
    /** @var int */
21
    private $size;
22
23
    /** @var int */
24
    private $tellPos = 0;
25
26
    /** @var array */
27
    private $metadata;
28
29
    /** @var BufferStream */
0 ignored issues
show
Bug introduced by
The type One\Http\BufferStream was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
    private $buffer;
31
32
    /**
33
     * @param callable $source Source of the stream data. The callable MAY
34
     *                         accept an integer argument used to control the
35
     *                         amount of data to return. The callable MUST
36
     *                         return a string when called, or false on error
37
     *                         or EOF.
38
     * @param array $options   Stream options:
39
     *                         - metadata: Hash of metadata to use with stream.
40
     *                         - size: Size of the stream, if known.
41
     */
42
    public function __construct(callable $source, array $options = [])
43
    {
44
        $this->source = $source;
45
        $this->size = isset($options['size']) ? $options['size'] : null;
46
        $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
47
        $this->buffer = new BufferStream();
48
    }
49
50
    public function __toString()
51
    {
52
        try {
53
            return copy_to_string($this);
0 ignored issues
show
Bug introduced by
The function copy_to_string was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

53
            return /** @scrutinizer ignore-call */ copy_to_string($this);
Loading history...
54
        } catch (\Exception $e) {
55
            return '';
56
        }
57
    }
58
59
    public function close()
60
    {
61
        $this->detach();
62
    }
63
64
    public function detach()
65
    {
66
        $this->tellPos = false;
0 ignored issues
show
Documentation Bug introduced by
The property $tellPos was declared of type integer, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
67
        $this->source = null;
68
    }
69
70
    public function getSize()
71
    {
72
        return $this->size;
73
    }
74
75
    public function tell()
76
    {
77
        return $this->tellPos;
78
    }
79
80
    public function eof()
81
    {
82
        return !$this->source;
83
    }
84
85
    public function isSeekable()
86
    {
87
        return false;
88
    }
89
90
    public function rewind()
91
    {
92
        $this->seek(0);
93
    }
94
95
    public function seek($offset, $whence = SEEK_SET)
96
    {
97
        throw new \RuntimeException('Cannot seek a PumpStream');
98
    }
99
100
    public function isWritable()
101
    {
102
        return false;
103
    }
104
105
    public function write($string)
106
    {
107
        throw new \RuntimeException('Cannot write to a PumpStream');
108
    }
109
110
    public function isReadable()
111
    {
112
        return true;
113
    }
114
115
    public function read($length)
116
    {
117
        $data = $this->buffer->read($length);
118
        $readLen = strlen($data);
119
        $this->tellPos += $readLen;
120
        $remaining = $length - $readLen;
121
122
        if ($remaining) {
123
            $this->pump($remaining);
124
            $data .= $this->buffer->read($remaining);
125
            $this->tellPos += strlen($data) - $readLen;
126
        }
127
128
        return $data;
129
    }
130
131
    public function getContents()
132
    {
133
        $result = '';
134
        while (!$this->eof()) {
135
            $result .= $this->read(1000000);
136
        }
137
138
        return $result;
139
    }
140
141
    public function getMetadata($key = null)
142
    {
143
        if (!$key) {
144
            return $this->metadata;
145
        }
146
147
        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
148
    }
149
150
    private function pump($length)
151
    {
152
        if ($this->source) {
153
            do {
154
                $data = call_user_func($this->source, $length);
155
                if ($data === false || $data === null) {
156
                    $this->source = null;
157
                    return;
158
                }
159
                $this->buffer->write($data);
160
                $length -= strlen($data);
161
            } while ($length > 0);
162
        }
163
    }
164
}
165