Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
68.18% |
15 / 22 |
CRAP | |
90.43% |
104 / 115 |
| Stream | |
0.00% |
0 / 1 |
|
68.18% |
15 / 22 |
59.84 | |
90.43% |
104 / 115 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| __destruct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| __toString | |
0.00% |
0 / 1 |
3.33 | |
66.67% |
4 / 6 |
|||
| close | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| detach | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| attach | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| getSize | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| tell | |
0.00% |
0 / 1 |
3.04 | |
83.33% |
5 / 6 |
|||
| eof | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
| isSeekable | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| seek | |
0.00% |
0 / 1 |
4.25 | |
75.00% |
6 / 8 |
|||
| rewind | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| isWritable | |
100.00% |
1 / 1 |
6 | |
100.00% |
10 / 10 |
|||
| write | |
0.00% |
0 / 1 |
4.03 | |
87.50% |
7 / 8 |
|||
| isReadable | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
| read | |
0.00% |
0 / 1 |
4.25 | |
75.00% |
6 / 8 |
|||
| getContents | |
0.00% |
0 / 1 |
3.04 | |
83.33% |
5 / 6 |
|||
| getMetaData | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
| setStream | |
100.00% |
1 / 1 |
5 | |
100.00% |
3 / 3 |
|||
| anonymous function | |
0.00% |
0 / 1 |
1.12 | |
50.00% |
1 / 2 |
|||
| isAvailableStream | |
100.00% |
1 / 1 |
3 | |
100.00% |
3 / 3 |
|||
| closeTmpStream | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| <?php | |
| namespace Puyo\Psr7; | |
| /** | |
| * ストリームを抽象化したもの | |
| * | |
| * PSR-7 の StreamInterface の実装。 | |
| * HTTPに限らず利用できるが、現状はバイナリにしか対応していない。 | |
| */ | |
| class Stream implements \Psr\Http\Message\StreamInterface | |
| { | |
| /** @var resource */ | |
| protected $fp; | |
| /** | |
| * ファイルのパスから生成した場合に内部で開くためのStream | |
| * @var resource | |
| */ | |
| protected $tmpFp; | |
| /** | |
| * ファイルのフルパスもしくはresourceから生成する | |
| * @param resource|string $fileOrStream | |
| * @param string $mode | |
| */ | |
| public function __construct($fileOrStream, $mode='rb') { | |
| $this->setStream($fileOrStream, $mode); | |
| } | |
| /** | |
| * ファイルパスから生成した場合の一時Streamを解放する | |
| */ | |
| public function __destruct() { | |
| $this->closeTmpStream(); | |
| } | |
| /** | |
| * すべてのデータを文字列で取得する | |
| * @return string content | |
| */ | |
| public function __toString() { | |
| if(!$this->isReadable()) { | |
| return ''; | |
| } | |
| try { | |
| $this->rewind(); | |
| return $this->getContents(); | |
| } catch(\RuntimeException $e) { | |
| return ''; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function close() { | |
| if(!$this->fp) { | |
| return; | |
| } | |
| $fp = $this->detach(); | |
| fclose($fp); | |
| } | |
| /** | |
| * @return resource | |
| */ | |
| public function detach() { | |
| $fp = $this->fp; | |
| $this->fp = null; | |
| return $fp; | |
| } | |
| /** | |
| * streamもしくはファイルのフルパスからstreamを割り当てる | |
| * @param string|resource $fileOrStream | |
| * @param string $mode | |
| */ | |
| public function attach($fileOrStream, $mode='rb') { | |
| $this->closeTmpStream(); | |
| $this->setStream($fileOrStream, $mode); | |
| } | |
| /** | |
| * データサイズを返す | |
| * @return int|null 閉じられている場合はnull | |
| */ | |
| public function getSize() { | |
| if(null === $this->fp) { | |
| return null; | |
| } | |
| $stats = fstat($this->fp); | |
| return $stats['size']; | |
| } | |
| /** | |
| * 現在の書き込みのポインタ位置を返す | |
| * {@inheritdoc} | |
| * @return int | |
| */ | |
| public function tell() { | |
| if(null === $this->fp) { | |
| throw new \RuntimeException('No resource available. Cannot tell position'); | |
| } | |
| $result = ftell($this->fp); | |
| if(!is_int($result)) { | |
| throw new \RuntimeException('Cannot tell position'); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @return bool 終端、もしくは未オープンの場合 | |
| */ | |
| public function eof() { | |
| if(null === $this->fp) { | |
| return true; | |
| } | |
| return feof($this->fp); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @return bool | |
| */ | |
| public function isSeekable() { | |
| if(!$this->isAvailableStream($this->fp)) { | |
| return false; | |
| } | |
| $meta = stream_get_meta_data($this->fp); | |
| return $meta['seekable']; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @param int $offset | |
| * @param int $whence SEEK_* constant | |
| * @return void | |
| * @throws \RuntimeException シーク失敗時 | |
| */ | |
| public function seek($offset, $whence=SEEK_SET) { | |
| if (null === $this->fp) { | |
| throw new \RuntimeException('No resource available. Cannot seek position'); | |
| } | |
| if (!$this->isSeekable()) { | |
| throw new \RuntimeException('Stream is not seekable'); | |
| } | |
| $result = fseek($this->fp, $offset, $whence); | |
| if (0 !== $result) { | |
| throw new \RuntimeException('Error seeking within stream'); | |
| } | |
| } | |
| /** | |
| * ポインタを先頭に戻す | |
| * @return void | |
| */ | |
| public function rewind() { | |
| return $this->seek(0); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @return bool | |
| */ | |
| public function isWritable() { | |
| if(null === $this->fp) { | |
| return false; | |
| } | |
| $meta = stream_get_meta_data($this->fp); | |
| $mode = $meta['mode']; | |
| return ( | |
| strstr($mode, 'x') | |
| || strstr($mode, 'w') | |
| || strstr($mode, 'c') | |
| || strstr($mode, 'a') | |
| || strstr($mode, '+') | |
| ); | |
| } | |
| /** | |
| * 書き込む | |
| * @param string $string | |
| * @return int 書き込みバイト数 | |
| */ | |
| public function write($string) { | |
| if(null === $this->fp) { | |
| throw new \RuntimeException('No resource available. Cannot write'); | |
| } | |
| if (!$this->isWritable()) { | |
| throw new \RuntimeException('Stream is not writable'); | |
| } | |
| $result = fwrite($this->fp, $string); | |
| if(false === $result) { | |
| throw new \RuntimeException('Error writing to stream'); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @return bool | |
| */ | |
| public function isReadable() { | |
| if(null === $this->fp) { | |
| return false; | |
| } | |
| $meta = stream_get_meta_data($this->fp); | |
| $mode = $meta['mode']; | |
| return ( | |
| strstr($mode, 'r') || strstr($mode, '+') | |
| ); | |
| } | |
| /** | |
| * 読み込み | |
| * @param int $length bytes | |
| * @return string 読み込んだデータ | |
| */ | |
| public function read($length) { | |
| if (null === $this->fp) { | |
| throw new \RuntimeException('No resource available. Cannot read'); | |
| } | |
| if (!$this->isReadable()) { | |
| throw new \RuntimeException('Stream is not readable'); | |
| } | |
| $result = fread($this->fp, $length); | |
| if (false === $result) { | |
| throw new \RuntimeException('Error reading from stream'); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * 残りのストリームすべてを返す | |
| * @return string | |
| */ | |
| public function getContents() { | |
| if (!$this->isReadable()) { | |
| throw new \RuntimeException('Stream is not readable'); | |
| } | |
| $result = stream_get_contents($this->fp); | |
| if (false === $result) { | |
| throw new \RuntimeException('Error reading from stream'); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| * @param string $key (optional) | |
| * @return array|null | |
| */ | |
| public function getMetaData($key=null) { | |
| $metadata = stream_get_meta_data($this->fp); | |
| if($key === null) { | |
| return $metadata; | |
| } | |
| if (!array_key_exists($key, $metadata)) { | |
| return null; | |
| } | |
| return $metadata[$key]; | |
| } | |
| private function setStream($fileOrStream, $mode='rb') { | |
| $error = null; | |
| $fp = $tmpFp = null; | |
| if(is_string($fileOrStream)) { | |
| set_error_handler(function ($e) use (&$error) { | |
| $error = $e; | |
| }, E_WARNING); | |
| $tmpFp = fopen($fileOrStream, $mode); | |
| restore_error_handler(); | |
| if($error !== null) { | |
| throw new \InvalidArgumentException('Invalid stream reference provided'); | |
| } | |
| } elseif($this->isAvailableStream($fileOrStream)) { | |
| $fp = $fileOrStream; | |
| } | |
| $this->fp = $fp; | |
| if($tmpFp !== null) { | |
| $this->fp = $this->tmpFp = $tmpFp; | |
| } | |
| } | |
| /** | |
| * @param resource $fp | |
| * @return bool | |
| */ | |
| private function isAvailableStream($fp) { | |
| if(is_resource($fp) && 'stream' === get_resource_type($fp)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| private function closeTmpStream() { | |
| if($this->isAvailableStream($this->tmpFp)) { | |
| fclose($this->tmpFp); | |
| } | |
| } | |
| } |