Completed
Pull Request — master (#5)
by Márk
02:37
created

FilteredStream::getContents()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 16
ccs 8
cts 9
cp 0.8889
rs 9.4286
cc 3
eloc 8
nc 3
nop 0
crap 3.0123
1
<?php
2
3
namespace Http\Message\Encoding;
4
5
use Clue\StreamFilter as Filter;
6
use GuzzleHttp\Psr7\StreamDecoratorTrait;
7
use Http\Message\Decorator\StreamDecorator;
8
use Psr\Http\Message\StreamInterface;
9
10
/**
11
 * A filtered stream has a filter for filtering output and a filter for filtering input made to a underlying stream
12
 *
13
 * @author Joel Wurtz <[email protected]>
14
 */
15
abstract class FilteredStream implements StreamInterface
16
{
17
    const BUFFER_SIZE = 65536;
18
19
    use StreamDecorator;
20
21
    /**
22
     * @var callable
23
     */
24
    protected $readFilterCallback;
25
26
    /**
27
     * @var resource
28
     */
29
    protected $readFilter;
30
31
    /**
32
     * @var callable
33
     */
34
    protected $writeFilterCallback;
35
36
    /**
37
     * @var resource
38
     */
39
    protected $writeFilter;
40
41
    /**
42
     * Internal buffer
43
     *
44
     * @var string
45
     */
46
    protected $buffer = '';
47
48
    /**
49
     * @param StreamInterface $stream
50
     * @param null            $readFilterOptions
51
     * @param null            $writeFilterOptions
52
     */
53 32
    public function __construct(StreamInterface $stream, $readFilterOptions = null, $writeFilterOptions = null)
54
    {
55 32
        $this->readFilterCallback  = Filter\fun($this->getReadFilter(), $readFilterOptions);
56 32
        $this->writeFilterCallback = Filter\fun($this->getWriteFilter(), $writeFilterOptions);
57 32
        $this->stream              = $stream;
58 32
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 16
    public function read($length)
64
    {
65 16
        if (strlen($this->buffer) >= $length) {
66 7
            $read = substr($this->buffer, 0, $length);
67 7
            $this->buffer = substr($this->buffer, $length);
68
69 7
            return $read;
70
        }
71
72 16
        if ($this->stream->eof()) {
73 10
            $buffer = $this->buffer;
74 10
            $this->buffer = '';
75
76 10
            return $buffer;
77
        }
78
79 16
        $read = $this->buffer;
80 16
        $this->buffer = '';
81 16
        $this->fill();
82
83 16
        return $read . $this->read($length - strlen($read));
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89 9
    public function eof()
90
    {
91 9
        return ($this->stream->eof() && $this->buffer === '');
92
    }
93
94
    /**
95
     * Buffer is filled by reading underlying stream
96
     *
97
     * Callback is reading once more even if the stream is ended.
98
     * This allow to get last data in the PHP buffer otherwise this
99
     * bug is present : https://bugs.php.net/bug.php?id=48725
100
     */
101 16
    protected function fill()
102
    {
103 16
        $readFilterCallback = $this->readFilterCallback;
104 16
        $this->buffer      .= $readFilterCallback($this->stream->read(self::BUFFER_SIZE));
105
106 16
        if ($this->stream->eof()) {
107 16
            $this->buffer .= $readFilterCallback();
108 16
        }
109 16
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 9
    public function getContents()
115
    {
116 9
        $buffer = '';
117
118 9
        while (!$this->eof()) {
119 9
            $buf = $this->read(1048576);
120
            // Using a loose equality here to match on '' and false.
121 9
            if ($buf == null) {
122
                break;
123
            }
124
125 9
            $buffer .= $buf;
126 9
        }
127
128 9
        return $buffer;
129
    }
130
131
    /**
132
     * Return the read filter name
133
     *
134
     * @return string
135
     */
136
    abstract public function getReadFilter();
137
138
    /**
139
     * Return the write filter name
140
     *
141
     * @return mixed
142
     */
143
    abstract public function getWriteFilter();
144
}
145