<?php
namespace Guzzle\Stream;
use Guzzle\Common\Exception\InvalidArgumentException;
class Stream implements StreamInterface {
const STREAM_TYPE = 'stream_type';
const WRAPPER_TYPE = 'wrapper_type';
const IS_LOCAL = 'is_local';
const IS_READABLE = 'is_readable';
const IS_WRITABLE = 'is_writable';
const SEEKABLE = 'seekable';
protected $stream;
protected $size;
protected $cache = array();
protected static $readWriteHash = array(
'read' => array(
'r' => true,
'w+' => true,
'r+' => true,
'x+' => true,
'c+' => true,
'rb' => true,
'w+b' => true,
'r+b' => true,
'x+b' => true,
'c+b' => true,
'rt' => true,
'w+t' => true,
'r+t' => true,
'x+t' => true,
'c+t' => true,
),
'write' => array(
'w' => true,
'w+' => true,
'rw' => true,
'r+' => true,
'x+' => true,
'c+' => true,
'w+b' => true,
'r+b' => true,
'x+b' => true,
'c+b' => true,
'w+t' => true,
'r+t' => true,
'x+t' => true,
'c+t' => true,
),
);
public function __construct($stream, $size = null) {
$this
->setStream($stream, $size);
}
public function __destruct() {
if (is_resource($this->stream)) {
fclose($this->stream);
}
}
public function __toString() {
if (!$this
->isReadable() || !$this
->isSeekable() && $this
->isConsumed()) {
return '';
}
$originalPos = $this
->ftell();
$body = stream_get_contents($this->stream, -1, 0);
$this
->seek($originalPos);
return $body;
}
public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) {
$pos = $stream
->ftell();
if (!$stream
->seek(0)) {
return false;
}
$ctx = hash_init($algo);
while ($data = $stream
->read(1024)) {
hash_update($ctx, $data);
}
$out = hash_final($ctx, (bool) $rawOutput);
$stream
->seek($pos);
return $out;
}
public function getMetaData($key = null) {
$meta = stream_get_meta_data($this->stream);
return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
}
public function getStream() {
return $this->stream;
}
public function setStream($stream, $size = null) {
if (!is_resource($stream)) {
throw new InvalidArgumentException('Stream must be a resource');
}
$this->size = $size;
$this->stream = $stream;
$this
->rebuildCache();
return $this;
}
public function getWrapper() {
return $this->cache[self::WRAPPER_TYPE];
}
public function getWrapperData() {
return $this
->getMetaData('wrapper_data') ?: array();
}
public function getStreamType() {
return $this->cache[self::STREAM_TYPE];
}
public function getUri() {
return $this->cache['uri'];
}
public function getSize() {
if ($this->size !== null) {
return $this->size;
}
if ($this
->isLocal() && $this
->getWrapper() == 'plainfile' && $this
->getUri() && file_exists($this
->getUri())) {
return filesize($this
->getUri());
}
if (!$this->cache[self::IS_READABLE] || !$this->cache[self::SEEKABLE]) {
return false;
}
else {
$pos = $this
->ftell();
$this->size = strlen((string) $this);
$this
->seek($pos);
return $this->size;
}
}
public function isReadable() {
return $this->cache[self::IS_READABLE];
}
public function isWritable() {
return $this->cache[self::IS_WRITABLE];
}
public function isConsumed() {
return feof($this->stream);
}
public function isLocal() {
return $this->cache[self::IS_LOCAL];
}
public function isSeekable() {
return $this->cache[self::SEEKABLE];
}
public function setSize($size) {
$this->size = $size;
return $this;
}
public function seek($offset, $whence = SEEK_SET) {
return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
}
public function read($length) {
return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false;
}
public function write($string) {
if (!$this->cache[self::IS_WRITABLE]) {
return 0;
}
$bytes = fwrite($this->stream, $string);
$this->size += $bytes;
return $bytes;
}
public function ftell() {
return ftell($this->stream);
}
public function rewind() {
return $this
->seek(0);
}
protected function rebuildCache() {
$this->cache = stream_get_meta_data($this->stream);
$this->cache[self::STREAM_TYPE] = strtolower($this->cache[self::STREAM_TYPE]);
$this->cache[self::WRAPPER_TYPE] = strtolower($this->cache[self::WRAPPER_TYPE]);
$this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
$this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
$this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
}
}