HPCloud-PHP  1.2.0
PHP bindings for HPCloud and OpenStack services.
 All Classes Namespaces Files Functions Variables Pages
Object.php
Go to the documentation of this file.
1 <?php
2 /* ============================================================================
3 (c) Copyright 2012 Hewlett-Packard Development Company, L.P.
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge,publish, distribute, sublicense, and/or sell copies of
8 the Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 ============================================================================ */
22 /**
23  * @file
24  * Contains the class Object for ObjectStorage.
25  */
26 
28 
29 /**
30  * An object for ObjectStorage.
31  *
32  * The HPCloud ObjectStorage system provides a method for storing
33  * complete chunks of data (objects) in the cloud. This class describes
34  * such a chunk of data.
35  *
36  * An object has the following basic components:
37  *
38  * - Name: A filename (which may be pathlike, subject to OpenStack's
39  * pathing rules).
40  * - Content: The content of the object.
41  * - Content type: The MIME type of the object. Examples:
42  * - text/plain; charset=UTF-8
43  * - image/png
44  * - application/x-my-custom-mime
45  * - Metadata: File attributes that are stored along with the file on
46  * object store.
47  *
48  * Objects are stored and retrieved <i>by name</i>. So it is assumed
49  * that, per container, no more than one file with a given name exists.
50  *
51  * You may create Object instance and then store them in Containers.
52  * Likewise, a Container instance can retrieve Object instances from the
53  * remote object store.
54  */
55 class Object {
56 
57  const DEFAULT_CONTENT_TYPE = 'application/octet-stream';
58 
59  /**
60  * The name of the object.
61  *
62  * This can be path-like, subject to OpenStack's definition
63  * of "path-like".
64  */
65  protected $name;
66 
67  /**
68  * The content.
69  *
70  * Subclasses needn't use this to store an object's content,
71  * as they may prefer filesystem backing.
72  */
73  protected $content;
74  /**
75  * The content type.
76  *
77  * The default type is 'application/octet-stream', which marks this as
78  * a generic byte stream.
79  */
80  protected $contentType = self::DEFAULT_CONTENT_TYPE;
81  /**
82  * Associative array of stored metadata.
83  */
84  protected $metadata = array();
85 
86  protected $contentEncoding;
88 
89  /**
90  * Extension mechanism for new headers.
91  */
92  protected $additionalHeaders = array();
93 
94 
95  /**
96  * Construct a new object for storage.
97  *
98  * @param string $name
99  * A name (may be pathlike) for the object.
100  * @param string $content
101  * Optional content to store in this object. This is the same
102  * as calling setContent().
103  * @param string $type
104  * Optional content type for this content. This is the same as
105  * calling setContentType().
106  */
107  public function __construct($name, $content = NULL, $type = NULL) {
108  $this->name = $name;
109 
110  if (!is_null($content)) {
111  $this->content = $content;
112  }
113  if (!empty($type)) {
114  $this->contentType = $type;
115  }
116  }
117 
118  /**
119  * Set the metadata.
120  *
121  * OpenStack allows you to specify metadata for a file. Metadata items
122  * must follow these conventions:
123  *
124  * - names must contain only letters, numbers, and short dashes. Since
125  * OpenStack normalizes the name to begin with uppercase, it is
126  * suggested that you follow this convetion: Foo, not foo. Or you
127  * can do your own normalizing (such as converting all to lowercase.
128  * OpenStack limits the name length to 126 unicode chars.
129  * - values must be encoded if they contain newlines or binary data.
130  * While the exact encoding is up to you, Base-64 encoding is probably
131  * your best bet. OpenStack limits the value to 256 unicode chars.
132  *
133  * (The docs are ambiguous -- they say chars, but they may mean
134  * bytes.)
135  *
136  * This library does only minimal processing of metadata, and does no
137  * error checking, escaping, etc. This is up to the implementor. The
138  * OpenStack Swift implementation does not dictate what encoding is
139  * used, though it suggests url encoding of both name and values.
140  *
141  * Currently, no length checking is performed in the library, nor is
142  * any encoding of the data performed.
143  *
144  * IMPORTANT: Current versions of OpenStack Swift normalize metadata
145  * names so that the name is always given an initial capital leter.
146  * That is, `foo` becomes `Foo`.
147  *
148  * @param array $array
149  * An associative array of metadata names to values.
150  *
151  * @retval HPCloud::Storage::ObjectStorage::Object
152  * @return \HPCloud\Storage\ObjectStorage\Object
153  * $this so the method can be used in chaining.
154  */
155  public function setMetadata(array $array) {
156  $this->metadata = $array;
157 
158  return $this;
159  }
160 
161  /**
162  * Get any associated metadata.
163  *
164  * This returns an associative array of all metadata for this object.
165  *
166  * @retval array
167  * @return array
168  * An associative array of metadata. This may be empty.
169  */
170  public function metadata() {
171  return $this->metadata;
172  }
173 
174  /**
175  * Override (change) the name of an object.
176  *
177  * Note that this changes only the <i>local copy</i> of an object. It
178  * does not rename the remote copy. In fact, changing the local name
179  * and then saving it will result in a new object being created in the
180  * object store.
181  *
182  * To copy an object, see
183  * HPCloud::Storage::ObjectStorage::Container::copyObject().
184  *
185  * @param string $name
186  * A file or object name.
187  *
188  * @retval HPCloud::Storage::ObjectStorage::Object
189  * @return \HPCloud\Storage\ObjectStorage\Object
190  * $this so the method can be used in chaining.
191  */
192  public function setName($name) {
193  $this->name = $name;
194  return $this;
195  }
196 
197  /**
198  * Get the name.
199  *
200  * Returns the name of an object. If the name has been overwritten
201  * using setName(), this will return the latest (overwritten) name.
202  *
203  * @retval string
204  * @return string
205  * The name of the object.
206  */
207  public function name() {
208  return $this->name;
209  }
210 
211  /**
212  * Set the content type (MIME type) for the object.
213  *
214  * Object storage is, to a certain degree, content-type aware. For
215  * that reason, a content type is mandatory.
216  *
217  * The default MIME type used is `application/octet-stream`, which is
218  * the generic content type for a byte stream. Where possible, you
219  * should set a more accurate content type.
220  *
221  * All HTTP type options are allowed. So, for example, you can add a
222  * charset to a text type:
223  *
224  * @code
225  * <?php
226  * $o = new Object('my.html');
227  * $o->setContentType('text/html; charset=iso-8859-13');
228  * ?>
229  * @endcode
230  *
231  * Content type is not parsed or verified locally (though it is
232  * remotely). It can be dangerous, too, to allow users to specify a
233  * content type.
234  *
235  * @param string $type
236  * A valid content type.
237  *
238  * @retval HPCloud::Storage::ObjectStorage::Object
239  * @return \HPCloud\Storage\ObjectStorage\Object
240  * $this so the method can be used in chaining.
241  */
242  public function setContentType($type) {
243  $this->contentType = $type;
244  return $this;
245  }
246 
247  /**
248  * Get the content type.
249  *
250  * This returns the currently set content type.
251  *
252  * @retval string
253  * @return string
254  * The content type, including any additional options.
255  */
256  public function contentType() {
257  return $this->contentType;
258  }
259 
260  /**
261  * Set the content for this object.
262  *
263  * Place the content into the object. Typically, this is string
264  * content that will be stored remotely.
265  *
266  * PHP's string is backed by a robust system that can accomodate
267  * moderately sized files. However, it is best to keep strings short
268  * (<2MB, for example -- test for your own system's sweet spot).
269  * Larger data may be better handled with file system entries or
270  * database storage.
271  *
272  * Note that the OpenStack will not allow files larger than 5G, and
273  * PHP will likely croak well before that marker. So use discretion.
274  *
275  * @param string $content
276  * The content of the object.
277  * @param string $type
278  * The content type (MIME type). This can be set here for
279  * convenience, or you can call setContentType() directly.
280  *
281  * @retval HPCloud::Storage::ObjectStorage::Object
282  * @return \HPCloud\Storage\ObjectStorage\Object
283  * $this so the method can be used in chaining.
284  */
285  public function setContent($content, $type = NULL) {
286  $this->content = $content;
287  if (!empty($type)) {
288  $this->contentType = $type;
289  }
290  return $this;
291  }
292 
293  /**
294  * Retrieve the content.
295  *
296  * Retrieve the ENTIRE content of an object.
297  *
298  * Note that this may be binary data (depending on what the original
299  * content is). PHP strings are generally binary safe, but use this
300  * with caution if you do not know what kind of data is stored in an
301  * object.
302  *
303  * OpenStack does not do anything to validate that the content type is
304  * accurate. While contentType() is intended to provide useful
305  * information, poorly managed data can be written with the wrong
306  * content type.
307  *
308  * When extending this class, you should make sure that this function
309  * returns the entire contents of an object.
310  *
311  * @retval string
312  * @return string
313  * The content of the file.
314  */
315  public function content() {
316  return $this->content;
317  }
318 
319  /**
320  * Calculate the content length.
321  *
322  * This returns the number of <i>bytes</i> in a piece of content (not
323  * the number of characters). Among other things, it is used to let
324  * the remote object store know how big of an object to expect when
325  * transmitting data.
326  *
327  * When extending this class, you should make sure to calculate the
328  * content length appropriately.
329  *
330  * @retval int
331  * @return int
332  * The length of the content, in bytes.
333  */
334  public function contentLength() {
335  // strlen() is binary safe (or at least it seems to be).
336  return strlen($this->content);
337  }
338 
339  /**
340  * Generate an ETag for the ObjectStorage server.
341  *
342  * OpenStack uses ETag to pass validation data. This generates an ETag
343  * using an MD5 hash of the content.
344  *
345  * When extending this class, generate an ETag by creating an MD5 of
346  * the entire object's content (but not the metadata or name).
347  *
348  * @retval string
349  * @return string
350  * An MD5 value as a string of 32 hex digits (0-9a-f).
351  */
352  public function eTag() {
353  return md5($this->content);
354  }
355 
356  /**
357  * Set the encoding for a file.
358  *
359  * You can use content encoding on compressed content to indicate to
360  * the receiving agent that a file is encoded using a specific
361  * compression type.
362  *
363  * Typical compression types are 'gzip', 'zip', and 'compress', though
364  * many others exist.
365  *
366  * This allows you, for example, to save a zipped file, yet preserve
367  * its underlying content type. For example, for a gzipped text/plain
368  * file, you can set the content type to "text/plain" and the encoding
369  * to "gzip". This allows many user agents to receive the compressed
370  * data and automatically decompress them and display them correctly.
371  *
372  * @param string $encoding
373  * A valid encoding type.
374  *
375  * @retval HPCloud::Storage::ObjectStorage::Object
376  * @return \HPCloud\Storage\ObjectStorage\Object
377  * $this so the method can be used in chaining.
378  */
379  public function setEncoding($encoding) {
380  $this->contentEncoding = $encoding;
381 
382  return $this;
383  }
384 
385  /**
386  * Get the encoding (if any) for this object.
387  *
388  * Encoding is used to indicate how a file was encoded or compressed.
389  * See setEncoding() for more information.
390  *
391  * @retval string
392  * @return string
393  * The encoding type.
394  */
395  public function encoding() {
396  return $this->contentEncoding;
397  }
398 
399  /**
400  * Set the content disposition.
401  *
402  * This makes it possible to have the file act like a download (in a
403  * browser or similar agent), even if the MIME type normally triggers
404  * a display.
405  *
406  * The typical value for this is:
407  * @code
408  * <?php
409  * $object->setDisposition('attachment; filename=foo.png');
410  * ?>
411  * @endcode
412  *
413  * A disposition string should not include any newline characters or
414  * binary data.
415  *
416  * @param string $disposition
417  * A valid disposition declaration. These are defined in various
418  * HTTP specifications.
419  *
420  * @retval HPCloud::Storage::ObjectStorage::Object
421  * @return \HPCloud\Storage\ObjectStorage\Object
422  * $this so the method can be used in chaining.
423  */
424  public function setDisposition($disposition) {
425  $this->contentDisposition = $disposition;
426 
427  return $this;
428  }
429 
430  /**
431  * Get the current disposition string, if any.
432  *
433  * See setDisposition() for discussion.
434  *
435  * @retval string
436  * @return string
437  * The disposition string, or NULL if none is set.
438  */
439  public function disposition() {
440  return $this->contentDisposition;
441  }
442 
443  /**
444  * Set additional headers for storage.
445  *
446  * @attention EXPERT: You will need to understand OpenStack
447  * internals to use this effectively.
448  *
449  * Headers set here will be added to the HTTP request during save
450  * operations. They are not merged into existing headers until
451  * save-time.
452  *
453  * This provides a mechanism for adding extension headers. CORS
454  * headers and possibly others are stored by Swift, but have no
455  * semantic value to Swift or to popular user agents.
456  *
457  * There are a few things to note about this mechanism:
458  *
459  * - Existing headers cannot be overwritten. Only new headers can be
460  * added.
461  * - Headers are not merged. They are simply sent to the remote
462  * server. A new object must be retrieved from the server before
463  * these headers will be accessible.
464  * - Swift only stores certain headers. If you supply an unrecognized
465  * header to Swift, it may simply ignore it.
466  * - The RemoteObject::headers() method provides access to all of the
467  * headers returned from Swift.
468  * - Headers are merged in as they are, with no cleaning, encoding, or
469  * checking. You must ensure that the headers are in the proper
470  * format.
471  *
472  * @param array $headers
473  * An associative array where each name is an HTTP header name, and
474  * each value is the HTTP header value. No encoding or escaping is
475  * done.
476  *
477  * @retval HPCloud::Storage::ObjectStorage::Object
478  * @return \HPCloud\Storage\ObjectStorage\Object
479  * $this so the method can be used in chaining.
480  */
481  public function setAdditionalHeaders($headers) {
482  $this->additionalHeaders = $headers;
483  return $this;
484  }
485 
486  /**
487  * Return additional headers.
488  *
489  * Headers here have likely not been stored remotely until
490  * Container::save() is called on the object.
491  */
492  public function additionalHeaders() {
493  return $this->additionalHeaders;
494  }
495 
496  /**
497  * Remove headers.
498  *
499  * This takes an array of header names, and removes
500  * any matching headers. Typically, only headers set
501  * by setAdditionalHeaders() are removed from an Object.
502  * (RemoteObject works differently).
503  *
504  * @attention
505  * Many headers are generated automatically, such as
506  * Content-Type and Content-Length. Removing these
507  * will simply result in their being regenerated.
508  *
509  * @param array $keys
510  * The header names to be removed.
511  *
512  * @retval HPCloud::Storage::ObjectStorage::Object
513  * @return \HPCloud\Storage\ObjectStorage\Object
514  * $this for the current object so it can be used in chaining methods.
515  */
516  public function removeHeaders($keys) {
517  foreach ($keys as $k) {
518  unset($this->additionalHeaders[$k]);
519  }
520 
521  return $this;
522  }
523 
524  /**
525  * This object should be transmitted in chunks.
526  *
527  * Indicates whether or not this object should be transmitted as
528  * chunked data (in HTTP).
529  *
530  * This should be used when (a) the file size is large, or (b) the
531  * exact size of the file is unknown.
532  *
533  * If this returns TRUE, it does not <i>guarantee</i> that the data
534  * will be transmitted in chunks. But it recommends that the
535  * underlying transport layer use chunked encoding.
536  *
537  * The contentLength() method is not called for chunked transfers. So
538  * if this returns TRUE, contentLength() is ignored.
539  *
540  * @retval boolean
541  * @return boolean
542  * TRUE to recommend chunked transfer, FALSE otherwise.
543  */
544  public function isChunked() {
545  // Currently, this value is hard-coded. The default Object
546  // implementation does not get chunked.
547  return FALSE;
548  }
549 }