README

auryn is a recursive dependency injector. Use auryn to bootstrap and wire together
S.O.L.I.D., object-oriented PHP applications.

How It Works

Among other things, auryn recursively instantiates class dependencies based on the parameter
type-hints specified in class constructor signatures. This requires the use of Reflection. You may
have heard that "reflection is slow". Let's clear something up: anything can be "slow" if you're
doing it wrong. Reflection is an order of magnitude faster than disk access and several orders of
magnitude faster than retrieving information (for example) from a remote database. Additionally,
each reflection offers the opportunity to cache the results if you're worried about speed. auryn
caches any reflections it generates to minimize the potential performance impact.

auryn is NOT a Service Locator. DO NOT turn it into one by passing the injector into your
application classes. Service Locator is an anti-pattern; it hides class dependencies, makes code
more difficult to maintain and makes a liar of your API! You should only use an injector for
wiring together the disparate parts of your application during your bootstrap phase.

Requirements and Installation

Installation

Github

You can clone the latest auryn iteration at anytime from the github repository:

$ git clone git://github.com/rdlowrey/auryn.git

Composer

You may also use composer to include auryn as a dependency in your projects composer.json. The relevant package is rdlowrey/auryn.

Alternatively require the package using composer cli:

composer require rdlowrey/auryn

Manual Download

Archived tagged release versions are also available for manual download on the project
tags page

Basic Usage

To start using the injector, simply create a new instance of the Auryn\Injector ("the Injector")
class:

<?php$injector=newAuryn\Injector;

Basic Instantiation

If a class doesn't specify any dependencies in its constructor signature there's little point in
using the Injector to generate it. However, for the sake of completeness consider that you can do
the following with equivalent results:

Concrete Type-hinted Dependencies

If a class only asks for concrete dependencies you can use the Injector to inject them without
specifying any injection definitions. For example, in the following scenario you can use the
Injector to automatically provision MyClass with the required SomeDependency and AnotherDependency
class instances:

Recursive Dependency Instantiation

One of the Injector's key attributes is that it recursively traverses class dependency trees to
instantiate objects. This is just a fancy way of saying, "if you instantiate object A which asks for
object B, the Injector will instantiate any of object B's dependencies so that B can be instantiated
and provided to A". This is perhaps best understood with a simple example. Consider the following
classes in which a Car asks for Engine and the Engine class has concrete dependencies of its
own:

Injection Definitions

You may have noticed that the previous examples all demonstrated instantiation of classes with
explicit, type-hinted, concrete constructor parameters. Obviously, many of your classes won't fit
this mold. Some classes will type-hint interfaces and abstract classes. Some will specify scalar
parameters which offer no possibility of type-hinting in PHP. Still other parameters will be arrays,
etc. In such cases we need to assist the Injector by telling it exactly what we want to inject.

Defining Class Names for Constructor Parameters

Let's look at how to provision a class with non-concrete type-hints in its constructor signature.
Consider the following code in which a Car needs an Engine and Engine is an interface:

The values in the definition array represent the class names to inject for the specified
parameter key

Because the Car constructor parameter we needed to define was named $engine, our definition
specified an engine key whose value was the name of the class (V8) that we want to inject.

Custom injection definitions are only necessary on a per-parameter basis. For example, in the
following class we only need to define the injectable class for $arg2 because $arg1 specifies a
concrete class type-hint:

NOTE: Since this define() call is passing raw values (as evidenced by the colon : usage),
you can achieve the same result by omitting the array key(s) and relying on parameter order rather
than name. Like so: $injector->define('MyClass', [$dependencyInstance]);

Specifying Injection Definitions On the Fly

You may also specify injection definitions at call-time with Auryn\Injector::make. Consider:

The above code shows how even though we haven't called the Injector's define method, the
call-time specification allows us to instantiate MyClass.

NOTE: on-the-fly instantiation definitions will override a pre-defined definition for the
specified class, but only in the context of that particular call to Auryn\Injector::make.

Type-Hint Aliasing

Programming to interfaces is one of the most useful concepts in object-oriented design (OOD), and
well-designed code should type-hint interfaces whenever possible. But does this mean we have to
assign injection definitions for every class in our application to reap the benefits of abstracted
dependencies? Thankfully the answer to this question is, "NO." The Injector accommodates this goal
by accepting "aliases". Consider:

In this example we've demonstrated how to specify an alias class for any occurrence of a particular
interface or abstract class type-hint. Once an implementation is assigned, the Injector will use it
to provision any parameter with a matching type-hint.

IMPORTANT: If an injection definition is defined for a parameter covered by an implementation
assignment, the definition takes precedence over the implementation.

Non-Class Parameters

All of the previous examples have demonstrated how the Injector class instantiates parameters based
on type-hints, class name definitions and existing instances. But what happens if we want to inject
a scalar or other non-object variable into a class? First, let's establish the following behavioral
rule:

IMPORTANT: The Injector assumes all named-parameter definitions are class names by default.

If you want the Injector to treat a named-parameter definition as a "raw" value and not a class name,
you must prefix the parameter name in your definition with a colon character :. For example,
consider the following code in which we tell the Injector to share a PDO database connection
instance and define its scalar constructor parameters:

The colon character preceding the parameter names tells the Injector that the associated values ARE
NOT class names. If the colons had been omitted above, auryn would attempt to instantiate classes of
the names specified in the string and an exception would result. Also, note that we could just as
easily specified arrays or integers or any other data type in the above definitions. As long as the
parameter name is prefixed with a :, auryn will inject the value directly without attempting to
instantiate it.

NOTE: As mentioned previously, since this define() call is passing raw values, you may opt to
assign the values by parameter order rather than name. Since PDO's first three parameters are $dsn,
$username, and $password, in that order, you could accomplish the same result by leaving out the
array keys, like so:
$injector->define('PDO', ['mysql:dbname=testdb;host=127.0.0.1', 'dbuser', 'dbpass']);

Global Parameter Definitions

Sometimes applications may reuse the same value everywhere. However, it can be a hassle to manually
specify definitions for this sort of thing everywhere it might be used in the app. auryn mitigates
this problem by exposing the Injector::defineParam() method. Consider the following example ...

Because we specified a global definition for myValue, all parameters that are not in some other
way defined (as below) that match the specified parameter name are auto-filled with the global value.
If a parameter matches any of the following criteria the global value is not used:

A typehint

A predefined injection definition

A custom call time definition

Advanced Usage

Instance Sharing

One of the more ubiquitous plagues in modern OOP is the Singleton anti-pattern. Coders looking to
limit classes to a single instance often fall into the trap of using static Singleton
implementations for things like configuration classes and database connections. While it's often
necessary to prevent multiple instances of a class, the Singleton method spells death to testability
and should generally be avoided. Auryn\Injector makes sharing class instances across contexts a
triviality while allowing maximum testability and API transparency.

Let's consider how a typical problem facing object-oriented web applications is easily solved by
wiring together your application using auryn. Here, we want to inject a single database connection
instance across multiple layers of an application. We have a controller class that asks for a
DataMapper that requires a PDO database connection instance:

In the above code, the DataMapper instance will be provisioned with the same PDO database
connection instance we originally shared. This example is contrived and overly simple, but the
implication should be clear:

By sharing an instance of a class, Auryn\Injector will always use that instance when
provisioning classes that type-hint the shared class.

Defining an object as shared will store the provisioned instance in the Injector's shared cache and
all future requests to the provider for an injected instance of that class will return the
originally created object. Note that in the above code, we shared the class name (Person)
instead of an actual instance. Sharing works with either a class name or an instance of a class.
The difference is that when you specify a class name, the Injector
will cache the shared instance the first time it is asked to create it.

NOTE: Once the Injector caches a shared instance, call-time definitions passed to
Auryn\Injector::make will have no effect. Once shared, an instance will always be returned for
instantiations of its type until the object is un-shared or refreshed:

Instantiation Delegates

Often factory classes/methods are used to prepare an object for use after instantiation. auryn
allows you to integrate factories and builders directly into the injection process by specifying
callable instantiation delegates on a per-class basis. Let's look at a very basic example to
demonstrate the concept of injection delegates:

In the above code we delegate instantiation of the MyComplexClass class to a closure,
$complexClassFactory. Once this delegation is made, the Injector will return the results of the
specified closure when asked to instantiate MyComplexClass.

Available Delegate Types

Any valid PHP callable may be registered as a class instantiation delegate using
Auryn\Injector::delegate. Additionally you may specify the name of a delegate class that
specifies an __invoke method and it will be automatically provisioned and have its __invoke
method called at delegation time. Instance methods from uninstantiated classes may also be specified
using the ['NonStaticClassName', 'factoryMethod'] construction. For example:

Prepares and Setter Injection

Constructor injection is almost always preferable to setter injection. However, some APIs require
additional post-instantiation mutations. auryn accommodates these use cases with its
Injector::prepare() method. Users may register any class or interface name for post-instantiation
modification. Consider:

Additionally, you can pass in the name of a class for a non-static method and the injector will
automatically provision an instance of the class (subject to any definitions or shared instances
already stored by the injector) before provisioning and invoking the specified method:

Dependency Resolution

If a shared instance exists for the class in question, the shared instance will always be returned

If a delegate callable is assigned for a class, its return result will always be used

If a call-time definition is passed to Auryn\Injector::make, that definition will be used

If a pre-defined definition exists, it will be used

If a dependency is type-hinted, the Injector will recursively instantiate it subject to any implementations or definitions

If no type-hint exists and the parameter has a default value, the default value is injected

If a global parameter value is defined that value is used

Throw an exception because you did something stupid

Example Use Cases

Dependency Injection Containers (DIC) are generally misunderstood in the PHP community. One of the
primary culprits is the misuse of such containers in the mainstream application frameworks. Often,
these frameworks warp their DICs into Service Locator anti-patterns. This is a shame because a
good DIC should be the exact opposite of a Service Locator.

auryn Is NOT A Service Locator!

There's a galaxy of differences between using a DIC to wire together your application versus
passing the DIC as a dependency to your objects (Service Locator). Service Locator (SL) is an
anti-pattern -- it hides class dependencies, makes code difficult to maintain and makes a liar of
your API.

When you pass a SL into your constructors it makes it difficult to determine what the class dependencies
really are. A House object depends on Door and Window objects. A House object DOES NOT depend
on an instance of ServiceLocator regardless of whether the ServiceLocator can provide Door and
Window objects.

In real life you wouldn't build a house by transporting the entire hardware store (hopefully) to
the construction site so you can access any parts you need. Instead, the foreman (__construct())
asks for the specific parts that will be needed (Door and Window) and goes about procuring them.
Your objects should function in the same way; they should ask only for the specific dependencies
required to do their jobs. Giving the House access to the entire hardware store is at best poor
OOP style and at worst a maintainability nightmare. The takeaway here is this:

IMPORTANT: do not use auryn like a Service Locator!

Avoiding Evil Singletons

A common difficulty in web applications is limiting the number of database connection instances.
It's wasteful and slow to open up new connections each time we need to talk to a database.
Unfortunately, using singletons to limit these instances makes code brittle and hard to test. Let's
see how we can use auryn to inject the same PDO instance across the entire scope of our application.

Say we have a service class that requires two separate data mappers to persist information to a database:

In the above code, the injector will pass the string definition as the $dsn argument in the
PDO::__construct method and generate the shared PDO instance automatically only if one of the
classes it instantiates requires a PDO instance!

App-Bootstrapping

DICs should be used to wire together the disparate objects of your application into a cohesive
functional unit (generally at the bootstrap or front-controller stage of the application). One such
usage provides an elegant solution for one of the thorny problems in object-oriented (OO) web
applications: how to instantiate classes in a routed environment where the dependencies are not
known ahead of time.

Consider the following front controller code whose job is to:

Load a list of application routes and pass them to the router

Generate a model of the client's HTTP request

Route the request instance given the application's route list

Instantiate the routed controller and invoke a method appropriate to the HTTP request

In the above example the auryn DIC allows us to write fully testable, fully OO controllers that ask
for their dependencies. Because the DIC recursively instantiates the dependencies of objects it
creates we have no need to pass around a Service Locator. Additionally, this example shows how we can
eliminate evil Singletons using the sharing capabilities of the auryn DIC. In the front controller
code, we share the request object so that any classes instantiated by the Auryn\Injector that ask
for a Request will receive the same instance. This feature not only helps eliminate Singletons,
but also the need for hard-to-test static properties.