PageRenderTime 26ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Zend/Service/Amazon/S3/Stream.php

https://bitbucket.org/acidel/buykoala
PHP | 506 lines | 251 code | 51 blank | 204 comment | 37 complexity | f8f2b4302ef48ea05dababc6ad721e11 MD5 | raw file
  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-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Stream.php 22621 2010-07-18 00:35:48Z torio $
  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-2010 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 !empty($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. * http://bugs.php.net/21641 - stream_read() is always passed PHP's
  163. * internal read buffer size (8192) no matter what is passed as $count
  164. * parameter to fread().
  165. *
  166. * @param integer $count
  167. * @return string
  168. */
  169. public function stream_read($count)
  170. {
  171. if (!$this->_objectName) {
  172. return false;
  173. }
  174. // make sure that count doesn't exceed object size
  175. if ($count + $this->_position > $this->_objectSize) {
  176. $count = $this->_objectSize - $this->_position;
  177. }
  178. $range_start = $this->_position;
  179. $range_end = $this->_position+$count;
  180. // Only fetch more data from S3 if we haven't fetched any data yet (postion=0)
  181. // OR, the range end position is greater than the size of the current object
  182. // buffer AND if the range end position is less than or equal to the object's
  183. // size returned by S3
  184. if (($this->_position == 0) || (($range_end > strlen($this->_objectBuffer)) && ($range_end <= $this->_objectSize))) {
  185. $headers = array(
  186. 'Range' => "bytes=$range_start-$range_end"
  187. );
  188. $response = $this->_s3->_makeRequest('GET', $this->_objectName, null, $headers);
  189. if ($response->getStatus() == 206) { // 206 Partial Content
  190. $this->_objectBuffer .= $response->getBody();
  191. }
  192. }
  193. $data = substr($this->_objectBuffer, $this->_position, $count);
  194. $this->_position += strlen($data);
  195. return $data;
  196. }
  197. /**
  198. * Write to the stream
  199. *
  200. * @param string $data
  201. * @return integer
  202. */
  203. public function stream_write($data)
  204. {
  205. if (!$this->_objectName) {
  206. return 0;
  207. }
  208. $len = strlen($data);
  209. $this->_objectBuffer .= $data;
  210. $this->_objectSize += $len;
  211. // TODO: handle current position for writing!
  212. return $len;
  213. }
  214. /**
  215. * End of the stream?
  216. *
  217. * @return boolean
  218. */
  219. public function stream_eof()
  220. {
  221. if (!$this->_objectName) {
  222. return true;
  223. }
  224. return ($this->_position >= $this->_objectSize);
  225. }
  226. /**
  227. * What is the current read/write position of the stream
  228. *
  229. * @return integer
  230. */
  231. public function stream_tell()
  232. {
  233. return $this->_position;
  234. }
  235. /**
  236. * Update the read/write position of the stream
  237. *
  238. * @param integer $offset
  239. * @param integer $whence
  240. * @return boolean
  241. */
  242. public function stream_seek($offset, $whence)
  243. {
  244. if (!$this->_objectName) {
  245. return false;
  246. }
  247. switch ($whence) {
  248. case SEEK_CUR:
  249. // Set position to current location plus $offset
  250. $new_pos = $this->_position + $offset;
  251. break;
  252. case SEEK_END:
  253. // Set position to end-of-file plus $offset
  254. $new_pos = $this->_objectSize + $offset;
  255. break;
  256. case SEEK_SET:
  257. default:
  258. // Set position equal to $offset
  259. $new_pos = $offset;
  260. break;
  261. }
  262. $ret = ($new_pos >= 0 && $new_pos <= $this->_objectSize);
  263. if ($ret) {
  264. $this->_position = $new_pos;
  265. }
  266. return $ret;
  267. }
  268. /**
  269. * Flush current cached stream data to storage
  270. *
  271. * @return boolean
  272. */
  273. public function stream_flush()
  274. {
  275. // If the stream wasn't opened for writing, just return false
  276. if (!$this->_writeBuffer) {
  277. return false;
  278. }
  279. $ret = $this->_s3->putObject($this->_objectName, $this->_objectBuffer);
  280. $this->_objectBuffer = null;
  281. return $ret;
  282. }
  283. /**
  284. * Returns data array of stream variables
  285. *
  286. * @return array
  287. */
  288. public function stream_stat()
  289. {
  290. if (!$this->_objectName) {
  291. return false;
  292. }
  293. $stat = array();
  294. $stat['dev'] = 0;
  295. $stat['ino'] = 0;
  296. $stat['mode'] = 0777;
  297. $stat['nlink'] = 0;
  298. $stat['uid'] = 0;
  299. $stat['gid'] = 0;
  300. $stat['rdev'] = 0;
  301. $stat['size'] = 0;
  302. $stat['atime'] = 0;
  303. $stat['mtime'] = 0;
  304. $stat['ctime'] = 0;
  305. $stat['blksize'] = 0;
  306. $stat['blocks'] = 0;
  307. if(($slash = strchr($this->_objectName, '/')) === false || $slash == strlen($this->_objectName)-1) {
  308. /* bucket */
  309. $stat['mode'] |= 040000;
  310. } else {
  311. $stat['mode'] |= 0100000;
  312. }
  313. $info = $this->_s3->getInfo($this->_objectName);
  314. if (!empty($info)) {
  315. $stat['size'] = $info['size'];
  316. $stat['atime'] = time();
  317. $stat['mtime'] = $info['mtime'];
  318. }
  319. return $stat;
  320. }
  321. /**
  322. * Attempt to delete the item
  323. *
  324. * @param string $path
  325. * @return boolean
  326. */
  327. public function unlink($path)
  328. {
  329. return $this->_getS3Client($path)->removeObject($this->_getNamePart($path));
  330. }
  331. /**
  332. * Attempt to rename the item
  333. *
  334. * @param string $path_from
  335. * @param string $path_to
  336. * @return boolean False
  337. */
  338. public function rename($path_from, $path_to)
  339. {
  340. // TODO: Renaming isn't supported, always return false
  341. return false;
  342. }
  343. /**
  344. * Create a new directory
  345. *
  346. * @param string $path
  347. * @param integer $mode
  348. * @param integer $options
  349. * @return boolean
  350. */
  351. public function mkdir($path, $mode, $options)
  352. {
  353. return $this->_getS3Client($path)->createBucket(parse_url($path, PHP_URL_HOST));
  354. }
  355. /**
  356. * Remove a directory
  357. *
  358. * @param string $path
  359. * @param integer $options
  360. * @return boolean
  361. */
  362. public function rmdir($path, $options)
  363. {
  364. return $this->_getS3Client($path)->removeBucket(parse_url($path, PHP_URL_HOST));
  365. }
  366. /**
  367. * Attempt to open a directory
  368. *
  369. * @param string $path
  370. * @param integer $options
  371. * @return boolean
  372. */
  373. public function dir_opendir($path, $options)
  374. {
  375. if (preg_match('@^([a-z0-9+.]|-)+://$@', $path)) {
  376. $this->_bucketList = $this->_getS3Client($path)->getBuckets();
  377. }
  378. else {
  379. $host = parse_url($path, PHP_URL_HOST);
  380. $this->_bucketList = $this->_getS3Client($path)->getObjectsByBucket($host);
  381. }
  382. return ($this->_bucketList !== false);
  383. }
  384. /**
  385. * Return array of URL variables
  386. *
  387. * @param string $path
  388. * @param integer $flags
  389. * @return array
  390. */
  391. public function url_stat($path, $flags)
  392. {
  393. $stat = array();
  394. $stat['dev'] = 0;
  395. $stat['ino'] = 0;
  396. $stat['mode'] = 0777;
  397. $stat['nlink'] = 0;
  398. $stat['uid'] = 0;
  399. $stat['gid'] = 0;
  400. $stat['rdev'] = 0;
  401. $stat['size'] = 0;
  402. $stat['atime'] = 0;
  403. $stat['mtime'] = 0;
  404. $stat['ctime'] = 0;
  405. $stat['blksize'] = 0;
  406. $stat['blocks'] = 0;
  407. $name = $this->_getNamePart($path);
  408. if(($slash = strchr($name, '/')) === false || $slash == strlen($name)-1) {
  409. /* bucket */
  410. $stat['mode'] |= 040000;
  411. } else {
  412. $stat['mode'] |= 0100000;
  413. }
  414. $info = $this->_getS3Client($path)->getInfo($name);
  415. if (!empty($info)) {
  416. $stat['size'] = $info['size'];
  417. $stat['atime'] = time();
  418. $stat['mtime'] = $info['mtime'];
  419. }
  420. return $stat;
  421. }
  422. /**
  423. * Return the next filename in the directory
  424. *
  425. * @return string
  426. */
  427. public function dir_readdir()
  428. {
  429. $object = current($this->_bucketList);
  430. if ($object !== false) {
  431. next($this->_bucketList);
  432. }
  433. return $object;
  434. }
  435. /**
  436. * Reset the directory pointer
  437. *
  438. * @return boolean True
  439. */
  440. public function dir_rewinddir()
  441. {
  442. reset($this->_bucketList);
  443. return true;
  444. }
  445. /**
  446. * Close a directory
  447. *
  448. * @return boolean True
  449. */
  450. public function dir_closedir()
  451. {
  452. $this->_bucketList = array();
  453. return true;
  454. }
  455. }