File indexing completed on 2024-12-29 05:28:01
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 0017 * @subpackage Amazon_S3 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * @see Zend_Service_Amazon_S3 0025 */ 0026 // require_once 'Zend/Service/Amazon/S3.php'; 0027 0028 /** 0029 * Amazon S3 PHP stream wrapper 0030 * 0031 * @category Zend 0032 * @package Zend_Service 0033 * @subpackage Amazon_S3 0034 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0035 * @license http://framework.zend.com/license/new-bsd New BSD License 0036 */ 0037 class Zend_Service_Amazon_S3_Stream 0038 { 0039 /** 0040 * @var boolean Write the buffer on fflush()? 0041 */ 0042 private $_writeBuffer = false; 0043 0044 /** 0045 * @var integer Current read/write position 0046 */ 0047 private $_position = 0; 0048 0049 /** 0050 * @var integer Total size of the object as returned by S3 (Content-length) 0051 */ 0052 private $_objectSize = 0; 0053 0054 /** 0055 * @var string File name to interact with 0056 */ 0057 private $_objectName = null; 0058 0059 /** 0060 * @var string Current read/write buffer 0061 */ 0062 private $_objectBuffer = null; 0063 0064 /** 0065 * @var array Available buckets 0066 */ 0067 private $_bucketList = array(); 0068 0069 /** 0070 * @var Zend_Service_Amazon_S3 0071 */ 0072 private $_s3 = null; 0073 0074 /** 0075 * Retrieve client for this stream type 0076 * 0077 * @param string $path 0078 * @return Zend_Service_Amazon_S3 0079 */ 0080 protected function _getS3Client($path) 0081 { 0082 if ($this->_s3 === null) { 0083 $url = explode(':', $path); 0084 0085 if (!$url) { 0086 /** 0087 * @see Zend_Service_Amazon_S3_Exception 0088 */ 0089 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0090 throw new Zend_Service_Amazon_S3_Exception("Unable to parse URL $path"); 0091 } 0092 0093 $this->_s3 = Zend_Service_Amazon_S3::getWrapperClient($url[0]); 0094 if (!$this->_s3) { 0095 /** 0096 * @see Zend_Service_Amazon_S3_Exception 0097 */ 0098 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0099 throw new Zend_Service_Amazon_S3_Exception("Unknown client for wrapper {$url[0]}"); 0100 } 0101 } 0102 0103 return $this->_s3; 0104 } 0105 0106 /** 0107 * Extract object name from URL 0108 * 0109 * @param string $path 0110 * @return string 0111 */ 0112 protected function _getNamePart($path) 0113 { 0114 $url = parse_url($path); 0115 if ($url['host']) { 0116 return !empty($url['path']) ? $url['host'].$url['path'] : $url['host']; 0117 } 0118 0119 return ''; 0120 } 0121 /** 0122 * Open the stream 0123 * 0124 * @param string $path 0125 * @param string $mode 0126 * @param integer $options 0127 * @param string $opened_path 0128 * @return boolean 0129 */ 0130 public function stream_open($path, $mode, $options, $opened_path) 0131 { 0132 $name = $this->_getNamePart($path); 0133 // If we open the file for writing, just return true. Create the object 0134 // on fflush call 0135 if (strpbrk($mode, 'wax')) { 0136 $this->_objectName = $name; 0137 $this->_objectBuffer = null; 0138 $this->_objectSize = 0; 0139 $this->_position = 0; 0140 $this->_writeBuffer = true; 0141 $this->_getS3Client($path); 0142 return true; 0143 } else { 0144 // Otherwise, just see if the file exists or not 0145 $info = $this->_getS3Client($path)->getInfo($name); 0146 if ($info) { 0147 $this->_objectName = $name; 0148 $this->_objectBuffer = null; 0149 $this->_objectSize = $info['size']; 0150 $this->_position = 0; 0151 $this->_writeBuffer = false; 0152 $this->_getS3Client($path); 0153 return true; 0154 } 0155 } 0156 return false; 0157 } 0158 0159 /** 0160 * Close the stream 0161 * 0162 * @return void 0163 */ 0164 public function stream_close() 0165 { 0166 $this->_objectName = null; 0167 $this->_objectBuffer = null; 0168 $this->_objectSize = 0; 0169 $this->_position = 0; 0170 $this->_writeBuffer = false; 0171 unset($this->_s3); 0172 } 0173 0174 /** 0175 * Read from the stream 0176 * 0177 * http://bugs.php.net/21641 - stream_read() is always passed PHP's 0178 * internal read buffer size (8192) no matter what is passed as $count 0179 * parameter to fread(). 0180 * 0181 * @param integer $count 0182 * @return string 0183 */ 0184 public function stream_read($count) 0185 { 0186 if (!$this->_objectName) { 0187 return false; 0188 } 0189 0190 // make sure that count doesn't exceed object size 0191 if ($count + $this->_position > $this->_objectSize) { 0192 $count = $this->_objectSize - $this->_position; 0193 } 0194 0195 $range_start = $this->_position; 0196 $range_end = $this->_position + $count - 1; 0197 0198 // Only fetch more data from S3 if we haven't fetched any data yet (postion=0) 0199 // OR, the range end position plus 1 is greater than the size of the current 0200 // object buffer 0201 if ($this->_objectBuffer === null || $range_end >= strlen($this->_objectBuffer)) { 0202 $headers = array( 0203 'Range' => "bytes=$range_start-$range_end" 0204 ); 0205 0206 $response = $this->_s3->_makeRequest('GET', $this->_objectName, null, $headers); 0207 0208 if ($response->getStatus() == 206) { // 206 Partial Content 0209 $this->_objectBuffer .= $response->getBody(); 0210 } 0211 } 0212 0213 $data = substr($this->_objectBuffer, $this->_position, $count); 0214 $this->_position += strlen($data); 0215 return $data; 0216 } 0217 0218 /** 0219 * Write to the stream 0220 * 0221 * @param string $data 0222 * @return integer 0223 */ 0224 public function stream_write($data) 0225 { 0226 if (!$this->_objectName) { 0227 return 0; 0228 } 0229 $len = strlen($data); 0230 $this->_objectBuffer .= $data; 0231 $this->_objectSize += $len; 0232 // TODO: handle current position for writing! 0233 return $len; 0234 } 0235 0236 /** 0237 * End of the stream? 0238 * 0239 * @return boolean 0240 */ 0241 public function stream_eof() 0242 { 0243 if (!$this->_objectName) { 0244 return true; 0245 } 0246 0247 return ($this->_position >= $this->_objectSize); 0248 } 0249 0250 /** 0251 * What is the current read/write position of the stream 0252 * 0253 * @return integer 0254 */ 0255 public function stream_tell() 0256 { 0257 return $this->_position; 0258 } 0259 0260 /** 0261 * Update the read/write position of the stream 0262 * 0263 * @param integer $offset 0264 * @param integer $whence 0265 * @return boolean 0266 */ 0267 public function stream_seek($offset, $whence) 0268 { 0269 if (!$this->_objectName) { 0270 return false; 0271 } 0272 0273 switch ($whence) { 0274 case SEEK_CUR: 0275 // Set position to current location plus $offset 0276 $new_pos = $this->_position + $offset; 0277 break; 0278 case SEEK_END: 0279 // Set position to end-of-file plus $offset 0280 $new_pos = $this->_objectSize + $offset; 0281 break; 0282 case SEEK_SET: 0283 default: 0284 // Set position equal to $offset 0285 $new_pos = $offset; 0286 break; 0287 } 0288 $ret = ($new_pos >= 0 && $new_pos <= $this->_objectSize); 0289 if ($ret) { 0290 $this->_position = $new_pos; 0291 } 0292 return $ret; 0293 } 0294 0295 /** 0296 * Flush current cached stream data to storage 0297 * 0298 * @return boolean 0299 */ 0300 public function stream_flush() 0301 { 0302 // If the stream wasn't opened for writing, just return false 0303 if (!$this->_writeBuffer) { 0304 return false; 0305 } 0306 0307 $ret = $this->_s3->putObject($this->_objectName, $this->_objectBuffer); 0308 0309 $this->_objectBuffer = null; 0310 0311 return $ret; 0312 } 0313 0314 /** 0315 * Returns data array of stream variables 0316 * 0317 * @return array 0318 */ 0319 public function stream_stat() 0320 { 0321 if (!$this->_objectName) { 0322 return false; 0323 } 0324 0325 $stat = array(); 0326 $stat['dev'] = 0; 0327 $stat['ino'] = 0; 0328 $stat['mode'] = 0777; 0329 $stat['nlink'] = 0; 0330 $stat['uid'] = 0; 0331 $stat['gid'] = 0; 0332 $stat['rdev'] = 0; 0333 $stat['size'] = 0; 0334 $stat['atime'] = 0; 0335 $stat['mtime'] = 0; 0336 $stat['ctime'] = 0; 0337 $stat['blksize'] = 0; 0338 $stat['blocks'] = 0; 0339 0340 if(($slash = strchr($this->_objectName, '/')) === false || $slash == strlen($this->_objectName)-1) { 0341 /* bucket */ 0342 $stat['mode'] |= 040000; 0343 } else { 0344 $stat['mode'] |= 0100000; 0345 } 0346 $info = $this->_s3->getInfo($this->_objectName); 0347 if (!empty($info)) { 0348 $stat['size'] = $info['size']; 0349 $stat['atime'] = time(); 0350 $stat['mtime'] = $info['mtime']; 0351 } 0352 0353 return $stat; 0354 } 0355 0356 /** 0357 * Attempt to delete the item 0358 * 0359 * @param string $path 0360 * @return boolean 0361 */ 0362 public function unlink($path) 0363 { 0364 return $this->_getS3Client($path)->removeObject($this->_getNamePart($path)); 0365 } 0366 0367 /** 0368 * Attempt to rename the item 0369 * 0370 * @param string $path_from 0371 * @param string $path_to 0372 * @return boolean False 0373 */ 0374 public function rename($path_from, $path_to) 0375 { 0376 // TODO: Renaming isn't supported, always return false 0377 return false; 0378 } 0379 0380 /** 0381 * Create a new directory 0382 * 0383 * @param string $path 0384 * @param integer $mode 0385 * @param integer $options 0386 * @return boolean 0387 */ 0388 public function mkdir($path, $mode, $options) 0389 { 0390 return $this->_getS3Client($path)->createBucket(parse_url($path, PHP_URL_HOST)); 0391 } 0392 0393 /** 0394 * Remove a directory 0395 * 0396 * @param string $path 0397 * @param integer $options 0398 * @return boolean 0399 */ 0400 public function rmdir($path, $options) 0401 { 0402 return $this->_getS3Client($path)->removeBucket(parse_url($path, PHP_URL_HOST)); 0403 } 0404 0405 /** 0406 * Attempt to open a directory 0407 * 0408 * @param string $path 0409 * @param integer $options 0410 * @return boolean 0411 */ 0412 public function dir_opendir($path, $options) 0413 { 0414 0415 if (preg_match('@^([a-z0-9+.]|-)+://$@', $path)) { 0416 $this->_bucketList = $this->_getS3Client($path)->getBuckets(); 0417 } 0418 else { 0419 $host = parse_url($path, PHP_URL_HOST); 0420 $this->_bucketList = $this->_getS3Client($path)->getObjectsByBucket($host); 0421 } 0422 0423 return ($this->_bucketList !== false); 0424 } 0425 0426 /** 0427 * Return array of URL variables 0428 * 0429 * @param string $path 0430 * @param integer $flags 0431 * @return array 0432 */ 0433 public function url_stat($path, $flags) 0434 { 0435 $stat = array(); 0436 $stat['dev'] = 0; 0437 $stat['ino'] = 0; 0438 $stat['mode'] = 0777; 0439 $stat['nlink'] = 0; 0440 $stat['uid'] = 0; 0441 $stat['gid'] = 0; 0442 $stat['rdev'] = 0; 0443 $stat['size'] = 0; 0444 $stat['atime'] = 0; 0445 $stat['mtime'] = 0; 0446 $stat['ctime'] = 0; 0447 $stat['blksize'] = 0; 0448 $stat['blocks'] = 0; 0449 0450 $name = $this->_getNamePart($path); 0451 if(($slash = strchr($name, '/')) === false || $slash == strlen($name)-1) { 0452 /* bucket */ 0453 $stat['mode'] |= 040000; 0454 } else { 0455 $stat['mode'] |= 0100000; 0456 } 0457 $info = $this->_getS3Client($path)->getInfo($name); 0458 0459 if (!empty($info)) { 0460 $stat['size'] = $info['size']; 0461 $stat['atime'] = time(); 0462 $stat['mtime'] = $info['mtime']; 0463 } 0464 0465 return $stat; 0466 } 0467 0468 /** 0469 * Return the next filename in the directory 0470 * 0471 * @return string 0472 */ 0473 public function dir_readdir() 0474 { 0475 $object = current($this->_bucketList); 0476 if ($object !== false) { 0477 next($this->_bucketList); 0478 } 0479 return $object; 0480 } 0481 0482 /** 0483 * Reset the directory pointer 0484 * 0485 * @return boolean True 0486 */ 0487 public function dir_rewinddir() 0488 { 0489 reset($this->_bucketList); 0490 return true; 0491 } 0492 0493 /** 0494 * Close a directory 0495 * 0496 * @return boolean True 0497 */ 0498 public function dir_closedir() 0499 { 0500 $this->_bucketList = array(); 0501 return true; 0502 } 0503 }