Overview
  • Namespace
  • Class

Namespaces

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

Classes

  • OpenCloud\Volume\Resource\Snapshot
  • OpenCloud\Volume\Resource\Volume
  • OpenCloud\Volume\Resource\VolumeType
  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\Common;
 19 
 20 use OpenCloud\Common\Collection\ResourceIterator;
 21 use OpenCloud\Common\Constants\Header as HeaderConst;
 22 use OpenCloud\Common\Constants\Mime as MimeConst;
 23 use OpenCloud\Common\Exceptions\JsonError;
 24 use Psr\Log\LoggerInterface;
 25 
 26 /**
 27  * The root class for all other objects used or defined by this SDK.
 28  *
 29  * It contains common code for error handling as well as service functions that
 30  * are useful. Because it is an abstract class, it cannot be called directly,
 31  * and it has no publicly-visible properties.
 32  */
 33 abstract class Base
 34 {
 35     /**
 36      * Holds all the properties added by overloading.
 37      *
 38      * @var array
 39      */
 40     private $properties = array();
 41 
 42     /**
 43      * The logger instance
 44      *
 45      * @var LoggerInterface
 46      */
 47     private $logger;
 48 
 49     /**
 50      * The aliases configure for the properties of the instance.
 51      *
 52      * @var array
 53      */
 54     protected $aliases = array();
 55 
 56     /**
 57      * @return static
 58      */
 59     public static function getInstance()
 60     {
 61         return new static();
 62     }
 63 
 64     /**
 65      * Intercept non-existent method calls for dynamic getter/setter functionality.
 66      *
 67      * @param $method
 68      * @param $args
 69      * @throws Exceptions\RuntimeException
 70      */
 71     public function __call($method, $args)
 72     {
 73         $prefix = substr($method, 0, 3);
 74 
 75         // Get property - convert from camel case to underscore
 76         $property = lcfirst(substr($method, 3));
 77 
 78         // Only do these methods on properties which exist
 79         if ($this->propertyExists($property) && $prefix == 'get') {
 80             return $this->getProperty($property);
 81         }
 82 
 83         // Do setter
 84         if ($this->propertyExists($property) && $prefix == 'set') {
 85             return $this->setProperty($property, $args[0]);
 86         }
 87 
 88         throw new Exceptions\RuntimeException(sprintf(
 89             'No method %s::%s()',
 90             get_class($this),
 91             $method
 92         ));
 93     }
 94 
 95     /**
 96      * We can set a property under three conditions:
 97      *
 98      * 1. If it has a concrete setter: setProperty()
 99      * 2. If the property exists
100      * 3. If the property name's prefix is in an approved list
101      *
102      * @param  mixed $property
103      * @param  mixed $value
104      * @return mixed
105      */
106     protected function setProperty($property, $value)
107     {
108         $setter = 'set' . $this->toCamel($property);
109 
110         if (method_exists($this, $setter)) {
111             return call_user_func(array($this, $setter), $value);
112         } elseif (false !== ($propertyVal = $this->propertyExists($property))) {
113             // Are we setting a public or private property?
114             if ($this->isAccessible($propertyVal)) {
115                 $this->$propertyVal = $value;
116             } else {
117                 $this->properties[$propertyVal] = $value;
118             }
119 
120             return $this;
121         } else {
122             $this->getLogger()->warning(
123                 'Attempted to set {property} with value {value}, but the'
124                 . ' property has not been defined. Please define first.',
125                 array(
126                     'property' => $property,
127                     'value'    => print_r($value, true)
128                 )
129             );
130         }
131     }
132 
133     /**
134      * Basic check to see whether property exists.
135      *
136      * @param string $property   The property name being investigated.
137      * @param bool   $allowRetry If set to TRUE, the check will try to format the name in underscores because
138      *                           there are sometimes discrepancies between camelCaseNames and underscore_names.
139      * @return bool
140      */
141     protected function propertyExists($property, $allowRetry = true)
142     {
143         if (!property_exists($this, $property) && !$this->checkAttributePrefix($property)) {
144             // Convert to under_score and retry
145             if ($allowRetry) {
146                 return $this->propertyExists($this->toUnderscores($property), false);
147             } else {
148                 $property = false;
149             }
150         }
151 
152         return $property;
153     }
154 
155     /**
156      * Convert a string to camelCase format.
157      *
158      * @param       $string
159      * @param  bool $capitalise Optional flag which allows for word capitalization.
160      * @return mixed
161      */
162     public function toCamel($string, $capitalise = true)
163     {
164         if ($capitalise) {
165             $string = ucfirst($string);
166         }
167 
168         return preg_replace_callback('/_([a-z])/', function ($char) {
169             return strtoupper($char[1]);
170         }, $string);
171     }
172 
173     /**
174      * Convert string to underscore format.
175      *
176      * @param $string
177      * @return mixed
178      */
179     public function toUnderscores($string)
180     {
181         $string = lcfirst($string);
182 
183         return preg_replace_callback('/([A-Z])/', function ($char) {
184             return "_" . strtolower($char[1]);
185         }, $string);
186     }
187 
188     /**
189      * Does the property exist in the object variable list (i.e. does it have public or protected visibility?)
190      *
191      * @param $property
192      * @return bool
193      */
194     private function isAccessible($property)
195     {
196         return array_key_exists($property, get_object_vars($this));
197     }
198 
199     /**
200      * Checks the attribute $property and only permits it if the prefix is
201      * in the specified $prefixes array
202      *
203      * This is to support extension namespaces in some services.
204      *
205      * @param string $property the name of the attribute
206      * @return boolean
207      */
208     private function checkAttributePrefix($property)
209     {
210         if (!method_exists($this, 'getService')) {
211             return false;
212         }
213         $prefix = strstr($property, ':', true);
214 
215         return in_array($prefix, $this->getService()->namespaces());
216     }
217 
218     /**
219      * Grab value out of the data array.
220      *
221      * @param string $property
222      * @return mixed
223      */
224     protected function getProperty($property)
225     {
226         if (array_key_exists($property, $this->properties)) {
227             return $this->properties[$property];
228         } elseif (array_key_exists($this->toUnderscores($property), $this->properties)) {
229             return $this->properties[$this->toUnderscores($property)];
230         } elseif (method_exists($this, 'get' . ucfirst($property))) {
231             return call_user_func(array($this, 'get' . ucfirst($property)));
232         } elseif (false !== ($propertyVal = $this->propertyExists($property)) && $this->isAccessible($propertyVal)) {
233             return $this->$propertyVal;
234         }
235 
236         return null;
237     }
238 
239     /**
240      * Sets the logger.
241      *
242      * @param LoggerInterface $logger
243      *
244      * @return $this
245      */
246     public function setLogger(LoggerInterface $logger = null)
247     {
248         $this->logger = $logger;
249 
250         return $this;
251     }
252 
253     /**
254      * Returns the Logger object.
255      *
256      * @return LoggerInterface
257      */
258     public function getLogger()
259     {
260         if (null === $this->logger) {
261             $this->setLogger(new Log\Logger);
262         }
263 
264         return $this->logger;
265     }
266 
267     /**
268      * @return bool
269      */
270     public function hasLogger()
271     {
272         return (null !== $this->logger);
273     }
274 
275     /**
276      * @deprecated
277      */
278     public function url($path = null, array $query = array())
279     {
280         return $this->getUrl($path, $query);
281     }
282 
283     /**
284      * Populates the current object based on an unknown data type.
285      *
286      * @param  mixed $info
287      * @param        bool
288      * @throws Exceptions\InvalidArgumentError
289      */
290     public function populate($info, $setObjects = true)
291     {
292         if (is_string($info) || is_integer($info)) {
293             $this->setProperty($this->primaryKeyField(), $info);
294             $this->refresh($info);
295         } elseif (is_object($info) || is_array($info)) {
296             foreach ($info as $key => $value) {
297                 if ($key == 'metadata' || $key == 'meta') {
298                     // Try retrieving existing value
299                     if (null === ($metadata = $this->getProperty($key))) {
300                         // If none exists, create new object
301                         $metadata = new Metadata;
302                     }
303 
304                     // Set values for metadata
305                     $metadata->setArray($value);
306 
307                     // Set object property
308                     $this->setProperty($key, $metadata);
309                 } elseif (!empty($this->associatedResources[$key]) && $setObjects === true) {
310                     // Associated resource
311                     try {
312                         $resource = $this->getService()->resource($this->associatedResources[$key], $value);
313                         $resource->setParent($this);
314 
315                         $this->setProperty($key, $resource);
316                     } catch (Exception\ServiceException $e) {
317                     }
318                 } elseif (!empty($this->associatedCollections[$key]) && $setObjects === true) {
319                     // Associated collection
320                     try {
321                         $className = $this->associatedCollections[$key];
322                         $options = $this->makeResourceIteratorOptions($className);
323                         $iterator = ResourceIterator::factory($this, $options, $value);
324 
325                         $this->setProperty($key, $iterator);
326                     } catch (Exception\ServiceException $e) {
327                     }
328                 } elseif (!empty($this->aliases[$key])) {
329                     // Sometimes we might want to preserve camelCase
330                     // or covert `rax-bandwidth:bandwidth` to `raxBandwidth`
331                     $this->setProperty($this->aliases[$key], $value);
332                 } else {
333                     // Normal key/value pair
334                     $this->setProperty($key, $value);
335                 }
336             }
337         } elseif (null !== $info) {
338             throw new Exceptions\InvalidArgumentError(sprintf(
339                 Lang::translate('Argument for [%s] must be string or object'),
340                 get_class()
341             ));
342         }
343     }
344 
345     /**
346      * Checks the most recent JSON operation for errors.
347      *
348      * @throws Exceptions\JsonError
349      * @codeCoverageIgnore
350      */
351     public static function checkJsonError()
352     {
353         switch (json_last_error()) {
354             case JSON_ERROR_NONE:
355                 return;
356             case JSON_ERROR_DEPTH:
357                 $jsonError = 'JSON error: The maximum stack depth has been exceeded';
358                 break;
359             case JSON_ERROR_STATE_MISMATCH:
360                 $jsonError = 'JSON error: Invalid or malformed JSON';
361                 break;
362             case JSON_ERROR_CTRL_CHAR:
363                 $jsonError = 'JSON error: Control character error, possibly incorrectly encoded';
364                 break;
365             case JSON_ERROR_SYNTAX:
366                 $jsonError = 'JSON error: Syntax error';
367                 break;
368             case JSON_ERROR_UTF8:
369                 $jsonError = 'JSON error: Malformed UTF-8 characters, possibly incorrectly encoded';
370                 break;
371             default:
372                 $jsonError = 'Unexpected JSON error';
373                 break;
374         }
375 
376         if (isset($jsonError)) {
377             throw new JsonError(Lang::translate($jsonError));
378         }
379     }
380 
381     public static function generateUuid()
382     {
383         return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
384             // 32 bits for "time_low"
385             mt_rand(0, 0xffff), mt_rand(0, 0xffff),
386 
387             // 16 bits for "time_mid"
388             mt_rand(0, 0xffff),
389 
390             // 16 bits for "time_hi_and_version",
391             // four most significant bits holds version number 4
392             mt_rand(0, 0x0fff) | 0x4000,
393 
394             // 16 bits, 8 bits for "clk_seq_hi_res",
395             // 8 bits for "clk_seq_low",
396             // two most significant bits holds zero and one for variant DCE1.1
397             mt_rand(0, 0x3fff) | 0x8000,
398 
399             // 48 bits for "node"
400             mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
401         );
402     }
403 
404     public function makeResourceIteratorOptions($resource)
405     {
406         $options = array('resourceClass' => $this->stripNamespace($resource));
407 
408         if (method_exists($resource, 'jsonCollectionName')) {
409             $options['key.collection'] = $resource::jsonCollectionName();
410         }
411 
412         if (method_exists($resource, 'jsonCollectionElement')) {
413             $options['key.collectionElement'] = $resource::jsonCollectionElement();
414         }
415 
416         return $options;
417     }
418 
419     public function stripNamespace($namespace)
420     {
421         $array = explode('\\', $namespace);
422 
423         return end($array);
424     }
425 
426     protected static function getJsonHeader()
427     {
428         return array(HeaderConst::CONTENT_TYPE => MimeConst::JSON);
429     }
430 }
431 
API documentation generated by ApiGen