1: <?php
2: namespace NGS;
3:
4: use AmazonS3;
5: use InvalidArgumentException;
6: use LogicException;
7: use NGS\Converter\PrimitiveConverter;
8: use NGS\S3;
9: use NGS\Utils;
10: use NGS\UUID;
11: use S3StreamWrapper;
12:
13: class S3
14: {
15: const HOST = 's3.amazonaws.com';
16:
17:
18: private static $client;
19:
20: private static $defaultBucket;
21:
22: private $bucket;
23: private $key;
24: private $name;
25: private $length;
26: private $mimeType;
27: private $metadata;
28:
29: 30: 31: 32: 33:
34: public function __construct($source=null, $bucket=null)
35: {
36: if(is_array($source)) {
37: self::fromArray($source);
38: }
39: elseif($source !== null) {
40: if ($bucket !== null) {
41: $this->bucket = PrimitiveConverter::toString($bucket);
42: }
43: if ($source instanceof S3) {
44: $this->fromArray($source->toArray());
45: }
46: elseif (is_string($source)) {
47: $this->upload($source);
48: }
49: elseif (is_resource($source) && get_resource_type($source)==='stream') {
50: $this->upload($source);
51: }
52: else {
53: throw new InvalidArgumentException('Cannot construct NGS\S3 from invalid type "'.Utils::getType($source).'"');
54: }
55: }
56: }
57:
58: private function fromArray(array $values)
59: {
60: if (isset($values['Bucket']))
61: $this->bucket = PrimitiveConverter::toString($values['Bucket']);
62: if (isset($values['Key']))
63: $this->key = PrimitiveConverter::toString($values['Key']);
64: if (isset($values['Length']))
65: $this->length = PrimitiveConverter::toInteger($values['Length']);
66: if (isset($values['Name']))
67: $this->setName($values['Name']);
68: if (isset($values['MimeType']))
69: $this->setMimeType($values['MimeType']);
70: if (isset($values['Metadata']))
71: $this->setMetadata($values['Metadata']);
72: }
73:
74: public function setName($name)
75: {
76: $this->name = PrimitiveConverter::toString($name);
77: }
78:
79: public function setMetadata(array $metadata)
80: {
81: $this->metadata = PrimitiveConverter::toMap($metadata);
82: }
83:
84: public function setMimeType($mimeType)
85: {
86: $this->mimeType = PrimitiveConverter::toString($mimeType);
87: }
88:
89: public function getBucket()
90: {
91: return $this->bucket;
92: }
93:
94: public function getKey()
95: {
96: return $this->key;
97: }
98:
99: public function getMetadata()
100: {
101: return $this->metadata;
102: }
103:
104: public function getMimeType()
105: {
106: return $this->mimeType;
107: }
108:
109: public function getName()
110: {
111: return $this->name;
112: }
113:
114: public function getURI()
115: {
116: return $this->bucket+':'+$this->key;
117: }
118:
119: public function __get($property)
120: {
121: switch ($property) {
122: case 'bucket':
123: return $this->getBucket();
124: case 'key':
125: return $this->getKey();
126: case 'metaData':
127: return $this->getMetadata();
128: case 'mimeType':
129: return $this->getMimeType();
130: case 'name':
131: return $this->getName();
132: case 'URI':
133: return $this->getURI();
134: default:
135: throw new InvalidArgumentException('Cannot access unexisting property "'.$property.'"');
136: }
137: }
138:
139: public function getUrl()
140: {
141: return 'http://'.self::HOST.'/'.$this->bucket.'/'.$this->key;
142: }
143:
144: public function __toString()
145: {
146: return $this->getUrl();
147: }
148:
149: public function toArray()
150: {
151: return array(
152: 'Bucket' => $this->bucket,
153: 'Key' => $this->key,
154: 'Length' => $this->length,
155: 'Name' => $this->name,
156: 'MimeType' => $this->mimeType,
157: 'Metadata' => $this->metadata,
158: );
159: }
160:
161: 162: 163: 164:
165: public static function setClient(AmazonS3 $client)
166: {
167: self::$client = $client;
168: }
169:
170: 171: 172: 173:
174: public static function getClient()
175: {
176: return self::$client;
177: }
178:
179: public static function getDefaultBucket()
180: {
181: return self::$defaultBucket;
182: }
183:
184: public static function setDefaultBucket($bucket)
185: {
186: if(!is_string($bucket)) {
187: throw new InvalidArgumentException('Cannot set default bucket, bucket name must be a string, invalid type was '.Utils::getType($bucket));
188: }
189: self::$defaultBucket = $bucket;
190: }
191:
192: public static function load($bucket, $key)
193: {
194: $response = self::loadContent($bucket, $key);
195: if($response->isOK()) {
196: return $response->body;
197: }
198: else {
199: throw new InvalidArgumentException('Could not load "'.$this->bucket.'/'.$this->key.'"');
200: }
201: }
202:
203: public function getStream()
204: {
205: self::registerS3StreamWrapper();
206: return fopen('s3://'.$this->bucket.'/'.$this->key, 'r');
207: }
208:
209: private function _upload(array $options)
210: {
211: if($this->bucket === null) {
212: if(self::getDefaultBucket() === null)
213: throw new LogicException('Cannot upload content to S3, no bucket was provided, and no default bucket is set.');
214: $this->bucket = self::getDefaultBucket();
215: }
216: $this->key = self::generateKey();
217:
218: if ($this->metadata !== null) {
219: $options['meta'] = $this->metadata;
220: }
221: if ($this->mimeType !== null) {
222: $options['contentType'] = $this->mimeType;
223: }
224: if ($this->length !== null) {
225: $options['length'] = $this->length;
226: }
227:
228: $response = self::getClient()->create_object($this->bucket, $this->key, $options);
229: return $this->checkResponse($response, 'uploading file');
230: }
231:
232: public function upload($file)
233: {
234: if (!is_resource($file) && !is_string($file)) {
235: throw new InvalidArgumentException('Cannot upload file to S3, provided type must be stream or existing filepath, invalid type was "'.Utils::getType($file).'"');
236: }
237: elseif (is_resource($file) && get_resource_type($file)!=='stream') {
238: throw new InvalidArgumentException('Cannot upload file to S3, provided file was stream but invalid type "'. get_resource_type($file).'"');
239: }
240: elseif (is_string($file) && !is_file($file)) {
241: throw new InvalidArgumentException('Cannot upload file to S3, no file found at provided path "'.$file.'"');
242: }
243:
244: if(is_string($file)) {
245: $file = fopen($file, 'r');
246: }
247: $stat = fstat($file);
248: if(isset($stat['size'])) {
249: $this->length = $stat['size'];
250: }
251: return $this->_upload(array('fileUpload' => $file));
252: }
253:
254: public function uploadString($content)
255: {
256: if(!is_string($content)) {
257: throw new InvalidArgumentException('Cannot upload string content to S3, type must be string, invalid type was "'.Utils::getType($file).'"');
258: }
259: $this->length = strlen($content);
260: return $this->_upload(array('upload' => $content));
261: }
262:
263: public function delete()
264: {
265: if(!isset($this->bucket)) {
266: throw new \InvalidArgumentException('Cannot delete S3 object with no bucket set');
267: }
268: if(!isset($this->key)) {
269: throw new \InvalidArgumentException('Cannot delete S3 object with no key set');
270: }
271: $response = self::getClient()->delete_object($this->bucket, $this->key);
272:
273: if(!$response->isOK()) {
274: throw new InvalidArgumentException('Could not delete object '.$this->getURI().'');
275: }
276: return true;
277: }
278:
279: private function checkResponse($response, $action)
280: {
281: if($response===null)
282: throw new LogicException('Response was null');
283: if($response->isOK())
284: return $this;
285: throw new LogicException(
286: 'Error while performing action ."'.$action.'. '
287: .'Response status was: '.$response->status.'. '
288: .'Response body: '.$response->body);
289: }
290:
291: private static function registerS3StreamWrapper()
292: {
293: static $wrapperIsRegistered = false;
294: if($wrapperIsRegistered)
295: return ;
296: $wrapperIsRegistered = true;
297: if(!S3StreamWrapper::register(self::getClient(), 's3'))
298: throw new LogicException('Failed to register S3StreamWrapper');
299: return ;
300: }
301:
302: private static function loadContent($bucket, $key)
303: {
304: return self::getClient()->get_object($bucket, $key);
305: }
306:
307: private static function generateKey()
308: {
309: return (string)UUID::v4();
310: }
311:
312: };
313: