Definitions

Introduction

Definitions are how Container describes your dependency map internally. Each definition contains information on how to build your classes.

Usage

Generally, Container will handle everything that is required to build a definition for you. When you invoke add, a Definition is built and returned meaning any further interaction is actually with the Definition, not Container.

<?php declare(strict_types=1);

$container  = new League\Container\Container;
$definition = $container->add(Acme\Foo::class);

var_dump($definition instanceof League\Container\Definition\Definition); // true

You can also extend a definition if needed.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container->add(Acme\Foo::class);

// ... some more code

$container->extend(Acme\Foo::class)->addArgument(Acme\Bar::class);
$container->add(Acme\Bar::class);

$foo = $container->get(Acme\Foo::class);

var_dump($foo instanceof Acme\Foo);      // true
var_dump($foo->bar instanceof Acme\Bar); // true

Creating definitions manually and passing them to the container is also possible.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$fooDefinition = (new Definition(Acme\Foo::class))->addArgument(Acme\Bar::class);
$barDefinition = (new Definition(Acme\Bar::class));

$container->add($fooDefinition->getAlias(), $fooDefinition);
$container->add($barDefinition->getAlias(), $barDefinition);

$foo = $container->get(Acme\Foo::class);

var_dump($foo instanceof Acme\Foo);      // true
var_dump($foo->bar instanceof Acme\Bar); // true

Aggregate

Container uses an aggregate to store all definitions. This means that you can build your aggregate first and pass it off to the container.

<?php declare(strict_types=1);

$definitions = [
    (new Definition(Acme\Foo::class))->addArgument(Acme\Bar::class),
    (new Definition(Acme\Bar::class)),
];

$aggregate = new League\Container\Definition\DefinitionAggregate($definitions);
$container = new League\Container\Container($aggregate);

$foo = $container->get(Acme\Foo::class);

var_dump($foo instanceof Acme\Foo);      // true
var_dump($foo->bar instanceof Acme\Bar); // true

Interfaces are provided for definitions and the aggregates meaning that you can build your own implementations and pass them to Container as above if you need more or less functionality defined for your dependency map.

Features

Definitions provide several methods to define what behaviour is desired on resolution of the object you are defining. All of these methods can be chained.

Adding Constructor Arguments

Adding an argument to a definition will pass that argument to the constructor of the defined class on instantiation, invoking this multiple times will pass more arguments, in the order they were added.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container
    ->add(Acme\Foo::class)
    ->addArgument(Acme\Bar::class)
    ->addArgument(Acme\Baz::class)
;

We also have a proxy method to pass multiple arguments in one call.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container->add(Acme\Foo::class)->addArguments([
    Acme\Bar::class,
    Acme\Baz::class,
]);

Adding Method Calls

We can define one or multiple method calls and the arguments to be passed to them, these arguments will be resolved via the container. (The same method call can be added multiple times for multiple invokations with the same or different arguments).

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container
    ->add(Acme\Foo::class)
    ->addMethodCall('setBar', [Acme\Bar::class])
    ->addMethodCall('setBaz', [Acme\Baz::class])
;

We also have a convenience method here to add multiple method calls to the definition at once.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container
    ->add(Acme\Foo::class)
    ->addMethodCalls([
        ['setBar', [Acme\Bar::class]],
        ['setBaz', [Acme\Baz::class]],
    ])
;

Defining Shared Objects

We can tell a definition to only resolve once and return the same instance every time it is resolved.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container
    ->add(Acme\Foo::class)
    ->setShared()
;

We also have a shortcut method to do this with one method call.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container->share(Acme\Foo::class);

If you would like to make all your definitions to default to shared, you can define that on Container, meaning that the add method will default to setting your definitions as shared and multiple calls to get will return the same instance. Only definitions after this is set will default to shared.

<?php declare(strict_types=1);

$container = (new League\Container\Container)->defaultToShared();

$container->add(Acme\Foo::class);

To define a definition not to be shared when the container is set to default to shared, we can reverse the functionality of defining it as shared.

<?php declare(strict_types=1);

$container = (new League\Container\Container)->defaultToShared();

$container->add(Acme\Foo::class, Acme\Foo::class, $shared = false);

If we have a definition marked as shared and we want to force the retrieval of a new instance, we can pass a second argument to Container on get.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container
    ->add(Acme\Foo::class)
    ->setShared()
;

$container->get(Acme\Foo::class, $new = true);

Tagged Definitions

We can tag definitions and retrieving the alias given to the tag will resolve all definitions using that tag in an indexed array. You can add multiple tags to each definition.

<?php declare(strict_types=1);

$container = new League\Container\Container;

$container->add(Acme\Foo::class)->addTag('foos');
$container->add(Acme\FooBar::class)->addTag('foos')->addTag('bars');
$container->add(Acme\Bar::class)->addTag('bars');

$foos = $container->get('foos'); // [Foo, FooBar]
$bars = $container->get('bars'); // [FooBar, Bar]