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 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 135 136 137 138 139 140
141 protected function propertyExists($property, $allowRetry = true)
142 {
143 if (!property_exists($this, $property) && !$this->checkAttributePrefix($property)) {
144
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 157 158 159 160 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 175 176 177 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 190 191 192 193
194 private function isAccessible($property)
195 {
196 return array_key_exists($property, get_object_vars($this));
197 }
198
199 200 201 202 203 204 205 206 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 220 221 222 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 241 242 243 244 245
246 public function setLogger(LoggerInterface $logger = null)
247 {
248 $this->logger = $logger;
249
250 return $this;
251 }
252
253 254 255 256 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 269
270 public function hasLogger()
271 {
272 return (null !== $this->logger);
273 }
274
275 276 277
278 public function url($path = null, array $query = array())
279 {
280 return $this->getUrl($path, $query);
281 }
282
283 284 285 286 287 288 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
299 if (null === ($metadata = $this->getProperty($key))) {
300
301 $metadata = new Metadata;
302 }
303
304
305 $metadata->setArray($value);
306
307
308 $this->setProperty($key, $metadata);
309 } elseif (!empty($this->associatedResources[$key]) && $setObjects === true) {
310
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
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
330
331 $this->setProperty($this->aliases[$key], $value);
332 } else {
333
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 347 348 349 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
385 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
386
387
388 mt_rand(0, 0xffff),
389
390
391
392 mt_rand(0, 0x0fff) | 0x4000,
393
394
395
396
397 mt_rand(0, 0x3fff) | 0x8000,
398
399
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