Skip to content

Commit

Permalink
Inject the input and output using type-hints in commands
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed May 22, 2017
1 parent 96c9ac7 commit d8f9a22
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 14 deletions.
40 changes: 26 additions & 14 deletions src/Application.php
Expand Up @@ -9,16 +9,17 @@
use Invoker\ParameterResolver\AssociativeArrayResolver;
use Invoker\ParameterResolver\Container\ParameterNameContainerResolver;
use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
use Invoker\ParameterResolver\DefaultValueResolver;
use Invoker\ParameterResolver\NumericArrayResolver;
use Invoker\ParameterResolver\ResolverChain;
use Invoker\ParameterResolver\TypeHintResolver;
use Invoker\Reflection\CallableReflection;
use Silly\Command\Command;
use Silly\Command\ExpressionParser;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Output\OutputInterface;

/**
Expand Down Expand Up @@ -70,9 +71,16 @@ public function command($expression, $callable, array $aliases = [])
$commandFunction = function (InputInterface $input, OutputInterface $output) use ($callable) {
$parameters = array_merge(
[
// Injection by parameter name
'input' => $input,
'output' => $output,
// Injections by type-hint
InputInterface::class => $input,
OutputInterface::class => $output,
Input::class => $input,
Output::class => $output,
],
// Arguments and options are injected by parameter names
$input->getArguments(),
$input->getOptions()
);
Expand Down Expand Up @@ -137,18 +145,15 @@ public function useContainer(
) {
$this->container = $container;

$resolvers = [
new AssociativeArrayResolver,
new HyphenatedInputResolver,
];
$resolver = $this->createParameterResolver();
if ($injectByTypeHint) {
$resolvers[] = new TypeHintContainerResolver($container);
$resolver->appendResolver(new TypeHintContainerResolver($container));
}
if ($injectByParameterName) {
$resolvers[] = new ParameterNameContainerResolver($container);
$resolver->appendResolver(new ParameterNameContainerResolver($container));
}

$this->invoker = new Invoker(new ResolverChain($resolvers), $container);
$this->invoker = new Invoker($resolver, $container);
}

/**
Expand Down Expand Up @@ -191,6 +196,11 @@ public function setInvoker(InvokerInterface $invoker)
$this->invoker = $invoker;
}

/**
* @param string $expression
* @param callable $callable
* @return Command
*/
private function createCommand($expression, callable $callable)
{
$result = $this->expressionParser->parse($expression);
Expand Down Expand Up @@ -220,6 +230,11 @@ private function assertCallableIsValid($callable)
}
}

/**
* @param Command $command
* @param callable $callable
* @return array
*/
private function defaultsViaReflection($command, $callable)
{
if (! is_callable($callable)) {
Expand Down Expand Up @@ -248,17 +263,14 @@ private function defaultsViaReflection($command, $callable)
}

/**
* Create the default parameter resolver.
*
* @return ParameterResolver
* @return ResolverChain
*/
private function createParameterResolver()
{
return new ResolverChain([
new NumericArrayResolver,
new AssociativeArrayResolver,
new HyphenatedInputResolver,
new DefaultValueResolver,
new TypeHintResolver,
]);
}

Expand Down
68 changes: 68 additions & 0 deletions tests/FunctionalTest.php
Expand Up @@ -6,7 +6,10 @@
use Silly\Test\Fixture\SpyOutput;
use Silly\Test\Mock\ArrayContainer;
use stdClass;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Output\OutputInterface as Out;

class FunctionalTest extends \PHPUnit_Framework_TestCase
Expand Down Expand Up @@ -46,6 +49,71 @@ public function it_should_return_the_exit_code()
$this->assertEquals(1, $code);
}

/**
* @test
*/
public function it_should_inject_the_output_and_input_by_name()
{
$this->application->command('greet name', function ($output, $input) {
$output->write('hello ' . $input->getArgument('name'));
});
$this->assertOutputIs('greet john', 'hello john');
}

/**
* @test
*/
public function it_should_inject_the_output_and_input_by_name_even_if_a_service_has_the_same_name()
{
$container = new ArrayContainer([
'input' => 'foo',
'output' => 'bar',
]);
$this->application->useContainer($container, false, true);
$this->application->command('greet name', function ($output, $input) {
$output->write('hello ' . $input->getArgument('name'));
});
$this->assertOutputIs('greet john', 'hello john');
}

/**
* @test
*/
public function it_should_inject_the_output_and_input_by_type_hint_on_interfaces()
{
$this->application->command('greet name', function (Out $out, InputInterface $in) {
$out->write('hello ' . $in->getArgument('name'));
});
$this->assertOutputIs('greet john', 'hello john');
}

/**
* @test
*/
public function it_should_inject_the_output_and_input_by_type_hint_on_classes()
{
$this->application->command('greet name', function (Output $out, Input $in) {
$out->write('hello ' . $in->getArgument('name'));
});
$this->assertOutputIs('greet john', 'hello john');
}

/**
* @test
*/
public function it_should_inject_the_output_and_input_by_type_hint_even_if_a_service_has_the_same_name()
{
$container = new ArrayContainer([
'in' => 'foo',
'out' => 'bar',
]);
$this->application->useContainer($container, false, true);
$this->application->command('greet name', function (Out $out, InputInterface $in) {
$out->write('hello ' . $in->getArgument('name'));
});
$this->assertOutputIs('greet john', 'hello john');
}

/**
* @test
*/
Expand Down

0 comments on commit d8f9a22

Please sign in to comment.