Completed
Pull Request — master (#1)
by
unknown
10:48
created

Obj::setMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * For the full copyright and license information, please view the LICENSE
4
 * file that was distributed with this source code.
5
 *
6
 * @author Nikita Vershinin <[email protected]>
7
 * @license MIT
8
 */
9
namespace OpenStackStorage;
10
11
/**
12
 * Storage data representing an object, (metadata and data).
13
 */
14
class Obj
15
{
16
17
    /**
18
     * Container object.
19
     *
20
     * @var \OpenStackStorage\Container
21
     */
22
    protected $container = null;
23
24
    /**
25
     * Object name.
26
     *
27
     * @var string
28
     */
29
    protected $name = null;
30
31
    /**
32
     * The object's MIME-type.
33
     *
34
     * @var string
35
     */
36
    protected $contentType = null;
37
38
    /**
39
     * The object size (in bytes).
40
     *
41
     * @var integer
42
     */
43
    protected $size = null;
44
45
    /**
46
     * Date and time of last file modification.
47
     *
48
     * @var \DateTime
49
     */
50
    protected $lastModified = null;
51
52
    /**
53
     * Object's etag.
54
     *
55
     * @var string
56
     */
57
    protected $etag = null;
58
59
    /**
60
     * Metadata.
61
     *
62
     * @var array
63
     */
64
    protected $metadata = array();
65
66
    /**
67
     * Headers.
68
     *
69
     * @var array
70
     */
71
    protected $headers = array();
72
73
    /**
74
     * Manifest, used when working with big file.
75
     *
76
     * @var string
77
     */
78
    protected $manifest = null;
79
80
    /**
81
     * The class constructor.
82
     *
83
     * Storage objects rarely if ever need to be instantiated directly by
84
     * the user.
85
     *
86
     * Instead, use the \OpenStackStorage\Container object methods:
87
     * <code>
88
     * $container->createObject('test.txt');
89
     * $container->getObject('test.txt');
90
     * $container->getObjects('test.txt');
91
     * </code>
92
     *
93
     * @param  \OpenStackStorage\Container               $container
94
     * @param  string                                    $name
95
     * @param  boolean                                   $forceExists
96
     * @param  array                                     $object
97
     * @throws \OpenStackStorage\Exceptions\NoSuchObject
98
     */
99
    public function __construct(Container $container, $name = null, $forceExists = false, array &$object = array())
100
    {
101
        $this->container = $container;
102
103
        if (!empty($object)) {
104
            $this->name         = $object['name'];
105
            $this->contentType  = $object['content_type'];
106
            $this->size         = $object['bytes'];
107
            $this->lastModified = $object['last_modified'];
108
            $this->etag         = $object['hash'];
109
        } else {
110
            $this->name = $name;
111
            if (!$this->initialize() && $forceExists) {
112
                throw new Exceptions\NoSuchObject();
113
            }
114
        }
115
    }
116
117
    /**
118
     * Set the value of property $name.
119
     *
120
     * @param string $name
121
     */
122
    public function setName($name)
123
    {
124
        $this->name = $name;
125
    }
126
127
    /**
128
     * Return the value of the $name property.
129
     *
130
     * @return string
131
     */
132
    public function getName()
133
    {
134
        return $this->name;
135
    }
136
137
    /**
138
     * Set the value of property $contentType.
139
     *
140
     * @param string $contentType
141
     */
142
    public function setContentType($contentType)
143
    {
144
        $this->contentType = $contentType;
145
    }
146
147
    /**
148
     * Return the value of the $contentType property.
149
     *
150
     * @return string
151
     */
152
    public function getContentType()
153
    {
154
        return $this->contentType;
155
    }
156
157
    /**
158
     * Set the value of property $metadata.
159
     *
160
     * @param array $metadata
161
     */
162
    public function setMetadata(array $metadata = array())
163
    {
164
        $this->metadata = $metadata;
165
    }
166
167
    /**
168
     * Return the value of the $metadata property.
169
     *
170
     * @return array
171
     */
172
    public function getMetadata()
173
    {
174
        return $this->metadata;
175
    }
176
177
    /**
178
     * Set the value of property $headers.
179
     *
180
     * @param array $headers
181
     */
182
    public function setHeaders(array $headers = array())
183
    {
184
        $this->headers = $headers;
185
    }
186
187
    /**
188
     * Return the value of the $headers property.
189
     *
190
     * @return array
191
     */
192
    public function getHeaders()
193
    {
194
        return $this->headers;
195
    }
196
197
    /**
198
     * Set the value of property $manifest.
199
     *
200
     * @param string $manifest
201
     */
202
    public function setManifest($manifest)
203
    {
204
        $this->manifest = $manifest;
205
    }
206
207
    /**
208
     * Return the value of the $manifest property.
209
     *
210
     * @return string
211
     */
212
    public function getManifest()
213
    {
214
        return $this->manifest;
215
    }
216
217
    /**
218
     * Returns the object size in bytes.
219
     *
220
     * @return integer
221
     */
222
    public function getSize()
223
    {
224
        return $this->size;
225
    }
226
227
    /**
228
     * Read the content from the remote storage object.
229
     *
230
     * By default this method will buffer the response in memory and
231
     * return it as a string. However, if a \SplFileObject object is passed
232
     * in $buffer, the response will be written to it instead.
233
     *
234
     * @param  integer        $size    combined with offset, defines the length
235
     *                                 of data to be read
236
     * @param  integer        $offset  combined with size, defines the start
237
     *                                 location to be read
238
     * @param  array          $headers
239
     * @param  \SplFileObject $buffer
240
     * @return null|string
241
     */
242
    public function read($size = -1, $offset = 0, array $headers = array(), \SplFileObject $buffer = null)
243
    {
244
        $this->validateName();
245
246
        if ($size > 0) {
247
            $headers['Range'] = sprintf(
248
                'bytes=%d-%d',
249
                $offset,
250
                ($offset + $size) - 1
251
            );
252
        }
253
254
        $response = $this->container->getConnection()->makeRequest(
255
            Client::GET,
256
            array($this->container->getName(), $this->name),
257
            $headers
258
        );
259
260
        if (null !== $buffer) {
261
            $buffer->fwrite($response['body']);
262
263
            return null;
264
        } else {
265
            return $response['body'];
266
        }
267
    }
268
269
    /**
270
     * Save the contents of the object to filename.
271
     *
272
     * @param string $filename
273
     */
274
    public function saveToFilename($filename)
275
    {
276
        $buffer = new \SplFileObject($filename, 'wb');
277
278
        $this->read(-1, 0, array(), $buffer);
279
    }
280
281
    /**
282
     * Write data to the remote storage system.
283
     * The $source may be one the following:
284
     *  — resource
285
     *  — \SplFileObject instance
286
     *  — scalar (string)
287
     *  — null (can be used to create directories when
288
     *    $contentType = 'application/directory')
289
     *
290
     * @param  mixed                              $data
291
     * @param  string                             $contentType
292
     * @param  integer                            $filesize
293
     * @throws \OpenStackStorage\Exceptions\Error
294
     */
295
    public function write($data, $contentType = null, $filesize = null)
296
    {
297
        $this->validateName();
298
299
        $path = null;
300
301
        if (is_resource($data)) {
302
            $meta = stream_get_meta_data($data);
303
            if (!$contentType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentType of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
304
                $path = $meta['uri'];
305
            }
306
307
            if (!$filesize) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filesize of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
308
                $this->size = filesize($meta['uri']);
309
            }
310
            $data = stream_get_contents($data);
311
        } elseif (is_object($data) && $data instanceof \SplFileObject) {
312
            $this->size = $data->getSize();
313
            $path       = $data->getType();
314
            $tmp        = '';
315
316
            while (!$data->eof()) {
317
                $tmp .= $data->fgets();
318
            }
319
320
            $data = &$tmp;
321
        } else {
322
            $data       = strval($data);
323
            $this->size = strlen($data);
324
        }
325
326
        if ((null === $contentType) && (null !== $path) && is_readable($path)) {
327
            $contentType = finfo_file(
328
                finfo_open(FILEINFO_MIME_TYPE),
329
                $path,
330
                FILEINFO_MIME_TYPE
331
            );
332
        }
333
334
        $this->contentType = (!$contentType
335
            ? 'application/octet-stream'
336
            : $contentType);
337
338
        $connection = $this->container->getConnection();
339
        $headers    = array_merge(
340
            array(
341
                'X-Auth-Token' => $connection->getAuthToken(),
342
            ),
343
            $this->getNewHeaders()
344
        );
345
        unset($headers['ETag']);
346
347
        $response = $connection->makeRequest(
348
            Client::PUT,
349
            array($this->container->getName(), $this->name),
350
            $headers,
351
            $data
0 ignored issues
show
Documentation introduced by
$data is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
352
        );
353
354
        $this->etag = $response['headers']['etag'];
355
    }
356
357
    /**
358
     * Commits the metadata and custom headers to the remote storage system.
359
     *
360
     * Example:
361
     * <code>
362
     * $object = $container->getObject('paradise_lost.pdf);
363
     * $object->setMetadata(array('author' => 'John Milton'));
364
     * $object->setHeaders(array('content-disposition' => 'foo'));
365
     * $object->syncMetadata();
366
     * </code>
367
     *
368
     * @throws \OpenStackStorage\Exceptions\ResponseError
369
     */
370
    public function syncMetadata()
371
    {
372
        $this->validateName();
373
374
        if (!empty($this->metadata) || !empty($this->headers)) {
375
            $headers = $this->getNewHeaders();
376
            $headers['Content-Length'] = '0';
377
378
            $response = $this->container->getConnection()->makeRequest(
379
                Client::POST,
380
                array($this->container->getName(), $this->name),
381
                $headers
382
            );
383
384
            if (202 != $response['status']) {
385
                throw new Exceptions\ResponseError($response);
0 ignored issues
show
Documentation introduced by
$response is of type string|object, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
386
            }
387
        }
388
    }
389
390
    /**
391
     * Commits the manifest to the remote storage system.
392
     *
393
     * Example:
394
     * <code>
395
     * $object = $container->getObject('paradise_lost.pdf);
396
     * $object->setManifest('container/prefix');
397
     * $object->syncManifest();
398
     * </code>
399
     */
400
    public function syncManifest()
401
    {
402
        $this->validateName();
403
404
        if ($this->manifest) {
405
            $headers = $this->getNewHeaders();
406
            $headers['Content-Length'] = '0';
407
408
            $this->container->getConnection()->makeRequest(
409
                Client::PUT,
410
                array($this->container->getName(), $this->name),
411
                $headers
412
            );
413
        }
414
    }
415
416
    /**
417
     * Return the URI for this object, if its container is public.
418
     *
419
     * @return string
420
     */
421
    public function getPublicUri()
422
    {
423
        return sprintf(
424
            '%s/%s',
425
            rtrim($this->container->getPublicUri(), '/'),
426
            urlencode($this->name)
427
        );
428
    }
429
430
    /**
431
     * Return the SSL URI for this object, if its container is public.
432
     *
433
     * @return string
434
     */
435
    public function getPublicSslUri()
436
    {
437
        return sprintf(
438
            '%s/%s',
439
            rtrim($this->container->getPublicSslUri(), '/'),
440
            urlencode($this->name)
441
        );
442
    }
443
444
    /**
445
     * Return the streaming URI for this object, if its container is public.
446
     *
447
     * @return string
448
     */
449
    public function getPublicStreamingUri()
450
    {
451
        return sprintf(
452
            '%s/%s',
453
            rtrim($this->container->getPublicStreamingUri(), '/'),
454
            urlencode($this->name)
455
        );
456
    }
457
458
    /**
459
     * Purge Edge cache for this object.
460
     *
461
     * You will be notified by email if one is provided when the
462
     * job completes.
463
     *
464
     * Example:
465
     * <code>
466
     * $object1->purgeFromCdn();
467
     * $object2->purgeFromCdn('[email protected]');
468
     * $object3->purgeFromCdn('[email protected],[email protected]);
469
     * </code>
470
     *
471
     * @param  string                                     $email
472
     * @throws \OpenStackStorage\Exceptions\CDNNotEnabled
473
     */
474
    public function purgeFromCdn($email = null)
475
    {
476
        if (!$this->container->getConnection()->getCdnEnabled()) {
477
            throw new Exceptions\CDNNotEnabled();
478
        }
479
480
        $headers = array();
481
        if (null !== $email) {
482
            $headers['X-Purge-Email'] = $email;
483
        }
484
485
        $this->container->getConnection()->makeCdnRequest(
486
            Client::DELETE,
487
            array($this->container->getName(), $this->name),
488
            $headers
489
        );
490
    }
491
492
    /**
493
     * Initialize the object with values from the remote service (if any).
494
     *
495
     * @return boolean
496
     * @throws \Exception|\OpenStackStorage\Exceptions\ResponseError
497
     */
498
    protected function initialize()
499
    {
500
        if (!$this->name) {
501
            return false;
502
        }
503
504
        try {
505
            $response = $this->container->getConnection()->makeRequest(
506
                Client::HEAD,
507
                array($this->container->getName(), $this->name)
508
            );
509
        } catch (Exceptions\ResponseError $e) {
510
            if (404 == $e->getCode()) {
511
                return false;
512
            }
513
514
            throw $e;
515
        }
516
517
        $this->manifest     = $response['headers']['x-object-manifest'];
518
        $this->contentType  = $response['headers']['content-type'];
519
        $this->etag         = $response['headers']['etag'];
520
        $this->size         = intval($response['headers']['content-length']);
521
        $this->lastModified = new \DateTime($response['headers']['last-modified']);
522
523
        foreach ($response['headers'] as $name => $values) {
524
            if (0 === strpos($name, 'x-object-meta-')) {
525
                $this->metadata[substr($name, 14)] = is_array($values) ? $values[0] : $values;
526
            }
527
        }
528
529
        return true;
530
    }
531
532
    /**
533
     * Validates the object name.
534
     *
535
     * @param  string                                         $name
536
     * @throws \OpenStackStorage\Exceptions\InvalidObjectName
537
     */
538
    protected function validateName($name = null)
539
    {
540
        if (null == $name) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $name of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
541
            $name = $this->name;
542
        }
543
544
        if (strlen($name) > OBJECT_NAME_LIMIT) {
545
            throw new Exceptions\InvalidObjectName();
546
        }
547
    }
548
549
    /**
550
     * Returns array representing http headers based on the
551
     * respective instance attributes.
552
     *
553
     * @return array
554
     * @throws \OpenStackStorage\Exceptions\InvalidMetaValue
555
     * @throws \OpenStackStorage\Exceptions\InvalidMetaName
556
     */
557
    protected function getNewHeaders()
558
    {
559
        $headers = array(
560
            'Content-Length' => (null !== $this->size) ? strval($this->size) : '0',
561
        );
562
563
        if (null !== $this->manifest) {
564
            $headers['X-Object-Manifest'] = $this->manifest;
565
        }
566
567
        if (null !== $this->etag) {
568
            $headers['ETag'] = $this->etag;
569
        }
570
571
        if (null !== $this->contentType) {
572
            $headers['Content-Type'] = $this->contentType;
573
        } else {
574
            $headers['Content-Type'] = 'application/octet-stream';
575
        }
576
577
        foreach ($this->metadata as $key => $value) {
578
            $key = str_ireplace('X-Container-Meta-', '', $key);
579
580
            if (strlen($key) > META_NAME_LIMIT) {
581
                throw new Exceptions\InvalidMetaName();
582
            }
583
584
            if (strlen($value) > META_VALUE_LIMIT) {
585
                throw new Exceptions\InvalidMetaValue();
586
            }
587
588
            $headers['X-Object-Meta-' . $key] = $value;
589
        }
590
591
        return array_merge($this->headers, $headers);
592
    }
593
}
594