      1: <?php
      2: /**
      3:  * Copyright 2012-2014 Rackspace US, Inc.
      4:  *
      5:  * Licensed under the Apache License, Version 2.0 (the "License");
      6:  * you may not use this file except in compliance with the License.
      7:  * You may obtain a copy of the License at
      8:  *
      9:  * http://www.apache.org/licenses/LICENSE-2.0
     10:  *
     11:  * Unless required by applicable law or agreed to in writing, software
     12:  * distributed under the License is distributed on an "AS IS" BASIS,
     13:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14:  * See the License for the specific language governing permissions and
     15:  * limitations under the License.
     16:  */
     17:
     18: namespace OpenCloud\ObjectStore\Resource;
     19:
     20: use Guzzle\Http\Message\Response;
     21: use OpenCloud\Common\Base;
     22: use OpenCloud\Common\Service\ServiceInterface;
     23:
     24: /**
     25:  * Abstract base class which implements shared functionality of ObjectStore
     26:  * resources. Provides support, for example, for metadata-handling and other
     27:  * features that are common to the ObjectStore components.
     28:  */
     29: abstract class AbstractResource extends Base
     30: {
     31:     const GLOBAL_METADATA_PREFIX = 'X';
     32:
     33:     /** @var \OpenCloud\Common\Metadata */
     34:     protected $metadata;
     35:
     36:     /** @var string The FQCN of the metadata object used for the container. */
     37:     protected $metadataClass = 'OpenCloud\\Common\\Metadata';
     38:
     39:     /** @var \OpenCloud\Common\Service\ServiceInterface The service object. */
     40:     protected $service;
     41:
     42:     public function __construct(ServiceInterface $service)
     43:     {
     44:         $this->service = $service;
     45:         $this->metadata = new $this->metadataClass;
     46:     }
     47:
     48:     public function getService()
     49:     {
     50:         return $this->service;
     51:     }
     52:
     53:     public function getCdnService()
     54:     {
     55:         return $this->service->getCDNService();
     56:     }
     57:
     58:     public function getClient()
     59:     {
     60:         return $this->service->getClient();
     61:     }
     62:
     63:     /**
     64:      * Factory method that allows for easy instantiation from a Response object.
     65:      *
     66:      * @param Response         $response
     67:      * @param ServiceInterface $service
     68:      * @return static
     69:      */
     70:     public static function fromResponse(Response $response, ServiceInterface $service)
     71:     {
     72:         $object = new static($service);
     73:
     74:         if (null !== ($headers = $response->getHeaders())) {
     75:             $object->setMetadata($headers, true);
     76:         }
     77:
     78:         return $object;
     79:     }
     80:
     81:     /**
     82:      * Trim headers of their resource-specific prefixes.
     83:      *
     84:      * @param  $headers
     85:      * @return array
     86:      */
     87:     public static function trimHeaders($headers)
     88:     {
     89:         $output = array();
     90:
     91:         foreach ($headers as $header => $value) {
     92:             // Only allow allow X-<keyword>-* headers to pass through after stripping them
     93:             if (static::headerIsValidMetadata($header) && ($key = self::stripPrefix($header))) {
     94:                 $output[$key] = (string) $value;
     95:             }
     96:         }
     97:
     98:         return $output;
     99:     }
    100:
    101:     protected static function headerIsValidMetadata($header)
    102:     {
    103:         $pattern = sprintf('#^%s\-#i', self::GLOBAL_METADATA_PREFIX);
    104:
    105:         return preg_match($pattern, $header);
    106:     }
    107:
    108:     /**
    109:      * Strip an individual header name of its resource-specific prefix.
    110:      *
    111:      * @param $header
    112:      * @return mixed
    113:      */
    114:     protected static function stripPrefix($header)
    115:     {
    116:         $pattern = '#^' . self::GLOBAL_METADATA_PREFIX . '\-(' . static::METADATA_LABEL . '-)?(Meta-)?#i';
    117:
    118:         return preg_replace($pattern, '', $header);
    119:     }
    120:
    121:     /**
    122:      * Prepend/stock the header names with a resource-specific prefix.
    123:      *
    124:      * @param array $headers
    125:      * @return array
    126:      */
    127:     public static function stockHeaders(array $headers)
    128:     {
    129:         $output = array();
    130:         foreach ($headers as $header => $value) {
    131:             $prefix = self::GLOBAL_METADATA_PREFIX . '-' . static::METADATA_LABEL . '-Meta-';
    132:             $output[$prefix . $header] = $value;
    133:         }
    134:
    135:         return $output;
    136:     }
    137:
    138:     /**
    139:      * Set the metadata (local-only) for this object.
    140:      *
    141:      * @param      $data
    142:      * @param bool $constructFromResponse
    143:      * @return $this
    144:      */
    145:     public function setMetadata($data, $constructFromResponse = false)
    146:     {
    147:         if ($constructFromResponse) {
    148:             $metadata = new $this->metadataClass;
    149:             $metadata->setArray(self::trimHeaders($data));
    150:             $data = $metadata;
    151:         }
    152:
    153:         $this->metadata = $data;
    154:
    155:         return $this;
    156:     }
    157:
    158:     /**
    159:      * @return \OpenCloud\Common\Metadata
    160:      */
    161:     public function getMetadata()
    162:     {
    163:         return $this->metadata;
    164:     }
    165:
    166:     /**
    167:      * Push local metadata to the API, thereby executing a permanent save.
    168:      *
    169:      * @param array $metadata    The array of values you want to set as metadata
    170:      * @param bool  $stockPrefix Whether to prepend each array key with the metadata-specific prefix. For objects, this
    171:      *                           would be X-Object-Meta-Foo => Bar
    172:      * @return mixed
    173:      */
    174:     public function saveMetadata(array $metadata, $stockPrefix = true)
    175:     {
    176:         $headers = ($stockPrefix === true) ? self::stockHeaders($metadata) : $metadata;
    177:
    178:         return $this->getClient()->post($this->getUrl(), $headers)->send();
    179:     }
    180:
    181:     /**
    182:      * Retrieve metadata from the API. This method will then set and return this value.
    183:      *
    184:      * @return \OpenCloud\Common\Metadata
    185:      */
    186:     public function retrieveMetadata()
    187:     {
    188:         $response = $this->getClient()
    189:             ->head($this->getUrl())
    190:             ->send();
    191:
    192:         $this->setMetadata($response->getHeaders(), true);
    193:
    194:         return $this->metadata;
    195:     }
    196:
    197:     /**
    198:      * To delete or unset a particular metadata item.
    199:      *
    200:      * @param $key
    201:      * @return mixed
    202:      */
    203:     public function unsetMetadataItem($key)
    204:     {
    205:         $header = sprintf('%s-Remove-%s-Meta-%s', self::GLOBAL_METADATA_PREFIX,
    206:             static::METADATA_LABEL, $key);
    207:
    208:         $headers = array($header => 'True');
    209:
    210:         return $this->getClient()
    211:             ->post($this->getUrl(), $headers)
    212:             ->send();
    213:     }
    214:
    215:     /**
    216:      * Append a particular array of values to the existing metadata. Analogous to a merge.
    217:      *
    218:      * @param array $values
    219:      * @return array
    220:      */
    221:     public function appendToMetadata(array $values)
    222:     {
    223:         return (!empty($this->metadata) && is_array($this->metadata))
    224:             ? array_merge($this->metadata, $values)
    225:             : $values;
    226:     }
    227: }
    228: