/** * Constructor * * @param \Chainnn\Chainable|object $object * @param \Chainnn\ChainOfCommand|array $chainOfCommand * * @throws \Chainnn\Exception\RuntimeException */ public function __construct($object, $chainOfCommand = null) { if (!is_object($object)) { throw new RuntimeException('Requires type object, got' . gettype($object)); } $this->object = $object; $this->methodList = array(); $reflection = new \ReflectionObject($object); foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { $isMagic = preg_match('/^_{2}/', $method->getName()); $chainableMethod = $method->getName() == 'getChainOfCommand'; if (!$isMagic && !$chainableMethod) { $this->methodList[$method->getName()] = $method; } } if (null === $chainOfCommand) { if ($this->object instanceof Chainable) { $chainOfCommand = $this->object->getChainOfCommand(); } } if (is_array($chainOfCommand)) { $chainOfCommand = ChainOfCommand::createFromArrayMap($chainOfCommand); } if (!$chainOfCommand instanceof ChainOfCommand) { throw new RuntimeException('Could not set a chain of command.'); } $this->chainOfCommand = $chainOfCommand; }
/** * Chain of command usage tests. */ public function testChainOfCommand() { $chainOfCommand = ChainOfCommand::createFromArrayMap(array('methodOne' => array('childOne' => array('subOne' => array('subSubOne'))), 'methodTwo' => array('childTwo' => array('subTwo' => array('subSubTwo'))))); // method one $methodOne = $chainOfCommand->findNextAvailableCommand('methodOne'); $this->assertNotNull($methodOne); $chainOfCommand->setCurrentCommand($methodOne); $childOne = $chainOfCommand->findNextAvailableCommand('childOne'); $this->assertNotNull($childOne); $chainOfCommand->setCurrentCommand($childOne); $subOne = $chainOfCommand->findNextAvailableCommand('subOne'); $this->assertNotNull($subOne); $chainOfCommand->setCurrentCommand($subOne); $subSubOne = $chainOfCommand->findNextAvailableCommand('subSubOne'); $this->assertNotNull($subSubOne); // method two $methodTwo = $chainOfCommand->findNextAvailableCommand('methodTwo'); $this->assertNotNull($methodTwo); $chainOfCommand->setCurrentCommand($methodTwo); $childTwo = $chainOfCommand->findNextAvailableCommand('childTwo'); $this->assertNotNull($childTwo); $chainOfCommand->setCurrentCommand($childTwo); $subTwo = $chainOfCommand->findNextAvailableCommand('subTwo'); $this->assertNotNull($subTwo); $chainOfCommand->setCurrentCommand($subTwo); $subSubTwo = $chainOfCommand->findNextAvailableCommand('subSubTwo'); $this->assertNotNull($subSubTwo); $chainOfCommand->setCurrentCommand($subSubTwo); // try accessing another base commands child $this->assertNull($chainOfCommand->findNextAvailableCommand('childOne')); $this->assertNull($chainOfCommand->findNextAvailableCommand('subOne')); $this->assertNull($chainOfCommand->findNextAvailableCommand('subSubOne')); // try bubbling up $this->assertNotNull($chainOfCommand->findNextAvailableCommand('methodTwo')); $this->assertNotNull($chainOfCommand->findNextAvailableCommand('childTwo')); $this->assertNotNull($chainOfCommand->findNextAvailableCommand('subTwo')); }
/** * This magic method will catch any commands on the chain of command and * invoke them, creating the chaining mechanism. * * @param string $method * @param array $params * @return \Chainnn\Chain|mixed * * @throws \Chainnn\Exception\RuntimeException */ public function __call($method, array $params) { if (!$this->object) { throw new RuntimeException('No object set on chain.'); } if (!$this->chainOfCommand) { throw new RuntimeException('No chain of command set.'); } $command = $this->chainOfCommand->findNextAvailableCommand($method); if (!$command) { throw new RuntimeException("{$method}() is not available on the chain."); } if ($command->willReturnToBase() || $command->willEndChain()) { $this->chainOfCommand->setCurrentCommand(); } else { $this->chainOfCommand->setCurrentCommand($command); } if (!array_key_exists($method, $this->methodList)) { throw new RuntimeException("No method {$method}() exists on the object."); } $methodReflection = $this->methodList[$method]; $result = $methodReflection->invokeArgs($this->object, $params); return $command->willEndChain() ? $result : $this; }