1: <?php
2: 3: 4: 5: 6: 7: 8: 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: 20: 21: 22: 23: 24:
25: abstract class Base
26: {
27: 28: 29:
30: private $properties = array();
31:
32: 33: 34: 35: 36: 37:
38: private $logger;
39:
40: 41: 42: 43: 44: 45: 46:
47: public function __call($method, $args)
48: {
49: $prefix = substr($method, 0, 3);
50:
51:
52: $property = lcfirst(substr($method, 3));
53:
54:
55: if ($this->propertyExists($property) && $prefix == 'get') {
56: return $this->getProperty($property);
57: }
58:
59:
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: 73: 74: 75: 76: 77: 78: 79: 80: 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:
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: 116: 117: 118: 119: 120: 121:
122: protected function propertyExists($property, $allowRetry = true)
123: {
124: if (!property_exists($this, $property) && !$this->checkAttributePrefix($property)) {
125:
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: 138: 139: 140: 141: 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: 155: 156: 157: 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: 169: 170: 171: 172:
173: private function isAccessible($property)
174: {
175: return array_key_exists($property, get_object_vars($this));
176: }
177:
178: 179: 180: 181: 182: 183: 184: 185: 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: 198: 199: 200: 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: 219: 220: 221: 222:
223: public function setLogger(Log\LoggerInterface $logger)
224: {
225: $this->logger = $logger;
226: return $this;
227: }
228:
229: 230: 231: 232: 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: 244: 245: 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: 256:
257: public function url($path = null, array $query = array())
258: {
259: return $this->getUrl($path, $query);
260: }
261:
262: 263: 264: 265: 266: 267: 268:
269: 270: 271: 272: 273: 274: 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:
290: if (null === ($metadata = $this->getProperty($key))) {
291:
292: $metadata = new Metadata;
293: }
294:
295:
296: $metadata->setArray($value);
297:
298:
299: $this->setProperty($key, $metadata);
300:
301: } elseif (!empty($this->associatedResources[$key]) && $setObjects === true) {
302:
303:
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:
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:
329:
330: $this->setProperty($this->aliases[$key], $value);
331:
332: } else {
333:
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: 348: 349: 350: 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:
386: mt_rand(0, 0xffff), mt_rand(0, 0xffff),
387:
388:
389: mt_rand(0, 0xffff),
390:
391:
392:
393: mt_rand(0, 0x0fff) | 0x4000,
394:
395:
396:
397:
398: mt_rand(0, 0x3fff) | 0x8000,
399:
400:
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: