Articles

Use Annotations in your PHP Code to achieve Dependency Injection with the Ding Container


In "Use annotations in your PHP Application to define Reusable Components with Ding" I've shown how to declare your beans. Now it's time to see how to achieve DI (dependency injection), also using annotations.

Use @Resource to inject a dependency by name in your PHP Application

This annotation will inject a dependency based on its name (wire-by-name). Can be used on properties and setter methods. It's pretty cool, but you have to know beforehand the name of the bean you want to inject. When used on a property, the name of the property will be used. When used on a method, the method MUST be a setter, like "setDependency()". From such a method name, the container will try to find the bean named "dependency". You can override this behaviour with the "name" argument.

Arguments

  • name: A string. Optional. Specifies the name of the bean to be injected.

Examples

Using @Resource with properties: In the following example, both properties are injected with exactly the same bean. The first one (without the "name" argument) will look a bean whose name is equal to the property name (in this case, "dependency"). The second one, specifies that the name IS "dependency", so the name of the property wont matter at all.

Now let's see how to use it in setter methods to inject properties. In the first method (setDependency), the name of the bean is derived from the setter method itself, in this case, "dependency" is the name of the bean. In the second method (setOtherStuff) the name is explicitely defined in the "name" argument:

Enforce dependencies with the @Required annotation

This annotation enforces the configuration of a particular property. It can only be used on setter methods, and the container will throw a BeanFactoryException if a @Required annotation is used on a setter method that is not configured to be injected with a property.

Examples

Lookup and inject property values in your PHP code with @Value

This annotation can be used on properties, constructors, and @Bean methods. It is used to specify an explicit value or a string that resolves to a property injected in the container, either through the PropertiesHolder bean or through the user properties set when instantiating the container.

Arguments

  • value: A string. Required. A literal string to inject or a string that can be mapped to a property defined in the container (in the format ${...}, like ${user.name}).
  • name: A string. Optional. When used in a property, there's no need to specify a name. Names are used when using @Value on constructors and @Bean methods, that have multiple arguments. Thus, the "name" argument specifies the name of the argument where to inject the specified string given in the "value" argument. If you dont specify the "name" argument, the order of the @Value annotations, will be used to know what goes where.

Examples

Using it on a property. In this case, if the container has the property "user.name" (let's say it is set to "nobody"), the property "string" will be set to "My username is nobody".

/** @Component */
class MyBean
{
    /** @Value(value="My username is ${user.name}") */
    protected $string;
}

Using it on a constructor. In the following case, we have a constructor with 2 arguments, named "string1" and "string2". The first one will be injected with the value of the property "user.name", and the second will be injected with the value of the property "user.group":

The above example is equivalent to the next one (where the "name" argument is not specified):

For @Bean methods, the usage is similar to using it on a constructor method:

Use @Inject to inject dependencies by class type in your code

@Inject is the JSR330 counterpart to spring's @Autowired. This one can be used to inject your dependencies by type (instead of by name, like @Resource). It can be used on properties, methods, and constructors. Using @Resource has the downside of having to use the name of the bean you want to inject. Wiring by type, on the other hand, lets you have the container choose the appropiate bean you need based just on its type. This will also result in a more strict type checking when injecting dependencies, something very valuable to have.

Arguments

  • type: String. Required for properties and methods/constructors/@Bean methods whose arguments types are not defined. A type, is a string that represents a class or interface full qualified name (without the leading \ for namespaces), like MyNameSpace\MyInterface. If this string contains [ ] at the end (like MyNameSpace\MyInterface[]), an array will be injected instead of a scalar, with all the components that implement that interface or extends the given class.
  • required: A boolean. Optional. "true" by default. By default, all @Inject dependencies are required, this means that the container will throw an exception if it cant inject it. Specify "false" and the container will continue execution without injecting the property (if no candidate was found or more than one was found and none of them are specified as a primary bean).
  • name: A string. Optional. When used in a property, there's no need to specify a name. Names are used when using @Inject on constructors and @Bean methods, that have multiple arguments. Thus, the "name" argument specifies the name of the argument where to inject the dependency. If you dont specify the "name" argument, the order of the @Inject annotations, will be used to know what goes where.

Examples

Let's say you have defined an interface and a component that implements it, like:

And you want another component to be injected with any component that implements it:

The property "oneRepository" will contain an instance of MyUserRepository. If more than one component is found as a candidate to be injected (i.e: more than 1 component implements IUserRepository), the container will throw an exception, because it cant decide by itself which one to inject. In this case, you can opt to use something like @Resource to specify a name. But you can also decide to inject all the components found instead of just one:

In the above case, the property "manyRepositories" will be an array of all the components that implements IUserRepository.

If you still need one and not all of the candidate components to be injected, you can specify @Primary in the interesting components:

Using @Primary (or primary="true" in the @Component annotation) will tell the container that if more than 1 candidate is found to be injected by type, then choose the one selected as the primary bean of that type.

You can also use this annotation in methods, like:

Notice how the container automatically picks up the type of the argument of the method. The name of the method can be any, it wont matter when injecting by type. This also applies to constructors:

In the above example, the container will try to inject a component of type IUserRepository and a component of type IOtherInterface in the constructor arguments.

You can also mix @Value and @Inject. In the following example, the constructor has 4 arguments (just as an example..), and the arguments $repository and $dependency will be handled by the @Inject annotation, $otherArgument will be handled by the second @Inject that specifies the name of the argument to be injected, and $aString will be handled by the @Value annotation: