Completed
Push — master ( 923ed1...a46998 )
by Márk
14:45 queued 12:43
created

FilteredStream::isSeekable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Http\Message\Encoding;
4
5
use Clue\StreamFilter as Filter;
6
use Http\Message\Decorator\StreamDecorator;
7
use Psr\Http\Message\StreamInterface;
8
9
/**
10
 * A filtered stream has a filter for filtering output and a filter for filtering input made to a underlying stream.
11
 *
12
 * @author Joel Wurtz <[email protected]>
13
 */
14
abstract class FilteredStream implements StreamInterface
15
{
16
    const BUFFER_SIZE = 8192;
17
18
    use StreamDecorator;
19
20
    /**
21
     * @var callable
22
     */
23
    protected $readFilterCallback;
24
25
    /**
26
     * @var resource
27
     *
28
     * @deprecated since version 1.5, will be removed in 2.0
29
     */
30
    protected $readFilter;
31
32
    /**
33
     * @var callable
34
     *
35
     * @deprecated since version 1.5, will be removed in 2.0
36
     */
37
    protected $writeFilterCallback;
38
39
    /**
40
     * @var resource
41
     *
42
     * @deprecated since version 1.5, will be removed in 2.0
43
     */
44
    protected $writeFilter;
45
46
    /**
47
     * Internal buffer.
48
     *
49
     * @var string
50
     */
51
    protected $buffer = '';
52
53
    /**
54
     * @param StreamInterface $stream
55
     * @param mixed|null      $readFilterOptions
56
     * @param mixed|null      $writeFilterOptions deprecated since 1.5, will be removed in 2.0
57
     */
58 45
    public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null)
59
    {
60 45
        if (null !== $readFilterOptions) {
61 34
            $this->readFilterCallback = Filter\fun($this->readFilter(), $readFilterOptions);
62 33
        } else {
63 11
            $this->readFilterCallback = Filter\fun($this->readFilter());
64
        }
65
66 44
        if (null !== $writeFilterOptions) {
67 1
            $this->writeFilterCallback = Filter\fun($this->writeFilter(), $writeFilterOptions);
0 ignored issues
show
Deprecated Code introduced by
The property Http\Message\Encoding\Fi...m::$writeFilterCallback has been deprecated with message: since version 1.5, will be removed in 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
68
69
            @trigger_error('The $writeFilterOptions argument is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
70
        } else {
71 43
            $this->writeFilterCallback = Filter\fun($this->writeFilter());
0 ignored issues
show
Deprecated Code introduced by
The property Http\Message\Encoding\Fi...m::$writeFilterCallback has been deprecated with message: since version 1.5, will be removed in 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
72
        }
73
74 43
        $this->stream = $stream;
75 43
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 18
    public function read($length)
81
    {
82 18
        if (strlen($this->buffer) >= $length) {
83 8
            $read = substr($this->buffer, 0, $length);
84 8
            $this->buffer = substr($this->buffer, $length);
85
86 8
            return $read;
87
        }
88
89 18
        if ($this->stream->eof()) {
90 11
            $buffer = $this->buffer;
91 11
            $this->buffer = '';
92
93 11
            return $buffer;
94
        }
95
96 18
        $read = $this->buffer;
97 18
        $this->buffer = '';
98 18
        $this->fill();
99
100 18
        return $read.$this->read($length - strlen($read));
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 10
    public function eof()
107
    {
108 10
        return $this->stream->eof() && '' === $this->buffer;
109
    }
110
111
    /**
112
     * Buffer is filled by reading underlying stream.
113
     *
114
     * Callback is reading once more even if the stream is ended.
115
     * This allow to get last data in the PHP buffer otherwise this
116
     * bug is present : https://bugs.php.net/bug.php?id=48725
117
     */
118 18
    protected function fill()
119
    {
120 18
        $readFilterCallback = $this->readFilterCallback;
121 18
        $this->buffer .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE));
122
123 18
        if ($this->stream->eof()) {
124 18
            $this->buffer .= $readFilterCallback();
125 18
        }
126 18
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 10
    public function getContents()
132
    {
133 10
        $buffer = '';
134
135 10
        while (!$this->eof()) {
136 10
            $buf = $this->read(self::BUFFER_SIZE);
137
            // Using a loose equality here to match on '' and false.
138 10
            if (null == $buf) {
139
                break;
140
            }
141
142 10
            $buffer .= $buf;
143 10
        }
144
145 10
        return $buffer;
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 9
    public function getSize()
152
    {
153 9
        return;
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function __toString()
160
    {
161
        return $this->getContents();
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function isSeekable()
168
    {
169
        return false;
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function rewind()
176
    {
177
        throw new \RuntimeException('Cannot rewind a filtered stream');
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function seek($offset, $whence = SEEK_SET)
184
    {
185
        throw new \RuntimeException('Cannot seek a filtered stream');
186
    }
187
188
    /**
189
     * Returns the read filter name.
190
     *
191
     * @return string
192
     *
193
     * @deprecated since version 1.5, will be removed in 2.0
194
     */
195
    public function getReadFilter()
196
    {
197
        @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
198
199
        return $this->readFilter();
200
    }
201
202
    /**
203
     * Returns the write filter name.
204
     *
205
     * @return string
206
     */
207
    abstract protected function readFilter();
208
209
    /**
210
     * Returns the write filter name.
211
     *
212
     * @return string
213
     *
214
     * @deprecated since version 1.5, will be removed in 2.0
215
     */
216
    public function getWriteFilter()
217
    {
218
        @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
219
220
        return $this->writeFilter();
221
    }
222
223
    /**
224
     * Returns the write filter name.
225
     *
226
     * @return string
227
     */
228
    abstract protected function writeFilter();
229
}
230