![therselman@gmail.com](/assets/img/avatar_default.png)
3 changed files with 206 additions and 98 deletions
@ -1,160 +1,157 @@ |
|||||||
<?php |
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Collection class with dynamic members and methods |
||||||
|
* Similar functionality to an Array or Dictionary class |
||||||
|
* Typically holding a collection/array of Entity members |
||||||
|
*/ |
||||||
|
|
||||||
namespace Twister; |
namespace Twister; |
||||||
|
|
||||||
class Collection implements \Iterator, \Countable, \ArrayAccess |
class Collection implements \Iterator, \Countable, \ArrayAccess |
||||||
{ |
{ |
||||||
protected $_ = null; |
protected $members = null; |
||||||
|
protected $methods = null; |
||||||
|
|
||||||
public function __construct(array $members = null) |
public function __construct(array $members = null, array $methods = null) |
||||||
{ |
{ |
||||||
$this->_ =& $members; |
$this->members =& $members; |
||||||
|
$this->methods =& $methods; |
||||||
} |
} |
||||||
|
|
||||||
function &__get($key) |
|
||||||
|
/** |
||||||
|
* Get member by id/index |
||||||
|
* |
||||||
|
* @param string|int $idx |
||||||
|
* @return mixed |
||||||
|
*/ |
||||||
|
public function __get($idx) |
||||||
{ |
{ |
||||||
return $this->_[$key] ?? null; |
return $this->members[$idx]; |
||||||
} |
} |
||||||
function __set($key, $value) |
|
||||||
|
/** |
||||||
|
* Set member by id/index |
||||||
|
* |
||||||
|
* @param string|int $idx |
||||||
|
* @param mixed $value |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function __set($idx, $value) |
||||||
{ |
{ |
||||||
return $this->_[$key] = $value; |
$this->members[$idx] = $value; |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
function __isset($key) |
function __isset($idx) |
||||||
{ |
{ |
||||||
return isset($this->_[$key]); |
return isset($this->members[$idx]); |
||||||
} |
} |
||||||
function __unset($key) |
function __unset($idx) |
||||||
{ |
{ |
||||||
unset($this->_[$key]); |
unset($this->members[$idx]); |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
function &all() |
public function &all() |
||||||
{ |
|
||||||
return $this->_; |
|
||||||
} |
|
||||||
function set($key, $value) // alias for __set() |
|
||||||
{ |
{ |
||||||
return $this->_[$key] =& $value; |
return $this->members; |
||||||
} |
} |
||||||
function &get($key, $default = null) // alias for __get() |
public function get($name) // alias for __get() |
||||||
{ |
{ |
||||||
return $this->_[$key] ?? $default; |
return $this->members[$name]; |
||||||
} |
} |
||||||
function has($key) // alias for __isset() |
public function set($name, $value) // alias for __set() |
||||||
{ |
{ |
||||||
return isset($this->_[$key]); |
return $this->members[$name] = $value; |
||||||
} |
} |
||||||
function &merge($key, array $arr) // we need this function because we cannot (re)`set` the arrays to new values without unsetting the old values first! ie. __set() will fail because it already exists! |
public function has($name) // alias for __isset() |
||||||
{ |
{ |
||||||
// TODO: Add is_array() checks to the container, and add variable number of array inputs! |
return isset($this->members[$name]); |
||||||
$this->_[$key] = array_merge($this->_[$key], $arr); |
|
||||||
return $this->_[$key]; |
|
||||||
} |
} |
||||||
function remove($key) // alias for __unset() |
public function remove($name) // alias for __unset() |
||||||
{ |
{ |
||||||
unset($this->_[$key]); |
unset($this->members[$name]); |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Workaround for the `array access functions` eg. array_push($obj->toArray(), $value); |
||||||
|
*/ |
||||||
|
public function &toArray() |
||||||
|
{ |
||||||
|
return $this->members; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
function &__call($method, $args) |
public function __call($method, $args) |
||||||
{ |
|
||||||
if (isset($this->_[$method])) |
|
||||||
if (is_callable($this->_[$method])) |
|
||||||
{ |
{ |
||||||
$result = call_user_func_array($this->_[$method], $args); |
array_unshift($args, $this); |
||||||
return $result; |
return call_user_func_array($this->methods[$method], $args); |
||||||
} |
} |
||||||
else |
public function __invoke() |
||||||
return $this->_[$method]; |
|
||||||
else |
|
||||||
{ |
{ |
||||||
if (preg_match('/^([gs]et|has|isset|unset)([A-Z_])(.*)$/', $method, $match)) |
return $this->methods['__invoke']($this); |
||||||
{ |
|
||||||
$property = strtolower($match[2]). $match[3]; |
|
||||||
switch($match[1]) |
|
||||||
{ |
|
||||||
case 'get': return $this->_[$property] ?? $args[0] ?? null; |
|
||||||
case 'set': return $this->_[$property] = $args[0]; |
|
||||||
case 'has': // fallthrough vvv alias for `isset` |
|
||||||
case 'isset': $result = isset($this->_[$property]); return $result; |
|
||||||
case 'unset': $result = null; unset($this->_[$property]); return $result; |
|
||||||
} |
|
||||||
//throw new \InvalidArgumentException("Property {$property} doesn't exist"); |
|
||||||
} |
|
||||||
throw new \InvalidArgumentException(__CLASS__ . "->{$method}() doesn't exist"); |
|
||||||
} |
|
||||||
} |
} |
||||||
|
public function setMethod($method, callable $callable) |
||||||
// |
|
||||||
// Workaround for the `array access functions` eg. array_push($obj->toArray(), 'Hello World!'); |
|
||||||
// |
|
||||||
public function &toArray() |
|
||||||
{ |
{ |
||||||
return $this->_; |
$this->methods[$method] = $callable; |
||||||
} |
return $this; |
||||||
public function &__invoke() |
|
||||||
{ // TODO: What do you think about this technique? We could just leave it if we don't use it! |
|
||||||
// Basically, we are calling an internal `__invoke` handler |
|
||||||
// eg. $myCollection['__invoke'] = function($c) { return $c->all(); } |
|
||||||
return $this->_['__invoke']($this); |
|
||||||
} |
} |
||||||
|
|
||||||
// |
|
||||||
// Iterator interface |
/** |
||||||
// |
* Iterator interface |
||||||
|
*/ |
||||||
public function rewind() |
public function rewind() |
||||||
{ |
{ |
||||||
return reset($this->_); |
return reset($this->members); |
||||||
} |
} |
||||||
public function current() |
public function current() |
||||||
{ |
{ |
||||||
return current($this->_); |
return current($this->members); |
||||||
} |
} |
||||||
public function key() |
public function key() |
||||||
{ |
{ |
||||||
return key($this->_); |
return key($this->members); |
||||||
} |
} |
||||||
public function next() |
public function next() |
||||||
{ |
{ |
||||||
return next($this->_); |
return next($this->members); |
||||||
} |
} |
||||||
public function valid() |
public function valid() |
||||||
{ |
{ |
||||||
return key($this->_) !== null; |
return key($this->members) !== null; |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
// |
/** |
||||||
// Countable interface |
* Countable interface |
||||||
// |
*/ |
||||||
public function count() |
public function count() |
||||||
{ |
{ |
||||||
return count($this->_); |
return count($this->members); |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
// |
/** |
||||||
// ArrayAccess interface |
* ArrayAccess interface |
||||||
// |
*/ |
||||||
public function offsetSet($id, $value) // eg. $obj['two'] = 'A value'; |
public function offsetGet($idx) // eg. var_dump($obj['two']); |
||||||
{ |
{ |
||||||
$this->_[$id] = $value; |
return $this->members[$idx]; |
||||||
} |
} |
||||||
public function offsetExists($id) // eg. isset($obj['two']) |
public function offsetSet($idx, $value) // eg. $obj['two'] = 'A value'; |
||||||
{ |
{ |
||||||
return isset($this->_[$id]); |
$this->members[$idx] = $value; |
||||||
} |
} |
||||||
public function offsetUnset($id) // eg. unset($obj['two']); |
public function offsetExists($idx) // eg. isset($obj['two']) |
||||||
{ |
{ |
||||||
unset($this->_[$id]); |
return isset($this->members[$idx]); |
||||||
} |
} |
||||||
public function offsetGet($id) // eg. var_dump($obj['two']); |
public function offsetUnset($idx) // eg. unset($obj['two']); |
||||||
{ |
{ |
||||||
return $this->_[$id]; |
unset($this->members[$idx]); |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,111 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* Entity class with dynamic properties |
||||||
|
* Properties can include callable functions, which can `lazy load` properties, or return dynamic/complex/calculated properties |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace Twister; |
||||||
|
|
||||||
|
class Entity implements \ArrayAccess |
||||||
|
{ |
||||||
|
protected $properties = null; |
||||||
|
|
||||||
|
public function __construct(array $properties = null) |
||||||
|
{ |
||||||
|
$this->properties =& $properties; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get entity field/property |
||||||
|
* |
||||||
|
* @param string $name |
||||||
|
* @return mixed |
||||||
|
*/ |
||||||
|
public function __get($name) |
||||||
|
{ |
||||||
|
$value = $this->properties[$name]; |
||||||
|
return is_callable($value) ? $value($this) : $value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set entity field/property |
||||||
|
* |
||||||
|
* @param string $name |
||||||
|
* @param mixed $value |
||||||
|
* @return void |
||||||
|
*/ |
||||||
|
public function __set($name, $value) |
||||||
|
{ |
||||||
|
$this->properties[$name] = $value; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public function __isset($name) |
||||||
|
{ |
||||||
|
return isset($this->properties[$name]); |
||||||
|
} |
||||||
|
public function __unset($name) |
||||||
|
{ |
||||||
|
unset($this->properties[$name]); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public function &all() |
||||||
|
{ |
||||||
|
return $this->properties; |
||||||
|
} |
||||||
|
public function get($name) // alias for __get() |
||||||
|
{ |
||||||
|
return $this->properties[$name]; |
||||||
|
} |
||||||
|
public function set($name, $value) // alias for __set() |
||||||
|
{ |
||||||
|
return $this->properties[$name] = $value; |
||||||
|
} |
||||||
|
public function has($name) // alias for __isset() |
||||||
|
{ |
||||||
|
return isset($this->properties[$name]); |
||||||
|
} |
||||||
|
public function remove($name) // alias for __unset() |
||||||
|
{ |
||||||
|
unset($this->properties[$name]); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public function __call($method, $args) |
||||||
|
{ |
||||||
|
array_unshift($args, $this); |
||||||
|
return call_user_func_array($this->properties[$method], $args); |
||||||
|
} |
||||||
|
public function __invoke() |
||||||
|
{ |
||||||
|
return $this->properties['__invoke']($this); |
||||||
|
} |
||||||
|
public function setMethod($method, callable $callable) |
||||||
|
{ |
||||||
|
$this->properties[$method] = $callable; |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* ArrayAccess interface |
||||||
|
*/ |
||||||
|
public function offsetGet($name) // eg. var_dump($obj['two']); |
||||||
|
{ |
||||||
|
return $this->properties[$name]; |
||||||
|
} |
||||||
|
public function offsetSet($name, $value) // eg. $obj['two'] = 'A value'; |
||||||
|
{ |
||||||
|
$this->properties[$name] = $value; |
||||||
|
} |
||||||
|
public function offsetExists($name) // eg. isset($obj['two']) |
||||||
|
{ |
||||||
|
return isset($this->properties[$name]); |
||||||
|
} |
||||||
|
public function offsetUnset($name) // eg. unset($obj['two']); |
||||||
|
{ |
||||||
|
unset($this->properties[$name]); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue