Dependency Injection / Service Location¶
Overview¶
Phalcon\Di\Di is a container that stores services or components (classes). These services are available throughout the application and ease development. Let us assume we are developing a component called InvoiceComponent
that performs some calculations for a customer's invoice. It requires a database connection to retrieve the Invoice
record from the database.
Our component can be implemented as follows:
<?php
use Phalcon\Db\Adapter\Mysql;
class InvoiceComponent
{
public function calculate()
{
$connection = new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
$invoice = $connection->exec(
'SELECT * FROM Invoices WHERE inv_id = 1'
);
// ...
}
}
$invoice = new InvoiceComponent();
$invoice->calculate();
We use the calculate
method to get our data. Inside the method, we create a new database connection to MySQL with set credentials and after that, we execute a query. Although this is a perfectly valid implementation, it is impractical and will hinder the maintenance of our application later on, due to the fact that our connection parameters or type of the database are hardcoded in the component. If in the future we need to change those, we will have to change them in this component and any other component designed in this manner.
<?php
use Phalcon\Db\Adapter\Mysql;
class InvoiceComponent
{
private $connection;
public function calculate()
{
$invoice = $this
->connection
->exec(
'SELECT * FROM Invoices WHERE inv_id = 1'
)
;
// ...
}
public function setConnection(
Mysql $connection
): InvoiceComponent {
$this->connection = $connection;
return $this;
}
}
$connection = new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
$invoice = new InvoiceComponent();
$invoice
->setConnection($connection)
->calculate()
;
To improve flexibility, we could create the database connection outside the component and set it in the InvoiceComponent
using a setter. Using this approach, we can inject the database connection to any component that requires it, using the setter. Again this is a perfectly valid implementation, but it does have some shortcomings. We will need for instance to construct the database connection every time we need to use any of our components that require database connectivity.
In order to centralize this functionality, we can implement a global registry pattern and store the connection object there. After that, we can reuse it wherever we need to.
<?php
use Phalcon\Db\Adapter\Mysql;
class Registry
{
public static function getConnection(): Mysql
{
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
}
}
class InvoiceComponent
{
private $connection;
public function calculate()
{
$invoice = $this
->connection
->exec(
'SELECT * FROM Invoices WHERE inv_id = 1'
)
;
// ...
}
public function setConnection(
Mysql $connection
): InvoiceComponent {
$this->connection = $connection;
return $this;
}
}
$invoice = new InvoiceComponent();
$invoice
->setConnection(Registry::getConnection())
->calculate()
;
The above implementation will create a new connection every time we call getConnection
on the Registry
component. To address this issue, we can modify our Registry
class to store the database connection and reuse it.
<?php
use Phalcon\Db\Adapter\Mysql;
class Registry
{
protected static $connection;
public static function getNewConnection(): Mysql
{
return self::createConnection();
}
public static function getSharedConnection(): Mysql
{
if (self::$connection === null) {
self::$connection = self::createConnection();
}
return self::$connection;
}
protected static function createConnection(): Mysql
{
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tuturial',
]
);
}
}
class InvoiceComponent
{
private $connection;
public function calculate()
{
$invoice = $this
->connection
->exec(
'SELECT * FROM Invoices WHERE inv_id = 1'
)
;
// ...
}
public function setConnection(
Mysql $connection
): InvoiceComponent {
$this->connection = $connection;
return $this;
}
}
$invoice = new InvoiceComponent();
$invoice
->setConnection(Registry::getSharedConnection())
->calculate()
;
$invoice = new InvoiceComponent();
$invoice
->setConnection(Registry::getNewConnection())
->calculate()
;
In the above example, we changed the Registry
class, exposing getNewConnection
which creates a brand-new database connection. It also exposes the getSharedConnection
which will store the connection internally and reuse it for any other component that requires it.
Injecting dependencies to our components solves the issues outlined above. Passing dependencies as arguments instead of creating them internally in methods makes our code more maintainable and decoupled. However, in the long term, this form of dependency injection has some disadvantages. If for instance, the component has many dependencies, we will need to create multiple setter arguments to pass the dependencies or create a constructor that will be used to pass all the dependencies required as arguments. We would also need to create those dependencies before using the component. This makes our code not as maintainable as we would like:
<?php
$connection = new Connection();
$fileSystem = new FileSystem();
$filter = new Filter();
$selector = new Selector();
$session = new Session();
$invoice = new InvoiceComponent(
$connection,
$session,
$fileSystem,
$filter,
$selector
);
$invoice
->setConnection($connection)
->setFileSystem($fileSystem)
->setFilter($filter)
->setSelector($selector)
->setSession($session)
;
The problem of maintainability arises though here. If we have to create this object in many parts of the application, we will need to perform the same initialization, injecting all the dependencies. If in the future we need to change any of our components to require additional dependencies we have to go through all the areas that we have used this component or others to adjust our code. To solve this issue, we will use the global registry class to create the component. However, this approach adds one more layer of abstraction before creating the object:
<?php
class InvoiceComponent
{
private $connection;
private $fileSystem;
private $filter;
private $selector;
private $session;
public function __construct(
Connection $connection,
FileSystem $fileSystem,
Filter $filter,
Selector $selector,
Session $session
) {
$this->connection = $connection;
$this->fileSystem = $fileSystem;
$this->filter = $filter;
$this->selector = $selector;
$this->session = $session;
}
public static function factory()
{
$connection = new Connection();
$fileSystem = new FileSystem();
$filter = new Filter();
$selector = new Selector();
$session = new Session();
return new self(
$connection,
$fileSystem,
$filter,
$selector,
$session
);
}
}
We are now back where we started, instantiating dependencies within the component. To solve this issue we will use a container that can store all of our dependencies. This is a practical and elegant way. The container will act as the global registry that we investigated earlier. Using this container as a bridge to retrieve any dependencies, allows us to reduce the complexity of our component:
<?php
use Phalcon\Db\Adapter\Mysql;
use Phalcon\Di\Di;
use Phalcon\Di\DiInterface;
class InvoiceComponent
{
protected $container;
public function __construct(
DiInterface $container
) {
$this->container = $container;
}
public function calculate()
{
$connection = $this
->container
->get('db')
;
}
public function view($id)
{
$filter = $this
->container
->get('filter')
;
$id = $filter->sanitize($id, null, 'int');
$connection = $this
->container
->getShared('db')
;
}
}
$container = new Di();
$container->set(
'db',
function () {
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
}
);
$container->set(
'filter',
function () {
return new Filter();
}
);
$container->set(
'session',
function () {
return new Session();
}
);
$invoice = new InvoiceComponent($container);
$invoice->calculate();
The component now can simply access the dependencies it requires when it needs them. If a dependency is not required, it will not be initialized ensuring minimum usage of memory. Our component is now highly decoupled. For instance, if we change the database connection in any way, it will not affect the component, while as far as maintenance is concerned, we only need to change the code in one place.
Phalcon\Di\Di is a component implementing Dependency Injection and a Service Locator. Since Phalcon is highly decoupled, Phalcon\Di\Di is essential to integrate the different components of the framework. The developer can also use this component to inject dependencies and manage global instances of the different classes used in the application. It also implements the Inversion of Control pattern. Because of this, the objects do not receive their dependencies using setters or constructors but request a service dependency injector. This reduces the overall complexity since there is only one way to get the required dependencies within a component.
Additionally, this pattern increases testability in the code, thus making it less prone to errors.
Methods¶
Magic method to get or set services using setters/getterspublic function attempt(
string $name,
mixed definition,
bool shared = false
): ServiceInterface | bool
Phalcon\Di\Service
instance Return the services registered in the DI Returns a shared service. The service is first resolved, then the resolved service is stored in the DI. Subsequent requests for this service will return the same instance Load services from a php config file. // /app/config/services.php
return [
'myComponent' => [
'className' => '\Acme\Components\MyComponent',
'shared' => true,
],
'group' => [
'className' => '\Acme\Group',
'arguments' => [
[
'type' => 'service',
'service' => 'myComponent',
],
],
],
'user' => [
'className' => '\Acme\User',
],
];
$container->loadFromPhp("/app/config/services.php");
// /app/config/services.yml
myComponent:
className: \Acme\Components\MyComponent
shared: true
group:
className: \Acme\Group
arguments:
- type: service
name: myComponent
user:
className: \Acme\User
$container->loadFromYaml(
"/app/config/services.yaml",
[
"!approot" => function ($value) {
return dirname(__DIR__) . $value;
}
]
);
use Phalcon\Di\DiInterface;
use Phalcon\Di\ServiceProviderInterface;
class SomeServiceProvider implements ServiceProviderInterface
{
public function register(DiInterface $container)
{
$container->setShared(
'service',
function () {
// ...
}
);
}
}
Registering Services¶
The framework itself or the developer can register services. When component A requires component B (or an instance of its class) to operate, it can request component B from the container, rather than creating a new instance of component B.
This approach offers the following advantages: * We can easily replace a component with one created by ourselves or a third party. * We have full control of the object initialization, allowing us to set these objects as needed before delivering them to components. * We can get global instances of components in a structured and unified way.
Services can be registered using several types of definitions. Below we explore the different ways that services can be registered:
String¶
This type expects the name of a valid class, returning an object of the specified class, if the class is not loaded it will be instantiated using an autoloader. This type of definition does not allow to specify arguments for the class constructor or parameters:
Class Instances¶
This type expects an object. Due to the fact that the object does not need to be resolved as it is already an object, one could say that it is not really a dependency injection, however, it is useful if you want to force the returned dependency to always be the same object/value:
Closures¶
This method offers greater freedom to build the dependency as desired, however, it is difficult to change some parameters externally without having to completely change the definition of dependency:
<?php
use Phalcon\Db\Adapter\Pdo\Mysql;
$container->set(
'db',
function () {
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'tutorial',
]
);
}
);
Some limitations can be overcome by passing additional variables to the closure's environment:
<?php
use Phalcon\Config;
use Phalcon\Db\Adapter\Pdo\Mysql;
$config = new Config(
[
'host' => 'localhost',
'username' => 'user',
'password' => 'pass',
'dbname' => 'tutorial',
]
);
$container->set(
'db',
function () use ($config) {
return new Mysql(
[
'host' => $config->host,
'username' => $config->username,
'password' => $config->password,
'dbname' => $config->name,
]
);
}
);
You can also access other DI services using the get()
method:
<?php
use Phalcon\Config\Config;
use Phalcon\Db\Adapter\Pdo\Mysql;
$container->set(
'config',
function () {
return new Config(
[
'host' => 'localhost',
'username' => 'user',
'password' => 'pass',
'dbname' => 'tutorial',
]
);
}
);
$container->set(
'db',
function () {
$config = $this->get('config');
return new Mysql(
[
'host' => $config->host,
'username' => $config->username,
'password' => $config->password,
'dbname' => $config->name,
]
);
}
);
NOTE
$this
can be used inside a closure
Complex Registration¶
If it is required to change the definition of a service without instantiating/resolving the service, then, we need to define the services using the array syntax. Define a service using an array definition can be a little more verbose:
<?php
use Phalcon\Annotations\Adapter\Apcu;
$container->set(
'annotations',
[
'className' => Apcu::class,
'arguments' => [
[
'type' => 'parameter',
'name' => 'prefix',
'value' => 'my-prefix',
],
[
'type' => 'parameter',
'name' => 'lifetime',
'value' => 3600,
],
],
]
);
$container->set(
'annotations',
function () {
return new Apcu(
[
'prefix' => 'my-prefix',
'lifetime' => 3600,
]
);
}
);
Both service registrations above produce the same result. The array definition, however, allows you to change the service parameters if you need to:
<?php
use Phalcon\Annotations\Adapter\Memory;
$container
->getService('annotations')
->setClassName(Memory::class)
;
$container
->getService('annotations')
->setParameter(
1,
[
'type' => 'parameter',
'name' => 'lifetime',
'value' => 7200,
]
);
Injections¶
In addition, by using the array syntax you can use three types of dependency injection:
Constructor Injection¶
This injection type passes the dependencies/arguments to the class constructor. Let's pretend we have the following components:
<?php
namespace MyApp\Http;
use Phalcon\Http\Response;
class Responder
{
/**
* @var Response
*/
protected $response;
/**
* @var string
*/
protected $contentType;
public function __construct(Response $response, string $contentType)
{
$this->response = $response;
$this->contentType = $contentType;
}
}
The service can be registered as follows:
<?php
use MyApp\Http\Responder;
use Phalcon\Http\Response;
$container->set(
'response',
[
'className' => Response::class
]
);
$container->set(
'my-responder',
[
'className' => Responder::class,
'arguments' => [
[
'type' => 'service',
'name' => 'response',
],
[
'type' => 'parameter',
'value' => 'application/json',
],
]
]
);
The service response
(Phalcon\Http\Response is resolved to be passed as the first argument of the constructor, while the second is a string
value that is passed as it is.
Setter Injection¶
Classes may have setters to inject optional dependencies, our previous class can be changed to accept the dependencies with setters:
<?php
namespace MyApp\Http;
use Phalcon\Http\Response;
class Responder
{
/**
* @var Response
*/
protected $response;
/**
* @var string
*/
protected $contentType;
public function setResponse(Response $response)
{
$this->response = $response;
}
public function setContentType($contentType)
{
$this->contentType = $contentType;
}
}
The above class can be registered as a service using the getter and setter:
<?php
use MyApp\Http\Responder;
use Phalcon\Http\Response;
$container->set(
'response',
[
'className' => Response::class,
]
);
$container->set(
'my-responder',
[
'className' => Responder::class,
'calls' => [
[
'method' => 'setResponse',
'arguments' => [
[
'type' => 'service',
'name' => 'response',
]
]
],
[
'method' => 'setContentType',
'arguments' => [
[
'type' => 'parameter',
'value' => 'application/json',
]
]
]
]
]
);
Properties Injection¶
A less common strategy is to inject dependencies or parameters directly into public attributes of the class:
<?php
namespace MyApp\Http;
use Phalcon\Http\Response;
class Responder
{
/**
* @var Response
*/
public $response;
/**
* @var string
*/
public $contentType;
}
A service with properties injection can be registered as follows:
<?php
use MyApp\Http\Responder;
use Phalcon\Http\Response;
$container->set(
'response',
[
'className' => Response::class,
]
);
$container->set(
'my-responder',
[
'className' => Responder::class,
'properties' => [
[
'name' => 'response',
'value' => [
'type' => 'service',
'name' => 'response',
],
],
[
'name' => 'contentType',
'value' => [
'type' => 'parameter',
'value' => 'application/json',
],
]
]
]
);
Supported parameter types include the following:
Type | Description | Example |
---|---|---|
instance | Represents an object that must be built dynamically | ['type' => 'instance', 'className' => \DateTime::class, 'arguments' => ['now']] |
parameter | Represents a literal value to be passed as parameter | ['type' => 'parameter', 'value' => 1234] |
service | Represents another service in the service container | ['type' => 'service', 'name' => 'request'] |
Resolving a service whose definition is complex may be slightly slower than simple definitions seen previously. However, these provide a more robust approach to defining and injecting services. Mixing different types of definitions is allowed, and you can decide which way is the most appropriate for you to register the services according to the application needs.
Array Syntax¶
The array syntax is also available to register services:
<?php
use Phalcon\Di\Di;
use Phalcon\Http\Request;
$container = new Di();
$container['request'] = Request::class;
$container['request'] = function () {
return new Request();
};
$container['request'] = new Request();
$container['request'] = [
'className' => Request::class,
];
In the examples above, when the framework needs to access the request data, it will ask for the service identified as request
in the container. The container in turn will return an instance of the required service. The component can be easily replaced with a different class if a need arises.
As shown in the above examples, each of the ways used to set/register a service has advantages and disadvantages. It is up to the developer and the particular requirements that will designate which one is used. Setting a service by a string is simple, but lacks flexibility. Setting services using an array offers a lot more flexibility, but makes the code more complicated. The lambda function is a good balance between the two but could lead to more maintenance than one would expect.
NOTE
Phalcon\Di\Di offers lazy loading for every service it stores. Unless the developer chooses to instantiate an object directly and store it in the container, any object stored in it (via array, string, etc.) will be lazy-loaded i.e. instantiated only when requested.
Load From Config¶
YAML
This feature will load services by parsing a YAML file:
<?php
use Phalcon\Di\Di;
$container = new Di();
$container->loadFromYaml('services.yml');
$container->get('/app/config/services.yml');
NOTE
This approach requires that the module Yaml be installed. Please refer to this document for more information.
PHP
You can also load services using a PHP array:
// /app/config/services.php
use Phalcon\Config\Config;
return [
'config' => [
'className' => Config::class,
'shared' => true,
],
];
<?php
use Phalcon\Di\Di;
$container = new Di();
$container->loadFromPhp('/app/config/services.php');
$container->get('config');
Resolving Services¶
Obtaining a service from the container is a matter of simply calling the 'get' method. A new instance of the service will be returned:
Or by calling through the magic method:
Or using the array-access syntax:
Arguments can be passed to the constructor by adding an array parameter to the method 'get':
<?php
use Phalcon\Annotations\Adapter\Stream;
$annotations = $container->get(
Stream::class,
[
['annotationsDir' => 'storage/cache/annotations'],
]
);
Events¶
Phalcon\Di\Di is able to send events to an EventsManager if it is present. Events are triggered using the type di
.
Event Name | Triggered |
---|---|
afterServiceResolve | Triggered after resolve service. Listeners receive the service name, instance, and the parameters passed to it. |
beforeServiceResolve | Triggered before resolve service. Listeners receive the service name and the parameters passed to it. |
Shared Services¶
Services can be registered as shared
services this means that they always will act as [singletons][singletons]. Once the service is resolved for the first time the same instance of it is returned every time the service is retrieved from the container:
<?php
use Phalcon\Session\Manager;
use Phalcon\Session\Adapter\Stream;
$container->setShared(
'session',
function () {
$session = new Manager();
$files = new Stream(
[
'savePath' => '/tmp',
]
);
$session->setAdapter($files);
$session->start();
return $session;
}
);
$session = $container->get('session');
$session = $container->getSession();
The first call to get
in the container resolves the service and returns the object back. The subsequent call to getSession
will return the same object.
An alternative way to register shared services is to pass true
as the third parameter of set
:
<?php
use Phalcon\Session\Manager;
use Phalcon\Session\Adapter\Stream;
$container->set(
'session',
function () {
$session = new Manager();
$files = new Stream(
[
'savePath' => '/tmp',
]
);
$session->setAdapter($files);
$session->start();
return $session;
},
true
);
$session = $container->get('session');
$session = $container->getSession();
NOTE
If a service is not registered as shared, and you want to ensure that a shared instance will be accessed every time the service is retrieved from the DI, you can use the getShared
method
Manipulating Services¶
Once a service is registered in the service container, you can retrieve it to manipulate it individually:
<?php
use Phalcon\Http\Request;
$container->set('request', 'Phalcon\Http\Request');
$requestService = $container->getService('request');
$requestService->setDefinition(
function () {
return new Request();
}
);
$requestService->setShared(true);
$request = $requestService->resolve();
Instantiating Classes¶
When you request a service from the container, if it cannot be found by using the same name, it will try to load a class with the same name. This behavior allows you to replace any service with another, by simply registering a service with the common name:
<?php
$container->set(
'IndexController',
function () {
return new Component();
},
true
);
$container->set(
'IndexController',
function () {
return new AnotherComponent();
}
);
$component = $container->get('IndexController');
IndexController
with another component of our choosing. Also, you can adjust your code to always instantiate your classes using the service container, even if they are not registered as services. The container will fall back to the autoloader you have defined to load the class itself. By using this technique, you can replace any class in the future by implementing a different definition for it. Automatic Injecting¶
If a class or component requires the DI itself to locate services, the DI can automatically inject itself into the instances it creates. To take advantage of this, all you need is to implement the Phalcon\Di\InjectionAwareInterface in your classes:
<?php
use Phalcon\Di\DiInterface;
use Phalcon\Di\InjectionAwareInterface;
class InvoiceComponent implements InjectionAwareInterface
{
/**
* @var DiInterface
*/
protected $container;
public function setDi(DiInterface $container)
{
$this->container = $container;
}
public function getDi(): DiInterface
{
return $this->container;
}
}
Then, once the service is resolved, the $container
will be passed to setDi()
automatically:
<?php
$container->set('inv-component', InvoiceComponent::class);
$invoiceComponent = $container->get('inv-component');
NOTE
$invoiceComponent->setDi($container)
is automatically called
For your convenience, you can also extend the Phalcon\Di\AbstractInjectionAware class which contains the above code and exposes the protected $container
property for you to use.
<?php
use Phalcon\Di\DiInterface;
use Phalcon\Di\AbstractInjectionAware;
class InvoiceComponent extends AbstractInjectionAware
{
}
Organizing Services in Files¶
You can better organize your application by moving the service registration to individual files instead of registering everything in the application's bootstrap:
Then in the file ('/app/config/routes.php'
) return the object resolved:
<?php
use Phalcon\Mvc\Router();
$router = new Router(false);
$router->post('/login');
return $router;
Static Access¶
The Phalcon\Di\Di offers the convenient getDefault()
static method, which returns the latest container created. This allows you to access the container even from static classes:
<?php
use Phalcon\Di\Di;
class InvoicesComponent
{
public static function calculate()
{
$connection = Di::getDefault()->getDb();
}
}
Service Providers¶
Another method of registering services is by putting each service in its own file and registering all the services one after another with a simple loop. Each file will contain a class or Provider
that implements the Phalcon\Di\ServiceProviderInterface. The reason you might want to do this is to have tiny files, each handling one service registration which will offer great flexibility, shortcode, and finally the ability to add/remove services whenever you wish to, without having to sift through a large file such as your bootstap.
Example
app/config/providers.php
<?php
return [
MyApp\Providers\ConfigProvider::class,
MyApp\Providers\RegistryProvider::class,
MyApp\Providers\LoggerProvider::class,
];
app/library/Providers/ConfigProvider.php
<?php
namespace MyApp\Providers;
use Phalcon\Config\Config;
use Phalcon\Di\ServiceProviderInterface;
use Phalcon\Di\DiInterface;
class ConfigProvider implements ServiceProviderInterface
{
/**
* @param DiInterface $container
*/
public function register(DiInterface $container)
{
$container->setShared(
'config',
function () {
$data = require 'app/config/config.php';
return new Config($data);
}
);
}
}
app/library/Providers/RegistryProvider.php
<?php
namespace MyApp\Providers;
use Phalcon\Config\Config;
use Phalcon\Di\ServiceProviderInterface;
use Phalcon\Di\DiInterface;
use Phalcon\Registry;
use function microtime;
class RegistryProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}
*
* @param DiInterface $container
*/
public function register(DiInterface $container)
{
/** @var Config $config */
$config = $container->getShared('config');
$devMode = $config->path('app.devMode', false);
$container->setShared(
'registry',
function () use ($devMode) {
$registry = new Registry();
$registry->offsetSet('devMode', $devMode);
$registry->offsetSet('execution', microtime(true));
return $registry;
}
);
}
}
app/library/Providers/LoggerProvider.php
```php <?php
namespace MyApp\Providers;
use Phalcon\Di\DiInterface; use Phalcon\Di\ServiceProviderInterface; use Phalcon\Logger\Logger; use Phalcon\Logger\Adapter\Stream;
class LoggerProvider implements ServiceProviderInterface { use LoggerTrait;
/**
* @param DiInterface $container
*
* @throws \Exception
*/
public function register(DiInterface $container)
{
$container->setShared(
'logger',
function () {
$adapter = new Stream('/storage/logs/main.log');
return new Logger(
'messages',
[
'main' => $adapter,
]
);
}
);
}
}
Now we can register all the services with a simple loop:
```php
<?php
use Phalcon\Di\Di;
$services = include('app/config/providers.php');
$container = new Di();
foreach ($services as $service) {
$container->register(new $service());
}
Factory Default¶
For convenience to developers, the Phalcon\Di\FactoryDefault is available with several preset services for you. Nothing stops you from registering all the services your application requires one by one. However, you can use the Phalcon\Di\FactoryDefault, which contains a list of services ready to be used. The list of services registered allows you to have a container suitable for a full-stack application.
NOTE
Since the services are always lazy loaded, instantiating the Phalcon\Di\FactoryDefault container will not consume more memory than a Phalcon\Di\Di one.
The services registered in the Phalcon\Di\FactoryDefault are:
Name | Object | Shared | Description |
---|---|---|---|
annotations | Phalcon\Annotations\Adapter\Memory | Yes | Annotations parser |
assets | [Phalcon\Assets\Manager][assets] | Yes | Assets manager |
crypt | Phalcon\Encryption\Crypt | Yes | Encrypt/Decrypt |
cookies | Phalcon\Http\Response\Cookies | Yes | HTTP Cookies manager |
dispatcher | Phalcon\Mvc\Dispatcher | Yes | Dispatcher |
escaper | Phalcon\Html\Escaper | Yes | Escaper |
eventsManager | Phalcon\Events\Manager | Yes | Events Manager |
flash | Phalcon\Flash\Direct | Yes | Flash Messaging |
flashSession | Phalcon\Flash\Session | Yes | Flash Session Messaging |
filter | Phalcon\Filter\Filter | Yes | Filtering / Sanitizing |
helper | Phalcon\Support\HelperFactory | Yes | String, array etc. helpers |
modelsManager | Phalcon\Mvc\Model\Manager | Yes | Models Management |
modelsMetadata | Phalcon\Mvc\Model\MetaData\Memory | No | Models MetaData |
request | Phalcon\Http\Request | Yes | HTTP Request |
response | Phalcon\Http\Response | Yes | HTTP Response |
router | Phalcon\Mvc\Router | Yes | Router |
security | Phalcon\Security | Yes | Security |
tag | Phalcon\Html\TagFactory | Yes | HTML Tag helpers |
transactionManager | Phalcon\Mvc\Model\Transaction\Manager | Yes | Database Transaction Manager |
url | Phalcon\Mvc\Url | Yes | URL Generation |
If certain components are registered (such as a database connection) they are used internally with the following names:
Name | Object | Shared | Description |
---|---|---|---|
db | Phalcon\Db | Yes | Database connection |
modelsCache | Cache backend for models | ||
session | Session Service | ||
sessionBag | [Phalcon\Session\Bag][session-bag] | Yes | Session Bag service |
The above names are used throughout the framework. For instance the db
service is used within the transactionManager
service. You can replace these components with the ones you prefer by just registering your component with the same name as the ones listed above.
Exceptions¶
Any exceptions thrown in the DI container will be either Phalcon\Di\Exception or Phalcon\Di\ServiceResolutionException. You can use this exception to selectively catch exceptions thrown only from this component.
<?php
use Phalcon\Di\Di;
use Phalcon\Di\Exception;
try {
$container = new Di();
$component = $container->get('unknown-service');
} catch (Exception $ex) {
echo $ex->getMessage();
}
Custom¶
The Phalcon\Di\DiInterface interface must be implemented to create your own DI replacing the one provided by Phalcon or extending the current one. You can also utilize the Phalcon\Di\ServiceInterface to create your own implementations of services and how they resolve in the DI container.