Argument Types

Introduction

You can define arguments as specific types known to the container, so that it knows what to do with these values without performing further checks. This has a performance benefit, as when one of these types of definitions is found, the value is either instantly returned, or further resolution is performed based on the type given.

Resolvable Arguments

Resolvable arguments are essentially the default internally for the container. The container will attempt to resolve any argument given to it until it ends up with a value that cannot be resolved further.

Where this becomes useful to explicitly define, is with nested aliases, you can tell the container that when it encounters an argument, that it should explicitly attempt to further resolve that value.

<?php 

declare(strict_types=1);

$container = new League\Container\Container();
$container->add('alias1', new League\Container\Argument\ResolvableArgument('alias2'));
$container->add('alias2', Acme\Foo::class);

$foo = $container->get('alias1');

var_dump($foo instanceof Acme\Foo); // true

Literal Arguments

Literal arguments do the opposite to resolvable ones, when the container encounters one of these, it just returns the associated value with no further resolution.

What problem does this solve?

It is mainly performance focused, however, the container will attempt possibly undesirable resolution on some types of argument.

String

By default, the container will exhaust all possible options before returning a string.

  • Is the string an alias of something else in the container?
  • Is the string a class that needs to be instantiated?
  • Is the string ultimately callable, and therefore should it be treated as a factory?

You can have the container treat a string as a literal string by defining that behaviour.

<?php 

declare(strict_types=1);

use League\Container\Argument\Literal;

$container = new League\Container\Container();
$container->add('alias1', new Literal\StringArgument('alias2'));
$container->add('alias2', Acme\Foo::class);

$foo = $container->get('alias1');

var_dump($foo instanceof Acme\Foo); // false
var_dump($foo === 'alias2'); // true

Array

Similarly to a string, the container wants to determine if an array, You can avoid these checks by defining it an array as a literal.

<?php 

declare(strict_types=1);

use League\Container\Argument\Literal;

$container = new League\Container\Container();
$container->add('an-array', new Literal\ArrayArgument(['blah', 'blah2']));

$arr = $container->get('an-array');

var_dump($arr === ['blah', 'blah2']); // true

Object and Callable

The container wants to treat any callable as a factory, so rather than returning your object, Closure etc, it will invoke it, and return the result of that.

Consider that you have an object that implements the magic __invoke method, but you don’t want the container to actually invoke the callable, just return the object passed as an argument.

<?php 

declare(strict_types=1);

namespace Acme;

class MyClass
{
    public function __invoke()
    {
        return 'hello';
    }
}
<?php 

declare(strict_types=1);

use League\Container\Argument\Literal;

$container = new League\Container\Container();
$container->add('object', new Acme\MyClass());
$container->add('literal-object', new Literal\ObjectArgument(new Acme\MyClass()); // Literal\CallableArgument could also be used here

$obj = $container->get('object');

var_dump($obj instanceof Acme\MyClass); // false
var_dump($obj === 'hello'); // true

$literalObj = $container->get('literal-object');

var_dump($literalObj instanceof Acme\MyClass); // true
var_dump($literalObj === 'hello'); // false

Similarly, if you want to pass any callable as an argument, the default behaviour will be to treat it as a factory and resolve it. You can avoid that by defining it as literal.

<?php 

declare(strict_types=1);

use League\Container\Argument\Literal;

$callback = function () {
    return 'hello';
};

$container = new League\Container\Container();
$container->add('callable', $callback);
$container->add('literal-callable', new Literal\CallableArgument($callback);

$cb = $container->get('callable');

var_dump($cb === $callback); // false
var_dump($cb === 'hello'); // true

$literalCb = $container->get('literal-callable');

var_dump($literalCb === $callback); // true
var_dump($literalCb === 'hello'); // false

Boolean, Integer and Float

These have zero effect, and only exist for clarity and readability in your code, with a little type checking.

All Literal Arguments

All literal argument classes are convenience sub-classes of League\Container\Argument\LiteralArgument.

  • League\Container\Argument\Literal\ArrayArgument
  • League\Container\Argument\Literal\BooleanArgument
  • League\Container\Argument\Literal\CallableArgument
  • League\Container\Argument\Literal\FloatArgument
  • League\Container\Argument\Literal\IntegerArgument
  • League\Container\Argument\Literal\ObjectArgument
  • League\Container\Argument\Literal\StringArgument