Stream.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Service
  17. * @subpackage Amazon_S3
  18. * @copyright Copyright (c) 2005-2008, Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: S3.php 9786 2008-06-24 23:50:25Z jplock $
  21. */
  22. /**
  23. * @see Zend_Service_Amazon_S3
  24. */
  25. require_once 'Zend/Service/Amazon/S3.php';
  26. /**
  27. * Amazon S3 PHP stream wrapper
  28. *
  29. * @category Zend
  30. * @package Zend_Service
  31. * @subpackage Amazon_S3
  32. * @copyright Copyright (c) 2005-2008, Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Service_Amazon_S3_Stream
  36. {
  37. /**
  38. * @var boolean Write the buffer on fflush()?
  39. */
  40. private $_writeBuffer = false;
  41. /**
  42. * @var integer Current read/write position
  43. */
  44. private $_position = 0;
  45. /**
  46. * @var integer Total size of the object as returned by S3 (Content-length)
  47. */
  48. private $_objectSize = 0;
  49. /**
  50. * @var string File name to interact with
  51. */
  52. private $_objectName = null;
  53. /**
  54. * @var string Current read/write buffer
  55. */
  56. private $_objectBuffer = null;
  57. /**
  58. * @var array Available buckets
  59. */
  60. private $_bucketList = array();
  61. /**
  62. * @var Zend_Service_Amazon_S3
  63. */
  64. private $_s3 = null;
  65. /**
  66. * Retrieve client for this stream type
  67. *
  68. * @param string $path
  69. * @return Zend_Service_Amazon_S3
  70. */
  71. protected function _getS3Client($path)
  72. {
  73. if ($this->_s3 === null) {
  74. $url = explode(':', $path);
  75. if (!$url) {
  76. /**
  77. * @see Zend_Service_Amazon_S3_Exception
  78. */
  79. require_once 'Zend/Service/Amazon/S3/Exception.php';
  80. throw new Zend_Service_Amazon_S3_Exception("Unable to parse URL $path");
  81. }
  82. $this->_s3 = Zend_Service_Amazon_S3::getWrapperClient($url[0]);
  83. if (!$this->_s3) {
  84. /**
  85. * @see Zend_Service_Amazon_S3_Exception
  86. */
  87. require_once 'Zend/Service/Amazon/S3/Exception.php';
  88. throw new Zend_Service_Amazon_S3_Exception("Unknown client for wrapper {$url[0]}");
  89. }
  90. }
  91. return $this->_s3;
  92. }
  93. /**
  94. * Extract object name from URL
  95. *
  96. * @param string $path
  97. * @return string
  98. */
  99. protected function _getNamePart($path)
  100. {
  101. $url = parse_url($path);
  102. if ($url['host']) {
  103. return $url['path'] ? $url['host'].'/'.$url['path'] : $url['host'];
  104. }
  105. return '';
  106. }
  107. /**
  108. * Open the stream
  109. *
  110. * @param string $path
  111. * @param string $mode
  112. * @param integer $options
  113. * @param string $opened_path
  114. * @return boolean
  115. */
  116. public function stream_open($path, $mode, $options, $opened_path)
  117. {
  118. $name = $this->_getNamePart($path);
  119. // If we open the file for writing, just return true. Create the object
  120. // on fflush call
  121. if (strpbrk($mode, 'wax')) {
  122. $this->_objectName = $name;
  123. $this->_objectBuffer = null;
  124. $this->_objectSize = 0;
  125. $this->_position = 0;
  126. $this->_writeBuffer = true;
  127. $this->_getS3Client($path);
  128. return true;
  129. }
  130. else {
  131. // Otherwise, just see if the file exists or not
  132. $info = $this->_getS3Client($path)->getInfo($name);
  133. if ($info) {
  134. $this->_objectName = $name;
  135. $this->_objectBuffer = null;
  136. $this->_objectSize = $info['size'];
  137. $this->_position = 0;
  138. $this->_writeBuffer = false;
  139. $this->_getS3Client($path);
  140. return true;
  141. }
  142. }
  143. return false;
  144. }
  145. /**
  146. * Close the stream
  147. *
  148. * @return void
  149. */
  150. public function stream_close()
  151. {
  152. $this->_objectName = null;
  153. $this->_objectBuffer = null;
  154. $this->_objectSize = 0;
  155. $this->_position = 0;
  156. $this->_writeBuffer = false;
  157. unset($this->_s3);
  158. }
  159. /**
  160. * Read from the stream
  161. *
  162. * @param integer $count
  163. * @return string
  164. */
  165. public function stream_read($count)
  166. {
  167. if (!$this->_objectName) {
  168. return false;
  169. }
  170. $range_start = $this->_position;
  171. $range_end = $this->_position+$count;
  172. // Only fetch more data from S3 if we haven't fetched any data yet (postion=0)
  173. // OR, the range end position is greater than the size of the current object
  174. // buffer AND if the range end position is less than or equal to the object's
  175. // size returned by S3
  176. if (($this->_position == 0) || (($range_end > strlen($this->_objectBuffer)) && ($range_end <= $this->_objectSize))) {
  177. $headers = array(
  178. 'Range' => "$range_start-$range_end"
  179. );
  180. $response = $this->_s3->_makeRequest('GET', $this->_objectName, null, $headers);
  181. if ($response->getStatus() == 200) {
  182. $this->_objectBuffer .= $response->getBody();
  183. }
  184. }
  185. $data = substr($this->_objectBuffer, $this->_position, $count);
  186. $this->_position += strlen($data);
  187. return $data;
  188. }
  189. /**
  190. * Write to the stream
  191. *
  192. * @param string $data
  193. * @return integer
  194. */
  195. public function stream_write($data)
  196. {
  197. if (!$this->_objectName) {
  198. return 0;
  199. }
  200. $len = strlen($data);
  201. $this->_objectBuffer .= $data;
  202. $this->_objectSize += $len;
  203. // TODO: handle current position for writing!
  204. return $len;
  205. }
  206. /**
  207. * End of the stream?
  208. *
  209. * @return boolean
  210. */
  211. public function stream_eof()
  212. {
  213. if (!$this->_objectName) {
  214. return true;
  215. }
  216. return ($this->_position >= $this->_objectSize);
  217. }
  218. /**
  219. * What is the current read/write position of the stream
  220. *
  221. * @return integer
  222. */
  223. public function stream_tell()
  224. {
  225. return $this->_position;
  226. }
  227. /**
  228. * Update the read/write position of the stream
  229. *
  230. * @param integer $offset
  231. * @param integer $whence
  232. * @return boolean
  233. */
  234. public function stream_seek($offset, $whence)
  235. {
  236. if (!$this->_objectName) {
  237. return false;
  238. }
  239. switch ($whence) {
  240. case SEEK_CUR:
  241. // Set position to current location plus $offset
  242. $new_pos = $this->_position + $offset;
  243. break;
  244. case SEEK_END:
  245. // Set position to end-of-file plus $offset
  246. $new_pos = $this->_objectSize + $offset;
  247. break;
  248. case SEEK_SET:
  249. default:
  250. // Set position equal to $offset
  251. $new_pos = $offset;
  252. break;
  253. }
  254. $ret = ($new_pos >= 0 && $new_pos <= $this->_objectSize);
  255. if ($ret) {
  256. $this->_position = $new_pos;
  257. }
  258. return $ret;
  259. }
  260. /**
  261. * Flush current cached stream data to storage
  262. *
  263. * @return boolean
  264. */
  265. public function stream_flush()
  266. {
  267. // If the stream wasn't opened for writing, just return false
  268. if (!$this->_writeBuffer) {
  269. return false;
  270. }
  271. $ret = $this->_s3->putObject($this->_objectName, $this->_objectBuffer);
  272. $this->_objectBuffer = null;
  273. return $ret;
  274. }
  275. /**
  276. * Returns data array of stream variables
  277. *
  278. * @return array
  279. */
  280. public function stream_stat()
  281. {
  282. if (!$this->_objectName) {
  283. return false;
  284. }
  285. $stat = array();
  286. $stat['dev'] = 0;
  287. $stat['ino'] = 0;
  288. $stat['mode'] = 0;
  289. $stat['nlink'] = 0;
  290. $stat['uid'] = 0;
  291. $stat['gid'] = 0;
  292. $stat['rdev'] = 0;
  293. $stat['size'] = 0;
  294. $stat['atime'] = 0;
  295. $stat['mtime'] = 0;
  296. $stat['ctime'] = 0;
  297. $stat['blksize'] = 0;
  298. $stat['blocks'] = 0;
  299. $info = $this->_s3->getInfo($this->_objectName);
  300. if (!empty($info)) {
  301. $stat['size'] = $info['size'];
  302. $stat['atime'] = time();
  303. $stat['mtime'] = $info['mtime'];
  304. }
  305. return $stat;
  306. }
  307. /**
  308. * Attempt to delete the item
  309. *
  310. * @param string $path
  311. * @return boolean
  312. */
  313. public function unlink($path)
  314. {
  315. return $this->_getS3Client($path)->removeObject($this->_getNamePart($path));
  316. }
  317. /**
  318. * Attempt to rename the item
  319. *
  320. * @param string $path_from
  321. * @param string $path_to
  322. * @return boolean False
  323. */
  324. public function rename($path_from, $path_to)
  325. {
  326. // TODO: Renaming isn't supported, always return false
  327. return false;
  328. }
  329. /**
  330. * Create a new directory
  331. *
  332. * @param string $path
  333. * @param integer $mode
  334. * @param integer $options
  335. * @return boolean
  336. */
  337. public function mkdir($path, $mode, $options)
  338. {
  339. return $this->_getS3Client($path)->createBucket(parse_url($path, PHP_URL_HOST));
  340. }
  341. /**
  342. * Remove a directory
  343. *
  344. * @param string $path
  345. * @param integer $options
  346. * @return boolean
  347. */
  348. public function rmdir($path, $options)
  349. {
  350. return $this->_getS3Client($path)->removeBucket(parse_url($path, PHP_URL_HOST));
  351. }
  352. /**
  353. * Attempt to open a directory
  354. *
  355. * @param string $path
  356. * @param integer $options
  357. * @return boolean
  358. */
  359. public function dir_opendir($path, $options)
  360. {
  361. if (preg_match('@^([a-z0-9+.]|-)+://$@', $path)) {
  362. $this->_bucketList = $this->_getS3Client($path)->getBuckets();
  363. }
  364. else {
  365. $url = parse_url($path);
  366. $this->_bucketList = $this->_getS3Client($path)->getObjectsByBucket($url["host"]);
  367. }
  368. return ($this->_bucketList !== false);
  369. }
  370. /**
  371. * Return array of URL variables
  372. *
  373. * @param string $path
  374. * @param integer $flags
  375. * @return array
  376. */
  377. public function url_stat($path, $flags)
  378. {
  379. $stat = array();
  380. $stat['dev'] = 0;
  381. $stat['ino'] = 0;
  382. $stat['mode'] = 0;
  383. $stat['nlink'] = 0;
  384. $stat['uid'] = 0;
  385. $stat['gid'] = 0;
  386. $stat['rdev'] = 0;
  387. $stat['size'] = 0;
  388. $stat['atime'] = 0;
  389. $stat['mtime'] = 0;
  390. $stat['ctime'] = 0;
  391. $stat['blksize'] = 0;
  392. $stat['blocks'] = 0;
  393. $info = $this->_getS3Client($path)->getInfo($this->_getNamePart($path));
  394. if (!empty($info)) {
  395. $stat['size'] = $info['size'];
  396. $stat['atime'] = time();
  397. $stat['mtime'] = $info['mtime'];
  398. }
  399. return $stat;
  400. }
  401. /**
  402. * Return the next filename in the directory
  403. *
  404. * @return string
  405. */
  406. public function dir_readdir()
  407. {
  408. $object = current($this->_bucketList);
  409. if ($object !== false) {
  410. next($this->_bucketList);
  411. }
  412. return $object;
  413. }
  414. /**
  415. * Reset the directory pointer
  416. *
  417. * @return boolean True
  418. */
  419. public function dir_rewinddir()
  420. {
  421. reset($this->_bucketList);
  422. return true;
  423. }
  424. /**
  425. * Close a directory
  426. *
  427. * @return boolean True
  428. */
  429. public function dir_closedir()
  430. {
  431. $this->_bucketList = array();
  432. return true;
  433. }
  434. }