First of all that is nice to know what is the concept of service container :)
"A Service Container (or dependency injection container) is simply a PHP object that manages the instantiation of services (i.e. objects)"
That is a short definition which i found it in "symfony.com"
But let's look at the code ... that is much better (if you want, you can find the project codes in My Github)
.....
class User {
private $mail;
public function __construct(Mail $mail)
{
$this->mail = $mail;
}
public function sendMail($username) {
$this->mail->sendMail($username);
}
}
class Mail {
private $mailSender;
public function __construct(MailSender $mailSender)
{
$this->mailSender = $mailSender;
}
public function sendMail($username) {
$this->mailSender->send($username);
}
}
class MailSender {
public function send($username) {
echo "Email was sent to ${username} \n";
}
}
.....
What is your idea if you want to make an object from user class :/
Maybe you should do somethings like below
$instance = new User(new Mail(new MailSender()));
$instance->sendMail("alex");
But what is your idea if you have one object that we call it container and it helps you to make the instance of object really easy ... one thing like this
$container = new Container;
$instance = $container->get('User');
$instance->sendMail("alex");
So ... let's go to make this theurgic class
1.Create Composer.json File
first of all create one json file like this
{
"name": "php/container",
"description": "That is a simple container service",
"type": "library",
"authors": [
{
"name": "azibom",
"email": "mrsh13610@gmail.com"
}
],
"require": {
"psr/container": "^1.0"
},
"autoload": {
"psr-4": {
"Php\\Container\\": "src/"
}
}
}
2.Create main.php File in the root of your project
we use it only for testing our container and it's really simple to understand
<?php
use Php\Container\Container;
require __DIR__ . '/vendor/autoload.php';
class User {
private $mail;
public function __construct(Mail $mail)
{
$this->mail = $mail;
}
public function sendMail($username) {
$this->mail->sendMail($username);
}
}
class Mail {
private $mailSender;
public function __construct(MailSender $mailSender)
{
$this->mailSender = $mailSender;
}
public function sendMail($username) {
$this->mailSender->send($username);
}
}
class MailSender {
public function send($username) {
echo "Email was sent to ${username} \n";
}
}
$container = new Container;
$instance = $container->get('User');
$instance->sendMail("alex");
$instance = new User(new Mail(new MailSender()));
$instance->sendMail("alex");
3.Create src folder in the root of the project and fill it like this
├── Container.php
└── Exceptions
├── ContainerException.php
└── NotFoundException.php
1 directory, 3 files
ContainerException.php
<?php
namespace Php\Container\Exceptions;
use Psr\Container\ContainerExceptionInterface;
use Exception;
/**
* Class could not be instantiated
*/
class ContainerException extends Exception implements ContainerExceptionInterface {}
NotFoundException.php
<?php
namespace Php\Container\Exceptions;
use Psr\Container\NotFoundExceptionInterface;
use Exception;
/**
* Class not found
*/
class NotFoundException extends Exception implements NotFoundExceptionInterface {}
Container.php which is the core of our project :)
<?php
namespace Php\Container;
use Psr\Container\ContainerInterface;
use Php\Container\Exceptions\NotFoundException;
use ReflectionClass;
use ReflectionException;
class Container implements ContainerInterface
{
private $services = [];
public function get($id)
{
$item = $this->resolve($id);
if (!($item instanceof ReflectionClass)) {
return $item;
}
return $this->getInstance($item);
}
public function has($id)
{
try {
$item = $this->resolve($id);
} catch (NotFoundException $e) {
return false;
}
if ($item instanceof ReflectionClass) {
return $item->isInstantiable();
}
return isset($item);
}
public function set(string $key, $value)
{
$this->services[$key] = $value;
return $this;
}
private function resolve($id)
{
try {
$name = $id;
if (isset($this->services[$id])) {
$name = $this->services[$id];
if (is_callable($name)) {
return $name();
}
}
return (new ReflectionClass($name));
} catch (ReflectionException $e) {
throw new NotFoundException($e->getMessage(), $e->getCode(), $e);
}
}
private function getInstance(ReflectionClass $item)
{
$constructor = $item->getConstructor();
if (is_null($constructor) || $constructor->getNumberOfRequiredParameters() == 0) {
return $item->newInstance();
}
$params = [];
foreach ($constructor->getParameters() as $param) {
if ($type = $param->getType()) {
$params[] = $this->get($type->getName());
}
}
return $item->newInstanceArgs($params);
}
}
4.Now you can run it
let's go to the root of your project and run these two commands
composer install
And then
php main.php
you should see
Email was sent to alex
Email was sent to alex
But how does it works
Two main function in container class are resolve and getInstance.
Resolve trying to make an object from ReflectionClass class ...
but what is this class "The ReflectionClass class reports information about a class."
so if you want more information you can check this link.
Now lets look at getInstace method
private function getInstance(ReflectionClass $item)
{
$constructor = $item->getConstructor();
if (is_null($constructor) || $constructor->getNumberOfRequiredParameters() == 0) {
return $item->newInstance();
}
$params = [];
foreach ($constructor->getParameters() as $param) {
if ($type = $param->getType()) {
$params[] = $this->get($type->getName());
}
}
return $item->newInstanceArgs($params);
}
At the first four lines we get constructor and then check if there is any constructor in the class or not ... but if we find it we do nice thing, we get the params from constructor and then try to make instance of them with get method which make me sure that we will not have any problem if we have a param in constructor which have param it self in its constructor(actually we are trying to make the instance of constructor params recursively) :)
And at the end we use newInstanceArgs for set constructor param and make a new object and then return it :))
Done
I hope you understand how does our simple service container works and if you have any questions feel free to ask them
And if you want you can find this project codes in My Github
have a nice time
Top comments (3)
Do you have any tutorial of how to create and understand the way best analogy of service providers? Most late i will learn reading this it article, for just now thank so much!
I personally look at the project in a big picture and seprate it to the diffrents bundles in my mind for example for example when you have a ecommerce project you can consider the order as a bundle and make a service provider for it and register all of your services which are related to the order in it (I use this strategy in the laravel for example but some frameworks like symfony have this bundles already)
how and when and from where we will use has() and set() method? what will be the use cases? may we know?