Created
September 15, 2015 17:59
-
-
Save bbrothers/f4b07f349feac36fe7bb to your computer and use it in GitHub Desktop.
Array/Object Unifier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php namespace Acme\Util; | |
use ArrayAccess; | |
use Countable; | |
trait DecoratorTrait | |
{ | |
/** | |
* The object or array being decorated | |
* | |
* @var object|array | |
*/ | |
protected $subject; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function toArray() | |
{ | |
if (is_array($this->subject)) { | |
return $this->subject; | |
} | |
if ($this->subject instanceof ArrayableInterface) { | |
return $this->subject->toArray(); | |
} | |
return json_decode(json_encode($this->subject), true); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function count() | |
{ | |
if ($this->subject instanceof Countable) { | |
return $this->subject->count(); | |
} | |
return count($this->subject); | |
} | |
/** | |
* Does the property exist on the decorated subject? | |
* | |
* @param mixed $offset | |
* | |
* @return bool | |
*/ | |
public function offsetExists($offset) | |
{ | |
return $this->__isset($offset); | |
} | |
/** | |
* Get the property from the subject | |
* | |
* @param mixed $offset | |
* | |
* @return mixed | |
*/ | |
public function offsetGet($offset) | |
{ | |
return $this->__get($offset); | |
} | |
/** | |
* Set the property on the decorated subject | |
* This method should be overridden if the subject | |
* is not an array, has publicly accessible properties | |
* and does not have a camel cased setter (i.e. foo_property -> setFooProperty) | |
* | |
* @param mixed $offset | |
* @param mixed $value | |
* | |
* @return mixed | |
*/ | |
public function offsetSet($offset, $value) | |
{ | |
if (is_array($this->subject)) { | |
return $this->subject[$offset] = $value; | |
} | |
$method = 'set' . $this->toCamelCase($offset); | |
if (method_exists($this->subject, $method)) { | |
return $this->subject->{$method}($value); | |
} | |
return $this->subject->{$offset} = $value; | |
} | |
/** | |
* Unset the property on the decorated subject | |
* | |
* @param mixed $offset | |
*/ | |
public function offsetUnset($offset) | |
{ | |
$this->__unset($offset); | |
} | |
/** | |
* Get the requested property from the decorated subject | |
* | |
* @param mixed $property | |
* | |
* @return mixed | |
*/ | |
public function __get($property) | |
{ | |
if (is_array($this->subject) or $this->subject instanceof ArrayAccess) { | |
if ( ! isset($this->subject[$property])) { | |
throw new \LogicException("The index \"{$property}\" is not set."); | |
} | |
return $this->subject[$property]; | |
} | |
$method = 'get' . $this->toCamelCase($property); | |
if (method_exists($this->subject, $method)) { | |
return $this->subject->{$method}(); | |
} | |
if ( ! property_exists($this->subject, $property)) { | |
throw new \LogicException(sprintf( | |
"The property \"%s\" is not set on the class \"%s\".", | |
$property, | |
get_class($this->subject) | |
)); | |
} | |
return $this->subject->{$property}; | |
} | |
/** | |
* Call the requested method on the decorated subject object | |
* | |
* @param string $method | |
* @param array $args | |
* | |
* @return mixed | |
*/ | |
public function __call($method, $args) | |
{ | |
if (method_exists($this->subject, $method)) { | |
return call_user_func_array([$this->subject, $method], $args); | |
} | |
throw new \BadMethodCallException("The method \"{$method}\" is not defined."); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __isset($parameter) | |
{ | |
if (is_array($this->subject) or $this->subject instanceof ArrayAccess) { | |
return isset($this->subject[$parameter]); | |
} | |
$methods = [ | |
'get' . $this->toCamelCase($parameter), | |
'has' . $this->toCamelCase($parameter) | |
]; | |
foreach ($methods as $method) { | |
if (method_exists($this->subject, $method)) { | |
return (bool) $this->subject->{$method}(); | |
} | |
} | |
return isset($this->subject->{$parameter}); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __unset($parameter) | |
{ | |
// If its an array or array accessible, just unset it. | |
if (is_array($this->subject) or $this->subject instanceof ArrayAccess) { | |
unset($this->subject[$parameter]); | |
return; | |
} | |
// If its an object with a camel cased setter, set it to null | |
$method = 'set' . $this->toCamelCase($parameter); | |
if (method_exists($this->subject, $method)) { | |
$this->subject->{$method}(null); | |
return; | |
} | |
// Assume it's an object and unset the property | |
unset($this->subject->{$parameter}); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function jsonSerialize() | |
{ | |
return $this->toArray(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __toString() | |
{ | |
return json_encode($this); | |
} | |
/** | |
* Simple helper to change from snake_case to camelCase | |
* | |
* @param $str | |
* | |
* @return mixed | |
*/ | |
private function toCamelCase($str) | |
{ | |
return preg_replace_callback( | |
'/_([a-z])/', | |
function ($match) { | |
return strtoupper($match[1]); | |
}, | |
$str | |
); | |
} | |
/** | |
* @return array|object | |
*/ | |
public function getSubject() | |
{ | |
return $this->subject; | |
} | |
/** | |
* @param array|object $subject | |
* | |
* @return self | |
*/ | |
public function setSubject($subject) | |
{ | |
$this->subject = $subject; | |
return $this; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment