1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 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: 28: 29: 30: 31: 32:
33: abstract class Base
34: {
35: 36: 37: 38: 39:
40: private $properties = array();
41:
42: 43: 44: 45: 46:
47: private $logger;
48:
49: 50: 51: 52: 53:
54: protected $aliases = array();
55:
56: 57: 58:
59: public static function getInstance()
60: {
61: return new static();
62: }
63:
64: 65: 66: 67: 68: 69: 70:
71: public function __call($method, $args)
72: {
73: $prefix = substr($method, 0, 3);
74:
75:
76: $property = lcfirst(substr($method, 3));
77:
78:
79: if ($this->propertyExists($property) && $prefix == 'get') {
80: return $this->getProperty($property);
81: }
82:
83:
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: 97: 98: 99: 100: 101: 102: 103: 104: 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:
114:
115: if ($this->isAccessible($propertyVal)) {
116: $this->$propertyVal = $value;
117: } else {
118: $this->properties[$propertyVal] = $value;
119: }
120:
121: return $this;
122: } else {
123:
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: 137: 138: 139: 140: 141: 142:
143: protected function propertyExists($property, $allowRetry = true)
144: {
145: if (!property_exists($this, $property) && !$this->checkAttributePrefix($property)) {
146:
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: 159: 160: 161: 162: 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: 177: 178: 179: 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: 192: 193: 194: 195:
196: private function isAccessible($property)
197: {
198: return array_key_exists($property, get_object_vars($this));
199: }
200:
201: 202: 203: 204: 205: 206: 207: 208: 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: 222: 223: 224: 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: 243: 244: 245: 246:
247: public function setLogger(Log\LoggerInterface $logger)
248: {
249: $this->logger = $logger;
250:
251: return $this;
252: }
253:
254: 255: 256: 257: 258:
259: public function getLogger()
260: {
261: if (null === $this->logger) {
262: $this->setLogger(new Log\Logger);
263: }
264:
265: return $this->logger;
266: }
267:
268: 269: 270:
271: public function url($path = null, array $query = array())
272: {
273: return $this->getUrl($path, $query);
274: }
275:
276: 277: 278: 279: 280: 281: 282:
283: public function populate($info, $setObjects = true)
284: {
285: if (is_string($info) || is_integer($info)) {
286:
287: $this->setProperty($this->primaryKeyField(), $info);
288: $this->refresh($info);
289: } elseif (is_object($info) || is_array($info)) {
290:
291: foreach ($info as $key => $value) {
292:
293: if ($key == 'metadata' || $key == 'meta') {
294:
295:
296: if (null === ($metadata = $this->getProperty($key))) {
297:
298: $metadata = new Metadata;
299: }
300:
301:
302: $metadata->setArray($value);
303:
304:
305: $this->setProperty($key, $metadata);
306: } elseif (!empty($this->associatedResources[$key]) && $setObjects === true) {
307:
308:
309: try {
310:
311: $resource = $this->getService()->resource($this->associatedResources[$key], $value);
312: $resource->setParent($this);
313:
314: $this->setProperty($key, $resource);
315: } catch (Exception\ServiceException $e) {
316: }
317: } elseif (!empty($this->associatedCollections[$key]) && $setObjects === true) {
318:
319:
320: try {
321:
322: $className = $this->associatedCollections[$key];
323: $options = $this->makeResourceIteratorOptions($className);
324: $iterator = ResourceIterator::factory($this, $options, $value);
325:
326: $this->setProperty($key, $iterator);
327: } catch (Exception\ServiceException $e) {
328: }
329: } elseif (!empty($this->aliases[$key])) {
330:
331:
332:
333: $this->setProperty($this->aliases[$key], $value);
334: } else {
335:
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: 349: 350: 351: 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:
387: mt_rand(0, 0xffff), mt_rand(0, 0xffff),
388:
389:
390: mt_rand(0, 0xffff),
391:
392:
393:
394: mt_rand(0, 0x0fff) | 0x4000,
395:
396:
397:
398:
399: mt_rand(0, 0x3fff) | 0x8000,
400:
401:
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 ()
429: {
430: return array(HeaderConst::CONTENT_TYPE => MimeConst::JSON);
431: }
432: }
433: