scratch – Blame information for rev 87
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
87 | office | 1 | ======= |
2 | Streams |
||
3 | ======= |
||
4 | |||
5 | Guzzle uses stream objects to represent request and response message bodies. |
||
6 | These stream objects allow you to work with various types of data all using a |
||
7 | common interface. |
||
8 | |||
9 | HTTP messages consist of a start-line, headers, and a body. The body of an HTTP |
||
10 | message can be very small or extremely large. Attempting to represent the body |
||
11 | of a message as a string can easily consume more memory than intended because |
||
12 | the body must be stored completely in memory. Attempting to store the body of a |
||
13 | request or response in memory would preclude the use of that implementation from |
||
14 | being able to work with large message bodies. The StreamInterface is used in |
||
15 | order to hide the implementation details of where a stream of data is read from |
||
16 | or written to. |
||
17 | |||
18 | Guzzle's StreamInterface exposes several methods that enable streams to be read |
||
19 | from, written to, and traversed effectively. |
||
20 | |||
21 | Streams expose their capabilities using three methods: ``isReadable()``, |
||
22 | ``isWritable()``, and ``isSeekable()``. These methods can be used by stream |
||
23 | collaborators to determine if a stream is capable of their requirements. |
||
24 | |||
25 | Each stream instance has various capabilities: they can be read-only, |
||
26 | write-only, read-write, allow arbitrary random access (seeking forwards or |
||
27 | backwards to any location), or only allow sequential access (for example in the |
||
28 | case of a socket or pipe). |
||
29 | |||
30 | Creating Streams |
||
31 | ================ |
||
32 | |||
33 | The best way to create a stream is using the static factory method, |
||
34 | ``GuzzleHttp\Stream\Stream::factory()``. This factory accepts strings, |
||
35 | resources returned from ``fopen()``, an object that implements |
||
36 | ``__toString()``, and an object that implements |
||
37 | ``GuzzleHttp\Stream\StreamInterface``. |
||
38 | |||
39 | .. code-block:: php |
||
40 | |||
41 | use GuzzleHttp\Stream\Stream; |
||
42 | |||
43 | $stream = Stream::factory('string data'); |
||
44 | echo $stream; |
||
45 | // string data |
||
46 | echo $stream->read(3); |
||
47 | // str |
||
48 | echo $stream->getContents(); |
||
49 | // ing data |
||
50 | var_export($stream->eof()); |
||
51 | // true |
||
52 | var_export($stream->tell()); |
||
53 | // 11 |
||
54 | |||
55 | Metadata Streams |
||
56 | ================ |
||
57 | |||
58 | Guzzle streams that implement ``GuzzleHttp\Stream\MetadataStreamInterface`` |
||
59 | expose stream metadata through the ``getMetadata()`` method. This method |
||
60 | provides the data you would retrieve when calling PHP's |
||
61 | `stream_get_meta_data() function <http://php.net/manual/en/function.stream-get-meta-data.php>`_. |
||
62 | |||
63 | .. code-block:: php |
||
64 | |||
65 | use GuzzleHttp\Stream\Stream; |
||
66 | |||
67 | $resource = fopen('/path/to/file', 'r'); |
||
68 | $stream = Stream::factory($resource); |
||
69 | echo $stream->getMetadata('uri'); |
||
70 | // /path/to/file |
||
71 | var_export($stream->isReadable()); |
||
72 | // true |
||
73 | var_export($stream->isWritable()); |
||
74 | // false |
||
75 | var_export($stream->isSeekable()); |
||
76 | // true |
||
77 | |||
78 | .. note:: |
||
79 | |||
80 | Streams created using ``GuzzleHttp\Stream\Stream::factory()`` all implement |
||
81 | ``GuzzleHttp\Stream\MetadataStreamInterface``. |
||
82 | |||
83 | Stream Decorators |
||
84 | ================= |
||
85 | |||
86 | With the small and focused interface, add custom functionality to streams is |
||
87 | very simple with stream decorators. Guzzle provides several built-in decorators |
||
88 | that provide additional stream functionality. |
||
89 | |||
90 | CachingStream |
||
91 | ------------- |
||
92 | |||
93 | The CachingStream is used to allow seeking over previously read bytes on |
||
94 | non-seekable streams. This can be useful when transferring a non-seekable |
||
95 | entity body fails due to needing to rewind the stream (for example, resulting |
||
96 | from a redirect). Data that is read from the remote stream will be buffered in |
||
97 | a PHP temp stream so that previously read bytes are cached first in memory, |
||
98 | then on disk. |
||
99 | |||
100 | .. code-block:: php |
||
101 | |||
102 | use GuzzleHttp\Stream\Stream; |
||
103 | use GuzzleHttp\Stream\CachingStream; |
||
104 | |||
105 | $original = Stream::factory(fopen('http://www.google.com', 'r')); |
||
106 | $stream = new CachingStream($original); |
||
107 | |||
108 | $stream->read(1024); |
||
109 | echo $stream->tell(); |
||
110 | // 1024 |
||
111 | |||
112 | $stream->seek(0); |
||
113 | echo $stream->tell(); |
||
114 | // 0 |
||
115 | |||
116 | LimitStream |
||
117 | ----------- |
||
118 | |||
119 | LimitStream can be used to read a subset or slice of an existing stream object. |
||
120 | This can be useful for breaking a large file into smaller pieces to be sent in |
||
121 | chunks (e.g. Amazon S3's multipart upload API). |
||
122 | |||
123 | .. code-block:: php |
||
124 | |||
125 | use GuzzleHttp\Stream\Stream; |
||
126 | use GuzzleHttp\Stream\LimitStream; |
||
127 | |||
128 | $original = Stream::factory(fopen('/tmp/test.txt', 'r+')); |
||
129 | echo $original->getSize(); |
||
130 | // >>> 1048576 |
||
131 | |||
132 | // Limit the size of the body to 1024 bytes and start reading from byte 2048 |
||
133 | $stream = new LimitStream($original, 1024, 2048); |
||
134 | echo $stream->getSize(); |
||
135 | // >>> 1024 |
||
136 | echo $stream->tell(); |
||
137 | // >>> 0 |
||
138 | |||
139 | NoSeekStream |
||
140 | ------------ |
||
141 | |||
142 | NoSeekStream wraps a stream and does not allow seeking. |
||
143 | |||
144 | .. code-block:: php |
||
145 | |||
146 | use GuzzleHttp\Stream\Stream; |
||
147 | use GuzzleHttp\Stream\LimitStream; |
||
148 | |||
149 | $original = Stream::factory('foo'); |
||
150 | $noSeek = new NoSeekStream($original); |
||
151 | |||
152 | echo $noSeek->read(3); |
||
153 | // foo |
||
154 | var_export($noSeek->isSeekable()); |
||
155 | // false |
||
156 | $noSeek->seek(0); |
||
157 | var_export($noSeek->read(3)); |
||
158 | // NULL |
||
159 | |||
160 | Creating Custom Decorators |
||
161 | -------------------------- |
||
162 | |||
163 | Creating a stream decorator is very easy thanks to the |
||
164 | ``GuzzleHttp\Stream\StreamDecoratorTrait``. This trait provides methods that |
||
165 | implement ``GuzzleHttp\Stream\StreamInterface`` by proxying to an underlying |
||
166 | stream. Just ``use`` the ``StreamDecoratorTrait`` and implement your custom |
||
167 | methods. |
||
168 | |||
169 | For example, let's say we wanted to call a specific function each time the last |
||
170 | byte is read from a stream. This could be implemented by overriding the |
||
171 | ``read()`` method. |
||
172 | |||
173 | .. code-block:: php |
||
174 | |||
175 | use GuzzleHttp\Stream\StreamDecoratorTrait; |
||
176 | |||
177 | class EofCallbackStream implements StreamInterface, MetadataStreamInterface |
||
178 | { |
||
179 | use StreamDecoratorTrait; |
||
180 | |||
181 | private $callback; |
||
182 | |||
183 | public function __construct(StreamInterface $stream, callable $callback) |
||
184 | { |
||
185 | $this->stream = $stream; |
||
186 | $this->callback = $callback; |
||
187 | } |
||
188 | |||
189 | public function read($length) |
||
190 | { |
||
191 | $result = $this->stream->read($length); |
||
192 | |||
193 | // Invoke the callback when EOF is hit. |
||
194 | if ($this->eof()) { |
||
195 | call_user_func($this->callback); |
||
196 | } |
||
197 | |||
198 | return $result; |
||
199 | } |
||
200 | } |
||
201 | |||
202 | This decorator could be added to any existing stream and used like so: |
||
203 | |||
204 | .. code-block:: php |
||
205 | |||
206 | use GuzzleHttp\Stream\Stream; |
||
207 | |||
208 | $original = Stream::factory('foo'); |
||
209 | $eofStream = new EofCallbackStream($original, function () { |
||
210 | echo 'EOF!'; |
||
211 | }); |
||
212 | |||
213 | $eofStream->read(2); |
||
214 | $eofStream->read(1); |
||
215 | // echoes "EOF!" |
||
216 | $eofStream->seek(0); |
||
217 | $eofStream->read(3); |
||
218 | // echoes "EOF!" |