A small PHP dependency injection container

Overview

Pimple

Caution!

Pimple is now closed for changes. No new features will be added and no cosmetic changes will be accepted either. The only accepted changes are compatibility with newer PHP versions and security issue fixes.

Caution!

This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read the Pimple 1.x documentation. Reading the Pimple 1.x code is also a good way to learn more about how to create a simple Dependency Injection Container (recent versions of Pimple are more focused on performance).

Pimple is a small Dependency Injection Container for PHP.

Installation

Before using Pimple in your project, add it to your composer.json file:

$ ./composer.phar require pimple/pimple "^3.0"

Usage

Creating a container is a matter of creating a Container instance:

use Pimple\Container;

$container = new Container();

As many other dependency injection containers, Pimple manages two different kind of data: services and parameters.

Defining Services

A service is an object that does something as part of a larger system. Examples of services: a database connection, a templating engine, or a mailer. Almost any global object can be a service.

Services are defined by anonymous functions that return an instance of an object:

// define some services
$container['session_storage'] = function ($c) {
    return new SessionStorage('SESSION_ID');
};

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

Notice that the anonymous function has access to the current container instance, allowing references to other services or parameters.

As objects are only created when you get them, the order of the definitions does not matter.

Using the defined services is also very easy:

// get the session object
$session = $container['session'];

// the above call is roughly equivalent to the following code:
// $storage = new SessionStorage('SESSION_ID');
// $session = new Session($storage);

Defining Factory Services

By default, each time you get a service, Pimple returns the same instance of it. If you want a different instance to be returned for all calls, wrap your anonymous function with the factory() method

$container['session'] = $container->factory(function ($c) {
    return new Session($c['session_storage']);
});

Now, each call to $container['session'] returns a new instance of the session.

Defining Parameters

Defining a parameter allows to ease the configuration of your container from the outside and to store global values:

// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';

If you change the session_storage service definition like below:

$container['session_storage'] = function ($c) {
    return new $c['session_storage_class']($c['cookie_name']);
};

You can now easily change the cookie name by overriding the cookie_name parameter instead of redefining the service definition.

Protecting Parameters

Because Pimple sees anonymous functions as service definitions, you need to wrap anonymous functions with the protect() method to store them as parameters:

$container['random_func'] = $container->protect(function () {
    return rand();
});

Modifying Services after Definition

In some cases you may want to modify a service definition after it has been defined. You can use the extend() method to define additional code to be run on your service just after it is created:

$container['session_storage'] = function ($c) {
    return new $c['session_storage_class']($c['cookie_name']);
};

$container->extend('session_storage', function ($storage, $c) {
    $storage->...();

    return $storage;
});

The first argument is the name of the service to extend, the second a function that gets access to the object instance and the container.

Extending a Container

If you use the same libraries over and over, you might want to reuse some services from one project to the next one; package your services into a provider by implementing Pimple\ServiceProviderInterface:

use Pimple\Container;

class FooProvider implements Pimple\ServiceProviderInterface
{
    public function register(Container $pimple)
    {
        // register some services and parameters
        // on $pimple
    }
}

Then, register the provider on a Container:

$pimple->register(new FooProvider());

Fetching the Service Creation Function

When you access an object, Pimple automatically calls the anonymous function that you defined, which creates the service object for you. If you want to get raw access to this function, you can use the raw() method:

$container['session'] = function ($c) {
    return new Session($c['session_storage']);
};

$sessionFunction = $container->raw('session');

PSR-11 compatibility

For historical reasons, the Container class does not implement the PSR-11 ContainerInterface. However, Pimple provides a helper class that will let you decouple your code from the Pimple container class.

The PSR-11 container class

The Pimple\Psr11\Container class lets you access the content of an underlying Pimple container using Psr\Container\ContainerInterface methods:

use Pimple\Container;
use Pimple\Psr11\Container as PsrContainer;

$container = new Container();
$container['service'] = function ($c) {
    return new Service();
};
$psr11 = new PsrContainer($container);

$controller = function (PsrContainer $container) {
    $service = $container->get('service');
};
$controller($psr11);

Using the PSR-11 ServiceLocator

Sometimes, a service needs access to several other services without being sure that all of them will actually be used. In those cases, you may want the instantiation of the services to be lazy.

The traditional solution is to inject the entire service container to get only the services really needed. However, this is not recommended because it gives services a too broad access to the rest of the application and it hides their actual dependencies.

The ServiceLocator is intended to solve this problem by giving access to a set of predefined services while instantiating them only when actually needed.

It also allows you to make your services available under a different name than the one used to register them. For instance, you may want to use an object that expects an instance of EventDispatcherInterface to be available under the name event_dispatcher while your event dispatcher has been registered under the name dispatcher:

use Monolog\Logger;
use Pimple\Psr11\ServiceLocator;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

class MyService
{
    /**
     * "logger" must be an instance of Psr\Log\LoggerInterface
     * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
     */
    private $services;

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

$container['logger'] = function ($c) {
    return new Monolog\Logger();
};
$container['dispatcher'] = function () {
    return new EventDispatcher();
};

$container['service'] = function ($c) {
    $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher'));

    return new MyService($locator);
};

Referencing a Collection of Services Lazily

Passing a collection of services instances in an array may prove inefficient if the class that consumes the collection only needs to iterate over it at a later stage, when one of its method is called. It can also lead to problems if there is a circular dependency between one of the services stored in the collection and the class that consumes it.

The ServiceIterator class helps you solve these issues. It receives a list of service names during instantiation and will retrieve the services when iterated over:

use Pimple\Container;
use Pimple\ServiceIterator;

class AuthorizationService
{
    private $voters;

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

    public function canAccess($resource)
    {
        foreach ($this->voters as $voter) {
            if (true === $voter->canAccess($resource)) {
                return true;
            }
        }

        return false;
    }
}

$container = new Container();

$container['voter1'] = function ($c) {
    return new SomeVoter();
}
$container['voter2'] = function ($c) {
    return new SomeOtherVoter($c['auth']);
}
$container['auth'] = function ($c) {
    return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2'));
}
Comments
  • Throw more specific / PSR-11 exceptions

    Throw more specific / PSR-11 exceptions

    Pimple now throws more specific exceptions.

    An attempt to modify a frozen service was made

    • Before: \RuntimeException
    • After: FrozenServiceException extends \RuntimeException

    The identifier of a valid service or parameter was expected

    • Before: \InvalidArgumentException
    • After: UnknownIdentifierException extends \InvalidArgumentException

    A closure or invokable object was expected

    • Before: \InvalidArgumentException
    • After: ExpectedInvokableException extends \InvalidArgumentException

    A attempt to perform an operation that requires a service identifier was made

    Thrown when trying to extend() a parameter

    • Before: \InvalidArgumentException
    • After: InvalidServiceIdentifierException extends \InvalidArgumentException

    PSR-11 compatibility

    All the classes implement Psr\Container\ContainerExceptionInterface and UnknownIdentifierException implements NotFoundExceptionInterface.

    I believe it doesn't hurt and couldn't find a reason not to do it like this.

    Also, this will make PSR-11 wrappers faster. Until now, these wrappers have to do something like this:

    class PimpleContainer implements \Psr\Container\ContainerInterface
    {
        public function get($id)
        {
            if (!isset($this->container[$id]) {
                throw new NotFoundException(sprintf('The identifier %s is not defined.', $id));
            }
    
            return $this->container[$id];
        }
    }
    

    The additional cost is:

    • 2 function calls: ContainerInterface::get() and Pimple\Container::offsetExists()
    • 1 isset() (the one in offsetExists())

    By making the Pimple exceptions Compliant with PSR-11, this cost can be reduced to a single function call:

    class PimpleContainer implements \Psr\Container\ContainerInterface
    {
        public function get($id)
        {
            return $this->container[$id];
        }
    }
    

    Code changes

    All the changes are pretty straightforward. The only one that may have to be discussed is the introduction of a test that throws a FrozenServiceException in extend(). I'll post a separate issue to explain the behavior this fixes.

    The tests

    I've kept the old tests and renamed them legacySomething(), so we could check that the changes didn't break BC. It's probably safer to keep them, but if you think that's superfluous I can remove them before merge.

    opened by skalpa 14
  • Introduce two new convenience methods: registerSharedService, extendService

    Introduce two new convenience methods: registerSharedService, extendService

    Registering a shared service for a certain class that has certain deps happens very often. I'd consider this the primary use case for pimple. As such, it makes sense to provide some shortcuts to make this easier to use.

    registerSharedService addresses the common use case of:

    $c['foo'] = $c->share(function ($c) {
        return new Foo($c['bar']);
    });
    

    The shorter version of that is:

    $c->registerSharedService('foo', 'Foo', array('bar'));
    

    The array of service ids is optional and can be omitted if the service constructor has no args.


    While not such a common use case, when extending a service, you usually want to re-share and assign it. And the code we have to write for that is clunky and verbose. It would make sense to have a dedicated method that makes things easier.

    extendService is a shorthand for:

    $c['foo'] = $c->share($c->extend('foo', function ($foo, $c) {
        $foo->addBar(new Bar());
        return $foo;
    }));
    

    The new version is:

    $c->extendService('foo', function ($foo, $c) {
        $foo->addBar(new Bar());
        return $foo;
    });
    

    I'm open to splitting this PR if one of the proposed additions is controversial.

    opened by igorw 14
  • Allow psr/container 2.x

    Allow psr/container 2.x

    Replaces #265, Obsoletes #267

    • [x] Composer: Allow psr/container ^2.0
    • [x] Depends on https://github.com/silexphp/Pimple/pull/269 being merged
    • [x] CI: test Pimple with version 1 AND version 2 of the PSR to be sure that both work well.
    opened by glensc 13
  • Introducing property access in Pimple

    Introducing property access in Pimple

    This PR introduces the concepts at the foundation of YAC into Pimple.

    This basically allows to use pimple like following:

    $pimple = new Pimple();
    $pimple['someService'] = ...;
    
    $pimple->someService->doStuff();
    $pimple['someService']->doStuff(); //equivalent
    
    opened by Ocramius 11
  • Replace Travis with GitHub Actions

    Replace Travis with GitHub Actions

    Based on my previous work on https://github.com/zf1s/zf1

    PHP 8.1 is omitted, it should be added via https://github.com/silexphp/Pimple/pull/266

    refs:

    • https://github.com/silexphp/Pimple/pull/268#issuecomment-952059965
    opened by glensc 10
  • Pimple PSR-11 implementation

    Pimple PSR-11 implementation

    Back in 2014 there was a proposal for container-interop: #162, which was rejected.

    PSR-11 is now in draft status and planty of projects implementing it.

    I don't know if draft status is enough for Pimple to implement it, I'd like to know it. That is the goal of this PR.

    What is your opinion?

    opened by paradajozsef 10
  • Support

    Support "env()" parameters

    Hi everbody,

    I needed support for environment variables in Pimple and sure you can build your own getenv() stuff but I thought while I'm on it I might as well just port the implementation of the SF container to Pimple because it's not really a big deal and it might make the lives of devs easier. :)

    opened by Toflar 9
  • Added getValues() method to allow read-only access to the private values array

    Added getValues() method to allow read-only access to the private values array

    When extending the Pimple class to provide additional functionality I was previously accessing $this->values directly when it was protected. As I was only reading the data, adding a getter (as I've done in this PR) is perfectly fine but I think it's important to expose this array in order to allow extensions.

    Note: I realize it would be possible to access the information by using the public keys() method and then iterating for each key and requesting it's value, however this would have a significant performance impact.

    opened by RobMasters 9
  • [2.0] changed services to be shared by default

    [2.0] changed services to be shared by default

    Now that we have several years of experience using Pimple, I think everyone agrees that having to call share everytime you need to define a service is cumbersome. And it is even error-prone when calling extend as one needs to call share again. #62 proposes a shortcut for the extend use case, but I want to go one step further in Pimple 2.0: remove the need to call share altogether.

    The reasoning is quite simple: services are 99.9% shared (I don't even have an example for a prototype service).

    So, this PR makes closure shared by default. If you need to define a prototype service, call prototype.

    This change is almost backward compatible as the share method is kept as a no-op method. The only BC break is if you have a prototype service in which case you need to wrap it with a prototype call.

    Of course, Pimple 2.0 will be used in Silex 2.0, which will make things much easier to read and shorter.

    TODO:

    • [x] update docs and website for 2.0
    opened by fabpot 9
  • Make Pimple a Composer

    Make Pimple a Composer "light-weight-distribution-package"

    In order to save space and bandwidth when installing this package using Composer, I have added .gitattributes in order not to include unnecessary files and folders (doc, tests, etc.) when packaging (git archive).

    (extracted from http://getcomposer.org/doc/02-libraries.md#light-weight-distribution-packages)

    Including the tests and other useless information like .travis.yml in distributed packages is not a good idea.

    The .gitattributes file is a git specific file like .gitignore also living at the root directory of your library. It overrides local and global configuration (.git/config and ~/.gitconfig respectively) when present and tracked by git.

    Use .gitattributes to prevent unwanted files from bloating the zip distribution packages.

    // .gitattributes /Tests export-ignore phpunit.xml.dist export-ignore Resources/doc/ export-ignore .travis.yml export-ignore Test it by inspecting the zip file generated manually:

    git archive branchName --format zip -o file.zip

    Note: Files would be still tracked by git just not included in the distribution. This will only work for GitHub packages installed from dist (i.e. tagged releases) for now.

    opened by carlosbuenosvinos 9
  • Fix segfault in extension's protect

    Fix segfault in extension's protect

    Fixes a segfault in the extension. The function being protected seems to need to have the refcount incremented.

    php --version
    PHP 5.5.9-1ubuntu4.11 (cli) (built: Jul  2 2015 15:23:08) 
    Copyright (c) 1997-2014 The PHP Group
    Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
        with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
        with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans
    

    tests/015.mem

    ==32129== Invalid read of size 1
    ==32129==    at 0x6DDE55: zval_call_destructor (zend_execute_API.c:203)
    ==32129==    by 0x6FBE63: zend_hash_reverse_apply (zend_hash.c:799)
    ==32129==    by 0x6DE290: shutdown_destructors (zend_execute_API.c:217)
    ==32129==    by 0x6EDD23: zend_call_destructors (zend.c:919)
    ==32129==    by 0x68E2A4: php_request_shutdown (main.c:1750)
    ==32129==    by 0x79EF49: do_cli (php_cli.c:1177)
    ==32129==    by 0x461E0F: main (php_cli.c:1378)
    ==32129==  Address 0x77d5b74 is 20 bytes inside a block of size 32 free'd
    ==32129==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==32129==    by 0x750C508: pimple_bucket_dtor (pimple.c:490)
    ==32129==    by 0x6FB977: zend_hash_destroy (zend_hash.c:560)
    ==32129==    by 0x750CEE1: pimple_free_object_storage (pimple.c:189)
    ==32129==    by 0x715E3B: zend_objects_store_del_ref_by_handle_ex (zend_objects_API.c:226)
    ==32129==    by 0x715E62: zend_objects_store_del_ref (zend_objects_API.c:178)
    ==32129==    by 0x6DDEBF: _zval_ptr_dtor (zend_variables.h:35)
    ==32129==    by 0x6FA334: zend_hash_apply_deleter (zend_hash.c:650)
    ==32129==    by 0x6FBE78: zend_hash_reverse_apply (zend_hash.c:804)
    ==32129==    by 0x6DE290: shutdown_destructors (zend_execute_API.c:217)
    ==32129==    by 0x6EDD23: zend_call_destructors (zend.c:919)
    ==32129==    by 0x68E2A4: php_request_shutdown (main.c:1750)
    ==32129== 
    ==32129== Invalid read of size 4
    ==32129==    at 0x6DDE62: zval_call_destructor (zend_execute_API.c:203)
    ==32129==    by 0x6FBE63: zend_hash_reverse_apply (zend_hash.c:799)
    ==32129==    by 0x6DE290: shutdown_destructors (zend_execute_API.c:217)
    ==32129==    by 0x6EDD23: zend_call_destructors (zend.c:919)
    ==32129==    by 0x68E2A4: php_request_shutdown (main.c:1750)
    ==32129==    by 0x79EF49: do_cli (php_cli.c:1177)
    ==32129==    by 0x461E0F: main (php_cli.c:1378)
    ==32129==  Address 0x77d5b70 is 16 bytes inside a block of size 32 free'd
    ==32129==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==32129==    by 0x750C508: pimple_bucket_dtor (pimple.c:490)
    ==32129==    by 0x6FB977: zend_hash_destroy (zend_hash.c:560)
    ==32129==    by 0x750CEE1: pimple_free_object_storage (pimple.c:189)
    ==32129==    by 0x715E3B: zend_objects_store_del_ref_by_handle_ex (zend_objects_API.c:226)
    ==32129==    by 0x715E62: zend_objects_store_del_ref (zend_objects_API.c:178)
    ==32129==    by 0x6DDEBF: _zval_ptr_dtor (zend_variables.h:35)
    ==32129==    by 0x6FA334: zend_hash_apply_deleter (zend_hash.c:650)
    ==32129==    by 0x6FBE78: zend_hash_reverse_apply (zend_hash.c:804)
    ==32129==    by 0x6DE290: shutdown_destructors (zend_execute_API.c:217)
    ==32129==    by 0x6EDD23: zend_call_destructors (zend.c:919)
    ==32129==    by 0x68E2A4: php_request_shutdown (main.c:1750)
    
    
    opened by jbboehr 8
Owner
Silex
Silex
💎 Flexible, compiled and full-featured Dependency Injection Container with perfectly usable autowiring and support for all new PHP 7 features.

Nette Dependency Injection (DI) Introduction Purpose of the Dependecy Injection (DI) is to free classes from the responsibility for obtaining objects

Nette Foundation 781 Dec 15, 2022
PSR-11 compatible Dependency Injection Container for PHP.

bitexpert/disco This package provides a PSR-11 compatible, annotation-based dependency injection container. Have a look at the disco-demos project to

bitExpert AG 137 Sep 29, 2022
🚀 PHP Service Container with fast and cachable dependency injection.

ClanCats Container A PHP Service Container featuring a simple meta-language with fast and compilable dependency injection. Requires PHP >= 7.0 Pros: M

ClanCats 28 Apr 13, 2022
Twittee is the smallest, and still useful, Dependency Injection Container in PHP

What is Twittee? Twittee is the smallest, and still useful, Dependency Injection Container in PHP; it is also probably one of the first public softwar

null 133 Dec 5, 2022
The dependency injection container for humans

layout home PHP-DI is a dependency injection container meant to be practical, powerful, and framework-agnostic. Read more on the website: php-di.org G

null 2.4k Jan 4, 2023
Dependency Injection System

Aura.Di A serializable dependency injection container with constructor and setter injection, interface and trait awareness, configuration inheritance,

Aura for PHP 342 Dec 1, 2022
Yii Dependency Injection PSR-11 compatible

Yii Dependency Injection PSR-11 compatible dependency injection container that is able to instantiate and configure classes resolving dependencies. Fe

Yii Software 161 Nov 10, 2022
Dependency Manager for PHP

Composer - Dependency Management for PHP Composer helps you declare, manage, and install dependencies of PHP projects. See https://getcomposer.org/ fo

Composer 27.2k Dec 31, 2022
IoC Dependency Injector

auryn 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 Am

Daniel Lowrey 725 Nov 23, 2022
IoC Dependency Injector

auryn 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 Am

Daniel Lowrey 710 Apr 15, 2021
DI Container (PSR-11)

My DI Container It's my own implementation PSR-11 Container Interface. Installation composer require scruwi/container Init $container = new Container(

null 4 Mar 15, 2022
This repository holds all interfaces related to PSR-11 (Container Interface).

Container interface This repository holds all interfaces related to PSR-11 (Container Interface). Note that this is not a Container implementation of

PHP-FIG 9.6k Jan 4, 2023
Extensible DI container for Symfony2

This package contains an implementation of the Symfony 2 DI container that can be extended using other DI containers (from other frameworks).

TheCodingMachine 1 Apr 2, 2014
Adapters for PHP framework containers to an interoperable interface

Acclimate - Container Adapters Get Acclimated! Use any third-party dependency injection containers and service locators in your code by adapting them

Acclimate Container 215 Dec 16, 2022
Small but powerful dependency injection container

Container (Dependency Injection) This package is compliant with PSR-1, PSR-2, PSR-4 and PSR-11. If you notice compliance oversights, please send a pat

The League of Extraordinary Packages 779 Dec 30, 2022
💎 Flexible, compiled and full-featured Dependency Injection Container with perfectly usable autowiring and support for all new PHP 7 features.

Nette Dependency Injection (DI) Introduction Purpose of the Dependecy Injection (DI) is to free classes from the responsibility for obtaining objects

Nette Foundation 781 Dec 15, 2022
PSR-11 compatible Dependency Injection Container for PHP.

bitexpert/disco This package provides a PSR-11 compatible, annotation-based dependency injection container. Have a look at the disco-demos project to

bitExpert AG 137 Sep 29, 2022
🚀 PHP Service Container with fast and cachable dependency injection.

ClanCats Container A PHP Service Container featuring a simple meta-language with fast and compilable dependency injection. Requires PHP >= 7.0 Pros: M

ClanCats 28 Apr 13, 2022
Twittee is the smallest, and still useful, Dependency Injection Container in PHP

What is Twittee? Twittee is the smallest, and still useful, Dependency Injection Container in PHP; it is also probably one of the first public softwar

null 133 Dec 5, 2022
The dependency injection container for humans

layout home PHP-DI is a dependency injection container meant to be practical, powerful, and framework-agnostic. Read more on the website: php-di.org G

null 2.4k Jan 4, 2023