2014-02-12 20:07:05 -08:00
|
|
|
=======
|
|
|
|
Streams
|
|
|
|
=======
|
2014-03-10 21:03:52 -07:00
|
|
|
|
|
|
|
Guzzle uses stream objects to represent request and response message bodies.
|
|
|
|
These stream objects allow you to work with various types of data all using a
|
|
|
|
common interface.
|
|
|
|
|
|
|
|
HTTP messages consist of a start-line, headers, and a body. The body of an HTTP
|
|
|
|
message can be very small or extremely large. Attempting to represent the body
|
|
|
|
of a message as a string can easily consume more memory than intended because
|
|
|
|
the body must be stored completely in memory. Attempting to store the body of a
|
|
|
|
request or response in memory would preclude the use of that implementation from
|
|
|
|
being able to work with large message bodies. The StreamInterface is used in
|
|
|
|
order to hide the implementation details of where a stream of data is read from
|
|
|
|
or written to.
|
|
|
|
|
|
|
|
Guzzle's StreamInterface exposes several methods that enable streams to be read
|
|
|
|
from, written to, and traversed effectively.
|
|
|
|
|
|
|
|
Streams expose their capabilities using three methods: ``isReadable()``,
|
|
|
|
``isWritable()``, and ``isSeekable()``. These methods can be used by stream
|
|
|
|
collaborators to determine if a stream is capable of their requirements.
|
|
|
|
|
|
|
|
Each stream instance has various capabilities: they can be read-only,
|
|
|
|
write-only, read-write, allow arbitrary random access (seeking forwards or
|
|
|
|
backwards to any location), or only allow sequential access (for example in the
|
|
|
|
case of a socket or pipe).
|
|
|
|
|
|
|
|
Creating Streams
|
|
|
|
================
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
The best way to create a stream is using the static factory method,
|
|
|
|
``GuzzleHttp\Stream\Stream::factory()``. This factory accepts strings,
|
2014-03-10 21:03:52 -07:00
|
|
|
resources returned from ``fopen()``, an object that implements
|
|
|
|
``__toString()``, and an object that implements
|
2014-03-16 20:03:33 -07:00
|
|
|
``GuzzleHttp\Stream\StreamInterface``.
|
2014-03-10 21:03:52 -07:00
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
$stream = Stream::factory('string data');
|
2014-03-10 21:03:52 -07:00
|
|
|
echo $stream;
|
|
|
|
// string data
|
|
|
|
echo $stream->read(3);
|
|
|
|
// str
|
|
|
|
echo $stream->getContents();
|
|
|
|
// ing data
|
|
|
|
var_export($stream->eof());
|
|
|
|
// true
|
|
|
|
var_export($stream->tell());
|
|
|
|
// 11
|
|
|
|
|
2014-09-17 23:41:49 -07:00
|
|
|
Metadata
|
|
|
|
========
|
2014-03-10 21:03:52 -07:00
|
|
|
|
2014-09-17 23:41:49 -07:00
|
|
|
Guzzle streams expose stream metadata through the ``getMetadata()`` method.
|
|
|
|
This method provides the data you would retrieve when calling PHP's
|
|
|
|
`stream_get_meta_data() function <http://php.net/manual/en/function.stream-get-meta-data.php>`_,
|
|
|
|
and can optionally expose other custom data.
|
2014-03-10 21:03:52 -07:00
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
|
|
|
|
$resource = fopen('/path/to/file', 'r');
|
2014-08-16 18:13:59 -07:00
|
|
|
$stream = Stream::factory($resource);
|
2014-03-10 21:03:52 -07:00
|
|
|
echo $stream->getMetadata('uri');
|
|
|
|
// /path/to/file
|
|
|
|
var_export($stream->isReadable());
|
|
|
|
// true
|
|
|
|
var_export($stream->isWritable());
|
|
|
|
// false
|
|
|
|
var_export($stream->isSeekable());
|
|
|
|
// true
|
|
|
|
|
|
|
|
Stream Decorators
|
|
|
|
=================
|
|
|
|
|
|
|
|
With the small and focused interface, add custom functionality to streams is
|
|
|
|
very simple with stream decorators. Guzzle provides several built-in decorators
|
|
|
|
that provide additional stream functionality.
|
|
|
|
|
|
|
|
CachingStream
|
|
|
|
-------------
|
|
|
|
|
|
|
|
The CachingStream is used to allow seeking over previously read bytes on
|
|
|
|
non-seekable streams. This can be useful when transferring a non-seekable
|
|
|
|
entity body fails due to needing to rewind the stream (for example, resulting
|
|
|
|
from a redirect). Data that is read from the remote stream will be buffered in
|
|
|
|
a PHP temp stream so that previously read bytes are cached first in memory,
|
|
|
|
then on disk.
|
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
use GuzzleHttp\Stream\CachingStream;
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
$original = Stream::factory(fopen('http://www.google.com', 'r'));
|
2014-03-10 21:03:52 -07:00
|
|
|
$stream = new CachingStream($original);
|
|
|
|
|
|
|
|
$stream->read(1024);
|
|
|
|
echo $stream->tell();
|
|
|
|
// 1024
|
|
|
|
|
|
|
|
$stream->seek(0);
|
|
|
|
echo $stream->tell();
|
|
|
|
// 0
|
|
|
|
|
|
|
|
LimitStream
|
|
|
|
-----------
|
|
|
|
|
|
|
|
LimitStream can be used to read a subset or slice of an existing stream object.
|
|
|
|
This can be useful for breaking a large file into smaller pieces to be sent in
|
|
|
|
chunks (e.g. Amazon S3's multipart upload API).
|
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
use GuzzleHttp\Stream\LimitStream;
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
$original = Stream::factory(fopen('/tmp/test.txt', 'r+'));
|
2014-03-10 21:03:52 -07:00
|
|
|
echo $original->getSize();
|
|
|
|
// >>> 1048576
|
|
|
|
|
|
|
|
// Limit the size of the body to 1024 bytes and start reading from byte 2048
|
|
|
|
$stream = new LimitStream($original, 1024, 2048);
|
|
|
|
echo $stream->getSize();
|
|
|
|
// >>> 1024
|
|
|
|
echo $stream->tell();
|
|
|
|
// >>> 0
|
|
|
|
|
|
|
|
NoSeekStream
|
|
|
|
------------
|
|
|
|
|
|
|
|
NoSeekStream wraps a stream and does not allow seeking.
|
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
use GuzzleHttp\Stream\LimitStream;
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
$original = Stream::factory('foo');
|
2014-03-10 21:03:52 -07:00
|
|
|
$noSeek = new NoSeekStream($original);
|
|
|
|
|
|
|
|
echo $noSeek->read(3);
|
|
|
|
// foo
|
|
|
|
var_export($noSeek->isSeekable());
|
|
|
|
// false
|
|
|
|
$noSeek->seek(0);
|
|
|
|
var_export($noSeek->read(3));
|
|
|
|
// NULL
|
|
|
|
|
|
|
|
Creating Custom Decorators
|
|
|
|
--------------------------
|
|
|
|
|
|
|
|
Creating a stream decorator is very easy thanks to the
|
|
|
|
``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that
|
|
|
|
implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying
|
|
|
|
stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom
|
|
|
|
methods.
|
|
|
|
|
|
|
|
For example, let's say we wanted to call a specific function each time the last
|
|
|
|
byte is read from a stream. This could be implemented by overriding the
|
|
|
|
``read()`` method.
|
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
|
|
|
use GuzzleHttp\Stream\StreamDecoratorTrait;
|
|
|
|
|
2014-09-17 23:41:49 -07:00
|
|
|
class EofCallbackStream implements StreamInterface
|
2014-03-10 21:03:52 -07:00
|
|
|
{
|
|
|
|
use StreamDecoratorTrait;
|
|
|
|
|
|
|
|
private $callback;
|
|
|
|
|
|
|
|
public function __construct(StreamInterface $stream, callable $callback)
|
|
|
|
{
|
|
|
|
$this->stream = $stream;
|
|
|
|
$this->callback = $callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function read($length)
|
|
|
|
{
|
|
|
|
$result = $this->stream->read($length);
|
|
|
|
|
|
|
|
// Invoke the callback when EOF is hit.
|
|
|
|
if ($this->eof()) {
|
|
|
|
call_user_func($this->callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
This decorator could be added to any existing stream and used like so:
|
|
|
|
|
|
|
|
.. code-block:: php
|
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
use GuzzleHttp\Stream\Stream;
|
2014-03-10 21:03:52 -07:00
|
|
|
|
2014-08-16 18:13:59 -07:00
|
|
|
$original = Stream::factory('foo');
|
2014-03-10 21:03:52 -07:00
|
|
|
$eofStream = new EofCallbackStream($original, function () {
|
|
|
|
echo 'EOF!';
|
|
|
|
});
|
|
|
|
|
|
|
|
$eofStream->read(2);
|
|
|
|
$eofStream->read(1);
|
|
|
|
// echoes "EOF!"
|
|
|
|
$eofStream->seek(0);
|
|
|
|
$eofStream->read(3);
|
|
|
|
// echoes "EOF!"
|