Tests
For the test-infected, the phemto test suite documents behaviour in detail.
Quick Start
Note that Phemto only supports constructor injection.
Type hints are used to figure out how to fill dependencies. When asked to instantiate a class like this:
<?php
class Foo {
function __construct(Bar $bar) {
...
}
...
}
Phemto identifies the "Bar" hint then searches its internal registry for a concrete implementation. Anything "greater than or equal to" Bar in the class hierarchy will do. If Bar is an interface any class which implements Bar is a viable candidate. If Bar is a class, ordinary or abstract, anything with Bar as an ancestor can be used including, of course, Bar itself (although that rather defeats the purpose). Usually you'll want to type hint to interfaces.
Configuration Stage
Phemto needs to be told about all the classes which you want to create as well as their dependencies, and the dependencies of dependencies etc etc. Pull out a configuration file to some easy-to-find location.
Suppose Bar is implemented by three classes: Quark, Strangeness and Charm:
<?php
interface Bar {
...
...
}
class Quark implements Bar {
...
...
}
class Strangeness implements Bar {
...
...
}
class Charm implements Bar {
...
...
}
Register Foo and select Charm:
<?php
$phemto->willUse('Foo');
$phemto->willUse('Charm');
You can save some typing in a long list by writing it out like this (fluent interface):
<?php
$phemto
->willUse('Foo')
->willUse('Charm')
...
...
...
;
For a singleton lifecycle:
<?php
$phemto->willUse(new Singleton('Charm'));
Any object can be singleton-ised. No changes are required to your own classes.
Note that objects are singletons with respect to their parent phemto instance not the script as a whole. If you have two or more containers on the go, each having registered a singleton Foo, they both hold a single instance of Foo but a different Foo per container.
Client Code
In the application code, pull objects out of the container with an instantiate call. If you forget to register a needed dependency an exception will be thrown here.
<?php
$foo = $phemto->instantiate('Foo');
Phemto wires up the Foo object and its cascade of dependencies behind the scenes. An injector represents the object graph (or a part of it) and here a node is being pulled out to do some work.
Note how easy it is to tweak the application behaviour simply by registering a different class: Quark, Strangeness or Charm. This is why you'd set things up to use an injector.
A secondary benefit is that there is less work to do in wiring up the object graph. It's all done automatically by the container. All you have to do is make a few willUse() calls and include the classes.
Scalar Parameters
Phemto will automatically deal with any type-hinted object parameters. Scalars should be passed in a separate array.
- Parameters should be listed in the array in the same order as they appear in the constructor.
- Singletons' parameters are passed at registration.
- For non-Singletons, pass parameters at instantiation.
<?php
class Foo {
function __construct($a, Bar $bar, $b) {
}
}
// singletons:
$phemto->willUse(new Singleton('Foo', array($a, $b)));
// non-singletons:
$phemto->instantiate('Foo', array($a, $b));
Although scalar vars can be passed to the requested object (Foo) in an instantiate() call, Phemto currently has no means of passing vars to any of its dependencies at instantiation. Consider:
<?php
class Foo {
function __construct(Bar $bar) {
}
}
class Charm implements Bar {
function __construct($a, $b) {
}
}
The only way round this is to register Charm as a singleton so that vars can be passed at registration:
<?php
$phemto->willUse('Foo');
$phemto->willUse(new Singleton('Charm', array($a, $b)));
Note that this only applies to un-hinted parameters: type-hinted object parameters are always automatically filled by Phemto.