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