diff --git a/Clim/App.php b/Clim/App.php index 288af0f..5929c28 100644 --- a/Clim/App.php +++ b/Clim/App.php @@ -124,7 +124,7 @@ public function add($middleware) */ public function task($callable) { - $this->spec->addTask($this->containerBoundCallable($callable)); + $this->spec->addTask(new Task($callable, $this->getContainer())); } /** diff --git a/Clim/Cli/Spec.php b/Clim/Cli/Spec.php index c5330a7..f103376 100644 --- a/Clim/Cli/Spec.php +++ b/Clim/Cli/Spec.php @@ -9,6 +9,7 @@ namespace Clim\Cli; use Clim\Option; +use Clim\Task; class Spec { @@ -46,7 +47,7 @@ public function addArgument(ArgumentInterface $argument) $this->arguments[] = $argument; } - public function addTask(callable $task) + public function addTask(Task $task) { $this->tasks[] = $task; } diff --git a/Clim/Context.php b/Clim/Context.php index 9d52cf7..c182306 100644 --- a/Clim/Context.php +++ b/Clim/Context.php @@ -107,18 +107,4 @@ public function setResult($result) { $this->result = $result; } - - /** - * @param callable $callable - * @return mixed - */ - public function execute(callable $callable) - { - $options = new Hash($this->options()); - $arguments = new Hash($this->arguments()); - - $result = call_user_func_array($callable, [$options, $arguments]); - if (!is_null($result)) $this->setResult($result); - return $result; - } } diff --git a/Clim/Helper/ArgumentsSupplier.php b/Clim/Helper/ArgumentsSupplier.php new file mode 100644 index 0000000..b50b7c5 --- /dev/null +++ b/Clim/Helper/ArgumentsSupplier.php @@ -0,0 +1,119 @@ +context = $context; + $this->container = $container; + $this->options = new Hash($context->options()); + $this->arguments = new Hash($context->arguments()); + } + + public function supplyFor(Closure $closure) + { + $args = []; + + $ref = new \ReflectionFunction($closure); + /** @var \ReflectionParameter[] $params */ + $params = $ref->getParameters(); + + if (count($params) > 0) { + $this->assignedArguments = []; + $this->missingArgs = []; + + foreach ($params as $index => $param) { + $class = $param->getClass(); + $allow_null = $param->allowsNull(); + + $arg = $this->findArgumentFor( + $param->getName(), + $index, + $class ? $class->getName() : null, + $param->isArray() + ); + + $args[] = $arg; + } + + } + + return $args; + } + + protected function findArgumentFor($name, $index, $class, $is_array) + { + if ($name === 'arguments' || $name === 'args') { + return $this->hashToDesiredArgument($this->arguments, $class, $is_array); + } + + if ($name === 'options' || $name === 'opts') { + return $this->hashToDesiredArgument($this->options, $class, $is_array); + } + + if ($name === 'context') { + return $this->context; + } + + if ($this->container->has($name)) { + $result = $this->container->get($name); + if (!$class || ($result instanceof $class)) { + return $result; + } + } + + if (isset($this->arguments[$name])) { + $result = $this->arguments[$name]; + return $result; + } + + if (isset($this->options[$name])) { + $result = $this->options[$name]; + return $result; + } + + foreach ($this->arguments->all() as $index => $value) { + if (is_integer($index) && in_array($index, $this->assignedArguments, true) === false) { + if (($is_array && is_array($value)) || ($class && $value instanceof $class)) { + $this->assignedArguments[] = $index; + return $value; + } + } + } + + return null; + } + + protected function hashToDesiredArgument(Hash $hash, $class, $is_array) + { + if ($is_array) { + return $hash->all(); + } elseif ($class && $class !== 'Clim\\Helper\\Hash') { + try { + return new $class($hash->all()); + } catch (\Exception $e) { + // ... pass through + } + } + + return $hash; + } +} \ No newline at end of file diff --git a/Clim/Runner.php b/Clim/Runner.php index e102b10..89d1751 100644 --- a/Clim/Runner.php +++ b/Clim/Runner.php @@ -60,7 +60,8 @@ public function run($argv, Context $context = null) } foreach ($this->spec->tasks() as $task) { - $context->execute($task); + $result = $task->execute($context); + if (!is_null($result)) $context->setResult($result); } return $context; diff --git a/Clim/Task.php b/Clim/Task.php new file mode 100644 index 0000000..a8da02a --- /dev/null +++ b/Clim/Task.php @@ -0,0 +1,46 @@ +callable = $callable; + $this->container = $container; + } + + public function execute(Context $context) + { + $callable = $this->resolveCallable($this->callable); + if ($callable instanceof Closure) { + $callable = $callable->bindTo($this->container); + + $supplier = new ArgumentsSupplier($context, $this->container); + $args = $supplier->supplyFor($callable); + } else { + $options = $context->options(); + $arguments = $context->arguments(); + + $args = [new Hash($options), new Hash($arguments)]; + } + + return call_user_func_array($callable, $args); + } +} diff --git a/tests/unit/DispatchCept.php b/tests/unit/DispatchCept.php index 6c60886..8b1f971 100644 --- a/tests/unit/DispatchCept.php +++ b/tests/unit/DispatchCept.php @@ -16,14 +16,14 @@ // available only for foo $app->option('--force'); - $app->task(function ($context) { - // $context['age'] : from option + $app->task(function ($options) { + // $options['age'] : from option // $this->name : from container - echo "FOO {$context['age']}-{$this->name}\n"; + echo "FOO {$options['age']}-{$this->name}\n"; }); }, 'bar' => function (App $app) { - $app->task(function ($context) { + $app->task(function (Context $context) { echo "BAR\n"; }); }, diff --git a/tests/unit/TaskHandlerCept.php b/tests/unit/TaskHandlerCept.php new file mode 100644 index 0000000..08e62ae --- /dev/null +++ b/tests/unit/TaskHandlerCept.php @@ -0,0 +1,29 @@ +wantTo('perform actions and see result'); + +$app = new \Clim\App(); +$container = $app->getContainer(); + +$container['hello'] = function () { + return "Hello World!!!"; +}; + +class SimpleArray +{ + public function __construct($array) + { + } +} + +$app->task(function ($hello, array $arguments, \Clim\Helper\Hash $opts, SimpleArray $options) use ($I) { + $I->assertEquals("Hello World!!!", $hello); + $I->assertTrue(is_array($arguments)); + $I->assertTrue($opts instanceof \Clim\Helper\Hash); + $I->assertTrue($options instanceof SimpleArray); + return 42; +}); + +$context = $app->runner()->run(['prog_name']); + +$I->assertEquals(42, $context->getResult());