FilteredStream::read()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
nc 3
nop 1
dl 0
loc 21
ccs 12
cts 12
cp 1
crap 3
rs 9.8666
c 1
b 0
f 0
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
        rewind as private doRewind;
20
        seek as private doSeek;
21
    }
22
23
    /**
24
     * @var callable
25
     */
26
    protected $readFilterCallback;
27
28
    /**
29
     * @var resource
30
     *
31
     * @deprecated since version 1.5, will be removed in 2.0
32
     */
33
    protected $readFilter;
34
35
    /**
36
     * @var callable
37
     *
38
     * @deprecated since version 1.5, will be removed in 2.0
39
     */
40
    protected $writeFilterCallback;
41
42
    /**
43
     * @var resource
44
     *
45
     * @deprecated since version 1.5, will be removed in 2.0
46
     */
47
    protected $writeFilter;
48
49
    /**
50
     * Internal buffer.
51
     *
52
     * @var string
53
     */
54
    protected $buffer = '';
55
56
    /**
57
     * @param mixed|null $readFilterOptions
58
     * @param mixed|null $writeFilterOptions deprecated since 1.5, will be removed in 2.0
59
     */
60
    public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null)
61 45
    {
62
        if (null !== $readFilterOptions) {
63 45
            $this->readFilterCallback = Filter\fun($this->readFilter(), $readFilterOptions);
0 ignored issues
show
Bug introduced by
The function fun 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

63
            $this->readFilterCallback = /** @scrutinizer ignore-call */ Filter\fun($this->readFilter(), $readFilterOptions);
Loading history...
64 34
        } else {
65
            $this->readFilterCallback = Filter\fun($this->readFilter());
66 11
        }
67
68
        if (null !== $writeFilterOptions) {
69 44
            $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: since version 1.5, will be removed in 2.0 ( Ignorable by Annotation )

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

69
            /** @scrutinizer ignore-deprecated */ $this->writeFilterCallback = Filter\fun($this->writeFilter(), $writeFilterOptions);

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...
70 1
71
            @trigger_error('The $writeFilterOptions argument is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
72
        } else {
73
            $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: since version 1.5, will be removed in 2.0 ( Ignorable by Annotation )

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

73
            /** @scrutinizer ignore-deprecated */ $this->writeFilterCallback = Filter\fun($this->writeFilter());

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...
74 43
        }
75
76
        $this->stream = $stream;
77 43
    }
78 43
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function read($length)
83 18
    {
84
        if (strlen($this->buffer) >= $length) {
85 18
            $read = substr($this->buffer, 0, $length);
86 8
            $this->buffer = substr($this->buffer, $length);
87 8
88
            return $read;
89 8
        }
90
91
        if ($this->stream->eof()) {
92 18
            $buffer = $this->buffer;
93 11
            $this->buffer = '';
94 11
95
            return $buffer;
96 11
        }
97
98
        $read = $this->buffer;
99 18
        $this->buffer = '';
100 18
        $this->fill();
101 18
102
        return $read.$this->read($length - strlen($read));
103 18
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public function eof()
109 10
    {
110
        return $this->stream->eof() && '' === $this->buffer;
111 10
    }
112
113
    /**
114
     * Buffer is filled by reading underlying stream.
115
     *
116
     * Callback is reading once more even if the stream is ended.
117
     * This allow to get last data in the PHP buffer otherwise this
118
     * bug is present : https://bugs.php.net/bug.php?id=48725
119
     */
120
    protected function fill()
121 18
    {
122
        $readFilterCallback = $this->readFilterCallback;
123 18
        $this->buffer .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE));
124 18
125
        if ($this->stream->eof()) {
126 18
            $this->buffer .= $readFilterCallback();
127 13
        }
128
    }
129 18
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function getContents()
134 10
    {
135
        $buffer = '';
136 10
137
        while (!$this->eof()) {
138 10
            $buf = $this->read(self::BUFFER_SIZE);
139 10
            // Using a loose equality here to match on '' and false.
140
            if (null == $buf) {
141 10
                break;
142
            }
143
144
            $buffer .= $buf;
145 10
        }
146
147
        return $buffer;
148 10
    }
149
150
    /**
151
     * Always returns null because we can't tell the size of a stream when we filter.
152
     */
153
    public function getSize()
154 9
    {
155
        return null;
156 9
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function __toString()
162
    {
163
        return $this->getContents();
164
    }
165
166
    /**
167
     * Filtered streams are not seekable.
168
     *
169
     * We would need to buffer and process everything to allow seeking.
170
     */
171
    public function isSeekable()
172
    {
173
        return false;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179
    public function rewind()
180
    {
181
        @trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', E_USER_DEPRECATED);
182
        $this->doRewind();
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function seek($offset, $whence = SEEK_SET)
189
    {
190
        @trigger_error('Filtered streams are not seekable. This method will start raising an exception in the next major version', E_USER_DEPRECATED);
191
        $this->doSeek($offset, $whence);
192
    }
193
194
    /**
195
     * Returns the read filter name.
196
     *
197
     * @return string
198
     *
199
     * @deprecated since version 1.5, will be removed in 2.0
200
     */
201
    public function getReadFilter()
202
    {
203
        @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
204
205
        return $this->readFilter();
206
    }
207
208
    /**
209
     * Returns the write filter name.
210
     *
211
     * @return string
212
     */
213
    abstract protected function readFilter();
214
215
    /**
216
     * Returns the write filter name.
217
     *
218
     * @return string
219
     *
220
     * @deprecated since version 1.5, will be removed in 2.0
221
     */
222
    public function getWriteFilter()
223
    {
224
        @trigger_error('The '.__CLASS__.'::'.__METHOD__.' method is deprecated since version 1.5 and will be removed in 2.0.', E_USER_DEPRECATED);
225
226
        return $this->writeFilter();
227
    }
228
229
    /**
230
     * Returns the write filter name.
231
     *
232
     * @return string
233
     */
234
    abstract protected function writeFilter();
235
}
236