Last active
December 14, 2015 07:08
-
-
Save mbrowne/5047982 to your computer and use it in GitHub Desktop.
(Hacky) DCI in PHP 5.4, version 2
This file contains 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 | |
//SEE ALSO: https://gist.github.com/mbrowne/5562643 (works in PHP 5.3 too) | |
//and related discussion: https://groups.google.com/d/msg/object-composition/g4BMSdluuC8/yPR3-a1b2sMJ | |
ini_set('display_errors', 1); | |
trait AssignableToRole | |
{ | |
protected $roles = array(); | |
protected $methods = array(); | |
protected $currentRole; | |
public function addRole($traitOrClassName) { | |
if (trait_exists($traitOrClassName)) { | |
$className = $traitOrClassName.'Class'; | |
//We need a real class in order to be able to instantiate it, | |
//so we can pass the instance to getClosure() below | |
if (!class_exists($className)) { | |
eval('class '.$className.' {use '.$traitOrClassName.';}'); | |
} | |
} | |
else { | |
$this->checkRoleDefined($traitOrClassName); | |
$className = $traitOrClassName; | |
} | |
//copy the methods from the role class | |
$tmp = new $className; | |
$reflClass = new ReflectionClass($tmp); | |
$reflMethods = $reflClass->getMethods(); | |
foreach ($reflMethods as $method) { | |
$this->addMethod($traitOrClassName, $method->name, $method->getClosure($tmp)); | |
} | |
return $this; | |
} | |
protected function addMethod($roleName, $methodName, $methodCallable) | |
{ | |
if (!is_callable($methodCallable)) { | |
throw new InvalidArgumentException('The $methodCallable parameter must be callable'); | |
} | |
$this->methods[$methodName][$roleName] = Closure::bind($methodCallable, $this, get_class()); | |
$this->roles[$roleName]['methodNames'][] = $methodName; | |
} | |
public function asRole($traitOrClassName) { | |
$this->checkRoleDefined($traitOrClassName); | |
$this->checkRoleAssigned($traitOrClassName); | |
$this->currentRole = $traitOrClassName; | |
return $this; | |
} | |
public function removeRole($traitOrClassName) { | |
$this->checkRoleDefined($traitOrClassName); | |
try { | |
$this->checkRoleAssigned($traitOrClassName); | |
foreach ($this->roles[$traitOrClassName]['methodNames'] as $methodName) { | |
unset($this->methods[$methodName][$traitOrClassName]); | |
} | |
unset($this->roles[$traitOrClassName]); | |
} | |
catch (InvalidArgumentException $e) { | |
trigger_error($e->getMessage(), E_USER_NOTICE); | |
} | |
return $this; | |
} | |
protected function checkRoleDefined($traitOrClassName) { | |
if (!trait_exists($traitOrClassName) && !class_exists($traitOrClassName.'Class')) { | |
throw new InvalidArgumentException("Trait or class named '$traitOrClassName' is not defined"); | |
} | |
} | |
protected function checkRoleAssigned($traitOrClassName) { | |
if (!array_key_exists($traitOrClassName, $this->roles)) { | |
throw new InvalidArgumentException("Trait or class named '$traitOrClassName' is not currently assigned to this instance"); | |
} | |
} | |
public function __call($methodName, array $args) | |
{ | |
if (isset($this->methods[$methodName])) { | |
if ($this->currentRole) { | |
$ret = call_user_func_array($this->methods[$methodName][$this->currentRole], $args); | |
$this->currentRole = null; | |
return $ret; | |
} | |
else { | |
$methods = $this->methods[$methodName]; | |
if (count($methods) > 1) { | |
throw new RuntimeException( | |
"There is more than one role with the method '$methodName' currently assigned to this object. | |
Use the asRole() method to specify which role to use." | |
); | |
} | |
$method = current($methods); | |
call_user_func_array($method, $args); | |
} | |
} | |
else throw new RuntimeException("There is no method named '$methodName' to call"); | |
} | |
} | |
class Person | |
{ | |
use AssignableToRole; | |
} | |
trait EmployeeRole | |
{ | |
function work() { | |
echo "I'm working on it..."; | |
} | |
function goofOff() { | |
echo "Goofing off now even though I should be doing my job :)"; | |
} | |
} | |
trait StudentRole | |
{ | |
function goofOff() { | |
echo "Goofing off now even though I should be studying :)"; | |
} | |
} | |
$person = new Person; | |
$person->addRole('EmployeeRole'); | |
$person->addRole('StudentRole'); | |
$person->work(); | |
$person->asRole('EmployeeRole')->goofOff(); | |
$person->asRole('StudentRole')->goofOff(); | |
$person->removeRole('StudentRole'); | |
//$person->asRole('StudentRole')->goofOff(); //would throw an exception |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment