PageRenderTime 36ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Guzzle/Common/Stream.php

http://github.com/guzzle/guzzle
PHP | 315 lines | 143 code | 36 blank | 136 comment | 15 complexity | bff976f2cf0e12d24f6d78627c2ec7a9 MD5 | raw file
  1. <?php
  2. namespace Guzzle\Common;
  3. use Guzzle\Common\Exception\InvalidArgumentException;
  4. /**
  5. * OO interface to PHP streams
  6. */
  7. class Stream
  8. {
  9. const STREAM_TYPE = 'stream_type';
  10. const WRAPPER_TYPE = 'wrapper_type';
  11. const IS_LOCAL = 'is_local';
  12. const IS_READABLE = 'is_readable';
  13. const IS_WRITABLE = 'is_writable';
  14. const SEEKABLE = 'seekable';
  15. /**
  16. * @var resource Stream resource
  17. */
  18. protected $stream;
  19. /**
  20. * @var int Size of the stream contents in bytes
  21. */
  22. protected $size;
  23. /**
  24. * @var array Stream cached data
  25. */
  26. protected $cache = array();
  27. /**
  28. * @var array Hash table of readable and writeable stream types for fast lookups
  29. */
  30. protected static $readWriteHash = array(
  31. 'read' => array(
  32. 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+', 'x+' => true,
  33. 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b', 'x+' => true,
  34. 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t', 'x+' => true
  35. ),
  36. 'write' => array(
  37. 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+', 'x+' => true,
  38. 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b', 'x+' => true,
  39. 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t', 'x+' => true
  40. )
  41. );
  42. /**
  43. * Construct a new Stream
  44. *
  45. * @param resource $stream Stream resource to wrap
  46. * @param int $size Size of the stream in bytes. Only pass this
  47. * parameter if the size cannot be obtained from
  48. * the stream.
  49. *
  50. * @throws InvalidArgumentException if the stream is not a stream resource
  51. */
  52. public function __construct($stream, $size = null)
  53. {
  54. if (!is_resource($stream)) {
  55. throw new InvalidArgumentException('Stream must be a resource');
  56. }
  57. $this->size = $size;
  58. $this->stream = $stream;
  59. $this->rebuildCache();
  60. }
  61. /**
  62. * Closes the stream when the helper is destructed
  63. */
  64. public function __destruct()
  65. {
  66. if (is_resource($this->stream)) {
  67. fclose($this->stream);
  68. }
  69. }
  70. /**
  71. * Reprocess stream metadata
  72. */
  73. protected function rebuildCache()
  74. {
  75. $this->cache = stream_get_meta_data($this->stream);
  76. $this->cache[self::STREAM_TYPE] = strtolower($this->cache[self::STREAM_TYPE]);
  77. $this->cache[self::WRAPPER_TYPE] = strtolower($this->cache[self::WRAPPER_TYPE]);
  78. $this->cache[self::IS_LOCAL] = stream_is_local($this->stream);
  79. $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]);
  80. $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]);
  81. }
  82. /**
  83. * Convert the stream to a string if the stream is readable and the stream
  84. * is seekable. This logic is enforced to ensure that outputting the stream
  85. * as a string does not affect an actual cURL request using non-repeatable
  86. * streams.
  87. *
  88. * @return string
  89. */
  90. public function __toString()
  91. {
  92. if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) {
  93. return '';
  94. }
  95. $body = stream_get_contents($this->stream, -1, 0);
  96. $this->seek(0);
  97. return $body;
  98. }
  99. /**
  100. * Get stream metadata
  101. *
  102. * @param string $key Specific metdata to retrieve
  103. *
  104. * @return array|mixed|null
  105. */
  106. public function getMetaData($key = null)
  107. {
  108. $meta = stream_get_meta_data($this->stream);
  109. return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null);
  110. }
  111. /**
  112. * Get the stream resource
  113. *
  114. * @return resource
  115. */
  116. public function getStream()
  117. {
  118. return $this->stream;
  119. }
  120. /**
  121. * Get the stream wrapper type
  122. *
  123. * @return string
  124. */
  125. public function getWrapper()
  126. {
  127. return $this->cache[self::WRAPPER_TYPE];
  128. }
  129. /**
  130. * Wrapper specific data attached to this stream.
  131. *
  132. * @return string
  133. */
  134. public function getWrapperData()
  135. {
  136. return $this->getMetaData('wrapper_data') ?: array();
  137. }
  138. /**
  139. * Get a label describing the underlying implementation of the stream
  140. *
  141. * @return string
  142. */
  143. public function getStreamType()
  144. {
  145. return $this->cache[self::STREAM_TYPE];
  146. }
  147. /**
  148. * Get the URI/filename associated with this stream
  149. *
  150. * @return string
  151. */
  152. public function getUri()
  153. {
  154. return $this->cache['uri'];
  155. }
  156. /**
  157. * Get the size of the stream if able
  158. *
  159. * @return int|false
  160. */
  161. public function getSize()
  162. {
  163. if ($this->size !== null) {
  164. return $this->size;
  165. }
  166. // If the stream is a file based stream and local, then check the filesize
  167. if ($this->isLocal() && $this->getWrapper() == 'plainfile' && $this->getUri() && file_exists($this->getUri())) {
  168. return filesize($this->getUri());
  169. }
  170. // Only get the size based on the content if the the stream is readable
  171. // and seekable so as to not interfere with actually reading the data
  172. if (!$this->cache[self::IS_READABLE] || !$this->cache[self::SEEKABLE]) {
  173. return false;
  174. } else {
  175. $this->size = strlen((string) $this);
  176. $this->seek(0);
  177. return $this->size;
  178. }
  179. }
  180. /**
  181. * Check if the stream is readable
  182. *
  183. * @return bool
  184. */
  185. public function isReadable()
  186. {
  187. return $this->cache[self::IS_READABLE];
  188. }
  189. /**
  190. * Check if the stream is writable
  191. *
  192. * @return bool
  193. */
  194. public function isWritable()
  195. {
  196. return $this->cache[self::IS_WRITABLE];
  197. }
  198. /**
  199. * Check if the stream has been consumed
  200. *
  201. * @return bool
  202. */
  203. public function isConsumed()
  204. {
  205. return feof($this->stream);
  206. }
  207. /**
  208. * Check if the stream is a local stream vs a remote stream
  209. *
  210. * @return bool
  211. */
  212. public function isLocal()
  213. {
  214. return $this->cache[self::IS_LOCAL];
  215. }
  216. /**
  217. * Check if the string is repeatable
  218. *
  219. * @return bool
  220. */
  221. public function isSeekable()
  222. {
  223. return $this->cache[self::SEEKABLE];
  224. }
  225. /**
  226. * Specify the size of the stream in bytes
  227. *
  228. * @param int $size Size of the stream contents in bytes
  229. *
  230. * @return Stream
  231. */
  232. public function setSize($size)
  233. {
  234. $this->size = $size;
  235. return $this;
  236. }
  237. /**
  238. * Seek to a position in the stream
  239. *
  240. * @param int $offset Stream offset
  241. * @param int $whence Where the offset is applied
  242. *
  243. * @return bool Returns TRUE on success or FALSE on failure
  244. * @link http://www.php.net/manual/en/function.fseek.php
  245. */
  246. public function seek($offset, $whence = SEEK_SET)
  247. {
  248. return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;
  249. }
  250. /**
  251. * Read data from the stream
  252. *
  253. * @param int $length Up to length number of bytes read.
  254. *
  255. * @return string|bool Returns the data read from the stream or FALSE on
  256. * failure or EOF
  257. */
  258. public function read($length)
  259. {
  260. return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false;
  261. }
  262. /**
  263. * Write data to the stream
  264. *
  265. * @param string $string The string that is to be written.
  266. *
  267. * @return int|bool Returns the number of bytes written to the stream on
  268. * success or FALSE on failure.
  269. */
  270. public function write($string)
  271. {
  272. if (!$this->cache[self::IS_WRITABLE]) {
  273. return 0;
  274. }
  275. $bytes = fwrite($this->stream, $string);
  276. $this->size += $bytes;
  277. return $bytes;
  278. }
  279. }