Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Jun 25, 2012
0 parents commit 8c34922
Show file tree
Hide file tree
Showing 14 changed files with 821 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
composer.phar
composer.lock
vendor
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2012 Francois Zaninotto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Streamer

Streamer is an Object-Oriented API for PHP streams.

## Why should I use Streams?

A stream is a flow of bytes from one container to the other. You already use streams a lot in PHP, for instance each time you load a file into memory (`file_get_contents()`). You should explicitly use streams each time that:

* You need to access data from a container, but you don't know the size of this container (e.g. reading from STDIN, or a web service using streaming)
* You need to start processing data from a container before the whole transfer is finished (e.g. start zipping a file before it's all in memory)
* You need to save time and memory

## What is Streamer?

PHP has a very elaborate stream API ; unfortunately, it uses functions for most stream operations (except for wrappers - go figure). Streamer is a generic library focusing on offering an object-oriented API to streams, and only that.

## Example

```php
use Streamer/Stream;

// basic usage
$stream = new Stream(fopen('smiley.png', 'r'));
$image = '';
while (!$stream->isAtEnd()) {
$image .= $stream->read();
}

// pipe dreams!
$stream1 = new Stream(fopen('smiley.png', 'r'));
$stream2 = new Stream(fopen('tmp.png', 'w'));
// copy the contents from the first stream to the second one
$stream1->pipe($stream2);
```

### Credits

Streamer is heavily inspired by other Stream class implementations:

* [Guzzle](https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Common/Stream.php)
* [Joomla's Filesystem Stream](http://api.joomla.org/__filesource/fsource_Joomla-Platform_FileSystem--_librariesjoomlafilesystemstream.php.html)
* [Node.Js Stream API](http://nodejs.org/api/stream.html)
7 changes: 7 additions & 0 deletions Streamer/Exception/ExceptionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Streamer\Exception;

interface ExceptionInterface
{
}
7 changes: 7 additions & 0 deletions Streamer/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Streamer\Exception;

class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}
7 changes: 7 additions & 0 deletions Streamer/Exception/LogicException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Streamer\Exception;

class LogicException extends \LogicException implements ExceptionInterface
{
}
7 changes: 7 additions & 0 deletions Streamer/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Streamer\Exception;

class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
11 changes: 11 additions & 0 deletions Streamer/FileStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Streamer;

class FileStream extends Stream
{
public static function create($filename, $mode, $use_include_path = false, $context = null)
{
return new static(fopen($filename, $mode, $use_include_path, $context));
}
}
17 changes: 17 additions & 0 deletions Streamer/NetworkStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Streamer;

class NetworkStream extends Stream
{
public static function create($address, $timeout = null, $flags = null, $context = null)
{
return new static(stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context));
}

public function getName($remote = true)
{
return stream_socket_get_name($this->stream, $remote);
}

}
246 changes: 246 additions & 0 deletions Streamer/Stream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<?php

namespace Streamer;

use Streamer\Exception\InvalidArgumentException;
use Streamer\Exception\LogicException;
use Streamer\Exception\RuntimeException;

class Stream
{
protected $bufferSize = 4096;
protected $stream;
protected $isOpen;

protected static $readableModes = array(
'r', 'r+', 'w+', 'a+', 'x+', 'c+', 'rb', 'r+b', 'w+b', 'a+b', 'x+b', 'c+b', 'rt', 'r+t', 'w+t', 'a+t', 'x+t', 'c+t'
);
protected static $writableModes = array(
'r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+', 'r+b', 'wb', 'w+b', 'ab', 'a+b', 'xb', 'x+b', 'cb', 'c+b', 'r+t', 'wt', 'w+t', 'at', 'a+t', 'xt', 'x+t', 'ct', 'c+t'
);

public function __construct($stream)
{
if (!is_resource($stream)) {
throw new InvalidArgumentException('A Stream object requires a stream resource as constructor argument');
}
$this->stream = $stream;
$this->isOpen = true;
}

public function getResource()
{
return $this->stream;
}

public function getMetadata()
{
return stream_get_meta_data($this->stream);
}

public function getMetadataForKey($key)
{
$metadata = $this->getMetadata();
if (isset($metadata[$key])) {
return $metadata[$key];
}
}

public function getUri()
{
return $this->getMetadataForKey('uri');
}

public function getStreamType()
{
return $this->getMetadataForKey('stream_type');
}

public function getWrapperType()
{
return $this->getMetadataForKey('wrapper_type');
}

public function getWrapperData()
{
return $this->getMetadataForKey('wrapper_data');
}

/**
* @return Boolean
*/
public function isLocal()
{
return stream_is_local($this->stream);
}

/**
* @return Boolean
*/
public function isReadable()
{
return in_array($this->getMetadataForKey('mode'), self::$readableModes);
}

/**
* @return Boolean
*/
public function isWritable()
{
return in_array($this->getMetadataForKey('mode'), self::$writableModes);
}

/**
* @return Boolean
*/
public function isSeekable()
{
return $this->getMetadataForKey('seekable');
}

/**
* @return Boolean
*/
public function isOpen()
{
return $this->isOpen;
}

/**
* @param int $bufferSize in Bytes
*/
public function setBufferSize($bufferSize)
{
$this->bufferSize = $bufferSize;
}

/**
* @return int BufferSize in Bytes
*/
public function getBufferSize()
{
return $this->bufferSize;
}

/**
* Read data from the stream.
* Binary-safe.
*
* @param int $length Maximum number of bytes to read. Defaults to self::$bufferSize.
*
* @return string The data read from the stream
*/
public function read($length = null)
{
if (!$this->isOpen) {
throw new LogicException('Cannot read from a closed stream');
}
if (!$this->isReadable()) {
throw new LogicException(sprintf('Cannot read on a non readable stream (current mode is %s)', $this->getMetadataForKey('mode')));
}
if (null == $length) {
$length = $this->bufferSize;
}
$ret = fread($this->stream, $length);
if (false === $ret) {
throw new RuntimeException('Cannot read stream');
}

return $ret;
}

/**
* @return Boolean
*/
public function isAtEnd()
{
return feof($this->stream);
}

/**
* Read the remaining data from the stream until its end.
* Binary-safe.
*
* @return string The data read from the stream
*/
public function getContent()
{
$contents = '';
while (!$this->isAtEnd()) {
$contents .= $this->read();
}
$this->close();

return $contents;
}

/**
* Write data to the stream.
* Binary-safe.
*
* @param string $string The string that is to be written.
* @param int $length If the length argument is given, writing will stop after length bytes have been written or the end of string is reached, whichever comes first.
*
* @return int Number of bytes written
*/
public function write($string, $length = null)
{
if (!$this->isOpen) {
throw new LogicException('Cannot write on a closed stream');
}
if (!$this->isWritable()) {
throw new LogicException(sprintf('Cannot write on a non-writable stream (current mode is %s)', $this->getMetadataForKey('mode')));
}
if (null === $length) {
$ret = fwrite($this->stream, $string);
} else {
$ret = fwrite($this->stream, $string, $length);
}
if (false === $ret) {
throw new RuntimeException('Cannot write on stream');
}

return $ret;
}

/**
* Read the content of this stream and write it to another stream, by chunks of $bufferSize
*
* @param Stream $stream The destination stream to write to
*
* @return int Number of piped bytes
*/
public function pipe(Stream $stream)
{
$nbWrittenBytes = 0;
while (!$this->isAtEnd()) {
$nbWrittenBytes += $stream->write($this->read());
}

return $nbWrittenBytes;
}

public function rewind()
{
rewind($this->stream);
}

public function close()
{
if (!$this->isOpen) {
throw new LogicException('Stream is already closed');
}
if ($ret = fclose($this->stream)) {
$this->isOpen = false;
}

return $ret;
}

public function __destruct()
{
if (is_resource($this->stream)) {
fclose($this->stream);
}
}
}
16 changes: 16 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "fzaninotto/Streamer",
"description": "Object-oriented API to PHP streams",
"homepage": "https://github.com/fzaninotto/Streamer",
"version": "0.0.1",
"license": "MIT",
"authors": [
{ "name": "Francois Zaninotto" }
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": { "Streamer": "." }
}
}
Loading

0 comments on commit 8c34922

Please sign in to comment.