db80 / ovo-container
A lightweight Dependency Injection / Inversion of control Framework for PHP 5.3
Requires
- php: >=5.3.0
- doctrine/common: 2.3.0
This package is not auto-updated.
Last update: 2018-08-29 11:11:07 UTC
README
Ovo Container it's a lightweight Dependency Injection / Inversion of control Framework for PHP 5.3.
It is inspired by spring framework but it is pure php5.3 implementation.
Some of the best articles are:
- http://martinfowler.com/articles/injection.html
- http://www.theserverside.com/news/1321158/A-beginners-guide-to-Dependency-Injection
- http://www.vogella.de/articles/SpringDependencyInjection/article.html
Main Goals
- Separates behaviour from dependency resolution, thus decoupling highly dependent components.
- Easy unit-test and reusability of the code
- Lightweight: the "bean" can be any php class (No need to implement specific interface)
- Focuses your application only on the business logic
- Scope management. A single class can have different "beans" scope
- Singleton: Scopes a single bean definition to untill the application's request ending (default behavior).
- Prototype: Scopes a single bean definition to any number of object instances.
- Session: Scopes a single bean definition to the lifecycle of a HTTP Session (only in HTTP context).
- All the beans can be created at application loading or lazy created (the singleton beans are pre initialized by default)
- Minimal Configuration: XML file or Annotations
- INI file for the configuration params
- Factory, init and destroy methods
- PSR-0 compliant package.
- Example and unit tests (PHPUnit) are available inside the folder "/test".
How to install
####Using Composer
touch composer.json && echo '{"require": {"db80/ovo-container": "dev-master"},"minimum-stability": "dev"}' > composer.json composer.phar install --prefer-dist //include_once('vendor/autoload.php'); inside your application
####Clone the project, run the tests and build you own application
git clone https://github.com/db80/ovo-container.git && cd ovo-container/ composer.phar install --prefer-dist //the phpunit framework must be present in your classpath ./run-tests
Bean Example
The bean is the representation of a "simple php object" inside the dependency injection container. Each single bean can have different scopes with different names ( Singleton, Prototype or Session ). We can use the annotations or a XML file to configure our application.
Annotation style:
The annotations:
namespace Ovo\Container\Test\Annotation; use Ovo\Container\Annotation\Bean; /** * @Bean(name= "prototypeSimpleRandom", scope = "prototype"); * @Bean(name= "sessionSimpleRandom", scope = "session"); * @Bean(name= "singletonSimpleRandom"); */ final class SimpleRandom { private $randomId = null; public function __construct() { $this->randomId = rand(0, 1000); } public function getRandomId() { return $this->randomId; } }
Application Example:
use Ovo\Container\AnnotationContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new AnnotationContainer('path/to/beans/annotation/folder'); //get a singleton instance of SimpleBean $singletonSimpleRandomA = $container->getBean('singletonSimpleRandom'); $singletonIdA = $singletonSimpleRandomA->getRandomId() $singletonSimpleRandomB = $container->getBean('singletonSimpleRandom'); $singletonIdB = $singletonSimpleRandomB->getRandomId() //$singletonIdA == $singletonIdB //$singletonSimpleRandomA == $singletonSimpleRandomB //get a prototype instance of SimpleBean $prototypeSimpleRandomA = $container->getBean('prototypeSimpleRandom'); $prototypeIdA = $prototypeSimpleRandomA->getRandomId(); $prototypeSimpleRandomB = $container->getBean('prototypeSimpleRandom'); $prototypeIdB = $prototypeSimpleRandomB->getRandomId(); //$singletonSimpleRandom != $prototypeSimpleRandom //$prototypeIdA != $prototypeIdB
XML style:
The XML config file:
<?xml version="1.0" encoding="utf-8" ?> <config> <bean name="singletonSimpleRandom" class="Ovo\Container\Test\Annotation\SimpleRandom" /> <bean name="prototypeSimpleRandom" class="Ovo\Container\Test\Annotation\SimpleRandom" scope="prototype"/> <bean name="sessionSimpleRandom" class="Ovo\Container\Test\Annotation\SimpleRandom" scope="session"/> </config>
Application Example:
use Ovo\Container\XmlContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new XmlContainer('path/to/xml/config/file.xml'); //get a singleton instance of SimpleBean $singletonSimpleRandom = $container->getBean('singletonSimpleRandom'); $singletonSimpleRandom->getRandomId() //get a prototypeSimpleRandom instance of SimpleBean $prototypeSimpleRandom = $container->getBean('prototypeSimpleRandom'); // $singletonSimpleRandom != $prototypeSimpleRandom
Dependencies Injection
Each single bean can have different bean dependencies ( php objects dependencies ). The container must be aware of these dependencies to create the bean in the right way.
Annotation Sytle:
The annotations:
namespace Ovo\Container\Test\App; use Ovo\Container\Test\App\DataSource; use Ovo\Container\Test\App\ApplicationService; use Ovo\Container\Annotation\Bean; use Ovo\Container\Annotation\Dependency; /** * @Bean(name= "applicationService"); */ final class ApplicationServiceImpl implementes ApplicationService { /** * @Dependency(ref = "dataSource"); */ private $dataSource; private function __construct() { } public function getEmailByUserId($id) { return $this->dataSource->getUserById($id)->getEmail(); } //the setter is used to inject the dependency public function setDataSource(DataSource $dataSource) { $this->dataSource = $dataSource; } }
namespace Ovo\Container\Test\App; use Ovo\Container\Annotation\Bean; use Ovo\Container\Annotation\Param; /** * @Bean(name= "dataSource") */ final class DataSource { private $username; private $password; private $database; private $host; /** * @Param(type="string", name="my_username") * @Param(type="string", name="my_password") * @Param(type="string", name="my_database") * @Param(type="string", name="my_host") */ public function __construct($username, $password, $database, $host) { $this->username = $username; $this->password= $password; $this->database = $database; $this->host = $host; } public function getUserById($id) { //Business logic here } //other methods }
Application Example:
use Ovo\Container\AnnotationContainer; use Ovo\Container\Test\App\DataSource use Ovo\Container\Test\App\ApplicationService $container = new AnnotationContainer('path/to/beans/annotation/folder'); $applicationService = $container->getBean('applicationService'); $email = $applicationService->getEmailByUserId($id)
XML Sytle:
The XML config file:
<?xml version="1.0" encoding="utf-8" ?> <config> <!-- Application Service --> <bean name="applicationService" class="Ovo\Container\Test\App\ApplicationServiceImpl"> <properties> <value name="dataSource" ref="dataSource"/> </properties> </bean> <bean name="dataSource" class="Ovo\Container\Test\App\DataSource"> <construct> <value name="my_username" type="string"/> <value name="my_password" type="string"/> <value name="my_database" type="string"/> <value name="my_host" type="string"/> </construct> </bean> </config>
Application Example:
use Ovo\Container\XmlContainer; use Ovo\Container\Test\App\DataSource use Ovo\Container\Test\App\ApplicationService $container = new XmlContainer('path/to/xml/config/file.xml'); $applicationService = $container->getBean('applicationService'); $email = $applicationService->getEmailByUserId($id)
Ini file to store the application's properties
In real life, it's useful to store the application parameters, such as password or URL, in a separate file. Assume we have a file like the one below:
username = daniele password = pa$$w0rd host = localhost
The container can use a generic .ini file to bind the parameters of our application during the creation of the beans.
Annotation Sytle:
The annotations:
namespace Ovo\Container\Test\App; use Ovo\Container\Annotation\Bean; use Ovo\Container\Annotation\Param; /** * @Bean(name= "emailReader") */ final class EmailReader { private $username; private $password; private $host; /** * @Param(type="string", name="[$username]") * @Param(type="string", name="[$password]") * @Param(type="string", name="[$host]") */ public function __construct($username, $password, $host) { $this->username = $username; $this->password= $password; $this->host = $host; } public function checkNewEmails() { //business logic here } }
Application Example:
use Ovo\Container\AnnotationContainer; use Ovo\Container\Test\App\EmailReader use Ovo\Container\Test\App\ApplicationService $container = new AnnotationContainer('path/to/beans/annotation/folder', 'path/to/ini/config.ini'); $emailReader = $container->getBean('emailReader'); $emailReader->checkNewEmails();
XML Sytle:
The XML config file:
<?xml version="1.0" encoding="utf-8" ?> <config> <!-- Application Service --> <bean name="emailReader" class="Ovo\Container\Test\App\DataSource\EmailReader"> <construct> <value name="[$username]" type="string"/> <value name="[$password]" type="string"/> <value name="[$host]" type="string"/> </construct> </bean> </config>
Application Example:
use Ovo\Container\XmlContainer; use Ovo\Container\Test\App\EmailReader use Ovo\Container\Test\App\ApplicationService $container = new XmlContainer('path/to/xml/config/file.xml', 'path/to/ini/config.ini'); $emailReader = $container->getBean('emailReader'); $emailReader->checkNewEmails();
Factory method
The DI Container supports the creation of a bean through a static factory method.
Annotation style:
The annotations:
namespace Ovo\Container\Test\Annotation; use Ovo\Container\Annotation\Bean; use Ovo\Container\Annotation\FactoryMethod; /** * @Bean(name= "prototypeSimpleRandom", scope = "prototype"); */ final class SimpleRandom { private $randomId; private function __construct() { $this->randomId = rand(0, 1000); } /** * @FactoryMethod */ public static function getInstance() { return new self(); } public function getRandomId() { return $this->randomId; } }
Application Example:
use Ovo\Container\AnnotationContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new AnnotationContainer('path/to/beans/annotation/folder'); //get a singleton instance of SimpleBean $singletonSimpleRandom = $container->getBean('singletonSimpleRandom'); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is int
XML Style:
The XML config file:
<?xml version="1.0" encoding="utf-8" ?> <config> <bean name="prototypeSimpleRandom" class="Ovo\Container\Test\Annotation\SimpleRandom" factory-method='getInstance' scope="prototype"/> </config>
Application Example:
use Ovo\Container\XmlContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new XmlContainer('path/to/xml/config/file.xml'); //get a singleton instance of SimpleBean $singletonSimpleRandom = $container->getBean('singletonSimpleRandom'); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is int
Init and Destroy methods
Sometimes we need to call a init method (after the bean creation) or a destroy method (before the shutdown of the container) on a bean. The DI container can do this task for us.
Annotation style:
The annotations:
namespace Ovo\Container\Test\Annotation; use Ovo\Container\Annotation\Bean; use Ovo\Container\Annotation\DestroyMethod; use Ovo\Container\Annotation\InitMethod; use Ovo\Container\Annotation\FactoryMethod; /** * @Bean(name= "prototypeSimpleRandom", scope = "prototype"); */ final class SimpleRandom { private $randomId = null; public function __construct() { } public function getRandomId() { return $this->randomId; } /** * @InitMethod */ public function generateId() { $this->randomId = rand(0, 1000); } /** * @DestroyMethod */ public function close() { $this->randomId = null; } }
Application Example:
use Ovo\Container\AnnotationContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new AnnotationContainer('path/to/beans/annotation/folder'); //get a singleton instance of SimpleBean $singletonSimpleRandom = $container->getBean('singletonSimpleRandom'); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is int $container->close(); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is null
XML Style:
The XML config file:
<?xml version="1.0" encoding="utf-8" ?> <config> <bean name="prototypeSimpleRandom" class="Ovo\Container\Test\Annotation\SimpleRandom" init-method="generateId" destroy-method="close" factory-method='getInstance' scope="prototype"/> </config>
Application Example:
use Ovo\Container\XmlContainer; use Ovo\Container\Test\Annotation\SimpleRandom $container = new XmlContainer('path/to/xml/config/file.xml'); //get a singleton instance of SimpleBean $singletonSimpleRandom = $container->getBean('singletonSimpleRandom'); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is int $container->close(); $singletonID = $singletonSimpleRandom->getRandomId() //$singletonID is null