1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17:
18: namespace OpenCloud\ObjectStore\Resource;
19:
20: use Guzzle\Http\EntityBody;
21: use Guzzle\Http\Message\Response;
22: use Guzzle\Http\Url;
23: use OpenCloud\Common\Constants\Header as HeaderConst;
24: use OpenCloud\Common\Exceptions;
25: use OpenCloud\Common\Lang;
26: use OpenCloud\ObjectStore\Constants\UrlType;
27: use OpenCloud\ObjectStore\Exception\ObjectNotEmptyException;
28:
29: 30: 31: 32: 33: 34: 35:
36: class DataObject extends AbstractResource
37: {
38: const METADATA_LABEL = 'Object';
39:
40: 41: 42:
43: private $container;
44:
45: 46: 47:
48: protected $name;
49:
50: 51: 52:
53: protected $content;
54:
55: 56: 57: 58:
59: protected $directory = false;
60:
61: 62: 63:
64: protected $contentType;
65:
66: 67: 68:
69: protected $contentLength;
70:
71: 72: 73:
74: protected $lastModified;
75:
76: 77: 78:
79: protected $etag;
80:
81: 82: 83:
84: protected $manifest = false;
85:
86: 87: 88: 89: 90: 91: 92:
93: public function __construct(Container $container, $data = null)
94: {
95: $this->setContainer($container);
96:
97: parent::__construct($container->getService());
98:
99:
100: if (!empty($data->subdir)) {
101: $this->setName($data->subdir)->setDirectory(true);
102:
103: return;
104: }
105:
106: $this->populate($data);
107: }
108:
109: 110: 111: 112: 113:
114: public function populate($info, $setObjects = true)
115: {
116: parent::populate($info, $setObjects);
117:
118: if (isset($info->bytes)) {
119: $this->setContentLength($info->bytes);
120: }
121: if (isset($info->last_modified)) {
122: $this->setLastModified($info->last_modified);
123: }
124: if (isset($info->content_type)) {
125: $this->setContentType($info->content_type);
126: }
127: if (isset($info->hash)) {
128: $this->setEtag($info->hash);
129: }
130: }
131:
132: 133: 134: 135: 136: 137:
138: public function populateFromResponse(Response $response)
139: {
140: $this->content = $response->getBody();
141:
142: $headers = $response->getHeaders();
143:
144: return $this->setMetadata($headers, true)
145: ->setContentType((string) $headers[HeaderConst::CONTENT_TYPE])
146: ->setLastModified((string) $headers[HeaderConst::LAST_MODIFIED])
147: ->setContentLength((string) $headers[HeaderConst::CONTENT_LENGTH])
148: ->setEtag((string) $headers[HeaderConst::ETAG])
149:
150: ->setManifest($headers[HeaderConst::X_OBJECT_MANIFEST]);
151: }
152:
153: public function refresh()
154: {
155: $response = $this->getService()->getClient()
156: ->get($this->getUrl())
157: ->send();
158:
159: return $this->populateFromResponse($response);
160: }
161:
162: 163: 164: 165:
166: public function setContainer(Container $container)
167: {
168: $this->container = $container;
169:
170: return $this;
171: }
172:
173: 174: 175:
176: public function getContainer()
177: {
178: return $this->container;
179: }
180:
181: 182: 183: 184:
185: public function setName($name)
186: {
187: $this->name = $name;
188:
189: return $this;
190: }
191:
192: 193: 194:
195: public function getName()
196: {
197: return $this->name;
198: }
199:
200: 201: 202: 203:
204: public function setDirectory($directory)
205: {
206: $this->directory = $directory;
207:
208: return $this;
209: }
210:
211: 212: 213:
214: public function getDirectory()
215: {
216: return $this->directory;
217: }
218:
219: 220: 221:
222: public function isDirectory()
223: {
224: return (bool) $this->directory;
225: }
226:
227: 228: 229: 230:
231: public function setContent($content)
232: {
233: $this->etag = null;
234: $this->contentType = null;
235: $this->content = EntityBody::factory($content);
236:
237: return $this;
238: }
239:
240: 241: 242:
243: public function getContent()
244: {
245: return $this->content;
246: }
247:
248: 249: 250: 251:
252: public function setContentType($contentType)
253: {
254: $this->contentType = $contentType;
255:
256: return $this;
257: }
258:
259: 260: 261:
262: public function getContentType()
263: {
264: return $this->contentType ? : $this->content->getContentType();
265: }
266:
267: 268: 269: 270:
271: public function setContentLength($contentLength)
272: {
273: $this->contentLength = $contentLength;
274:
275: return $this;
276: }
277:
278: 279: 280:
281: public function getContentLength()
282: {
283: return $this->contentLength !== null ? $this->contentLength : $this->content->getContentLength();
284: }
285:
286: 287: 288: 289:
290: public function setEtag($etag)
291: {
292: $this->etag = $etag;
293:
294: return $this;
295: }
296:
297: 298: 299:
300: public function getEtag()
301: {
302: return $this->etag ? : $this->content->getContentMd5();
303: }
304:
305: 306: 307: 308:
309: protected function setManifest($manifest)
310: {
311: $this->manifest = $manifest;
312:
313: return $this;
314: }
315:
316: 317: 318:
319: public function getManifest()
320: {
321:
322: return $this->manifest !== false ? $this->manifest : $this->getManifestHeader();
323: }
324:
325: public function setLastModified($lastModified)
326: {
327: $this->lastModified = $lastModified;
328:
329: return $this;
330: }
331:
332: public function getLastModified()
333: {
334: return $this->lastModified;
335: }
336:
337: public function primaryKeyField()
338: {
339: return 'name';
340: }
341:
342: public function getUrl($path = null, array $params = array())
343: {
344: if (!$this->name) {
345: throw new Exceptions\NoNameError(Lang::translate('Object has no name'));
346: }
347:
348: return $this->container->getUrl($this->name);
349: }
350:
351: public function update($params = array())
352: {
353: $metadata = is_array($this->metadata) ? $this->metadata : $this->metadata->toArray();
354: $metadata = self::stockHeaders($metadata);
355:
356:
357: $metadata += array(
358: HeaderConst::CONTENT_TYPE => $this->contentType,
359: HeaderConst::LAST_MODIFIED => $this->lastModified,
360: HeaderConst::CONTENT_LENGTH => $this->contentLength,
361: HeaderConst::ETAG => $this->etag,
362: HeaderConst::X_OBJECT_MANIFEST => $this->manifest
363: );
364:
365: return $this->container->uploadObject($this->name, $this->content, $metadata);
366: }
367:
368: 369: 370: 371:
372: public function copy($destination)
373: {
374: return $this->getService()
375: ->getClient()
376: ->createRequest('COPY', $this->getUrl(), array(
377: 'Destination' => (string) $destination
378: ))
379: ->send();
380: }
381:
382: public function delete($params = array())
383: {
384: return $this->getService()->getClient()->delete($this->getUrl())->send();
385: }
386:
387: 388: 389: 390: 391: 392: 393: 394:
395: public function createSymlinkTo($destination)
396: {
397: if (!$this->name) {
398: throw new Exceptions\NoNameError(Lang::translate('Object has no name'));
399: }
400:
401: if ($this->getContentLength()) {
402: throw new ObjectNotEmptyException($this->getContainer()->getName() . '/' . $this->getName());
403: }
404:
405: $response = $this->getService()
406: ->getClient()
407: ->createRequest('PUT', $this->getUrl(), array(
408: HeaderConst::X_OBJECT_MANIFEST => (string) $destination
409: ))
410: ->send();
411:
412: if ($response->getStatusCode() == 201) {
413: $this->setManifest($source);
414: }
415:
416: return $response;
417: }
418:
419: 420: 421: 422: 423: 424: 425: 426:
427: public function createSymlinkFrom($source)
428: {
429: if (!strlen($source)) {
430: throw new Exceptions\NoNameError(Lang::translate('Object has no name'));
431: }
432:
433:
434: list($containerName, $resourceName) = explode("/", ltrim($source, '/'), 2);
435: $container = $this->getService()->getContainer($containerName);
436:
437: if ($container->objectExists($resourceName)) {
438: $object = $container->getPartialObject($source);
439: if ($object->getContentLength() > 0) {
440: throw new ObjectNotEmptyException($source);
441: }
442: }
443:
444: return $container->uploadObject($resourceName, 'data', array(
445: HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl()
446: ));
447: }
448:
449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464:
465: public function getTemporaryUrl($expires, $method, $forcePublicUrl = false)
466: {
467: $method = strtoupper($method);
468: $expiry = time() + (int) $expires;
469:
470:
471: if ($method != 'GET' && $method != 'PUT') {
472: throw new Exceptions\InvalidArgumentError(sprintf(
473: 'Bad method [%s] for TempUrl; only GET or PUT supported',
474: $method
475: ));
476: }
477:
478:
479: if (!($secret = $this->getService()->getAccount()->getTempUrlSecret())) {
480: throw new Exceptions\ObjectError('Cannot produce temporary URL without an account secret.');
481: }
482:
483:
484: $url = ($forcePublicUrl === true) ? $this->getService()->getEndpoint()->getPublicUrl() : $this->getUrl();
485: $urlPath = urldecode($url->getPath());
486: $body = sprintf("%s\n%d\n%s", $method, $expiry, $urlPath);
487: $hash = hash_hmac('sha1', $body, $secret);
488:
489: return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry);
490: }
491:
492: 493: 494: 495: 496: 497:
498: public function purge($email = null)
499: {
500: if (!$cdn = $this->getContainer()->getCdn()) {
501: return false;
502: }
503:
504: $url = clone $cdn->getUrl();
505: $url->addPath($this->name);
506:
507: $headers = ($email !== null) ? array('X-Purge-Email' => $email) : array();
508:
509: return $this->getService()
510: ->getClient()
511: ->delete($url, $headers)
512: ->send();
513: }
514:
515: 516: 517: 518:
519: public function getPublicUrl($type = UrlType::CDN)
520: {
521: $cdn = $this->container->getCdn();
522:
523: switch ($type) {
524: case UrlType::CDN:
525: $uri = $cdn->getCdnUri();
526: break;
527: case UrlType::SSL:
528: $uri = $cdn->getCdnSslUri();
529: break;
530: case UrlType::STREAMING:
531: $uri = $cdn->getCdnStreamingUri();
532: break;
533: case UrlType::IOS_STREAMING:
534: $uri = $cdn->getIosStreamingUri();
535: break;
536: }
537:
538: return (isset($uri)) ? Url::factory($uri)->addPath($this->name) : false;
539: }
540:
541: protected static function ($header)
542: {
543: $pattern = sprintf('#^%s-%s-Meta-#i', self::GLOBAL_METADATA_PREFIX, self::METADATA_LABEL);
544:
545: return preg_match($pattern, $header);
546: }
547:
548: 549: 550:
551: protected function ()
552: {
553: $response = $this->getService()
554: ->getClient()
555: ->head($this->getUrl())
556: ->send();
557:
558: $manifest = $response->getHeader(HeaderConst::X_OBJECT_MANIFEST);
559:
560: $this->setManifest($manifest);
561:
562: return $manifest;
563: }
564: }
565: