Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/packages/zendframework/stream.php

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