Stream.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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_WindowsAzure_Storage
  17. * @subpackage Blob
  18. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://todo name_todo
  20. * @version $Id$
  21. */
  22. /**
  23. * @category Zend
  24. * @package Zend_Service_WindowsAzure_Storage
  25. * @subpackage Blob
  26. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  27. * @license http://framework.zend.com/license/new-bsd New BSD License
  28. */
  29. class Zend_Service_WindowsAzure_Storage_Blob_Stream
  30. {
  31. /**
  32. * Current file name
  33. *
  34. * @var string
  35. */
  36. protected $_fileName = null;
  37. /**
  38. * Temporary file name
  39. *
  40. * @var string
  41. */
  42. protected $_temporaryFileName = null;
  43. /**
  44. * Temporary file handle
  45. *
  46. * @var resource
  47. */
  48. protected $_temporaryFileHandle = null;
  49. /**
  50. * Blob storage client
  51. *
  52. * @var Zend_Service_WindowsAzure_Storage_Blob
  53. */
  54. protected $_storageClient = null;
  55. /**
  56. * Write mode?
  57. *
  58. * @var boolean
  59. */
  60. protected $_writeMode = false;
  61. /**
  62. * List of blobs
  63. *
  64. * @var array
  65. */
  66. protected $_blobs = null;
  67. /**
  68. * Retrieve storage client for this stream type
  69. *
  70. * @param string $path
  71. * @return Zend_Service_WindowsAzure_Storage_Blob
  72. */
  73. protected function _getStorageClient($path = '')
  74. {
  75. if (is_null($this->_storageClient)) {
  76. $url = explode(':', $path);
  77. if (!$url) {
  78. throw new Zend_Service_WindowsAzure_Exception('Could not parse path "' . $path . '".');
  79. }
  80. $this->_storageClient = Zend_Service_WindowsAzure_Storage_Blob::getWrapperClient($url[0]);
  81. if (!$this->_storageClient) {
  82. throw new Zend_Service_WindowsAzure_Exception('No storage client registered for stream type "' . $url[0] . '://".');
  83. }
  84. }
  85. return $this->_storageClient;
  86. }
  87. /**
  88. * Extract container name
  89. *
  90. * @param string $path
  91. * @return string
  92. */
  93. protected function _getContainerName($path)
  94. {
  95. $url = parse_url($path);
  96. if ($url['host']) {
  97. return $url['host'];
  98. }
  99. return '';
  100. }
  101. /**
  102. * Extract file name
  103. *
  104. * @param string $path
  105. * @return string
  106. */
  107. protected function _getFileName($path)
  108. {
  109. $url = parse_url($path);
  110. if ($url['host']) {
  111. $fileName = isset($url['path']) ? $url['path'] : $url['host'];
  112. if (strpos($fileName, '/') === 0) {
  113. $fileName = substr($fileName, 1);
  114. }
  115. return $fileName;
  116. }
  117. return '';
  118. }
  119. /**
  120. * Open the stream
  121. *
  122. * @param string $path
  123. * @param string $mode
  124. * @param integer $options
  125. * @param string $opened_path
  126. * @return boolean
  127. */
  128. public function stream_open($path, $mode, $options, &$opened_path)
  129. {
  130. $this->_fileName = $path;
  131. $this->_temporaryFileName = tempnam(sys_get_temp_dir(), 'azure');
  132. // Check the file can be opened
  133. $fh = @fopen($this->_temporaryFileName, $mode);
  134. if ($fh === false) {
  135. return false;
  136. }
  137. fclose($fh);
  138. // Write mode?
  139. if (strpbrk($mode, 'wax+')) {
  140. $this->_writeMode = true;
  141. } else {
  142. $this->_writeMode = false;
  143. }
  144. // If read/append, fetch the file
  145. if (!$this->_writeMode || strpbrk($mode, 'ra+')) {
  146. $this->_getStorageClient($this->_fileName)->getBlob(
  147. $this->_getContainerName($this->_fileName),
  148. $this->_getFileName($this->_fileName),
  149. $this->_temporaryFileName
  150. );
  151. }
  152. // Open temporary file handle
  153. $this->_temporaryFileHandle = fopen($this->_temporaryFileName, $mode);
  154. // Ok!
  155. return true;
  156. }
  157. /**
  158. * Close the stream
  159. *
  160. * @return void
  161. */
  162. public function stream_close()
  163. {
  164. @fclose($this->_temporaryFileHandle);
  165. // Upload the file?
  166. if ($this->_writeMode) {
  167. // Make sure the container exists
  168. $containerExists = $this->_getStorageClient($this->_fileName)->containerExists(
  169. $this->_getContainerName($this->_fileName)
  170. );
  171. if (!$containerExists) {
  172. $this->_getStorageClient($this->_fileName)->createContainer(
  173. $this->_getContainerName($this->_fileName)
  174. );
  175. }
  176. // Upload the file
  177. try {
  178. $this->_getStorageClient($this->_fileName)->putBlob(
  179. $this->_getContainerName($this->_fileName),
  180. $this->_getFileName($this->_fileName),
  181. $this->_temporaryFileName
  182. );
  183. } catch (Zend_Service_WindowsAzure_Exception $ex) {
  184. @unlink($this->_temporaryFileName);
  185. unset($this->_storageClient);
  186. throw $ex;
  187. }
  188. }
  189. @unlink($this->_temporaryFileName);
  190. unset($this->_storageClient);
  191. }
  192. /**
  193. * Read from the stream
  194. *
  195. * @param integer $count
  196. * @return string
  197. */
  198. public function stream_read($count)
  199. {
  200. if (!$this->_temporaryFileHandle) {
  201. return false;
  202. }
  203. return fread($this->_temporaryFileHandle, $count);
  204. }
  205. /**
  206. * Write to the stream
  207. *
  208. * @param string $data
  209. * @return integer
  210. */
  211. public function stream_write($data)
  212. {
  213. if (!$this->_temporaryFileHandle) {
  214. return 0;
  215. }
  216. $len = strlen($data);
  217. fwrite($this->_temporaryFileHandle, $data, $len);
  218. return $len;
  219. }
  220. /**
  221. * End of the stream?
  222. *
  223. * @return boolean
  224. */
  225. public function stream_eof()
  226. {
  227. if (!$this->_temporaryFileHandle) {
  228. return true;
  229. }
  230. return feof($this->_temporaryFileHandle);
  231. }
  232. /**
  233. * What is the current read/write position of the stream?
  234. *
  235. * @return integer
  236. */
  237. public function stream_tell()
  238. {
  239. return ftell($this->_temporaryFileHandle);
  240. }
  241. /**
  242. * Update the read/write position of the stream
  243. *
  244. * @param integer $offset
  245. * @param integer $whence
  246. * @return boolean
  247. */
  248. public function stream_seek($offset, $whence)
  249. {
  250. if (!$this->_temporaryFileHandle) {
  251. return false;
  252. }
  253. return (fseek($this->_temporaryFileHandle, $offset, $whence) === 0);
  254. }
  255. /**
  256. * Flush current cached stream data to storage
  257. *
  258. * @return boolean
  259. */
  260. public function stream_flush()
  261. {
  262. $result = fflush($this->_temporaryFileHandle);
  263. // Upload the file?
  264. if ($this->_writeMode) {
  265. // Make sure the container exists
  266. $containerExists = $this->_getStorageClient($this->_fileName)->containerExists(
  267. $this->_getContainerName($this->_fileName)
  268. );
  269. if (!$containerExists) {
  270. $this->_getStorageClient($this->_fileName)->createContainer(
  271. $this->_getContainerName($this->_fileName)
  272. );
  273. }
  274. // Upload the file
  275. try {
  276. $this->_getStorageClient($this->_fileName)->putBlob(
  277. $this->_getContainerName($this->_fileName),
  278. $this->_getFileName($this->_fileName),
  279. $this->_temporaryFileName
  280. );
  281. } catch (Zend_Service_WindowsAzure_Exception $ex) {
  282. @unlink($this->_temporaryFileName);
  283. unset($this->_storageClient);
  284. throw $ex;
  285. }
  286. }
  287. return $result;
  288. }
  289. /**
  290. * Returns data array of stream variables
  291. *
  292. * @return array
  293. */
  294. public function stream_stat()
  295. {
  296. if (!$this->_temporaryFileHandle) {
  297. return false;
  298. }
  299. return $this->url_stat($this->_fileName, 0);
  300. }
  301. /**
  302. * Attempt to delete the item
  303. *
  304. * @param string $path
  305. * @return boolean
  306. */
  307. public function unlink($path)
  308. {
  309. $this->_getStorageClient($path)->deleteBlob(
  310. $this->_getContainerName($path),
  311. $this->_getFileName($path)
  312. );
  313. // Clear the stat cache for this path.
  314. clearstatcache(true, $path);
  315. return true;
  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. if ($this->_getContainerName($path_from) != $this->_getContainerName($path_to)) {
  327. throw new Zend_Service_WindowsAzure_Exception('Container name can not be changed.');
  328. }
  329. if ($this->_getFileName($path_from) == $this->_getContainerName($path_to)) {
  330. return true;
  331. }
  332. $this->_getStorageClient($path_from)->copyBlob(
  333. $this->_getContainerName($path_from),
  334. $this->_getFileName($path_from),
  335. $this->_getContainerName($path_to),
  336. $this->_getFileName($path_to)
  337. );
  338. $this->_getStorageClient($path_from)->deleteBlob(
  339. $this->_getContainerName($path_from),
  340. $this->_getFileName($path_from)
  341. );
  342. // Clear the stat cache for the affected paths.
  343. clearstatcache(true, $path_from);
  344. clearstatcache(true, $path_to);
  345. return true;
  346. }
  347. /**
  348. * Return array of URL variables
  349. *
  350. * @param string $path
  351. * @param integer $flags
  352. * @return array
  353. */
  354. public function url_stat($path, $flags)
  355. {
  356. $stat = array();
  357. $stat['dev'] = 0;
  358. $stat['ino'] = 0;
  359. $stat['mode'] = 0;
  360. $stat['nlink'] = 0;
  361. $stat['uid'] = 0;
  362. $stat['gid'] = 0;
  363. $stat['rdev'] = 0;
  364. $stat['size'] = 0;
  365. $stat['atime'] = 0;
  366. $stat['mtime'] = 0;
  367. $stat['ctime'] = 0;
  368. $stat['blksize'] = 0;
  369. $stat['blocks'] = 0;
  370. $info = null;
  371. try {
  372. $info = $this->_getStorageClient($path)->getBlobInstance(
  373. $this->_getContainerName($path),
  374. $this->_getFileName($path)
  375. );
  376. $stat['size'] = $info->Size;
  377. // Set the modification time and last modified to the Last-Modified header.
  378. $lastmodified = strtotime($info->LastModified);
  379. $stat['mtime'] = $lastmodified;
  380. $stat['ctime'] = $lastmodified;
  381. // Entry is a regular file.
  382. $stat['mode'] = 0100000;
  383. return array_values($stat) + $stat;
  384. } catch (Zend_Service_WindowsAzure_Exception $ex) {
  385. // Unexisting file...
  386. return false;
  387. }
  388. }
  389. /**
  390. * Create a new directory
  391. *
  392. * @param string $path
  393. * @param integer $mode
  394. * @param integer $options
  395. * @return boolean
  396. */
  397. public function mkdir($path, $mode, $options)
  398. {
  399. if ($this->_getContainerName($path) == $this->_getFileName($path)) {
  400. // Create container
  401. try {
  402. $this->_getStorageClient($path)->createContainer(
  403. $this->_getContainerName($path)
  404. );
  405. return true;
  406. } catch (Zend_Service_WindowsAzure_Exception $ex) {
  407. return false;
  408. }
  409. } else {
  410. throw new Zend_Service_WindowsAzure_Exception('mkdir() with multiple levels is not supported on Windows Azure Blob Storage.');
  411. }
  412. }
  413. /**
  414. * Remove a directory
  415. *
  416. * @param string $path
  417. * @param integer $options
  418. * @return boolean
  419. */
  420. public function rmdir($path, $options)
  421. {
  422. if ($this->_getContainerName($path) == $this->_getFileName($path)) {
  423. // Clear the stat cache so that affected paths are refreshed.
  424. clearstatcache();
  425. // Delete container
  426. try {
  427. $this->_getStorageClient($path)->deleteContainer(
  428. $this->_getContainerName($path)
  429. );
  430. return true;
  431. } catch (Zend_Service_WindowsAzure_Exception $ex) {
  432. return false;
  433. }
  434. } else {
  435. throw new Zend_Service_WindowsAzure_Exception('rmdir() with multiple levels is not supported on Windows Azure Blob Storage.');
  436. }
  437. }
  438. /**
  439. * Attempt to open a directory
  440. *
  441. * @param string $path
  442. * @param integer $options
  443. * @return boolean
  444. */
  445. public function dir_opendir($path, $options)
  446. {
  447. $this->_blobs = $this->_getStorageClient($path)->listBlobs(
  448. $this->_getContainerName($path)
  449. );
  450. return is_array($this->_blobs);
  451. }
  452. /**
  453. * Return the next filename in the directory
  454. *
  455. * @return string
  456. */
  457. public function dir_readdir()
  458. {
  459. $object = current($this->_blobs);
  460. if ($object !== false) {
  461. next($this->_blobs);
  462. return $object->Name;
  463. }
  464. return false;
  465. }
  466. /**
  467. * Reset the directory pointer
  468. *
  469. * @return boolean True
  470. */
  471. public function dir_rewinddir()
  472. {
  473. reset($this->_blobs);
  474. return true;
  475. }
  476. /**
  477. * Close a directory
  478. *
  479. * @return boolean True
  480. */
  481. public function dir_closedir()
  482. {
  483. $this->_blobs = null;
  484. return true;
  485. }
  486. }