Overview

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CloudMonitoring
      • Exception
      • Resource
    • Common
      • Collection
      • Constants
      • Exceptions
      • Http
        • Message
      • Identity
      • Log
      • Service
    • Compute
      • Constants
      • Exception
      • Resource
    • Database
      • Resource
    • DNS
      • Resource
    • LoadBalancer
      • Resource
    • ObjectStore
      • Constants
      • Exception
      • Resource
      • Upload
    • Orchestration
    • Queues
      • Exception
      • Resource
    • Volume
      • Resource
  • PHP

Classes

  • AbstractContainer
  • AbstractResource
  • Account
  • CDNContainer
  • Container
  • ContainerMetadata
  • DataObject
  • Overview
  • Namespace
  • Class
  • Tree
  • Download
  1: <?php
  2: /**
  3:  * PHP OpenCloud library.
  4:  *
  5:  * @copyright 2013 Rackspace Hosting, Inc. See LICENSE for information.
  6:  * @license   https://www.apache.org/licenses/LICENSE-2.0
  7:  * @author    Jamie Hannaford <jamie.hannaford@rackspace.com>
  8:  * @author    Glen Campbell <glen.campbell@rackspace.com>
  9:  */
 10: 
 11: namespace OpenCloud\ObjectStore\Resource;
 12: 
 13: use Guzzle\Http\EntityBody;
 14: use Guzzle\Http\Exception\ClientErrorResponseException;
 15: use Guzzle\Http\Message\Response;
 16: use Guzzle\Http\Url;
 17: use OpenCloud\Common\Constants\Size;
 18: use OpenCloud\Common\Exceptions;
 19: use OpenCloud\Common\Service\AbstractService;
 20: use OpenCloud\ObjectStore\Constants\Header as HeaderConst;
 21: use OpenCloud\ObjectStore\Upload\TransferBuilder;
 22: use OpenCloud\Common\Http\Message\Formatter;
 23: 
 24: /**
 25:  * A container is a storage compartment for your data and provides a way for you 
 26:  * to organize your data. You can think of a container as a folder in Windows 
 27:  * or a directory in Unix. The primary difference between a container and these 
 28:  * other file system concepts is that containers cannot be nested.
 29:  * 
 30:  * A container can also be CDN-enabled (for public access), in which case you
 31:  * will need to interact with a CDNContainer object instead of this one.
 32:  */
 33: class Container extends AbstractContainer
 34: {
 35:     const METADATA_LABEL = 'Container';
 36:     
 37:     /**
 38:      * This is the object that holds all the CDN functionality. This Container therefore acts as a simple wrapper and is
 39:      * interested in storage concerns only.
 40:      *
 41:      * @var CDNContainer|null
 42:      */
 43:     private $cdn;
 44: 
 45:     public function __construct(AbstractService $service, $data = null)
 46:     {
 47:         parent::__construct($service, $data);
 48: 
 49:         // Set metadata items for collection listings
 50:         if (isset($data->count)) {
 51:             $this->metadata->setProperty('Object-Count', $data->count);
 52:         }
 53:         if (isset($data->bytes)) {
 54:             $this->metadata->setProperty('Bytes-Used', $data->bytes);
 55:         }
 56:     }
 57: 
 58:     /**
 59:      * Factory method that instantiates an object from a Response object.
 60:      *
 61:      * @param Response        $response
 62:      * @param AbstractService $service
 63:      * @return static
 64:      */
 65:     public static function fromResponse(Response $response, AbstractService $service)
 66:     {
 67:         $self = parent::fromResponse($response, $service);
 68:         
 69:         $segments = Url::factory($response->getEffectiveUrl())->getPathSegments();
 70:         $self->name = end($segments);
 71:         
 72:         return $self;
 73:     }
 74: 
 75:     /**
 76:      * Get the CDN object.
 77:      *
 78:      * @return null|CDNContainer
 79:      * @throws \OpenCloud\Common\Exceptions\CdnNotAvailableError
 80:      */
 81:     public function getCdn()
 82:     {
 83:         if (!$this->isCdnEnabled() || !$this->cdn) {
 84:             throw new Exceptions\CdnNotAvailableError(
 85:                 'Either this container is not CDN-enabled or the CDN is not available'
 86:             );
 87:         }
 88:         
 89:         return $this->cdn;
 90:     }
 91: 
 92:     /**
 93:      * It would be awesome to put these convenience methods (which are identical to the ones in the Account object) in
 94:      * a trait, but we have to wait for v5.3 EOL first...
 95:      *
 96:      * @return null|string|int
 97:      */
 98:     public function getObjectCount()
 99:     {
100:         return $this->metadata->getProperty('Object-Count');
101:     }
102: 
103:     /**
104:      * @return null|string|int
105:      */
106:     public function getBytesUsed()
107:     {
108:         return $this->metadata->getProperty('Bytes-Used');
109:     }
110: 
111:     /**
112:      * @param $value
113:      * @return mixed
114:      */
115:     public function setCountQuota($value)
116:     {
117:         $this->metadata->setProperty('Quota-Count', $value);
118:         return $this->saveMetadata($this->metadata->toArray());
119:     }
120: 
121:     /**
122:      * @return null|string|int
123:      */
124:     public function getCountQuota()
125:     {
126:         return $this->metadata->getProperty('Quota-Count');
127:     }
128: 
129:     /**
130:      * @param $value
131:      * @return mixed
132:      */
133:     public function setBytesQuota($value)
134:     {
135:         $this->metadata->setProperty('Quota-Bytes', $value);
136:         return $this->saveMetadata($this->metadata->toArray());
137:     }
138: 
139:     /**
140:      * @return null|string|int
141:      */
142:     public function getBytesQuota()
143:     {
144:         return $this->metadata->getProperty('Quota-Bytes');
145:     }
146:     
147:     public function delete($deleteObjects = false)
148:     {
149:         if ($deleteObjects === true) {
150:             $this->deleteAllObjects();
151:         }
152: 
153:         return $this->getClient()->delete($this->getUrl())->send();
154:     }
155: 
156:     /**
157:      * Deletes all objects that this container currently contains. Useful when doing operations (like a delete) that
158:      * require an empty container first.
159:      *
160:      * @return mixed
161:      */
162:     public function deleteAllObjects()
163:     {
164:         $requests = array();
165:         
166:         $list = $this->objectList();
167:         
168:         foreach ($list as $object) {
169:             $requests[] = $this->getClient()->delete($object->getUrl());
170:         }
171: 
172:         return $this->getClient()->send($requests);
173:     }
174:     
175:     /**
176:      * Creates a Collection of objects in the container
177:      *
178:      * @param array $params associative array of parameter values.
179:      * * account/tenant - The unique identifier of the account/tenant.
180:      * * container- The unique identifier of the container.
181:      * * limit (Optional) - The number limit of results.
182:      * * marker (Optional) - Value of the marker, that the object names
183:      *      greater in value than are returned.
184:      * * end_marker (Optional) - Value of the marker, that the object names
185:      *      less in value than are returned.
186:      * * prefix (Optional) - Value of the prefix, which the returned object
187:      *      names begin with.
188:      * * format (Optional) - Value of the serialized response format, either
189:      *      json or xml.
190:      * * delimiter (Optional) - Value of the delimiter, that all the object
191:      *      names nested in the container are returned.
192:      * @link http://api.openstack.org for a list of possible parameter
193:      *      names and values
194:      * @return 'OpenCloud\Common\Collection
195:      * @throws ObjFetchError
196:      */
197:     public function objectList(array $params = array())
198:     {
199:         $params['format'] = 'json';
200:         return $this->getService()->resourceList('DataObject', $this->getUrl(null, $params), $this);
201:     }
202: 
203:     /**
204:      * Turn on access logs, which track all the web traffic that your data objects accrue.
205:      *
206:      * @return \Guzzle\Http\Message\Response
207:      */
208:     public function enableLogging()
209:     {
210:         return $this->saveMetadata($this->appendToMetadata(array(
211:             HeaderConst::ACCESS_LOGS => 'True'
212:         )));
213:     }
214: 
215:     /**
216:      * Disable access logs.
217:      *
218:      * @return \Guzzle\Http\Message\Response
219:      */
220:     public function disableLogging()
221:     {
222:         return $this->saveMetadata($this->appendToMetadata(array(
223:             HeaderConst::ACCESS_LOGS => 'False'
224:         )));
225:     }
226: 
227:     /**
228:      * Enable this container for public CDN access.
229:      *
230:      * @param null $ttl
231:      */
232:     public function enableCdn($ttl = null)
233:     {
234:         $headers = array('X-CDN-Enabled' => 'True');
235:         if ($ttl) {
236:             $headers['X-TTL'] = (int) $ttl;
237:         }
238: 
239:         $this->getClient()->put($this->getCdnService()->getUrl($this->name), $headers)->send();
240:         $this->refresh();
241:     }
242: 
243:     /**
244:      * Disables the containers CDN function. Note that the container will still 
245:      * be available on the CDN until its TTL expires.
246:      * 
247:      * @return \Guzzle\Http\Message\Response
248:      */
249:     public function disableCdn()
250:     {
251:         return $this->getClient()
252:             ->put($this->getCdnService()->getUrl($this->name), array('X-CDN-Enabled' => 'False'))
253:             ->send();
254:     }
255: 
256:     public function refresh($id = null, $url = null)
257:     {
258:         $headers = $this->createRefreshRequest()->send()->getHeaders();
259:         $this->setMetadata($headers, true);
260:         
261:         try {
262:             
263:             $cdn = new CDNContainer($this->getService()->getCDNService());
264:             $cdn->setName($this->name);
265:             
266:             $response = $cdn->createRefreshRequest()->send();
267:             
268:             if ($response->isSuccessful()) {
269:                 $this->cdn = $cdn;
270:                 $this->cdn->setMetadata($response->getHeaders(), true);
271:             }
272:             
273:         } catch (ClientErrorResponseException $e) {}   
274:     }
275: 
276:     /**
277:      * Get either a fresh data object (no $info), or get an existing one by passing in data for population.
278:      *
279:      * @param  mixed $info
280:      * @return DataObject
281:      */
282:     public function dataObject($info = null)
283:     {
284:         return new DataObject($this, $info);
285:     }
286:     
287:     /**
288:      * Retrieve an object from the API. Apart from using the name as an 
289:      * identifier, you can also specify additional headers that will be used 
290:      * fpr a conditional GET request. These are
291:      * 
292:      * * `If-Match'
293:      * * `If-None-Match'
294:      * * `If-Modified-Since'
295:      * * `If-Unmodified-Since'
296:      * * `Range'  For example: 
297:      *      bytes=-5    would mean the last 5 bytes of the object
298:      *      bytes=10-15 would mean 5 bytes after a 10 byte offset
299:      *      bytes=32-   would mean all dat after first 32 bytes
300:      * 
301:      * These are also documented in RFC 2616.
302:      * 
303:      * @param string $name
304:      * @param array $headers
305:      * @return DataObject
306:      */
307:     public function getObject($name, array $headers = array())
308:     {
309:         $response = $this->getClient()
310:             ->get($this->getUrl($name), $headers)
311:             ->send();
312: 
313:         return $this->dataObject()
314:             ->populateFromResponse($response)
315:             ->setName($name);
316:     }
317: 
318:     /**
319:      * Upload a single file to the API.
320:      *
321:      * @param       $name    Name that the file will be saved as in your container.
322:      * @param       $data    Either a string or stream representation of the file contents to be uploaded.
323:      * @param array $headers Optional headers that will be sent with the request (useful for object metadata).
324:      * @return DataObject
325:      */
326:     public function uploadObject($name, $data, array $headers = array())
327:     {
328:         $entityBody = EntityBody::factory($data);
329: 
330:         $url = clone $this->getUrl();
331:         $url->addPath($name);
332: 
333:         // @todo for new major release: Return response rather than populated DataObject
334: 
335:         $response = $this->getClient()->put($url, $headers, $entityBody)->send();
336: 
337:         return $this->dataObject()
338:             ->populateFromResponse($response)
339:             ->setName($name)
340:             ->setContent($entityBody);
341:     }
342: 
343:     /**
344:      * Upload an array of objects for upload. This method optimizes the upload procedure by batching requests for
345:      * faster execution. This is a very useful procedure when you just have a bunch of unremarkable files to be
346:      * uploaded quickly. Each file must be under 5GB.
347:      *
348:      * @param array $files With the following array structure:
349:      *                      `name' Name that the file will be saved as in your container. Required.
350:      *                      `path' Path to an existing file, OR
351:      *                      `body' Either a string or stream representation of the file contents to be uploaded.
352:      * @param array $headers Optional headers that will be sent with the request (useful for object metadata).
353:      *
354:      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
355:      * @return \Guzzle\Http\Message\Response
356:      */
357:     public function uploadObjects(array $files, array $commonHeaders = array())
358:     {
359:         $requests = $entities = array();
360: 
361:         foreach ($files as $entity) {
362:             
363:             if (empty($entity['name'])) {
364:                 throw new Exceptions\InvalidArgumentError('You must provide a name.');
365:             }
366:             
367:             if (!empty($entity['path']) && file_exists($entity['path'])) {
368:                 $body = fopen($entity['path'], 'r+');
369: 
370:             } elseif (!empty($entity['body'])) {
371:                 $body = $entity['body'];
372:             } else {
373:                 throw new Exceptions\InvalidArgumentError('You must provide either a readable path or a body');
374:             }
375:             
376:             $entityBody = $entities[] = EntityBody::factory($body);
377: 
378:             // @codeCoverageIgnoreStart
379:             if ($entityBody->getContentLength() >= 5 * Size::GB) {
380:                 throw new Exceptions\InvalidArgumentError(
381:                     'For multiple uploads, you cannot upload more than 5GB per '
382:                     . ' file. Use the UploadBuilder for larger files.'
383:                 );
384:             }
385:             // @codeCoverageIgnoreEnd
386: 
387:             // Allow custom headers and common
388:             $headers = (isset($entity['headers'])) ? $entity['headers'] : $commonHeaders;
389: 
390:             $url = clone $this->getUrl();
391:             $url->addPath($entity['name']);
392: 
393:             $requests[] = $this->getClient()->put($url, $headers, $entityBody);
394:         }
395: 
396:         $responses = $this->getClient()->send($requests);
397: 
398:         foreach ($entities as $entity) {
399:             $entity->close();
400:         }
401: 
402:         return $responses;
403:     }
404: 
405:     /**
406:      * When uploading large files (+5GB), you need to upload the file as chunks using multibyte transfer. This method
407:      * sets up the transfer, and in order to execute the transfer, you need to call upload() on the returned object.
408:      *
409:      * @param array Options
410:      * @see \OpenCloud\ObjectStore\Upload\UploadBuilder::setOptions for a list of accepted options.
411:      * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
412:      * @return mixed
413:      */
414:     public function setupObjectTransfer(array $options = array())
415:     {
416:         // Name is required
417:         if (empty($options['name'])) {
418:             throw new Exceptions\InvalidArgumentError('You must provide a name.');
419:         }
420: 
421:         // As is some form of entity body
422:         if (!empty($options['path']) && file_exists($options['path'])) {
423:             $body = fopen($options['path'], 'r+');
424:         } elseif (!empty($options['body'])) {
425:             $body = $options['body'];
426:         } else {
427:             throw new Exceptions\InvalidArgumentError('You must provide either a readable path or a body');
428:         }
429:         
430:         // Build upload
431:         $transfer = TransferBuilder::newInstance()
432:             ->setOption('objectName', $options['name'])
433:             ->setEntityBody(EntityBody::factory($body))
434:             ->setContainer($this);
435:         
436:         // Add extra options
437:         if (!empty($options['metadata'])) {
438:             $transfer->setOption('metadata', $options['metadata']);
439:         }
440:         if (!empty($options['partSize'])) {
441:             $transfer->setOption('partSize', $options['partSize']);
442:         }
443:         if (!empty($options['concurrency'])) {
444:             $transfer->setOption('concurrency', $options['concurrency']);
445:         }
446:         if (!empty($options['progress'])) {
447:             $transfer->setOption('progress', $options['progress']);
448:         }
449: 
450:         return $transfer->build();
451:     }
452: 
453: }
PHP OpenCloud API API documentation generated by ApiGen 2.8.0