1: <?php
2:
3: namespace Guzzle\Stream;
4:
5: use Guzzle\Common\Exception\InvalidArgumentException;
6:
7: 8: 9:
10: class Stream implements StreamInterface
11: {
12: const STREAM_TYPE = 'stream_type';
13: const WRAPPER_TYPE = 'wrapper_type';
14: const IS_LOCAL = 'is_local';
15: const IS_READABLE = 'is_readable';
16: const IS_WRITABLE = 'is_writable';
17: const SEEKABLE = 'seekable';
18:
19: 20: 21:
22: protected $stream;
23:
24: 25: 26:
27: protected $size;
28:
29: 30: 31:
32: protected $cache = array();
33:
34: 35: 36:
37: protected $customData = array();
38:
39: 40: 41:
42: protected static $readWriteHash = array(
43: 'read' => array(
44: 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
45: 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
46: 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true
47: ),
48: 'write' => array(
49: 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true,
50: 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true,
51: 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
52: )
53: );
54:
55: 56: 57: 58: 59: 60: 61: 62:
63: public function __construct($stream, $size = null)
64: {
65: $this->setStream($stream, $size);
66: }
67:
68: 69: 70:
71: public function __destruct()
72: {
73: $this->close();
74: }
75:
76: 77: 78:
79: public function __toString()
80: {
81: if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) {
82: return '';
83: }
84:
85: $originalPos = $this->ftell();
86: $body = stream_get_contents($this->stream, -1, 0);
87: $this->seek($originalPos);
88:
89: return $body;
90: }
91:
92: 93: 94:
95: public function close()
96: {
97: if (is_resource($this->stream)) {
98: fclose($this->stream);
99: }
100: $this->cache[self::IS_READABLE] = false;
101: $this->cache[self::IS_WRITABLE] = false;
102: }
103:
104: 105: 106: 107: 108: 109: 110: 111: 112:
113: public static function getHash(StreamInterface $stream, $algo, $rawOutput = false)
114: {
115: $pos = $stream->ftell();
116: if (!$stream->seek(0)) {
117: return false;
118: }
119:
120: $ctx = hash_init($algo);
121: while ($data = $stream->read(8192)) {
122: hash_update($ctx, $data);
123: }
124:
125: $out = hash_final($ctx, (bool) $rawOutput);
126: $stream->seek($pos);
127:
128: return $out;
129: }
130:
131: 132: 133:
134: public function getMetaData($key = null)
135: {
136: $meta = stream_get_meta_data($this->stream);
137:
138: return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
139: }
140:
141: 142: 143:
144: public function getStream()
145: {
146: return $this->stream;
147: }
148:
149: 150: 151:
152: public function setStream($stream, $size = null)
153: {
154: if (!is_resource($stream)) {
155: throw new InvalidArgumentException('Stream must be a resource');
156: }
157:
158: $this->size = $size;
159: $this->stream = $stream;
160: $this->rebuildCache();
161:
162: return $this;
163: }
164:
165: 166: 167:
168: public function getWrapper()
169: {
170: return $this->cache[self::WRAPPER_TYPE];
171: }
172:
173: 174: 175:
176: public function getWrapperData()
177: {
178: return $this->getMetaData('wrapper_data') ?: array();
179: }
180:
181: 182: 183:
184: public function getStreamType()
185: {
186: return $this->cache[self::STREAM_TYPE];
187: }
188:
189: 190: 191:
192: public function getUri()
193: {
194: return $this->cache['uri'];
195: }
196:
197: 198: 199:
200: public function getSize()
201: {
202: if ($this->size !== null) {
203: return $this->size;
204: }
205:
206:
207: if ($this->isLocal()) {
208: clearstatcache(true, $this->getUri());
209: $stats = fstat($this->stream);
210: if (isset($stats['size'])) {
211: return $stats['size'];
212: }
213: }
214:
215:
216: if (!$this->cache[self::IS_READABLE] || !$this->cache[self::SEEKABLE]) {
217: return false;
218: } else {
219: $pos = $this->ftell();
220: $this->size = strlen((string) $this);
221: $this->seek($pos);
222: return $this->size;
223: }
224: }
225:
226: 227: 228:
229: public function isReadable()
230: {
231: return $this->cache[self::IS_READABLE];
232: }
233:
234: 235: 236:
237: public function isWritable()
238: {
239: return $this->cache[self::IS_WRITABLE];
240: }
241:
242: 243: 244:
245: public function isConsumed()
246: {
247: return feof($this->stream);
248: }
249:
250: 251: 252:
253: public function feof()
254: {
255: return $this->isConsumed();
256: }
257:
258: 259: 260:
261: public function isLocal()
262: {
263: return $this->cache[self::IS_LOCAL];
264: }
265:
266: 267: 268:
269: public function isSeekable()
270: {
271: return $this->cache[self::SEEKABLE];
272: }
273:
274: 275: 276:
277: public function setSize($size)
278: {
279: $this->size = $size;
280:
281: return $this;
282: }
283:
284: 285: 286:
287: public function seek($offset, $whence = SEEK_SET)
288: {
289: return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
290: }
291:
292: 293: 294:
295: public function read($length)
296: {
297: return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false;
298: }
299:
300: 301: 302:
303: public function write($string)
304: {
305: if (!$this->cache[self::IS_WRITABLE]) {
306: return 0;
307: }
308:
309: $bytes = fwrite($this->stream, $string);
310:
311:
312: if ($bytes) {
313: $this->size = null;
314: }
315:
316: return $bytes;
317: }
318:
319: 320: 321:
322: public function ftell()
323: {
324: return ftell($this->stream);
325: }
326:
327: 328: 329:
330: public function rewind()
331: {
332: return $this->seek(0);
333: }
334:
335: 336: 337:
338: public function readLine($maxLength = null)
339: {
340: if (!$this->cache[self::IS_READABLE]) {
341: return false;
342: } else {
343: return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream());
344: }
345: }
346:
347: 348: 349:
350: public function setCustomData($key, $value)
351: {
352: $this->customData[$key] = $value;
353:
354: return $this;
355: }
356:
357: 358: 359:
360: public function getCustomData($key)
361: {
362: return isset($this->customData[$key]) ? $this->customData[$key] : null;
363: }
364:
365:
366: 367: 368:
369: protected function rebuildCache()
370: {
371: $this->cache = stream_get_meta_data($this->stream);
372: $this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
373: $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
374: $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
375: }
376: }
377: