File indexing completed on 2024-09-22 05:21:21

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Service_WindowsAzure_Storage
0017  * @subpackage Blob
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://todo     name_todo
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @category   Zend
0025  * @package    Zend_Service_WindowsAzure_Storage
0026  * @subpackage Blob
0027  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0028  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0029  */
0030 class Zend_Service_WindowsAzure_Storage_Blob_Stream
0031 {
0032     /**
0033      * Current file name
0034      * 
0035      * @var string
0036      */
0037     protected $_fileName = null;
0038     
0039     /**
0040      * Temporary file name
0041      * 
0042      * @var string
0043      */
0044     protected $_temporaryFileName = null;
0045     
0046     /**
0047      * Temporary file handle
0048      * 
0049      * @var resource
0050      */
0051     protected $_temporaryFileHandle = null;
0052     
0053     /**
0054      * Blob storage client
0055      * 
0056      * @var Zend_Service_WindowsAzure_Storage_Blob
0057      */
0058     protected $_storageClient = null;
0059     
0060     /**
0061      * Write mode?
0062      * 
0063      * @var boolean
0064      */
0065     protected $_writeMode = false;
0066     
0067     /**
0068      * List of blobs
0069      * 
0070      * @var array
0071      */
0072     protected $_blobs = null;
0073     
0074     /**
0075      * Retrieve storage client for this stream type
0076      * 
0077      * @param string $path
0078      * @return Zend_Service_WindowsAzure_Storage_Blob
0079      */
0080     protected function _getStorageClient($path = '')
0081     {
0082         if (is_null($this->_storageClient)) {
0083             $url = explode(':', $path);
0084             if (!$url) {
0085                 throw new Zend_Service_WindowsAzure_Exception('Could not parse path "' . $path . '".');
0086             }
0087 
0088             $this->_storageClient = Zend_Service_WindowsAzure_Storage_Blob::getWrapperClient($url[0]);
0089             if (!$this->_storageClient) {
0090                 throw new Zend_Service_WindowsAzure_Exception('No storage client registered for stream type "' . $url[0] . '://".');
0091             }
0092         }
0093         
0094         return $this->_storageClient;
0095     }
0096     
0097     /**
0098      * Extract container name
0099      *
0100      * @param string $path
0101      * @return string
0102      */
0103     protected function _getContainerName($path)
0104     {
0105         $url = parse_url($path);
0106         if ($url['host']) {
0107             return $url['host'];
0108         }
0109 
0110         return '';
0111     }
0112     
0113     /**
0114      * Extract file name
0115      *
0116      * @param string $path
0117      * @return string
0118      */
0119     protected function _getFileName($path)
0120     {
0121         $url = parse_url($path);
0122         if ($url['host']) {
0123             $fileName = isset($url['path']) ? $url['path'] : $url['host'];
0124           if (strpos($fileName, '/') === 0) {
0125               $fileName = substr($fileName, 1);
0126           }
0127             return $fileName;
0128         }
0129 
0130         return '';
0131     }
0132        
0133     /**
0134      * Open the stream
0135      *
0136      * @param  string  $path
0137      * @param  string  $mode
0138      * @param  integer $options
0139      * @param  string  $opened_path
0140      * @return boolean
0141      */
0142     public function stream_open($path, $mode, $options, &$opened_path)
0143     {
0144         $this->_fileName = $path;
0145         $this->_temporaryFileName = tempnam(sys_get_temp_dir(), 'azure');
0146         
0147         // Check the file can be opened
0148         $fh = @fopen($this->_temporaryFileName, $mode);
0149         if ($fh === false) {
0150             return false;
0151         }
0152         fclose($fh);
0153         
0154         // Write mode?
0155         if (strpbrk($mode, 'wax+')) {
0156             $this->_writeMode = true;
0157       } else {
0158             $this->_writeMode = false;
0159         }
0160         
0161         // If read/append, fetch the file
0162         if (!$this->_writeMode || strpbrk($mode, 'ra+')) {
0163             $this->_getStorageClient($this->_fileName)->getBlob(
0164                 $this->_getContainerName($this->_fileName),
0165                 $this->_getFileName($this->_fileName),
0166                 $this->_temporaryFileName
0167             );
0168         }
0169         
0170         // Open temporary file handle
0171         $this->_temporaryFileHandle = fopen($this->_temporaryFileName, $mode);
0172         
0173         // Ok!
0174         return true;
0175     }
0176 
0177     /**
0178      * Close the stream
0179      *
0180      * @return void
0181      */
0182     public function stream_close()
0183     {
0184         @fclose($this->_temporaryFileHandle);
0185         
0186         // Upload the file?
0187         if ($this->_writeMode) {
0188             // Make sure the container exists
0189             $containerExists = $this->_getStorageClient($this->_fileName)->containerExists(
0190                 $this->_getContainerName($this->_fileName)
0191             );
0192             if (!$containerExists) {
0193                 $this->_getStorageClient($this->_fileName)->createContainer(
0194                     $this->_getContainerName($this->_fileName)
0195                 );
0196             }
0197             
0198             // Upload the file
0199             try {
0200                 $this->_getStorageClient($this->_fileName)->putBlob(
0201                     $this->_getContainerName($this->_fileName),
0202                     $this->_getFileName($this->_fileName),
0203                     $this->_temporaryFileName
0204                 );
0205             } catch (Zend_Service_WindowsAzure_Exception $ex) {
0206                 @unlink($this->_temporaryFileName);
0207                 unset($this->_storageClient);
0208                 
0209                 throw $ex;
0210             }
0211         }
0212         
0213         @unlink($this->_temporaryFileName);
0214         unset($this->_storageClient);
0215     }
0216 
0217     /**
0218      * Read from the stream
0219      *
0220      * @param  integer $count
0221      * @return string
0222      */
0223     public function stream_read($count)
0224     {
0225         if (!$this->_temporaryFileHandle) {
0226             return false;
0227         }
0228 
0229         return fread($this->_temporaryFileHandle, $count);
0230     }
0231 
0232     /**
0233      * Write to the stream
0234      *
0235      * @param  string $data
0236      * @return integer
0237      */
0238     public function stream_write($data)
0239     {
0240         if (!$this->_temporaryFileHandle) {
0241             return 0;
0242         }
0243         
0244         $len = strlen($data);
0245         fwrite($this->_temporaryFileHandle, $data, $len);
0246         return $len;
0247     }
0248 
0249     /**
0250      * End of the stream?
0251      *
0252      * @return boolean
0253      */
0254     public function stream_eof()
0255     {
0256         if (!$this->_temporaryFileHandle) {
0257             return true;
0258         }
0259 
0260         return feof($this->_temporaryFileHandle);
0261     }
0262 
0263     /**
0264      * What is the current read/write position of the stream?
0265      *
0266      * @return integer
0267      */
0268     public function stream_tell()
0269     {
0270         return ftell($this->_temporaryFileHandle);
0271     }
0272 
0273     /**
0274      * Update the read/write position of the stream
0275      *
0276      * @param  integer $offset
0277      * @param  integer $whence
0278      * @return boolean
0279      */
0280     public function stream_seek($offset, $whence)
0281     {
0282         if (!$this->_temporaryFileHandle) {
0283             return false;
0284         }
0285         
0286         return (fseek($this->_temporaryFileHandle, $offset, $whence) === 0);
0287     }
0288 
0289     /**
0290      * Flush current cached stream data to storage
0291      *
0292      * @return boolean
0293      */
0294     public function stream_flush()
0295     {
0296         $result = fflush($this->_temporaryFileHandle);
0297         
0298          // Upload the file?
0299         if ($this->_writeMode) {
0300             // Make sure the container exists
0301             $containerExists = $this->_getStorageClient($this->_fileName)->containerExists(
0302                 $this->_getContainerName($this->_fileName)
0303             );
0304             if (!$containerExists) {
0305                 $this->_getStorageClient($this->_fileName)->createContainer(
0306                     $this->_getContainerName($this->_fileName)
0307                 );
0308             }
0309             
0310             // Upload the file
0311             try {
0312                 $this->_getStorageClient($this->_fileName)->putBlob(
0313                     $this->_getContainerName($this->_fileName),
0314                     $this->_getFileName($this->_fileName),
0315                     $this->_temporaryFileName
0316                 );
0317             } catch (Zend_Service_WindowsAzure_Exception $ex) {
0318                 @unlink($this->_temporaryFileName);
0319                 unset($this->_storageClient);
0320                 
0321                 throw $ex;
0322             }
0323         }
0324         
0325         return $result;
0326     }
0327 
0328     /**
0329      * Returns data array of stream variables
0330      *
0331      * @return array
0332      */
0333     public function stream_stat()
0334     {
0335         if (!$this->_temporaryFileHandle) {
0336             return false;
0337         }
0338 
0339         return $this->url_stat($this->_fileName, 0);
0340     }
0341 
0342     /**
0343      * Attempt to delete the item
0344      *
0345      * @param  string $path
0346      * @return boolean
0347      */
0348     public function unlink($path)
0349     {
0350         $this->_getStorageClient($path)->deleteBlob(
0351             $this->_getContainerName($path),
0352             $this->_getFileName($path)
0353         );
0354 
0355         // Clear the stat cache for this path.
0356         clearstatcache(true, $path);
0357         return true;
0358     }
0359 
0360     /**
0361      * Attempt to rename the item
0362      *
0363      * @param  string  $path_from
0364      * @param  string  $path_to
0365      * @return boolean False
0366      */
0367     public function rename($path_from, $path_to)
0368     {
0369         if ($this->_getContainerName($path_from) != $this->_getContainerName($path_to)) {
0370             throw new Zend_Service_WindowsAzure_Exception('Container name can not be changed.');
0371         }
0372         
0373         if ($this->_getFileName($path_from) == $this->_getContainerName($path_to)) {
0374             return true;
0375         }
0376             
0377         $this->_getStorageClient($path_from)->copyBlob(
0378             $this->_getContainerName($path_from),
0379             $this->_getFileName($path_from),
0380             $this->_getContainerName($path_to),
0381             $this->_getFileName($path_to)
0382         );
0383         $this->_getStorageClient($path_from)->deleteBlob(
0384             $this->_getContainerName($path_from),
0385             $this->_getFileName($path_from)
0386         );
0387 
0388         // Clear the stat cache for the affected paths.
0389         clearstatcache(true, $path_from);
0390         clearstatcache(true, $path_to);
0391         return true;
0392     }
0393     
0394     /**
0395      * Return array of URL variables
0396      *
0397      * @param  string $path
0398      * @param  integer $flags
0399      * @return array
0400      */
0401     public function url_stat($path, $flags)
0402     {
0403         $stat = array();
0404         $stat['dev'] = 0;
0405         $stat['ino'] = 0;
0406         $stat['mode'] = 0;
0407         $stat['nlink'] = 0;
0408         $stat['uid'] = 0;
0409         $stat['gid'] = 0;
0410         $stat['rdev'] = 0;
0411         $stat['size'] = 0;
0412         $stat['atime'] = 0;
0413         $stat['mtime'] = 0;
0414         $stat['ctime'] = 0;
0415         $stat['blksize'] = 0;
0416         $stat['blocks'] = 0;
0417 
0418         $info = null;
0419         try {
0420             $info = $this->_getStorageClient($path)->getBlobInstance(
0421                         $this->_getContainerName($path),
0422                         $this->_getFileName($path)
0423                     );
0424             $stat['size']  = $info->Size;
0425 
0426             // Set the modification time and last modified to the Last-Modified header.
0427             $lastmodified = strtotime($info->LastModified);
0428             $stat['mtime'] = $lastmodified;
0429             $stat['ctime'] = $lastmodified;
0430 
0431             // Entry is a regular file.
0432             $stat['mode'] = 0100000;
0433 
0434             return array_values($stat) + $stat;
0435         } catch (Zend_Service_WindowsAzure_Exception $ex) {
0436             // Unexisting file...
0437             return false;
0438         }
0439     }
0440 
0441     /**
0442      * Create a new directory
0443      *
0444      * @param  string  $path
0445      * @param  integer $mode
0446      * @param  integer $options
0447      * @return boolean
0448      */
0449     public function mkdir($path, $mode, $options)
0450     {
0451         if ($this->_getContainerName($path) == $this->_getFileName($path)) {
0452             // Create container
0453             try {
0454                 $this->_getStorageClient($path)->createContainer(
0455                     $this->_getContainerName($path)
0456                 );
0457                 return true;
0458             } catch (Zend_Service_WindowsAzure_Exception $ex) {
0459                 return false;
0460             }
0461         } else {
0462             throw new Zend_Service_WindowsAzure_Exception('mkdir() with multiple levels is not supported on Windows Azure Blob Storage.');
0463         }
0464     }
0465 
0466     /**
0467      * Remove a directory
0468      *
0469      * @param  string  $path
0470      * @param  integer $options
0471      * @return boolean
0472      */
0473     public function rmdir($path, $options)
0474     {
0475         if ($this->_getContainerName($path) == $this->_getFileName($path)) {
0476             // Clear the stat cache so that affected paths are refreshed.
0477             clearstatcache();
0478 
0479             // Delete container
0480             try {
0481                 $this->_getStorageClient($path)->deleteContainer(
0482                     $this->_getContainerName($path)
0483                 );
0484                 return true;
0485             } catch (Zend_Service_WindowsAzure_Exception $ex) {
0486                 return false;
0487             }
0488         } else {
0489             throw new Zend_Service_WindowsAzure_Exception('rmdir() with multiple levels is not supported on Windows Azure Blob Storage.');
0490         }
0491     }
0492 
0493     /**
0494      * Attempt to open a directory
0495      *
0496      * @param  string $path
0497      * @param  integer $options
0498      * @return boolean
0499      */
0500     public function dir_opendir($path, $options)
0501     {
0502         $this->_blobs = $this->_getStorageClient($path)->listBlobs(
0503             $this->_getContainerName($path)
0504         );
0505         return is_array($this->_blobs);
0506     }
0507 
0508     /**
0509      * Return the next filename in the directory
0510      *
0511      * @return string
0512      */
0513     public function dir_readdir()
0514     {
0515         $object = current($this->_blobs);
0516         if ($object !== false) {
0517             next($this->_blobs);
0518             return $object->Name;
0519         }
0520         return false;
0521     }
0522 
0523     /**
0524      * Reset the directory pointer
0525      *
0526      * @return boolean True
0527      */
0528     public function dir_rewinddir()
0529     {
0530         reset($this->_blobs);
0531         return true;
0532     }
0533 
0534     /**
0535      * Close a directory
0536      *
0537      * @return boolean True
0538      */
0539     public function dir_closedir()
0540     {
0541         $this->_blobs = null;
0542         return true;
0543     }
0544 }