Overview

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CDN
      • 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
      • Enum
      • Exception
      • Resource
      • Upload
    • Orchestration
      • Resource
    • Queues
      • Collection
      • Exception
      • Resource
    • Volume
      • Resource
  • PHP

Classes

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