db80/ovo-container

This package is abandoned and no longer maintained. No replacement package was suggested.

A lightweight Dependency Injection / Inversion of control Framework for PHP 5.3

0.2.1 2012-10-08 10:38 UTC

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:

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