njasm / container
Dependency Container for PHP
Installs: 49 886
Dependents: 1
Suggesters: 0
Security: 0
Stars: 20
Watchers: 1
Forks: 3
Open Issues: 4
Requires
- php: >=5.3
- container-interop/container-interop: ~1.0
Requires (Dev)
- aura/di: ~1.0
- illuminate/container: ~4.2
- joomla/di: ~1.2
- mnapoli/php-di: ~4.0
- nette/di: ~2.2
- orno/di: ~2.0
- phpunit/phpunit: 4.4.*@dev
- pimple/pimple: ~3.0
- ray/di: ~1.4
- satooshi/php-coveralls: dev-master
- symfony/dependency-injection: ~2.5
- zendframework/zend-di: ~2.0
This package is not auto-updated.
Last update: 2020-11-27 20:03:26 UTC
README
Dependency Container / Service Locator
Features
- Alias Service Keys support
- Circular Dependency guard
- Primitive data-types registration
- Automatic Constructor Dependency Resolution and Injection for non-registered Services
- Lazy and Eager instantiation approaches
- Lazy and Eager Instantiation Singleton services registration
- Support for public Setter injection/Method calls after service instantiation
- Support for public Properties/Attributes Injection after Service instantiation
- Ability to override existing dependency (Properties & Setters) declarations by supplying new ones when call to
Container::get
- Nested Providers/Containers support
- Comply with
Container-Interop
interfaces
Requirements
- PHP 5.3 or higher.
Installation
Include njasm\container
in your project, by adding it to your composer.json
file.
{ "require": { "njasm/container": "~1.0" } }
Usage
To create a container, simply instantiate the Container
class.
use Njasm\Container\Container; $container = new Container();
Using Alias
There are time that your key
are too long to be convenient for your client code, one example for instance,
is when binding an interface
to an implementation
or when using for your key
the FQCN of your classes.
namespace Very\Long\Name\Space; interface SomeInterface {} class SomeImplementation implements SomeInterface { // code here } $container = new Njasm\Container\Container(); $container->set('Very\Long\Name\Space\SomeInterface', new SomeImplementation()); $container->alias('Some', 'Very\Long\Name\Space\SomeInterface'); $some = $container->get('Some');
Defining Services
Services are defined with two params. A key
and a value
.
The order you define your services is irrelevant.
Defining Services - Primitive data-types
$container->set("Username", "John"); echo $container->get("Username");
Defining Services - Binding Services (Lazy Loading)
You can bind a key
to a instantiable FCQN value
.
$container->bind("MyKey", "\My\Namespace\SomeClass");
If you want to bind a Service, and register that Service as a Singleton
Service.
$container->bindSingleton("MyKey", "\My\Namespace\SomeClass");
Both Container::bind
and Container::bindSingleton
uses Lazy Loading approach,
so that \My\Namespace\SomeClass
will only be evaluated/instantiated when MyKey
is requested.
When binding a service, constructor dependencies can be declared, public attributes be set and methods called with arguments, so they are injected/setted when instantiating the service.
namespace \App\Actors; class Person { protected $name; protected $age = 24; public genre = 'Male'; public function __construct($name = 'John') { $this->name = $name; } public function getName() { return $this->name; } public function getAge() { return $this->age; } public function setAge($age) { $this->age = (int) $age; } } $container->bind( "Person", // key "\App\Actors\Person", // FQCN array("Jane"), // constructor dependencies array("genre" => "Female"), // attributes injection array("setAge" => array(33)) // call methods ); // binding with chaining methods $container->bind("Person", '\App\Actors\Person') ->setConstructorArguments(array("Jane")) // setConstructorArgument($index, $argument) ->setProperty("genre" => "Female") // setProperties(array("genre" => "Female", ...) also work ->callMethod("setAge", array(33)); // callMethods(array('methodName' => 'methodValue', ...)); // retrieving the object $person = $container->get("Person"); echo $person->getName(); // Jane echo $person->getAge(); // 33 echo $person->genre // Female // calling services and overriding declared dependencies $person2 = $container->get( "Person", array("Mark"), array("genre" => "Male"), array("setAge" => array(55)) ); echo $person2->getName(); // Mark echo $person2->getAge(); // 55 echo $person2->genre // Male
Defining Services - Objects (Eager Loading)
$mailer = new \Namespace\For\My\MailTransport( "smtp.example.com", "username", "password", 25 ); $container->set( "Mail.Transport", $mailer, array(), // constructor args array(), // public properties injection array("withSSL" => array(false)) // calling methods ); $mailerTransport = $container->get("Mail.Transport");
Overwriting existent declared dependencies is also possible for set
definitions.
// calling methods and injecting attributes is also possible $mailerTransportSsl = $container->get( "Mail.Transport", array(), array(), array("withSSL" => array(true)) );
Defining Services - Complex builds (Lazy Loading)
There are time when you'll want to instantiate an object, but the build process is reall complex and you want to control that process. You use anonymous functions for that.
$container->set( "Complex", function($firstName = "John", $lastName = "Doe") { // complex logic here // ... $theComplexObject = new Complex($firstName, $lastName); return $theComplexObject; } ); $complex = $container->get("Complex"); // injecting closure dependencies is also possible $complexJane = $container->get("Complex", array("Jane", "Fonda"));
Defining Services - Complex builds With Nested Dependencies (Lazy Loading)
Creation of nested dependencies is also possible. You just need to pass the container to the closure.
$container->set( "Mail.Transport", function() use (&$container) { return new \Namespace\For\My\MailTransport( $container->get("Mail.Transport.Config") ); } ); $container->set( "Mail.Transport.Config", function() { return new \Namespace\For\My\MailTransportConfig( "smtp.example.com", "username", "password", 25 ); } ); $mailer = $container->get("Mail.Transport");
Defining Singleton Services
For registering singleton services, you use the singleton method invocation.
$container->singleton( "Database.Connection", function() { return new \Namespace\For\My\Database( "mysql:host=example.com;port=3306;dbname=your_db", "username", "password" ); } ); // MyDatabase is instantiated and stored, for future requests to this service, // and then returned. $db = $container->get("Database.Connection"); $db2 = $container->get("Database.Connection"); // $db === $db2 TRUE
Defining Sub/Nested Containers
Nesting container is possible as long as you use an existing Container Adapter for your application existing container.
The Adapter class must implement the ServicesProviderInterface
for more examples please see the Adapter
folder.
$pimple; // is your instantiated pimple container $pimple["Name"] = $pimple->factory(function() { return "John"; } $pimpleAdapter = new \Njasm\Container\Adapter\PimpleAdapter($pimple); $mainContainer = new \Njasm\Container\Container(); $mainContainer->provider($pimpleAdapter); $mainContainer->has("Name"); // TRUE echo $mainContainer->get("Name"); // John
Automatic Resolution of Services
When the Container is requested for a service that is not registered, it will try to find the class, and will automatically try to resolve your class's constructor dependencies.
namespace My\Name\Space; class Something { // code } // without registering the Something class in the container you can... $container = new Njasm\Container\Container(); $something = $container->get('My\Name\Space\Something'); //$something instanceof 'My\Name\Space\Something' == true //once again you can also inject dependencies when calling get method. $something = $container->get( "My\Name\Space\Something", array("constructor value 1", "constructor value 2"), array("attributeName" => "value 1"), // attributes array("methodName" => array("value 1", "value 2")) );
Roadmap
In no Particular order - check Milestones for a more organized picture.
- Load definitions from configuration files
- Optimizations
Contributing
Do you wanna help on feature development/improving existing code through refactoring, etc? Or wanna discuss a feature/bug/idea? Issues and Pull Requests are welcome as long as you follow some guidelines for PRs:
Pull Requests must:
- Be PSR-2 compliant.
- Submit tests with your pull request to your own changes / new code introduction.
- having fun.