Overview

Namespaces

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

Classes

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