{"id":16728928,"url":"https://github.com/lucatume/di52","last_synced_at":"2025-04-06T19:13:15.216Z","repository":{"id":25525284,"uuid":"28957454","full_name":"lucatume/di52","owner":"lucatume","description":"The dependency injection container for streamlined WordPress development. Easily manage and inject dependencies for efficient and hassle-free app creation.","archived":false,"fork":false,"pushed_at":"2024-04-26T14:46:34.000Z","size":3087,"stargazers_count":59,"open_issues_count":0,"forks_count":12,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-13T23:12:28.295Z","etag":null,"topics":["dependency-injection","php","wordpress"],"latest_commit_sha":null,"homepage":"http://theaveragedev.com","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lucatume.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-01-08T08:56:58.000Z","updated_at":"2024-09-19T16:21:12.000Z","dependencies_parsed_at":"2024-04-02T09:45:21.998Z","dependency_job_id":null,"html_url":"https://github.com/lucatume/di52","commit_stats":{"total_commits":503,"total_committers":10,"mean_commits":50.3,"dds":0.1908548707753479,"last_synced_commit":"2ad290de6143a26bfbc61ee0eb0dce10edf09dd6"},"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucatume%2Fdi52","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucatume%2Fdi52/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucatume%2Fdi52/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucatume%2Fdi52/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lucatume","download_url":"https://codeload.github.com/lucatume/di52/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247535520,"owners_count":20954576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","php","wordpress"],"created_at":"2024-10-12T23:12:27.550Z","updated_at":"2025-04-06T19:13:15.196Z","avatar_url":"https://github.com/lucatume.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"A PHP 5.6+ compatible dependency injection container inspired\nby [Laravel IOC](https://laravel.com/docs/5.0/container \"Service Container - Laravel - The PHP Framework For Web Artisans\")\nand [Pimple](http://pimple.sensiolabs.org/ \"Pimple - A simple PHP Dependency Injection Container\") that works even\nbetter on newer version of PHP.\n\nA quick overview of the Container features:\n\n* **Auto-wiring** - The Container will use Reflection to find what class should be built and how, it's _almost_ magic.\n* **Flexible** - Legacy code? Fat constructors with tons of side-effects? The Container offers auto-wiring without\n  making it a requirement. It will adapt to existing code and will not require your code to adapt to it.\n* **Fatal error handling** - On PHP 7.0+ the Container will take care of handling fatal errors that might happen at\n  class file load time and handle them.\n* **Fast** - The Container is optimized for speed as much as it can be squeezed out of the required PHP compatibility.\n* **Flexible default mode** - Singleton (build at most once) an prototype (build new each time) default modes available.\n* **Global Application** - Like using `App::get($service)-\u003edoStuff()`? The `App` facade allows using the DI Container as\n  a globally available Service Locator.\n* **PSR-11 compatible** - The container is fully compatible\n  with [PSR-11 specification](https://www.php-fig.org/psr/psr-11/).\n* **Ready for WordPress and other Event-driven frameworks** - The container API provides methods\n  like [`callback`](#the-callback-method) and [`instance`](#the-instance-method) to easily be integrated with\n  Event-driven frameworks like WordPress that require hooking callbacks to events.\n* **Service Providers** - To keep your code organized, the library provides\n  an [advanced Service Provider implementation](#service-providers).\n\n## Table of Contents\n\n- [Code Example](#code-example)\n- [Installation](#installation)\n- [Upgrading from version 2 to version 3](#upgrading-from-version-2-to-version-3)\n- [Upgrading from version 3.2 to version 3.3](#upgrading-from-version-32-to-version-33)\n- [Quick and dirty introduction to dependency injection](#quick-and-dirty-introduction-to-dependency-injection)\n  * [What is dependency injection?](#what-is-dependency-injection-)\n  * [What is a DI container?](#what-is-a-di-container-)\n  * [What is a Service Locator?](#what-is-a-service-locator-)\n  * [Construction templates](#construction-templates)\n- [The power of `get`](#the-power-of--get-)\n- [Storing variables](#storing-variables)\n- [Binding implementations](#binding-implementations)\n- [Binding implementations to slugs](#binding-implementations-to-slugs)\n- [Contextual binding](#contextual-binding)\n- [Binding decorator chains](#binding-decorator-chains)\n- [Tagging](#tagging)\n- [The callback method](#the-callback-method)\n- [Service providers](#service-providers)\n  * [Booting service providers](#booting-service-providers)\n  * [Deferred service providers](#deferred-service-providers)\n  * [Dependency injection with service providers](#dependency-injection-with-service-providers)\n- [Customizing the container](#customizing-the-container)\n  * [Unbound classes resolution](#unbound-classes-resolution)\n  * [Exception masking](#exception-masking)\n\n## Code Example\n\nIn the application bootstrap file we define how the components will come together:\n\n```php\n\u003c?php\n/**\n * The application bootstrap file: here the container is provided the minimal set of instructions\n * required to set up the application objects.\n */\n\nnamespace lucatume\\DI52\\Example1;\n\nuse lucatume\\DI52\\App;\nuse lucatume\\DI52\\Container;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n// Start by building an instance of the DI container.\n$container = new Container();\n\n// When an instance of `TemplateInterface` is required, build and return an instance\n// of `PlainPHPTemplate`; build at most once (singleton).\n$container-\u003esingleton(\n    TemplateInterface::class,\n    static function () {\n        return new PlainPHPTemplate(__DIR__ . '/templates');\n    }\n);\n\n// The default application Repository is the Posts one.\n// When a class needs an instance of the `RepositoryInterface`, then\n// return an instance of the `PostsRepository` class.\n$container-\u003ebind(RepositoryInterface::class, PostsRepository::class);\n\n// But the Users page should use the Users repository.\n$container-\u003ewhen(UsersPageRequest::class)\n    -\u003eneeds(RepositoryInterface::class)\n    -\u003egive(UsersRepository::class);\n\n// Bind primitive values, e.g. public function __construct( int $per_page ) {}\n$container-\u003ewhen(UsersPageRequest::class)\n    -\u003eneeds('$per_page')\n    -\u003egive(10);\n\n// Fetch the above class without any further definitions\n$container-\u003eget(UsersPageRequest::class)\n\n// The `UsersRepository` will require a `DbConnection` instance, that\n// should be built at most once (singleton).\n$container-\u003esingleton(DbConnection::class);\n\n// Set the routes.\n$container-\u003ebind('home', HomePageRequest::class);\n$container-\u003ebind('users', UsersPageRequest::class);\n\n// Make the container globally available as a service locator using the App.\nApp::setContainer($container);\n```\n\nIn the application entrypoint, the `index.php` file, we'll **lazily** resolve the whole dependency tree following the\nrules set in the bootstrap file:\n\n```php\n\u003c?php\nuse lucatume\\DI52\\App;\n\nrequire_once __DIR__ . '/../../vendor/autoload.php';\nrequire_once __DIR__ . '/bootstrap.php';\n\n$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);\n$route = basename(basename($path, '.php'), '.html') ?: 'home';\n\nApp::get($route)-\u003eserve(); ?\u003e\n?\u003e\n```\n\nThat's it.\n\n## Installation\nUse [Composer](https://getcomposer.org/) to require the library:\n\n```bash\ncomposer require lucatume/di52\n```\n\nInclude the [Composer](https://getcomposer.org/) autoload file in your project entry point and create a new instance of\nthe container to start using it:\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\n$container = new lucatume\\DI52\\Container();\n\n$container-\u003esingleton(DbInterface::class, MySqlDb::class);\n```\n\nIf you would prefer using the Dependency Injection Container as a globally-available Service Locator, then you\ncan use the `lucatume\\DI52\\App`:\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nlucatume\\DI52\\App::singleton(DbInterface::class, MySqlDb::class);\n```\n\nSee the [example above](#code-example) for more usage examples.\n\n## Upgrading from version 2 to version 3\n\nThe main change introduced by version `3.0.0` of the library is dropping compatibility with PHP 5.2 to require a minimum\nversion of PHP 5.6. The library is tested up to PHP 8.1.\n\nIf you're using version 2 of DI52 in your project, then there _should_ be nothing you need to do.\nThe new, namespaced, classes of version 3 are aliased to their version 2 correspondent, e.g. `tad_DI52_Container` is\naliased to `lucatume\\di52\\Container` and `tad_DI52_ServiceProvider` is aliased to `lucatume\\di52\\ServiceProvider`.\n\nI suggest an update for **a small performance gain**, though, to use the new, namespaced, class names in place of the\nPHP 5.2\ncompatible ones:\n\n* replace uses of `tad_DI52_Container` with `lucatume\\di52\\Container`\n* replace uses of `tad_DI52_ServiceProvider` with `lucatume\\DI52\\ServiceProvider`\n\nThe new version implemented [PSR-11](https://www.php-fig.org/psr/psr-11/) compatibility and the main method to get hold\nof an object instance from the container changed from `make` to `get`.\nDo not worry, the `lucatume\\di52\\Container::make` method is still there: it's just an alias of\nthe `lucatume\\di52\\Container::get` one.\nFor another small performance gain replace uses of `tad_DI52_Container::make` with `lucatume\\di52\\Container::get`.\n\nThat should be all of it.\n\n## Upgrading from version 3.2 to version 3.3\n\nVersion 3.3.0 of the library removed the `aliases.php` file, which previously helped to load non-PSR namespaced class names.\nHowever, if you're using the `tad_DI52_Container` and `tad_DI52_ServiceProvider` classes in your project, you can set up the aliases by adding a few lines of code to your project's bootstrap file to ensure your code continues to work as expected:\n\n```php\n\u003c?php\n\n$aliases = [\n    ['lucatume\\DI52\\Container', 'tad_DI52_Container'],\n    ['lucatume\\DI52\\ServiceProvider', 'tad_DI52_ServiceProvider']\n];\nforeach ($aliases as list($class, $alias)) {\n    if (!class_exists($alias)) {\n        class_alias($class, $alias);\n    }\n}\n```\n\n## Quick and dirty introduction to dependency injection\n\n### What is dependency injection?\n\nA [Dependency Injection (DI) Container](https://en.wikipedia.org/wiki/Dependency_injection \"Dependency injection - Wikipedia\")\nis a tool meant to make dependency injection possible and easy to manage.\nDependencies are specified by a class constructor method via\n[**type-hinting**](http://php.net/manual/en/language.oop5.typehinting.php \"PHP: Type Hinting - Manual\"):\n\n```php\nclass A {\n    private $b;\n    private $c;\n\n    public function __construct(B $b, C $c){\n        $this-\u003eb = $b;\n        $this-\u003ec = $c;\n    }\n}\n```\n\nAny instance of class `A` **depends** on implementations of the `B` and `C` classes.\nThe \"injection\" happens when class `A` dependencies are passed to it, \"injected\" in its constructor method, in place of\nbeing created inside the class itself.\n\n```php\n$a = new A(new B(), new C());\n```\n\nThe flexibility of type hinting allows injecting into `A` not just instances of `B` and `C` but instances of any class\nextending the two:\n\n```php\nclass ExtendedB extends B {}\n\nclass ExtendedC extends C {}\n\n$a = new a(new ExtendedB(), new ExtendedC());\n```\n\nPHP allows type hinting not just **concrete implementations** (classes) but **interfaces** too:\n\n```php\nclass A {\n    private $b;\n    private $c;\n\n    public function __construct(BInterface $b, CInterface $c){\n        $this-\u003eb = $b;\n        $this-\u003ec = $c;\n    }\n}\n```\n\nThis extends the possibilities of dependency injection even further and avoids strict coupling of the code:\n\n```php\nclass B implements BInterface {}\n\nclass C implements CInterface {}\n\n$a = new a(new B(), new C());\n```\n\n### What is a DI container?\n\nThe `B` and `C` classes are concrete (as in \"you can instance them\") implementations of interfaces and while the\ninterfaces might never change the implementations might and should change in the lifecycle of code: that's\nthe [Dependency Inversion principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle) or \"depend upon\nabstractions, non concretions\".\nIf the implementation of `BInterface` changes from `B` to `BetterB` then I'd have to update all the code where I'm\nbuilding instances of `A` to use `BetterB` in place of `B`:\n\n```php\n\n// before\n$a = new A(new B(), new C());\n\n//after\n$a = new A(new BetterB(), new C());\n```\n\nOn smaller code-bases this might prove to be a quick solution but, as the code grows, it will become less and less an\napplicable solution.\nAdding classes to the mix proves the point when dependencies start to stack:\n\n```php\nclass D implements DInterface{\n    public function __construct(AInterface $a, CInterface $c){}\n}\n\nclass E {\n    public function __construct(DInterface $d){}\n}\n\n$a = new A (new BetterB(), new C());\n$d = new D($a, $c);\n$e = new E($d);\n```\n\nAnother issue with this approach is that classes have to be built immediately to be injected, see `$a` and `$d` above to\nfeed `$e`, with the immediate cost of \"eager\" instantiation, if `$e` is never used than the effort put into building it,\nin terms of time and resources spent by PHP to build `$a`, `$b`, `$c`, `$d` and finally `$e`, are wasted.\nA **dependency injection container**  will take care of building only objects that are needed taking care of\n**resolving** nested dependencies.\n\n\u003e Need an instance of `E`? I will build instances of `B` and `C` to build an instance of `A` to build an instance of `D`\n to finally build and return an instance of `E`.\n\n### What is a Service Locator?\n\nA \"Service Locator\" is an object, or function, that will answer to this question made by your code:\n\n```php\n$database = $serviceLocator-\u003eget('database');\n```\n\nIn Plain English \"I do not care how it's built or where it comes from, give me the current implementation of the\ndatabase service.\".\n\nService Locators are, usually, globally-available DI Containers for obvious reasons: the DI Container knows how to build\nthe services the Service Locator will provide when required.\nThe concept of Service Locators and DI Containers are often conflated as a DI Container, when globally available,\nmakes a good implementation of a Service Locator.\n\nAn example of this is the `lucatume\\DI52\\App` class: it will expose, by means of static methods, a globally-available\ninstance of the `lucatume\\DI52\\Container` class.\n\n```php\n\u003c?php\nuse lucatume\\DI52\\Container;\nuse lucatume\\DI52\\App;\n\n// This is a DI Container.\n$diContainer = new Container();\n\n// If we make it globally-available, then it will be used by the Service Locator (the `App` class).\nApp::setContainer($diContainer);\n\n// Register a binding in the DI Container.\n$diContainer-\u003esingleton('database', MySqlDb::class);\n\n// We can now globally, i.e. anywhere in the code, access the `db` service.\n$db = App::get('database');\n```\n\nSince the `lucatume\\DI52\\App` class proxies calls to the Container, the example could be made shorter:\n\n```php\n\u003c?php\nuse lucatume\\DI52\\App;\n\n// Register a binding in the App (Service Locator).\nApp::singleton('database', MySqlDb::class);\n\n// We can now globally, i.e. anywhere in the code, access the `db` service.\n$db = App::get('database');\n```\n\n### Construction templates\n\nThe container will need to be told, just once, how objects should be built.\nFor the container it's easy to understand that a class type-hinting an instance of the concrete class `A` will require a\nnew instance of `A` but loosely coupled code leveraging the use of a DI container will probably type-hint an `interface`\nin place of concrete `class`es.\nTelling the container what concrete `class` to instance when a certain `interface` is requested by an\nobject `__construct` method is called \"binding and implementation to an interface\".\nWhile dependency injection can be made in other methods too beyond the `__construct` one that's what di52 supports at\nthe moment; if you want to read more the web is full of good reference\nmaterial, [this article by Fabien Potencier](http://fabien.potencier.org/what-is-dependency-injection.html) is a very\ngood start.\n\n## The power of `get`\n\nAt its base the container is a dependency resolution and injection machine: given a class to its `get` method it will\nread the class type-hinted dependencies, build them and inject them in the class.\n\n```php\n// file ClassThree.php\nclass ClassThree {\n    private $one;\n    private $two;\n\n    public function __construct(ClassOne $one, ClassTwo $two){\n        $this-\u003eone = $one;\n        $this-\u003etwo = $two;\n    }\n}\n\n// The application bootstrap file\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$three = $container-\u003eget('ClassThree');\n```\n\nKeep that in mind while reading the following paragraphs.\n\n## Storing variables\n\nIn its most basic use case the container can store variables:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003esetVar('number', 23);\n\n$number = $container-\u003egetVar('number');\n```\n\nSince the container will treat any callable object as a factory (see below) callables have to be protected using the\ncontainer `protect` method:\n\n```php\n$container = new tad_DI52_Container();\n\n$container-\u003esetVar('randomNumberGenerator', $container-\u003eprotect(function($val){\n    return mt_rand(1,100) + 23;\n}));\n\n$randomNumberGenerator = $container-\u003egetVar('randomNumberGenerator');\n```\n\nThe protect method tells the container that, when `get`ting the `randomNumberGenerator` alias, we do not want to run the function and\nget its result, but we want to get back the function itself.\n\n## Binding implementations\n\nTelling the container what should be built and when is done by an API similar to the one exposed by the [Laravel Service container](https://laravel.com/docs/5.3/container \"Service Container - Laravel - The PHP Framework For Web ...\")\nand while the inner workings are different the good idea (kudos to Laravel's creator and maintainers) is reused.\nReusing the example above:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n// Bind to a class name.\n$container-\u003ebind(AInterface::class, A::class);\n// Bind to a Closure.\n$container-\u003ebind(BInterface::class, function(){\n    return new BetterB();\n});\n// Bind to a constructor and methods that should be called on the built object.\n$container-\u003ebind(CInterface::class, LegacyC::class, ['init','register']);\n// Bind to a factory method.\n$container-\u003ebind(D::interface, [DFactory::class,'buildInstance'])\n// Bind to an object, it will be a singleton by default.\n$container-\u003ebind(E::interface, new EImplementation());\n\n$e = $container-\u003eget(F::class);\n```\n\nThe `get` method will build the `F` object resolving its requirements to the bound implementations when requested.\nWhen using the `bind` method a new instance of the bound implementations will be returned on each request; this might\nnot be the wanted behaviour especially for object costly to build (like a database driver that needs to connect): in\nthat case the `singleton` method should be used:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003esingleton(DBDriverInterface::class, MYSqlDriver::class);\n$container-\u003esingleton(RepositoryInterface::class, MYSQLRepository::class);\n\n$container-\u003eget(RepositoryInterface::class);\n```\n\nBinding an implementation to an interface using the `singleton` methods tells the container the implementations should\nbe built just the first time: any later call for that same interface should return the same instance.\nImplementations can be redefined in any moment simple calling the `bind` or `singleton` methods again specifying a\ndifferent implementation.\n\nYou can customize how unbound classes are resolved by the container, check the [unbound classes](#unbound-classes-resolution) section.\n\n## Binding implementations to slugs\n\nThe container was heavily inspired\nby [Pimple](http://pimple.sensiolabs.org/ \"Pimple - A simple PHP Dependency Injection Container\") and offers some\nfeatures of the PHP 5.3+ DI container as well:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n// Storing vars using the ArrayAccess API.\n$container['db.name'] = 'appDb';\n$container['db.user'] = 'root';\n$container['db.pass'] = 'secret';\n$container['db.host'] = 'localhost:3306';\n\n// Bindings can be set using ArrayAccess methods.\n$container['db.driver'] = MYSQLDriver::class;\n\n// Bound closures will receive the container instance as argument.\n$container['db.connection'] = function($container){\n    $host = $container['db.host']\n    $user = $container['db.user'],\n    $pass = $container['db.pass'],\n    $name = $container['db.name'],\n\n    $dbDriver = $container['db.driver'];\n    $dbDriver-\u003econnect($host, $user, $pass, $name);\n\n    return new DBConnection($dbDriver);\n};\n\n// Equivalent to $container-\u003eget('db.connection');\n$dbConnection = $container['db.connection'];\n\n// Using ArrayAccess API to store a closure as a variable.\n$container['uniqid'] = $container-\u003eprotect(function(){\n    return uniqid('id', true);\n});\n```\n\nThere is no replacement for the `factory` method offered by Pimple: the `bind` method should be used instead.\n\n## Contextual binding\n\nBorrowing an excellent idea from Laravel's container the possibility of contextual binding exists (supporting all the\nbinding possibilities above).\nContextual binding solves the problem of different objects requiring different implementations of the same interface (or\nclass, see above):\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n/*\n * By default any object requiring an implementation of the `CacheInterface`\n * should be given the same instance of `Array Cache`\n */\n$container-\u003esingleton(CacheInterface::class, ArrayCache::class);\n\n$container-\u003ebind(DbCache::class, function($container){\n    $cache = $container-\u003eget(CacheInterface::class);\n    $dbCache = new DbCache($cache);\n\n    return $dbCache;\n});\n\n/*\n * But when an implementation of the `CacheInterface` is requested by\n * `TransactionManager`, then it should be given an instance of `Array Cache`.\n */\n$container-\u003ewhen(TransactionManager::class)\n    -\u003eneeds(CacheInterface::class)\n    -\u003egive(DbCache::class);\n\n/*\n * We can also bind primitives where the container doesn't know how to auto-wire\n * them.\n */\n$container-\u003ewhen(MysqlOrm:class)\n    -\u003eneeds('$dbUrl')\n    -\u003egive('mysql://user:password@127.0.0.1:3306/app');\n\n/*\n * When primitives are bound to a class the container will correctly resolve them when building the class\n * bound to an interface.\n */\n$container-\u003ebind(ORMInterface::class, MysqlOrm::class);\n\n// The `ORMInterface` will be resolved an instance of the `MysqlOrm` class, with the `$dbUrl` argument set correctly.\n$orm = $container-\u003eget(ORMInterface::class);\n```\n\n## Binding decorator chains\n\nThe [Decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern \"Decorator pattern - Wikipedia\") allows\nextending the functionalities of an implementation without creating an extension and leveraging interfaces.\nThe container allows binding \"chain of decorators\" to an interface (or slug à la Pimple, or class) using\nthe `bindDecorators` and `singletonDecorators`.\nThe two methods are the `bind` and `singleton` equivalents for decorators.\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003ebind(RepositoryInterface::class, PostRepository::class);\n$container-\u003ebind(CacheInterface::class, ArrayCache::class);\n$container-\u003ebind(LoggerInterface::class, FileLogger::class);\n// Decorators are built left to right, outer decorators are listed first.\n$container-\u003ebindDecorators(PostEndpoint::class, [\n    LoggingEndpoint::class,\n    CachingEndpoint::class,\n    BaseEndpoint::class\n]);\n```\n\nSimilarly to a `bind` or `singleton` call, you can specify a set of methods to call after the decorator chain is built\nwith the `afterBuildMethods` parameter:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003ebind(RepositoryInterface::class, PostRepository::class);\n$container-\u003ebind(CacheInterface::class, ArrayCache::class);\n$container-\u003ebind(LoggerInterface::class, FileLogger::class);\n// Decorators are built left to right, outer decorators are listed first.\n$container-\u003ebindDecorators(PostEndpoint::class, [\n    LoggingEndpoint::class,\n    CachingEndpoint::class,\n    BaseEndpoint::class\n], ['register']);\n```\n\nBy default, the `register` method will be called **only on the base instance**, the one on the right of the decorator chain.  \nIn the example above only `BaseEndpoint::register` would be called.  \n\nIf you need to call the same set of after-build methods on all instances after each is build, you can set the value of \nthe `afterBuildAll` parameter to `true`:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003ebind(RepositoryInterface::class, PostRepository::class);\n$container-\u003ebind(CacheInterface::class, ArrayCache::class);\n$container-\u003ebind(LoggerInterface::class, FileLogger::class);\n// Decorators are built left to right, outer decorators are listed first.\n$container-\u003ebindDecorators(PostEndpoint::class, [\n    LoggingEndpoint::class,\n    CachingEndpoint::class,\n    BaseEndpoint::class\n], ['register'], true);\n```\n\nIn this example the `register` method will be called on the `BaseEndpoint` after it's built, then on the\n`CachingEndpoint` instance after it's built, and finally on the `LoggingEndpoint` instance after it's built.  \n\nDifferent  and more complex combinations of decorators and after-build methods should be handled binding, with a\n`bind` or `singleton` call, a Closure to build the decorator chain.\n\n## Tagging\n\nTagging allows grouping similar implementations for the purpose of referencing them by group.\nGrouping implementations makes sense when, as an example, the same method has to be called on each implementation:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003ebind(UnsupportedEndpoint::class, function($container){\n    $template = '404';\n    $message = 'Nope';\n    $redirectAfter = 3;\n    $redirectTo = $container-\u003eget(HomeEndpoint::class);\n\n    return new UnsupportedEndpoint($template, $message, $redirectAfter, $redirectTo);\n});\n\n$container-\u003etag([\n    HomeEndpoint::class,\n    PostEndpoint::class,\n    UnsupportedEndpoint::class,\n    ], 'endpoints');\n\nforeach($container-\u003etagged('endpoints') as $endpoint) {\n    $endpoint-\u003eregister();\n}\n```\n\nThe `tag` method supports any possibility offered by the container in terms of binding of objects, closures, decorator\nchains and after-build methods.\n\n## The callback method\n\nSome applications require callbacks (or some form of callable) to be returned in specific pieces of code.\nThis is especially the case with WordPress and\nits [event-based architecture](https://codex.wordpress.org/Plugin_API/Filter_Reference \"Plugin API/Filter Reference « WordPress Codex\")\n.\nUsing the container does not removes that possibility:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\nadd_filter('some_filter', [$container-\u003eget(SomeFilteringClass::class), 'filter']);\n```\n\nThis code suffers from an eager instantiation problem: `SomeFilteringClass` is built for the purpose of binding it but\nmight never be used.\nThe problem is easy to solve using the `Container::callback` method:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n$container-\u003esingleton(SomeFilteringClass::class);\n\nadd_filter('some_filter', $container-\u003ecallback(SomeFilteringClass::class, 'filter'));\n```\n\nThe advantage of this solution is the container will return the same callback every time it's called with the same\narguments when the called class is a singleton:\n\n```php\n// Some code later we need to remove the filter: we'll get the same callback.\nremove_filter('some_filter', App::callback(SomeFilteringClass::class, 'filter'));\n```\n\n## Service providers\n\nTo avoid passing the container instance around (\nsee [Service Locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern \"Service locator pattern - Wikipedia\"))\nor globalising it all the binding should happen in the same PHP file: this could lead, as the application grows, to a\nthousand lines monster.\nTo avoid that the container supports service providers: those are classes extending\nthe `lucatume\\DI52\\ServiceProvider` class, that\nallow organizing the binding registrations into logical, self-contained and manageable units:\n\n```php\nuse lucatume\\DI52\\ServiceProvider;\n\n// file ProviderOne.php\nclass ProviderOne extends ServiceProvider {\n    public function register() {\n        $this-\u003econtainer-\u003ebind(InterfaceOne::class, ClassOne::class);\n        $this-\u003econtainer-\u003ebind(InterfaceTwo::class, ClassTwo::class);\n        $this-\u003econtainer-\u003esingleton(InterfaceThree::class, ClassThree::class);\n    }\n}\n\n// Application bootstrap file.\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003eregister(ProviderOne::class);\n$container-\u003eregister(ProviderTwo::class);\n$container-\u003eregister(ProviderThree::class);\n$container-\u003eregister(ProviderFour::class);\n```\n\n### Booting service providers\n\nThe container implements a `boot` method that will, in turn, call the `boot` method on any service provider that\noverloads it.\nSome applications might define constants and environment variables at \"boot\" time (e.g. WordPress `plugins_loaded`\naction) that might make an immediate registration futile.\nIn that case service providers can overload the `boot` method:\n\n```php\n// file ProviderOne.php\n\nuse lucatume\\DI52\\ServiceProvider;\n\nclass ProviderOne extends ServiceProvider {\n    public function register() {\n        $this-\u003econtainer-\u003ebind(InterfaceOne::class, ClassOne::class);\n        $this-\u003econtainer-\u003ebind(InterfaceTwo::class, ClassTwo::class);\n        $this-\u003econtainer-\u003esingleton(InterfaceThree::class, ClassThree::class);\n    }\n\n    public function boot() {\n        if(defined('SOME_CONSTANT')) {\n            $this-\u003econtainer-\u003ebind(InterfaceFour::class, ClassFour::class);\n        } else {\n            $this-\u003econtainer-\u003ebind(InterfaceFour::class, AnotherClassFour::class);\n        }\n    }\n}\n\n// Application bootstrap file.\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003eregister(ProviderOne::class);\n$container-\u003eregister(ProviderTwo::class);\n$container-\u003eregister(ProviderThree::class);\n\n// Some code later ...\n$container-\u003eboot();\n```\n\n### Deferred service providers\n\nSometimes even just setting up the implementations might require such an up-front cost to make it undesirable unless\nit's needed.\nThis might happen with non-autoloading code that will require a tangle of files to load (and side load) to grab a simple\nclass instance.\nTo \"defer\" that cost service providers can overload the `deferred` property and the `provides` method:\n\n```php\n// file ProviderOne.php\n\nuse lucatume\\DI52\\ServiceProvider;\n\nclass ProviderOne extends ServiceProvider {\n    public $deferred = true;\n\n    public function provides() {\n        return array(LegacyClassOne::class, LegacyInterfaceTwo::class);\n    }\n\n    public function register() {\n        include_once('legacy-file-one.php')\n        include_once('legacy-file-two.php')\n\n        $db = new Db();\n\n        $details = $db-\u003egetDetails();\n\n        $this-\u003econtainer-\u003esingleton(LegacyClassOne::class, new LegacyClassOne($details));\n        $this-\u003econtainer-\u003ebind(LegacyInterfaceTwo::class, new LegacyClassTwo($details));\n    }\n}\n\n// Application bootstrap file\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n// The provider `register` method will not be called immediately...\n$container-\u003eregister(ProviderOne::class);\n\n// ...it will be called here as it provides the binding of `LegacyClassOne`\n$legacyOne = $container-\u003eget(LegacyClassOne::class);\n\n// Will not be called again here, done already.\n$legacyTwo = $container-\u003eget(LegacyInterfaceTwo::class);\n```\n\n### Dependency injection with service providers\n\nThe container supports additional dependency injection for service providers (version 3.0.3+). Auto-wiring\nwill work the same as any class, simply override the service provider's constructor and add any additional concrete dependencies (don't forget to call the parent!):\n\n```php\n// file ProviderOne.php\n\nuse lucatume\\DI52\\ServiceProvider;\n\nclass ProviderOne extends ServiceProvider {\n\n    /**\n     * @var ConfigHelper\n     */\n    protected $config;\n\n    public function __construct(\\lucatume\\DI52\\Container $container, ConfigHelper $config)\n    {\n        parent::__construct($container);\n\n        $this-\u003econfig = $config;\n    }\n\n    public function register()\n    {\n        $this-\u003econtainer-\u003ewhen(ClassFour::class)\n            -\u003eneeds('$value')\n            -\u003egive($this-\u003econfig-\u003eget('value'));\n    }\n\n}\n\n// Application bootstrap file.\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003eregister(ProviderOne::class);\n```\nIf you want to inject primitives into a service provider, you need to utilize the `when`, `needs`, `give` methods **_before_** registering the provider in the container:\n\n```php\n// file ProviderOne.php\n\nuse lucatume\\DI52\\ServiceProvider;\n\nclass ProviderOne extends ServiceProvider {\n\n    /**\n     * @var bool\n     */\n    protected $service_enabled;\n\n    public function __construct(\\lucatume\\DI52\\Container $container, $service_enabled)\n    {\n        parent::__construct($container);\n\n        $this-\u003eservice_enabled = $service_enabled;\n    }\n\n    public function register()\n    {\n        if (!$this-\u003eservice_enabled) {\n            return;\n        }\n\n        $this-\u003econtainer-\u003ebind(InterfaceOne::class, ClassOne::class);\n    }\n\n}\n\n// Application bootstrap file.\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n$container-\u003ewhen(ProviderOne::class)\n    -\u003eneeds('$service_enabled')\n    -\u003egive(true);\n\n$container-\u003eregister(ProviderOne::class);\n```\n\n## Customizing the container\n\nThe container will be built with some opinionated defaults; those are not set in stone and you can customize the\ncontainer to your needs.\n\n### Unbound classes resolution\nThe container will use reflection to work out the dependencies of an object, and will not require setup when resolving\nobjects with type-hinted object dependencies in the `__construct` method.\nBy default those _unbound_ classes will be resolved **as prototypes**, built new on **each** `get` request.\n\nTo control the mode used to resolve unbound classes, a flag property can be set on the container when constructing it:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container1 = new Container();\n$container2 = new Container(true);\n\n// Default resolution of unbound classes is prototype.\nassert($container1-\u003eget(A::class) !== $container1-\u003eget(A::class));\n// The second container will resolve unbound classes once, then store them as singletons.\nassert($container2-\u003eget(A::class) === $container2-\u003eget(A::class));\n```\n\nThis will only apply to unbound classes! Whatever the flag used to build the container instance, the mode set in the\nbinding phase using `Container::bind()` or `Container::singleton()` methods will **always** be respected.\n\n### Exception masking\n\nBy default the container will catch any exception thrown during a service resolution and wrap into a `ContainerException`\ninstance.  \nThe container will modify the exception message and the trace file and line to provide information about the nested\nresolution tree and point your debug to the file and line that caused the issue.  \nYou can customize how the container will handle exceptions by using the `Container::setExceptionMask()` method:\n\n```php\nuse lucatume\\DI52\\Container;\n\n$container = new Container();\n\n// The container will throw any exception thrown during a service resolution without any modification.\n$container-\u003esetExceptionMask(Container::EXCEPTION_MASK_NONE);\n\n// Wrap any exception thrown during a service resolution in a `ContainerException` instance, modify the message.\n$container-\u003esetExceptionMask(Container::EXCEPTION_MASK_MESSAGE);\n\n// Wrap any exception thrown during a service resolution in a `ContainerException` instance, modify the trace file and line.\n$container-\u003esetExceptionMask(Container::EXCEPTION_MASK_FILE_LINE);\n\n// You can combine the options, this is the default value.\n$container-\u003esetExceptionMask(Container::EXCEPTION_MASK_MESSAGE | Container::EXCEPTION_MASK_FILE_LINE);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucatume%2Fdi52","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucatume%2Fdi52","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucatume%2Fdi52/lists"}