fp = self::quietCall( 'fopen', [ $filename, 'r' ], false, 'Failed to open file' ); } /** * Check if the stream is open * @throws RuntimeException if closed */ private function checkOpen() { if ( !$this->fp ) { throw new RuntimeException( 'Stream is not open' ); } } public function __destruct() { $this->close(); } public function __toString() { try { $this->seek( 0 ); return $this->getContents(); } catch ( Exception $ex ) { // Not allowed to throw return ''; } catch ( Throwable $ex ) { // Not allowed to throw return ''; } } public function close() { if ( $this->fp ) { // Spec doesn't care about close errors. AtEase::quietCall( 'fclose', $this->fp ); $this->fp = null; } } public function detach() { $ret = $this->fp; $this->fp = null; return $ret; } public function getSize() { if ( $this->size === false ) { $this->size = null; if ( $this->fp ) { // Spec doesn't care about errors here. $stat = AtEase::quietCall( 'fstat', $this->fp ); $this->size = $stat['size'] ?? null; } } return $this->size; } public function tell() { $this->checkOpen(); return self::quietCall( 'ftell', [ $this->fp ], -1, 'Cannot determine stream position' ); } public function eof() { // Spec doesn't care about errors here. return !$this->fp || AtEase::quietCall( 'feof', $this->fp ); } public function isSeekable() { return (bool)$this->fp; } public function seek( $offset, $whence = SEEK_SET ) { $this->checkOpen(); self::quietCall( 'fseek', [ $this->fp, $offset, $whence ], -1, 'Seek failed' ); } public function rewind() { $this->seek( 0 ); } public function isWritable() { return false; } public function write( $string ) { $this->checkOpen(); throw new RuntimeException( 'Stream is read-only' ); } public function isReadable() { return (bool)$this->fp; } public function read( $length ) { $this->checkOpen(); return self::quietCall( 'fread', [ $this->fp, $length ], false, 'Read failed' ); } public function getContents() { $this->checkOpen(); return self::quietCall( 'stream_get_contents', [ $this->fp ], false, 'Read failed' ); } public function getMetadata( $key = null ) { $this->checkOpen(); $ret = self::quietCall( 'stream_get_meta_data', [ $this->fp ], false, 'Metadata fetch failed' ); if ( $key !== null ) { $ret = $ret[$key] ?? null; } return $ret; } }