| 
<?php
 /**
 * This file is part of the PHP Generics package.
 *
 * @package Generics
 */
 namespace Generics\Streams;
 
 use Generics\FileNotFoundException;
 use Generics\LockException;
 use Generics\Lockable;
 
 /**
 * This class provides an input stream for files.
 *
 * @author Maik Greubel <[email protected]>
 */
 class FileInputStream implements InputStream, Lockable
 {
 
 /**
 * The file handle
 *
 * @var resource
 */
 private $handle;
 
 /**
 * The absolute file path and name
 *
 * @var string
 */
 private $fileName;
 
 /**
 * Whether the access is locked
 *
 * @var bool
 */
 private $locked;
 
 /**
 * Create a new FileInputStream
 *
 * @param string $file
 *            The absolute (or relative) path to the file to open
 * @throws FileNotFoundException
 */
 public function __construct($file)
 {
 if (! file_exists($file)) {
 throw new FileNotFoundException("File {file} could not be found", array(
 'file' => $file
 ));
 }
 
 $this->handle = fopen($file, "rb");
 
 if (! $this->ready()) {
 throw new StreamException("Could not open {file} for reading", array(
 'file' => $file
 ));
 }
 
 $this->fileName = $file;
 }
 
 /**
 * Cleanup (e.g.
 * release lock)
 */
 public function __destruct()
 {
 try {
 if ($this->locked) {
 $this->unlock();
 }
 } catch (\Generics\GenericsException $ex) {
 // Do nothing
 }
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::close()
 */
 public function close()
 {
 if ($this->handle != null) {
 fclose($this->handle);
 $this->handle = null;
 }
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::ready()
 */
 public function ready(): bool
 {
 return is_resource($this->handle) && ! feof($this->handle);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\InputStream::read()
 */
 public function read($length = 1, $offset = null): string
 {
 if (! $this->ready()) {
 throw new StreamException("Stream is not ready!");
 }
 
 if ($offset !== null && intval($offset) > 0) {
 if (fseek($this->handle, $offset, SEEK_SET) != 0) {
 throw new StreamException("Could not set offset!");
 }
 }
 
 return fread($this->handle, $length);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Countable::count()
 */
 public function count(): int
 {
 $stat = fstat($this->handle);
 return $stat['size'];
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\InputStream::reset()
 */
 public function reset()
 {
 fseek($this->handle, 0, SEEK_SET);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Lockable::lock()
 */
 public function lock()
 {
 if ($this->locked || flock($this->handle, LOCK_SH) === false) {
 throw new LockException("Could not acquire lock");
 }
 $this->locked = true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Lockable::unlock()
 */
 public function unlock()
 {
 if (! $this->locked || flock($this->handle, LOCK_UN) === false) {
 throw new LockException("Could not release lock");
 }
 $this->locked = false;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Lockable::isLocked()
 */
 public function isLocked(): bool
 {
 return $this->locked;
 }
 
 /**
 * Retrieve the file path and name
 *
 * @return string
 */
 public function __toString(): string
 {
 return realpath($this->fileName);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::isOpen()
 */
 public function isOpen(): bool
 {
 return is_resource($this->handle);
 }
 }
 
 |