Home

Awesome

Needle

Annotations based injection on top of Pimple

Build Status

Installation

The easiest way to install Needle is using Composer with the following requirement (Pimple will be automatically installed):

{
    "require": {
        "maximebf/needle": ">=0.1.0"
    }
}

Alternatively, you can download the archive and add the src/ folder to PHP's include path:

set_include_path('/path/to/src' . PATH_SEPARATOR . get_include_path());

Needle does not provide an autoloader but follows the PSR-0 convention.
You can use the following snippet to autoload Needle classes:

spl_autoload_register(function($className) {
    if (substr($className, 0, 6) === 'Needle') {
        $filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
        require_once $filename;
    }
});

(You will also need to install and autoload Pimple classes)

Basic usage

Needle injects services from a Pimple container into properties of an object according to annotations.

class Permissions {}

class User
{
    /**
     * @inject
     * @var Permissions
     */
    public $permissions;
}

$n = new Needle();
$n['Permissions'] = function($n) { return new Permissions(); };
$n['User'] = $n->inject('User');

$user = $n['User'];
assert(!empty($user->permissions));

The inject() function takes as argument either a class name like above or a closure like any other Pimple service:

$n['User'] = $n->inject(function($n) { return new User(); });

Annotations

There are two ways to specify how a property can be injected:

Using Needle without annotations

It's not mandatory to use annotations to define which properties will be injected. @inject() can take as second argument an array of key/value pairs where keys are property names and values are service names.

class Permissions {}
class User { public $permissions; }

$n = new Needle();
$n['Permissions'] = function($n) { return new Permissions(); };
$n['User'] = $n->inject('User', array('permissions' => 'Permissions'));

Properties will be injected according to their annotations and the properties array. The latter overtake any annotations. Set a value to null to prevent the injection.

Injection mechanism

In the default use case, the property value will be directly set with the service object. The property must exist before it can be injected.

To inject properties which are not defined (thus creating them on the fly), use true as the third argument to @inject:

// ...
class User {}
// ...
$n['User'] = $n->inject('User', array('permissions' => 'Permissions'), true);

Needle can also use a setter method (if it exists) when true is used as the fourth argument:

// ...
class User
{
    /** @inject Permissions */
    protected $permissions;

    protected $modified = false;

    public function setPermissions(Permissions $p)
    {
        $this->permissions = $p;
        $this->modified = true;
    }

    public function getPermissions()
    {
        return $this->permissions;
    }
}
// ...
$n['User'] = $n->inject('User', array(), false, true);

Factories

Needle also introduces the concept of factories to create closures which return injected objects.

class Permissions {}

class User
{
    /**
     * @inject
     * @var Permissions
     */
    public $permissions;

    public $name;

    public function __construct($name)
    {
        $this->name = $name;
    }
}

$n = new Needle();
$n['Permissions'] = function($n) { return new Permissions(); };
$n['UserFactory'] = $n->factory('User');

$User = $n['UserFactory'];

$foo = $User('foo');
$bar = $User('bar');

In this example, a factory is created for the ̀€Userclass.$n['UserFactory']returns a closure which can be used to create injectedUser` objects. Arguments passed to the closure will be forwarded to the class' constructor.

Factories can also be used using closures instead of class names. The closure has two parameters: the container and an array of arguments.

$n['UserFactory'] = $n->factory(function($n, $args) {
    return new User($args[0]);
});

Instead of first getting the closure using $n['UserFactory'] you can invoke the Needle object with the name of the service as first argument and an array of arguments as the second:

$foo = $n('UserFactory', array('foo'));
$bar = $n('UserFactory', array('bar'));