The Molajo Inversion of Control (IoC) package offers a full-featured, dependency injection solution and a services layer for PHP applications.
These are the basic steps to implementing the Inversion of Control (IoC) package:
- Create a Service Folder to store custom dependency injection handlers.
- Update the Front Controller for the Inversion of Control Container (IoCC).
- Request Services from the IoCC within the Application.
- Create Custom Factory Methods for Services.
Create a folder within your application to store Service Factory Methods. Each Customer Handler has a folder
containing a class file named ClassnameFactoryMethod.php
. When instantiating the IoCC, you'll provide
the namespace of the Services Folder.
Molajo
.. Services
.. .. Database
.. .. .. DatabaseFactoryMethod.php
.. .. Configuration
.. .. .. ConfigurationFactoryMethod.php
.. .. Modelread
.. .. .. ModelreadFactoryMethod.php
.. .. etc ..
The default namespace for a services folder is Molajo\Services
. Whatever value is used, it will
be passed in as a parameter when instantiating the Container class.
The Front Controller should be the only point of entry into the Inversion of Control Container (IoCC).
Add a use statement for the IoC Container class.
use Molajo\IoC\Container;
Define a class property in which to store the IoCC instance.
/**
* Inversion of Control Container
*
* @var object
* @since 1.0.0
*/
protected $iocc;
Add these four methods: getService, setService, cloneService, and removeService to the Front Controller.
/**
* Get a service instance
*
* @param string $service
* @param array $options
*
* @results null|object
* @since 1.0
* @throws FrontcontrollerException
*/
public function getService($service, $options = array())
{
return $this->ioc->getService($service, $options);
}
/**
* Replace the existing service instance
*
* @param string $service
* @param object $instance
*
* @results $this
* @since 1.0
* @throws FrontcontrollerException
*/
public function setService($service, $instance = null)
{
$this->ioc->getService($service, $instance);
return $this;
}
/**
* Clone the existing service instance
*
* @param string $service
*
* @results null|object
* @since 1.0
* @throws FrontcontrollerException
*/
public function cloneService($service)
{
return $this->ioc->cloneService($service);
}
/**
* Remove the existing service instance
*
* @param string $service
*
* @results $this
* @since 1.0
*/
public function removeContainerEntries($service)
{
$this->ioc->removeContainerEntries($service);
return $this;
}
In the Front Controller boot process, instantiate the Container using the code that begins with $connect and ends before "// Automatically Load These Services". The four closure statements passed into the Container will be used outside of the Front Controller to access the IoCC methods defined in the previous step. Note, also, the location of the $services_folder is passed into the Container.
/**
* Initialise Application, including invoking Inversion of Control Container and
* Services defined in Services.xml
*
* @return $this
* @since 1.0.0
* @throws FrontcontrollerException
*/
public function initialise()
{
$this->checkPHPMinimum();
set_exception_handler(array($this, 'handleException'));
set_error_handler(array($this, 'handlePHPErrors'), 0);
$connect = $this;
$getService = function ($service, $options = array()) use ($connect) {
return $connect->getService($service, $options);
};
$setService = function ($service, $instance) use ($connect) {
return $connect->setService($service, $instance);
};
$cloneService = function ($service) use ($connect) {
return $connect->cloneService($service);
};
$removeService = function ($service) use ($connect) {
return $connect->removeContainerEntries($service);
};
$services_folder = 'Molajo\\Services';
$this->ioc = new Container($getService, $setService, $cloneService, $removeService, $services_folder);
// Automatically Load These Services
$xml_string = $this->readXMLFile(__DIR__ . '/' . 'Services.xml');
$services = simplexml_load_string($xml_string);
foreach ($services->service as $service) {
$this->getService((string)$service->attributes()->name, array());
}
return;
}
The four closures provide a way to access the Front Controller entry points into the IoCC.
- $getService - create the $service using the $options specified, or return an existing service with the same name;
- $setService - replace the existing $service registry in the container with the instance specified;
- $cloneService - clone the container registry entry for a specified service, returning the cloned instance;
- $removeService - remove the container registry entry specified;
When the IoC Container creates a Factory Method instance, it injects it with all four closures before injecting the handler into the DI Adapter. The handler can use those class properties to interact with the IoCC. In this example, the handler requests the dependent Application Service.
$getService = $this->getService;
$application = $getService('Application');
/** Has cache been activated? */
$cache_service = $application->get('cache_service');
if ((int)$cache_service === 0) {
return $this;
}
- $service Fully qualified namespace (ex.
Molajo\\Services\\Database\\DatabaseInjector
) or the name of the Services sub-folder (ex.Database
). - $options Optional associative array contain runtime parameters required by the service.
When the Container processes a getService request, it first determines if the named service exists in the
registry. If it is, the existing service is returned. If it does not exist, a new instance will be created unless
the if_exists
$options entry was specified in the request.
When the fully qualified namespace is not defined, the Container looks for a folder with that name in the Services library. In this example, the $options array defines runtime variables for the instantiation process.
$getService = $this->getService;
$options = array;
$options['model_type'] = 'Application';
$options['model_name'] = 'Includers';
$database = $getService('LocateFile', $options);
This request instructs the Container only return an instance of the User if that instance already exists.
$getService = $this->getService;
$options = array;
$options['if_exists'] = true;
$database = $getService('Molajo\\User', $options);
A Service Factory Method is not always needed. Classes can be created by the Standard Factory Method. It will match the values defined in the $options associative array with the Constructor parameters and use that data to create the class. For the service name, use the fully qualified class name.
$getService = $this->getService;
$options = array;
$options['constructor_parameter1'] = true;
$options['constructor_parameter2'] = true;
$database = $getService('Molajo\\Utilities\\Classname', $options);
Replaces the existing Container registry entry for this service with the value sent in.
$setService = $this->setService;
$database = $setService('Application', $instance);
Clones the existing Container registry entry for this service and returns the cloned value.
$cloneService = $this->cloneService;
$database = $cloneService('Database');
Removes the existing Container registry entry for this service.
$removeService = $this->removeService;
$database = $removeContainerEntries('Database');
To create a Custom Dependency Injection Handler:
- Add a folder to the Services Folder defined in Step 1. The folder name is the name of the service.
- Create a PHP file in that folder named
ServiceNameFactoryMethod.php
.
Molajo
.. Services
.. .. ServiceName
.. .. .. ServiceNameFactoryMethod.php
The Custom Factory Method has access to the following class properties:
- $getService - Closure to request a service of the IoCC, defined above
- $setService - Closure to set a service contained within the IoCC registry, defined above
- $cloneService - Closure to clone a service contained within the IoCC registry, defined above
- $removeService - Closure to remove a service contained within the the IoCC registry, defined above
- $service - The name specified in the getService statement
- $service_namespace - Set the fully qualified namespace for the Class to be instantiated in the constructor
- $store_instance_indicator - defaults to false, set to true in the constructor to store the instance in the IoCC registry
- $static_instance_indicator - defaults to false, set to true in the constructor to request a static instance
- $store_properties_indicator - defaults to false, set to true in the constructor to store the properties, not the instance, in the IoCC registry
- $product_result - populated with the instantiated class
- $options - associative array provided by the getService call
Below is a basic starting pattern for a Custom Dependency Injection Handler. The event methods available to the Factory Method are: onBeforeInstantiation, Instantiate, onAfterInstantiation, initialise, onAfterServiceInitialise, and getProductValue. Each method can be used to inject code at different points in the class creation process. The like-named AbstractHandler method will be used for any omitted methods in the custom class. It is a good idea to become familiar with that class.
The Molajo\Services folder is also a good source of examples of Custom DI Injectors.
<?php
/**
* Example Custom Dependency Injection Handler
*
* @package Molajo
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @copyright 2014-2015 Amy Stephen. All rights reserved.
*/
namespace Molajo\Services\Example;
use Molajo\IoC\AbstractFactoryMethod;
use CommonApi\IoC\FactoryMethodInterface;
use CommonApi\Exception\RuntimeException;
/**
* Example Custom Dependency Injection Handler
*
* @author Amy Stephen
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @copyright 2014-2015 Amy Stephen. All rights reserved.
* @since 1.0
*/
class ExampleFactoryMethod extends AbstractFactoryMethod implements FactoryMethodInterface
{
/**
* Constructor
*
* @param $options
*
* @since 1.0.0
*/
public function __construct(array $options = array())
{
$this->service_namespace = 'Molajo\\Example\\Classname';
// These default to false - set to true here if needed
$this->store_instance_indicator = false;
$this->static_instance_indicator = false;
$this->store_properties_indicator = false;
parent::__construct($options);
}
/**
* Follows instantiation of the service class and before the method identified as the "start" method
*
* @return object
* @since 1.0.0
*/
public funonBeforeInstantiationtances()
{
retonBeforeInstantiationncyInstances();
}
/**
* Instantiate Class
*
* @param bool $create_static
*
* @return $this
* @since 1.0.0
* @throws \CommonApi\Exception\RuntimeException
*/
public function instantiateClass()
{
return parent::instantiate($create_static);
}
/**
* Follows the completion of the instantiate service method
*
* @return object
* @since 1.0.0
* @throws \CommonApi\Exception\RuntimeException
*/
public function onAfterInstantiation()
{
return parent::onAfterInstantiation();
}
/**
* Initialise Service Class, if the method exists
*
* @return object
* @since 1.0.0
* @throws \CommonApi\Exception\RuntimeException
*/
public function initialise()
{
return parent::initialise();
}
/**
* Follows the completion of Initialise
*
* @return object
* @since 1.0.0
* @throws \CommonApi\Exception\RuntimeException
*/
public function onAfterServiceInitialise()
{
return parent::onAfterServiceInitialise();
}
/**
* Get Product Result
*
* @return object
* @since 1.0.0
* @throws \CommonApi\Exception\RuntimeException
*/
public function getProductValue()
{
return parent::getProductValue();
}
}
curl -s https://getcomposer.org/installer | php
{
"require": {
"Molajo/Ioc": "1.*"
}
}
php composer.phar install
- PHP framework independent, no dependencies
- Requires PHP 5.4, or above
- Semantic Versioning
- Compliant with:
- [phpDocumentor2] (https://github.com/phpDocumentor/phpDocumentor2)
- [phpUnit Testing] (https://github.com/sebastianbergmann/phpunit)
- [Travis Continuous Improvement] (https://travis-ci.org/profile/Molajo)
- Listed on [Packagist] (http://packagist.org) and installed using [Composer] (http://getcomposer.org/)
- Use github to submit pull requests and features
- Author Amy Stephen
- MIT License see the
LICENSE
file for details