Download the PHP package lucatume/di52 without Composer
On this page you can find all versions of the php package lucatume/di52. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download lucatume/di52
More information about lucatume/di52
Files in lucatume/di52
Package di52
Short Description A PHP 5.6 compatible dependency injection container.
License GPL-3.0
Informations about the package di52
A PHP 5.6+ compatible dependency injection container inspired by Laravel IOC and Pimple that works even better on newer version of PHP.
A quick overview of the Container features:
- Auto-wiring - The Container will use Reflection to find what class should be built and how, it's almost magic.
- Flexible - Legacy code? Fat constructors with tons of side-effects? The Container offers auto-wiring without making it a requirement. It will adapt to existing code and will not require your code to adapt to it.
- Fatal error handling - On PHP 7.0+ the Container will take care of handling fatal errors that might happen at class file load time and handle them.
- Fast - The Container is optimized for speed as much as it can be squeezed out of the required PHP compatibility.
- Flexible default mode - Singleton (build at most once) an prototype (build new each time) default modes available.
- Global Application - Like using
App::get($service)->doStuff()
? TheApp
facade allows using the DI Container as a globally available Service Locator. - PSR-11 compatible - The container is fully compatible with PSR-11 specification.
- Ready for WordPress and other Event-driven frameworks - The container API provides methods
like
instance
to easily be integrated with Event-driven frameworks like WordPress that require hooking callbacks to events. - Service Providers - To keep your code organized, the library provides an advanced Service Provider implementation.
Table of Contents
- Code Example
- Installation
- Upgrading from version 2 to version 3
- Upgrading from version 3.2 to version 3.3
- Quick and dirty introduction to dependency injection
- What is dependency injection?
- What is a DI container?
- What is a Service Locator?
- Construction templates
- The power of
get
- Storing variables
- Binding implementations
- Binding implementations to slugs
- Contextual binding
- Binding decorator chains
- Tagging
- The callback method
- Service providers
- Booting service providers
- Deferred service providers
- Dependency injection with service providers
- Customizing the container
- Unbound classes resolution
- Exception masking
Code Example
In the application bootstrap file we define how the components will come together:
In the application entrypoint, the index.php
file, we'll lazily resolve the whole dependency tree following the
rules set in the bootstrap file:
That's it.
Installation
Use Composer to require the library:
Include the Composer autoload file in your project entry point and create a new instance of the container to start using it:
If you would prefer using the Dependency Injection Container as a globally-available Service Locator, then you
can use the lucatume\DI52\App
:
See the example above for more usage examples.
Upgrading from version 2 to version 3
The main change introduced by version 3.0.0
of the library is dropping compatibility with PHP 5.2 to require a minimum
version of PHP 5.6. The library is tested up to PHP 8.1.
If you're using version 2 of DI52 in your project, then there should be nothing you need to do.
The new, namespaced, classes of version 3 are aliased to their version 2 correspondent, e.g. tad_DI52_Container
is
aliased to lucatume\di52\Container
and tad_DI52_ServiceProvider
is aliased to lucatume\di52\ServiceProvider
.
I suggest an update for a small performance gain, though, to use the new, namespaced, class names in place of the PHP 5.2 compatible ones:
- replace uses of
tad_DI52_Container
withlucatume\di52\Container
- replace uses of
tad_DI52_ServiceProvider
withlucatume\DI52\ServiceProvider
The new version implemented PSR-11 compatibility and the main method to get hold
of an object instance from the container changed from make
to get
.
Do not worry, the lucatume\di52\Container::make
method is still there: it's just an alias of
the lucatume\di52\Container::get
one.
For another small performance gain replace uses of tad_DI52_Container::make
with lucatume\di52\Container::get
.
That should be all of it.
Upgrading from version 3.2 to version 3.3
Version 3.3.0 of the library removed the aliases.php
file, which previously helped to load non-PSR namespaced class names.
However, if you're using the tad_DI52_Container
and tad_DI52_ServiceProvider
classes in your project, you can set up the aliases by adding a few lines of code to your project's bootstrap file to ensure your code continues to work as expected:
Quick and dirty introduction to dependency injection
What is dependency injection?
A Dependency Injection (DI) Container is a tool meant to make dependency injection possible and easy to manage. Dependencies are specified by a class constructor method via type-hinting:
Any instance of class A
depends on implementations of the B
and C
classes.
The "injection" happens when class A
dependencies are passed to it, "injected" in its constructor method, in place of
being created inside the class itself.
The flexibility of type hinting allows injecting into A
not just instances of B
and C
but instances of any class
extending the two:
PHP allows type hinting not just concrete implementations (classes) but interfaces too:
This extends the possibilities of dependency injection even further and avoids strict coupling of the code:
What is a DI container?
The B
and C
classes are concrete (as in "you can instance them") implementations of interfaces and while the
interfaces might never change the implementations might and should change in the lifecycle of code: that's
the Dependency Inversion principle or "depend upon
abstractions, non concretions".
If the implementation of BInterface
changes from B
to BetterB
then I'd have to update all the code where I'm
building instances of A
to use BetterB
in place of B
:
On smaller code-bases this might prove to be a quick solution but, as the code grows, it will become less and less an applicable solution. Adding classes to the mix proves the point when dependencies start to stack:
Another issue with this approach is that classes have to be built immediately to be injected, see $a
and $d
above to
feed $e
, with the immediate cost of "eager" instantiation, if $e
is never used than the effort put into building it,
in terms of time and resources spent by PHP to build $a
, $b
, $c
, $d
and finally $e
, are wasted.
A dependency injection container will take care of building only objects that are needed taking care of
resolving nested dependencies.
Need an instance of
E
? I will build instances ofB
andC
to build an instance ofA
to build an instance ofD
to finally build and return an instance ofE
.
What is a Service Locator?
A "Service Locator" is an object, or function, that will answer to this question made by your code:
In Plain English "I do not care how it's built or where it comes from, give me the current implementation of the database service.".
Service Locators are, usually, globally-available DI Containers for obvious reasons: the DI Container knows how to build the services the Service Locator will provide when required. The concept of Service Locators and DI Containers are often conflated as a DI Container, when globally available, makes a good implementation of a Service Locator.
An example of this is the lucatume\DI52\App
class: it will expose, by means of static methods, a globally-available
instance of the lucatume\DI52\Container
class.
Since the lucatume\DI52\App
class proxies calls to the Container, the example could be made shorter:
Construction templates
The container will need to be told, just once, how objects should be built.
For the container it's easy to understand that a class type-hinting an instance of the concrete class A
will require a
new instance of A
but loosely coupled code leveraging the use of a DI container will probably type-hint an interface
in place of concrete class
es.
Telling the container what concrete class
to instance when a certain interface
is requested by an
object __construct
method is called "binding and implementation to an interface".
While dependency injection can be made in other methods too beyond the __construct
one that's what di52 supports at
the moment; if you want to read more the web is full of good reference
material, this article by Fabien Potencier is a very
good start.
The power of get
At its base the container is a dependency resolution and injection machine: given a class to its get
method it will
read the class type-hinted dependencies, build them and inject them in the class.
Keep that in mind while reading the following paragraphs.
Storing variables
In its most basic use case the container can store variables:
Since the container will treat any callable object as a factory (see below) callables have to be protected using the
container protect
method:
The protect method tells the container that, when get
ting the randomNumberGenerator
alias, we do not want to run the function and
get its result, but we want to get back the function itself.
Binding implementations
Telling the container what should be built and when is done by an API similar to the one exposed by the Laravel Service container and while the inner workings are different the good idea (kudos to Laravel's creator and maintainers) is reused. Reusing the example above:
The get
method will build the F
object resolving its requirements to the bound implementations when requested.
When using the bind
method a new instance of the bound implementations will be returned on each request; this might
not be the wanted behaviour especially for object costly to build (like a database driver that needs to connect): in
that case the singleton
method should be used:
Binding an implementation to an interface using the singleton
methods tells the container the implementations should
be built just the first time: any later call for that same interface should return the same instance.
Implementations can be redefined in any moment simple calling the bind
or singleton
methods again specifying a
different implementation.
You can customize how unbound classes are resolved by the container, check the unbound classes section.
Binding implementations to slugs
The container was heavily inspired by Pimple and offers some features of the PHP 5.3+ DI container as well:
There is no replacement for the factory
method offered by Pimple: the bind
method should be used instead.
Contextual binding
Borrowing an excellent idea from Laravel's container the possibility of contextual binding exists (supporting all the binding possibilities above). Contextual binding solves the problem of different objects requiring different implementations of the same interface (or class, see above):
Binding decorator chains
The Decorator pattern allows
extending the functionalities of an implementation without creating an extension and leveraging interfaces.
The container allows binding "chain of decorators" to an interface (or slug à la Pimple, or class) using
the bindDecorators
and singletonDecorators
.
The two methods are the bind
and singleton
equivalents for decorators.
Similarly to a bind
or singleton
call, you can specify a set of methods to call after the decorator chain is built
with the afterBuildMethods
parameter:
By default, the register
method will be called only on the base instance, the one on the right of the decorator chain.
In the example above only BaseEndpoint::register
would be called.
If you need to call the same set of after-build methods on all instances after each is build, you can set the value of
the afterBuildAll
parameter to true
:
In this example the register
method will be called on the BaseEndpoint
after it's built, then on the
CachingEndpoint
instance after it's built, and finally on the LoggingEndpoint
instance after it's built.
Different and more complex combinations of decorators and after-build methods should be handled binding, with a
bind
or singleton
call, a Closure to build the decorator chain.
Tagging
Tagging allows grouping similar implementations for the purpose of referencing them by group. Grouping implementations makes sense when, as an example, the same method has to be called on each implementation:
The tag
method supports any possibility offered by the container in terms of binding of objects, closures, decorator
chains and after-build methods.
The callback method
Some applications require callbacks (or some form of callable) to be returned in specific pieces of code. This is especially the case with WordPress and its event-based architecture . Using the container does not removes that possibility:
This code suffers from an eager instantiation problem: SomeFilteringClass
is built for the purpose of binding it but
might never be used.
The problem is easy to solve using the Container::callback
method:
The advantage of this solution is the container will return the same callback every time it's called with the same arguments when the called class is a singleton:
Service providers
To avoid passing the container instance around (
see Service Locator pattern)
or globalising it all the binding should happen in the same PHP file: this could lead, as the application grows, to a
thousand lines monster.
To avoid that the container supports service providers: those are classes extending
the lucatume\DI52\ServiceProvider
class, that
allow organizing the binding registrations into logical, self-contained and manageable units:
Booting service providers
The container implements a boot
method that will, in turn, call the boot
method on any service provider that
overloads it.
Some applications might define constants and environment variables at "boot" time (e.g. WordPress plugins_loaded
action) that might make an immediate registration futile.
In that case service providers can overload the boot
method:
Deferred service providers
Sometimes even just setting up the implementations might require such an up-front cost to make it undesirable unless
it's needed.
This might happen with non-autoloading code that will require a tangle of files to load (and side load) to grab a simple
class instance.
To "defer" that cost service providers can overload the deferred
property and the provides
method:
Dependency injection with service providers
The container supports additional dependency injection for service providers (version 3.0.3+). Auto-wiring will work the same as any class, simply override the service provider's constructor and add any additional concrete dependencies (don't forget to call the parent!):
If you want to inject primitives into a service provider, you need to utilize the when
, needs
, give
methods before registering the provider in the container:
Customizing the container
The container will be built with some opinionated defaults; those are not set in stone and you can customize the container to your needs.
Unbound classes resolution
The container will use reflection to work out the dependencies of an object, and will not require setup when resolving
objects with type-hinted object dependencies in the __construct
method.
By default those unbound classes will be resolved as prototypes, built new on each get
request.
To control the mode used to resolve unbound classes, a flag property can be set on the container when constructing it:
This will only apply to unbound classes! Whatever the flag used to build the container instance, the mode set in the
binding phase using Container::bind()
or Container::singleton()
methods will always be respected.
Exception masking
By default the container will catch any exception thrown during a service resolution and wrap into a ContainerException
instance.
The container will modify the exception message and the trace file and line to provide information about the nested
resolution tree and point your debug to the file and line that caused the issue.
You can customize how the container will handle exceptions by using the Container::setExceptionMask()
method: