- 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
- PHP 5.3 or higher.
Include njasm\container
in your project, by adding it to your composer.json
file.
{
"require": {
"njasm/container": "~1.0"
}
}
To create a container, simply instantiate the Container
class.
use Njasm\Container\Container;
$container = new Container();
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');
Services are defined with two params. A key
and a value
.
The order you define your services is irrelevant.
$container->set("Username", "John");
echo $container->get("Username");
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
$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))
);
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"));
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");
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
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
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"))
);
In no Particular order - check Milestones for a more organized picture.
- Load definitions from configuration files
- Optimizations
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.