xref: /plugin/dw2pdf/vendor/setasign/fpdi/src/PdfParser/StreamReader.php (revision dc4d9dc689082c963d5c1d9ee679553326788c6e)
1*dc4d9dc6SAnna Dabrowska<?php
2*dc4d9dc6SAnna Dabrowska/**
3*dc4d9dc6SAnna Dabrowska * This file is part of FPDI
4*dc4d9dc6SAnna Dabrowska *
5*dc4d9dc6SAnna Dabrowska * @package   setasign\Fpdi
6*dc4d9dc6SAnna Dabrowska * @copyright Copyright (c) 2020 Setasign GmbH & Co. KG (https://www.setasign.com)
7*dc4d9dc6SAnna Dabrowska * @license   http://opensource.org/licenses/mit-license The MIT License
8*dc4d9dc6SAnna Dabrowska */
9*dc4d9dc6SAnna Dabrowska
10*dc4d9dc6SAnna Dabrowskanamespace setasign\Fpdi\PdfParser;
11*dc4d9dc6SAnna Dabrowska
12*dc4d9dc6SAnna Dabrowska/**
13*dc4d9dc6SAnna Dabrowska * A stream reader class
14*dc4d9dc6SAnna Dabrowska *
15*dc4d9dc6SAnna Dabrowska * @package setasign\Fpdi\PdfParser
16*dc4d9dc6SAnna Dabrowska */
17*dc4d9dc6SAnna Dabrowskaclass StreamReader
18*dc4d9dc6SAnna Dabrowska{
19*dc4d9dc6SAnna Dabrowska    /**
20*dc4d9dc6SAnna Dabrowska     * Creates a stream reader instance by a string value.
21*dc4d9dc6SAnna Dabrowska     *
22*dc4d9dc6SAnna Dabrowska     * @param string $content
23*dc4d9dc6SAnna Dabrowska     * @param int $maxMemory
24*dc4d9dc6SAnna Dabrowska     * @return StreamReader
25*dc4d9dc6SAnna Dabrowska     */
26*dc4d9dc6SAnna Dabrowska    public static function createByString($content, $maxMemory = 2097152)
27*dc4d9dc6SAnna Dabrowska    {
28*dc4d9dc6SAnna Dabrowska        $h = \fopen('php://temp/maxmemory:' . ((int) $maxMemory), 'r+b');
29*dc4d9dc6SAnna Dabrowska        \fwrite($h, $content);
30*dc4d9dc6SAnna Dabrowska        \rewind($h);
31*dc4d9dc6SAnna Dabrowska
32*dc4d9dc6SAnna Dabrowska        return new self($h, true);
33*dc4d9dc6SAnna Dabrowska    }
34*dc4d9dc6SAnna Dabrowska
35*dc4d9dc6SAnna Dabrowska    /**
36*dc4d9dc6SAnna Dabrowska     * Creates a stream reader instance by a filename.
37*dc4d9dc6SAnna Dabrowska     *
38*dc4d9dc6SAnna Dabrowska     * @param string $filename
39*dc4d9dc6SAnna Dabrowska     * @return StreamReader
40*dc4d9dc6SAnna Dabrowska     */
41*dc4d9dc6SAnna Dabrowska    public static function createByFile($filename)
42*dc4d9dc6SAnna Dabrowska    {
43*dc4d9dc6SAnna Dabrowska        $h = \fopen($filename, 'rb');
44*dc4d9dc6SAnna Dabrowska        return new self($h, true);
45*dc4d9dc6SAnna Dabrowska    }
46*dc4d9dc6SAnna Dabrowska
47*dc4d9dc6SAnna Dabrowska    /**
48*dc4d9dc6SAnna Dabrowska     * Defines whether the stream should be closed when the stream reader instance is deconstructed or not.
49*dc4d9dc6SAnna Dabrowska     *
50*dc4d9dc6SAnna Dabrowska     * @var bool
51*dc4d9dc6SAnna Dabrowska     */
52*dc4d9dc6SAnna Dabrowska    protected $closeStream;
53*dc4d9dc6SAnna Dabrowska
54*dc4d9dc6SAnna Dabrowska    /**
55*dc4d9dc6SAnna Dabrowska     * The stream resource.
56*dc4d9dc6SAnna Dabrowska     *
57*dc4d9dc6SAnna Dabrowska     * @var resource
58*dc4d9dc6SAnna Dabrowska     */
59*dc4d9dc6SAnna Dabrowska    protected $stream;
60*dc4d9dc6SAnna Dabrowska
61*dc4d9dc6SAnna Dabrowska    /**
62*dc4d9dc6SAnna Dabrowska     * The byte-offset position in the stream.
63*dc4d9dc6SAnna Dabrowska     *
64*dc4d9dc6SAnna Dabrowska     * @var int
65*dc4d9dc6SAnna Dabrowska     */
66*dc4d9dc6SAnna Dabrowska    protected $position;
67*dc4d9dc6SAnna Dabrowska
68*dc4d9dc6SAnna Dabrowska    /**
69*dc4d9dc6SAnna Dabrowska     * The byte-offset position in the buffer.
70*dc4d9dc6SAnna Dabrowska     *
71*dc4d9dc6SAnna Dabrowska     * @var int
72*dc4d9dc6SAnna Dabrowska     */
73*dc4d9dc6SAnna Dabrowska    protected $offset;
74*dc4d9dc6SAnna Dabrowska
75*dc4d9dc6SAnna Dabrowska    /**
76*dc4d9dc6SAnna Dabrowska     * The buffer length.
77*dc4d9dc6SAnna Dabrowska     *
78*dc4d9dc6SAnna Dabrowska     * @var int
79*dc4d9dc6SAnna Dabrowska     */
80*dc4d9dc6SAnna Dabrowska    protected $bufferLength;
81*dc4d9dc6SAnna Dabrowska
82*dc4d9dc6SAnna Dabrowska    /**
83*dc4d9dc6SAnna Dabrowska     * The total length of the stream.
84*dc4d9dc6SAnna Dabrowska     *
85*dc4d9dc6SAnna Dabrowska     * @var int
86*dc4d9dc6SAnna Dabrowska     */
87*dc4d9dc6SAnna Dabrowska    protected $totalLength;
88*dc4d9dc6SAnna Dabrowska
89*dc4d9dc6SAnna Dabrowska    /**
90*dc4d9dc6SAnna Dabrowska     * The buffer.
91*dc4d9dc6SAnna Dabrowska     *
92*dc4d9dc6SAnna Dabrowska     * @var string
93*dc4d9dc6SAnna Dabrowska     */
94*dc4d9dc6SAnna Dabrowska    protected $buffer;
95*dc4d9dc6SAnna Dabrowska
96*dc4d9dc6SAnna Dabrowska    /**
97*dc4d9dc6SAnna Dabrowska     * StreamReader constructor.
98*dc4d9dc6SAnna Dabrowska     *
99*dc4d9dc6SAnna Dabrowska     * @param resource $stream
100*dc4d9dc6SAnna Dabrowska     * @param bool $closeStream Defines whether to close the stream resource if the instance is destructed or not.
101*dc4d9dc6SAnna Dabrowska     */
102*dc4d9dc6SAnna Dabrowska    public function __construct($stream, $closeStream = false)
103*dc4d9dc6SAnna Dabrowska    {
104*dc4d9dc6SAnna Dabrowska        if (!\is_resource($stream)) {
105*dc4d9dc6SAnna Dabrowska            throw new \InvalidArgumentException(
106*dc4d9dc6SAnna Dabrowska                'No stream given.'
107*dc4d9dc6SAnna Dabrowska            );
108*dc4d9dc6SAnna Dabrowska        }
109*dc4d9dc6SAnna Dabrowska
110*dc4d9dc6SAnna Dabrowska        $metaData = \stream_get_meta_data($stream);
111*dc4d9dc6SAnna Dabrowska        if (!$metaData['seekable']) {
112*dc4d9dc6SAnna Dabrowska            throw new \InvalidArgumentException(
113*dc4d9dc6SAnna Dabrowska                'Given stream is not seekable!'
114*dc4d9dc6SAnna Dabrowska            );
115*dc4d9dc6SAnna Dabrowska        }
116*dc4d9dc6SAnna Dabrowska
117*dc4d9dc6SAnna Dabrowska        $this->stream = $stream;
118*dc4d9dc6SAnna Dabrowska        $this->closeStream = $closeStream;
119*dc4d9dc6SAnna Dabrowska        $this->reset();
120*dc4d9dc6SAnna Dabrowska    }
121*dc4d9dc6SAnna Dabrowska
122*dc4d9dc6SAnna Dabrowska    /**
123*dc4d9dc6SAnna Dabrowska     * The destructor.
124*dc4d9dc6SAnna Dabrowska     */
125*dc4d9dc6SAnna Dabrowska    public function __destruct()
126*dc4d9dc6SAnna Dabrowska    {
127*dc4d9dc6SAnna Dabrowska        $this->cleanUp();
128*dc4d9dc6SAnna Dabrowska    }
129*dc4d9dc6SAnna Dabrowska
130*dc4d9dc6SAnna Dabrowska    /**
131*dc4d9dc6SAnna Dabrowska     * Closes the file handle.
132*dc4d9dc6SAnna Dabrowska     */
133*dc4d9dc6SAnna Dabrowska    public function cleanUp()
134*dc4d9dc6SAnna Dabrowska    {
135*dc4d9dc6SAnna Dabrowska        if ($this->closeStream && is_resource($this->stream)) {
136*dc4d9dc6SAnna Dabrowska            \fclose($this->stream);
137*dc4d9dc6SAnna Dabrowska        }
138*dc4d9dc6SAnna Dabrowska    }
139*dc4d9dc6SAnna Dabrowska
140*dc4d9dc6SAnna Dabrowska    /**
141*dc4d9dc6SAnna Dabrowska     * Returns the byte length of the buffer.
142*dc4d9dc6SAnna Dabrowska     *
143*dc4d9dc6SAnna Dabrowska     * @param bool $atOffset
144*dc4d9dc6SAnna Dabrowska     * @return int
145*dc4d9dc6SAnna Dabrowska     */
146*dc4d9dc6SAnna Dabrowska    public function getBufferLength($atOffset = false)
147*dc4d9dc6SAnna Dabrowska    {
148*dc4d9dc6SAnna Dabrowska        if ($atOffset === false) {
149*dc4d9dc6SAnna Dabrowska            return $this->bufferLength;
150*dc4d9dc6SAnna Dabrowska        }
151*dc4d9dc6SAnna Dabrowska
152*dc4d9dc6SAnna Dabrowska        return $this->bufferLength - $this->offset;
153*dc4d9dc6SAnna Dabrowska    }
154*dc4d9dc6SAnna Dabrowska
155*dc4d9dc6SAnna Dabrowska    /**
156*dc4d9dc6SAnna Dabrowska     * Get the current position in the stream.
157*dc4d9dc6SAnna Dabrowska     *
158*dc4d9dc6SAnna Dabrowska     * @return int
159*dc4d9dc6SAnna Dabrowska     */
160*dc4d9dc6SAnna Dabrowska    public function getPosition()
161*dc4d9dc6SAnna Dabrowska    {
162*dc4d9dc6SAnna Dabrowska        return $this->position;
163*dc4d9dc6SAnna Dabrowska    }
164*dc4d9dc6SAnna Dabrowska
165*dc4d9dc6SAnna Dabrowska    /**
166*dc4d9dc6SAnna Dabrowska     * Returns the current buffer.
167*dc4d9dc6SAnna Dabrowska     *
168*dc4d9dc6SAnna Dabrowska     * @param bool $atOffset
169*dc4d9dc6SAnna Dabrowska     * @return string
170*dc4d9dc6SAnna Dabrowska     */
171*dc4d9dc6SAnna Dabrowska    public function getBuffer($atOffset = true)
172*dc4d9dc6SAnna Dabrowska    {
173*dc4d9dc6SAnna Dabrowska        if ($atOffset === false) {
174*dc4d9dc6SAnna Dabrowska            return $this->buffer;
175*dc4d9dc6SAnna Dabrowska        }
176*dc4d9dc6SAnna Dabrowska
177*dc4d9dc6SAnna Dabrowska        $string = \substr($this->buffer, $this->offset);
178*dc4d9dc6SAnna Dabrowska
179*dc4d9dc6SAnna Dabrowska        return (string) $string;
180*dc4d9dc6SAnna Dabrowska    }
181*dc4d9dc6SAnna Dabrowska
182*dc4d9dc6SAnna Dabrowska    /**
183*dc4d9dc6SAnna Dabrowska     * Gets a byte at a specific position in the buffer.
184*dc4d9dc6SAnna Dabrowska     *
185*dc4d9dc6SAnna Dabrowska     * If the position is invalid the method will return false.
186*dc4d9dc6SAnna Dabrowska     *
187*dc4d9dc6SAnna Dabrowska     * If the $position parameter is set to null the value of $this->offset will be used.
188*dc4d9dc6SAnna Dabrowska     *
189*dc4d9dc6SAnna Dabrowska     * @param int|null $position
190*dc4d9dc6SAnna Dabrowska     * @return string|bool
191*dc4d9dc6SAnna Dabrowska     */
192*dc4d9dc6SAnna Dabrowska    public function getByte($position = null)
193*dc4d9dc6SAnna Dabrowska    {
194*dc4d9dc6SAnna Dabrowska        $position = (int) ($position !== null ? $position : $this->offset);
195*dc4d9dc6SAnna Dabrowska        if ($position >= $this->bufferLength &&
196*dc4d9dc6SAnna Dabrowska            (!$this->increaseLength() || $position >= $this->bufferLength)
197*dc4d9dc6SAnna Dabrowska        ) {
198*dc4d9dc6SAnna Dabrowska            return false;
199*dc4d9dc6SAnna Dabrowska        }
200*dc4d9dc6SAnna Dabrowska
201*dc4d9dc6SAnna Dabrowska        return $this->buffer[$position];
202*dc4d9dc6SAnna Dabrowska    }
203*dc4d9dc6SAnna Dabrowska
204*dc4d9dc6SAnna Dabrowska    /**
205*dc4d9dc6SAnna Dabrowska     * Returns a byte at a specific position, and set the offset to the next byte position.
206*dc4d9dc6SAnna Dabrowska     *
207*dc4d9dc6SAnna Dabrowska     * If the position is invalid the method will return false.
208*dc4d9dc6SAnna Dabrowska     *
209*dc4d9dc6SAnna Dabrowska     * If the $position parameter is set to null the value of $this->offset will be used.
210*dc4d9dc6SAnna Dabrowska     *
211*dc4d9dc6SAnna Dabrowska     * @param int|null $position
212*dc4d9dc6SAnna Dabrowska     * @return string|bool
213*dc4d9dc6SAnna Dabrowska     */
214*dc4d9dc6SAnna Dabrowska    public function readByte($position = null)
215*dc4d9dc6SAnna Dabrowska    {
216*dc4d9dc6SAnna Dabrowska        if ($position !== null) {
217*dc4d9dc6SAnna Dabrowska            $position = (int) $position;
218*dc4d9dc6SAnna Dabrowska            // check if needed bytes are available in the current buffer
219*dc4d9dc6SAnna Dabrowska            if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
220*dc4d9dc6SAnna Dabrowska                $this->reset($position);
221*dc4d9dc6SAnna Dabrowska                $offset = $this->offset;
222*dc4d9dc6SAnna Dabrowska            } else {
223*dc4d9dc6SAnna Dabrowska                $offset = $position - $this->position;
224*dc4d9dc6SAnna Dabrowska            }
225*dc4d9dc6SAnna Dabrowska        } else {
226*dc4d9dc6SAnna Dabrowska            $offset = $this->offset;
227*dc4d9dc6SAnna Dabrowska        }
228*dc4d9dc6SAnna Dabrowska
229*dc4d9dc6SAnna Dabrowska        if ($offset >= $this->bufferLength &&
230*dc4d9dc6SAnna Dabrowska            ((!$this->increaseLength()) || $offset >= $this->bufferLength)
231*dc4d9dc6SAnna Dabrowska        ) {
232*dc4d9dc6SAnna Dabrowska            return false;
233*dc4d9dc6SAnna Dabrowska        }
234*dc4d9dc6SAnna Dabrowska
235*dc4d9dc6SAnna Dabrowska        $this->offset = $offset + 1;
236*dc4d9dc6SAnna Dabrowska        return $this->buffer[$offset];
237*dc4d9dc6SAnna Dabrowska    }
238*dc4d9dc6SAnna Dabrowska
239*dc4d9dc6SAnna Dabrowska    /**
240*dc4d9dc6SAnna Dabrowska     * Read bytes from the current or a specific offset position and set the internal pointer to the next byte.
241*dc4d9dc6SAnna Dabrowska     *
242*dc4d9dc6SAnna Dabrowska     * If the position is invalid the method will return false.
243*dc4d9dc6SAnna Dabrowska     *
244*dc4d9dc6SAnna Dabrowska     * If the $position parameter is set to null the value of $this->offset will be used.
245*dc4d9dc6SAnna Dabrowska     *
246*dc4d9dc6SAnna Dabrowska     * @param int $length
247*dc4d9dc6SAnna Dabrowska     * @param int|null $position
248*dc4d9dc6SAnna Dabrowska     * @return string
249*dc4d9dc6SAnna Dabrowska     */
250*dc4d9dc6SAnna Dabrowska    public function readBytes($length, $position = null)
251*dc4d9dc6SAnna Dabrowska    {
252*dc4d9dc6SAnna Dabrowska        $length = (int) $length;
253*dc4d9dc6SAnna Dabrowska        if ($position !== null) {
254*dc4d9dc6SAnna Dabrowska            // check if needed bytes are available in the current buffer
255*dc4d9dc6SAnna Dabrowska            if (!($position >= $this->position && $position < $this->position + $this->bufferLength)) {
256*dc4d9dc6SAnna Dabrowska                $this->reset($position, $length);
257*dc4d9dc6SAnna Dabrowska                $offset = $this->offset;
258*dc4d9dc6SAnna Dabrowska            } else {
259*dc4d9dc6SAnna Dabrowska                $offset = $position - $this->position;
260*dc4d9dc6SAnna Dabrowska            }
261*dc4d9dc6SAnna Dabrowska        } else {
262*dc4d9dc6SAnna Dabrowska            $offset = $this->offset;
263*dc4d9dc6SAnna Dabrowska        }
264*dc4d9dc6SAnna Dabrowska
265*dc4d9dc6SAnna Dabrowska        if (($offset + $length) > $this->bufferLength &&
266*dc4d9dc6SAnna Dabrowska            ((!$this->increaseLength($length)) || ($offset + $length) > $this->bufferLength)
267*dc4d9dc6SAnna Dabrowska        ) {
268*dc4d9dc6SAnna Dabrowska            return false;
269*dc4d9dc6SAnna Dabrowska        }
270*dc4d9dc6SAnna Dabrowska
271*dc4d9dc6SAnna Dabrowska        $bytes = \substr($this->buffer, $offset, $length);
272*dc4d9dc6SAnna Dabrowska        $this->offset = $offset + $length;
273*dc4d9dc6SAnna Dabrowska
274*dc4d9dc6SAnna Dabrowska        return $bytes;
275*dc4d9dc6SAnna Dabrowska    }
276*dc4d9dc6SAnna Dabrowska
277*dc4d9dc6SAnna Dabrowska    /**
278*dc4d9dc6SAnna Dabrowska     * Read a line from the current position.
279*dc4d9dc6SAnna Dabrowska     *
280*dc4d9dc6SAnna Dabrowska     * @param int $length
281*dc4d9dc6SAnna Dabrowska     * @return string|bool
282*dc4d9dc6SAnna Dabrowska     */
283*dc4d9dc6SAnna Dabrowska    public function readLine($length = 1024)
284*dc4d9dc6SAnna Dabrowska    {
285*dc4d9dc6SAnna Dabrowska        if ($this->ensureContent() === false) {
286*dc4d9dc6SAnna Dabrowska            return false;
287*dc4d9dc6SAnna Dabrowska        }
288*dc4d9dc6SAnna Dabrowska
289*dc4d9dc6SAnna Dabrowska        $line = '';
290*dc4d9dc6SAnna Dabrowska        while ($this->ensureContent()) {
291*dc4d9dc6SAnna Dabrowska            $char = $this->readByte();
292*dc4d9dc6SAnna Dabrowska
293*dc4d9dc6SAnna Dabrowska            if ($char === "\n") {
294*dc4d9dc6SAnna Dabrowska                break;
295*dc4d9dc6SAnna Dabrowska            }
296*dc4d9dc6SAnna Dabrowska
297*dc4d9dc6SAnna Dabrowska            if ($char === "\r") {
298*dc4d9dc6SAnna Dabrowska                if ($this->getByte() === "\n") {
299*dc4d9dc6SAnna Dabrowska                    $this->addOffset(1);
300*dc4d9dc6SAnna Dabrowska                }
301*dc4d9dc6SAnna Dabrowska                break;
302*dc4d9dc6SAnna Dabrowska            }
303*dc4d9dc6SAnna Dabrowska
304*dc4d9dc6SAnna Dabrowska            $line .= $char;
305*dc4d9dc6SAnna Dabrowska
306*dc4d9dc6SAnna Dabrowska            if (\strlen($line) >= $length) {
307*dc4d9dc6SAnna Dabrowska                break;
308*dc4d9dc6SAnna Dabrowska            }
309*dc4d9dc6SAnna Dabrowska        }
310*dc4d9dc6SAnna Dabrowska
311*dc4d9dc6SAnna Dabrowska        return $line;
312*dc4d9dc6SAnna Dabrowska    }
313*dc4d9dc6SAnna Dabrowska
314*dc4d9dc6SAnna Dabrowska    /**
315*dc4d9dc6SAnna Dabrowska     * Set the offset position in the current buffer.
316*dc4d9dc6SAnna Dabrowska     *
317*dc4d9dc6SAnna Dabrowska     * @param int $offset
318*dc4d9dc6SAnna Dabrowska     */
319*dc4d9dc6SAnna Dabrowska    public function setOffset($offset)
320*dc4d9dc6SAnna Dabrowska    {
321*dc4d9dc6SAnna Dabrowska        if ($offset > $this->bufferLength || $offset < 0) {
322*dc4d9dc6SAnna Dabrowska            throw new \OutOfRangeException(
323*dc4d9dc6SAnna Dabrowska                \sprintf('Offset (%s) out of range (length: %s)', $offset, $this->bufferLength)
324*dc4d9dc6SAnna Dabrowska            );
325*dc4d9dc6SAnna Dabrowska        }
326*dc4d9dc6SAnna Dabrowska
327*dc4d9dc6SAnna Dabrowska        $this->offset = (int) $offset;
328*dc4d9dc6SAnna Dabrowska    }
329*dc4d9dc6SAnna Dabrowska
330*dc4d9dc6SAnna Dabrowska    /**
331*dc4d9dc6SAnna Dabrowska     * Returns the current offset in the current buffer.
332*dc4d9dc6SAnna Dabrowska     *
333*dc4d9dc6SAnna Dabrowska     * @return int
334*dc4d9dc6SAnna Dabrowska     */
335*dc4d9dc6SAnna Dabrowska    public function getOffset()
336*dc4d9dc6SAnna Dabrowska    {
337*dc4d9dc6SAnna Dabrowska        return $this->offset;
338*dc4d9dc6SAnna Dabrowska    }
339*dc4d9dc6SAnna Dabrowska
340*dc4d9dc6SAnna Dabrowska    /**
341*dc4d9dc6SAnna Dabrowska     * Add an offset to the current offset.
342*dc4d9dc6SAnna Dabrowska     *
343*dc4d9dc6SAnna Dabrowska     * @param int $offset
344*dc4d9dc6SAnna Dabrowska     */
345*dc4d9dc6SAnna Dabrowska    public function addOffset($offset)
346*dc4d9dc6SAnna Dabrowska    {
347*dc4d9dc6SAnna Dabrowska        $this->setOffset($this->offset + $offset);
348*dc4d9dc6SAnna Dabrowska    }
349*dc4d9dc6SAnna Dabrowska
350*dc4d9dc6SAnna Dabrowska    /**
351*dc4d9dc6SAnna Dabrowska     * Make sure that there is at least one character beyond the current offset in the buffer.
352*dc4d9dc6SAnna Dabrowska     *
353*dc4d9dc6SAnna Dabrowska     * @return bool
354*dc4d9dc6SAnna Dabrowska     */
355*dc4d9dc6SAnna Dabrowska    public function ensureContent()
356*dc4d9dc6SAnna Dabrowska    {
357*dc4d9dc6SAnna Dabrowska        while ($this->offset >= $this->bufferLength) {
358*dc4d9dc6SAnna Dabrowska            if (!$this->increaseLength()) {
359*dc4d9dc6SAnna Dabrowska                return false;
360*dc4d9dc6SAnna Dabrowska            }
361*dc4d9dc6SAnna Dabrowska        }
362*dc4d9dc6SAnna Dabrowska        return true;
363*dc4d9dc6SAnna Dabrowska    }
364*dc4d9dc6SAnna Dabrowska
365*dc4d9dc6SAnna Dabrowska    /**
366*dc4d9dc6SAnna Dabrowska     * Returns the stream.
367*dc4d9dc6SAnna Dabrowska     *
368*dc4d9dc6SAnna Dabrowska     * @return resource
369*dc4d9dc6SAnna Dabrowska     */
370*dc4d9dc6SAnna Dabrowska    public function getStream()
371*dc4d9dc6SAnna Dabrowska    {
372*dc4d9dc6SAnna Dabrowska        return $this->stream;
373*dc4d9dc6SAnna Dabrowska    }
374*dc4d9dc6SAnna Dabrowska
375*dc4d9dc6SAnna Dabrowska    /**
376*dc4d9dc6SAnna Dabrowska     * Gets the total available length.
377*dc4d9dc6SAnna Dabrowska     *
378*dc4d9dc6SAnna Dabrowska     * @return int
379*dc4d9dc6SAnna Dabrowska     */
380*dc4d9dc6SAnna Dabrowska    public function getTotalLength()
381*dc4d9dc6SAnna Dabrowska    {
382*dc4d9dc6SAnna Dabrowska        if ($this->totalLength === null) {
383*dc4d9dc6SAnna Dabrowska            $stat = \fstat($this->stream);
384*dc4d9dc6SAnna Dabrowska            $this->totalLength = $stat['size'];
385*dc4d9dc6SAnna Dabrowska        }
386*dc4d9dc6SAnna Dabrowska
387*dc4d9dc6SAnna Dabrowska        return $this->totalLength;
388*dc4d9dc6SAnna Dabrowska    }
389*dc4d9dc6SAnna Dabrowska
390*dc4d9dc6SAnna Dabrowska    /**
391*dc4d9dc6SAnna Dabrowska     * Resets the buffer to a position and re-read the buffer with the given length.
392*dc4d9dc6SAnna Dabrowska     *
393*dc4d9dc6SAnna Dabrowska     * If the $pos parameter is negative the start buffer position will be the $pos'th position from
394*dc4d9dc6SAnna Dabrowska     * the end of the file.
395*dc4d9dc6SAnna Dabrowska     *
396*dc4d9dc6SAnna Dabrowska     * If the $pos parameter is negative and the absolute value is bigger then the totalLength of
397*dc4d9dc6SAnna Dabrowska     * the file $pos will set to zero.
398*dc4d9dc6SAnna Dabrowska     *
399*dc4d9dc6SAnna Dabrowska     * @param int|null $pos Start position of the new buffer
400*dc4d9dc6SAnna Dabrowska     * @param int $length Length of the new buffer. Mustn't be negative
401*dc4d9dc6SAnna Dabrowska     */
402*dc4d9dc6SAnna Dabrowska    public function reset($pos = 0, $length = 200)
403*dc4d9dc6SAnna Dabrowska    {
404*dc4d9dc6SAnna Dabrowska        if ($pos === null) {
405*dc4d9dc6SAnna Dabrowska            $pos = $this->position + $this->offset;
406*dc4d9dc6SAnna Dabrowska        } elseif ($pos < 0) {
407*dc4d9dc6SAnna Dabrowska            $pos = \max(0, $this->getTotalLength() + $pos);
408*dc4d9dc6SAnna Dabrowska        }
409*dc4d9dc6SAnna Dabrowska
410*dc4d9dc6SAnna Dabrowska        \fseek($this->stream, $pos);
411*dc4d9dc6SAnna Dabrowska
412*dc4d9dc6SAnna Dabrowska        $this->position = $pos;
413*dc4d9dc6SAnna Dabrowska        $this->buffer = $length > 0 ? \fread($this->stream, $length) : '';
414*dc4d9dc6SAnna Dabrowska        $this->bufferLength = \strlen($this->buffer);
415*dc4d9dc6SAnna Dabrowska        $this->offset = 0;
416*dc4d9dc6SAnna Dabrowska
417*dc4d9dc6SAnna Dabrowska        // If a stream wrapper is in use it is possible that
418*dc4d9dc6SAnna Dabrowska        // length values > 8096 will be ignored, so use the
419*dc4d9dc6SAnna Dabrowska        // increaseLength()-method to correct that behavior
420*dc4d9dc6SAnna Dabrowska        if ($this->bufferLength < $length && $this->increaseLength($length - $this->bufferLength)) {
421*dc4d9dc6SAnna Dabrowska            // increaseLength parameter is $minLength, so cut to have only the required bytes in the buffer
422*dc4d9dc6SAnna Dabrowska            $this->buffer = \substr($this->buffer, 0, $length);
423*dc4d9dc6SAnna Dabrowska            $this->bufferLength = \strlen($this->buffer);
424*dc4d9dc6SAnna Dabrowska        }
425*dc4d9dc6SAnna Dabrowska    }
426*dc4d9dc6SAnna Dabrowska
427*dc4d9dc6SAnna Dabrowska    /**
428*dc4d9dc6SAnna Dabrowska     * Ensures bytes in the buffer with a specific length and location in the file.
429*dc4d9dc6SAnna Dabrowska     *
430*dc4d9dc6SAnna Dabrowska     * @param int $pos
431*dc4d9dc6SAnna Dabrowska     * @param int $length
432*dc4d9dc6SAnna Dabrowska     * @see reset()
433*dc4d9dc6SAnna Dabrowska     */
434*dc4d9dc6SAnna Dabrowska    public function ensure($pos, $length)
435*dc4d9dc6SAnna Dabrowska    {
436*dc4d9dc6SAnna Dabrowska        if ($pos >= $this->position
437*dc4d9dc6SAnna Dabrowska            && $pos < ($this->position + $this->bufferLength)
438*dc4d9dc6SAnna Dabrowska            && ($this->position + $this->bufferLength) >= ($pos + $length)
439*dc4d9dc6SAnna Dabrowska        ) {
440*dc4d9dc6SAnna Dabrowska            $this->offset = $pos - $this->position;
441*dc4d9dc6SAnna Dabrowska        } else {
442*dc4d9dc6SAnna Dabrowska            $this->reset($pos, $length);
443*dc4d9dc6SAnna Dabrowska        }
444*dc4d9dc6SAnna Dabrowska    }
445*dc4d9dc6SAnna Dabrowska
446*dc4d9dc6SAnna Dabrowska    /**
447*dc4d9dc6SAnna Dabrowska     * Forcefully read more data into the buffer.
448*dc4d9dc6SAnna Dabrowska     *
449*dc4d9dc6SAnna Dabrowska     * @param int $minLength
450*dc4d9dc6SAnna Dabrowska     * @return bool Returns false if the stream reaches the end
451*dc4d9dc6SAnna Dabrowska     */
452*dc4d9dc6SAnna Dabrowska    public function increaseLength($minLength = 100)
453*dc4d9dc6SAnna Dabrowska    {
454*dc4d9dc6SAnna Dabrowska        $length = \max($minLength, 100);
455*dc4d9dc6SAnna Dabrowska
456*dc4d9dc6SAnna Dabrowska        if (\feof($this->stream) || $this->getTotalLength() === $this->position + $this->bufferLength) {
457*dc4d9dc6SAnna Dabrowska            return false;
458*dc4d9dc6SAnna Dabrowska        }
459*dc4d9dc6SAnna Dabrowska
460*dc4d9dc6SAnna Dabrowska        $newLength = $this->bufferLength + $length;
461*dc4d9dc6SAnna Dabrowska        do {
462*dc4d9dc6SAnna Dabrowska            $this->buffer .= \fread($this->stream, $newLength - $this->bufferLength);
463*dc4d9dc6SAnna Dabrowska            $this->bufferLength = \strlen($this->buffer);
464*dc4d9dc6SAnna Dabrowska        } while (($this->bufferLength !== $newLength) && !\feof($this->stream));
465*dc4d9dc6SAnna Dabrowska
466*dc4d9dc6SAnna Dabrowska        return true;
467*dc4d9dc6SAnna Dabrowska    }
468*dc4d9dc6SAnna Dabrowska}
469