DEV Community

Cover image for PHP - Create Dependency Injection Container PSR-11 like PHP-DI (DI)
F.R Michel
F.R Michel

Posted on • Updated on

PHP - Create Dependency Injection Container PSR-11 like PHP-DI (DI)

PHP Dependency Injection Container PSR-11

Install the standard interfaces for Container.:

composer require psr/container
Enter fullscreen mode Exit fullscreen mode

Let's create the class that will implement the containerInterface

<?php

namespace DevCoder\DependencyInjection;

use DevCoder\DependencyInjection\Exception\NotFoundException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;

class Container implements ContainerInterface
{
    /**
     * @var array
     */
    private $definitions = [];

    /**
     * @var array
     */
    private $resolvedEntries = [];

    public function __construct(array $definitions)
    {
        $this->definitions = array_merge(
            $definitions,
            [ContainerInterface::class => $this]
        );
    }

    /**
     * Finds an entry of the container by its identifier and returns it.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @return mixed Entry.
     * @throws ContainerExceptionInterface Error while retrieving the entry.
     *
     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
     */
    public function get($id)
    {
        if (!$this->has($id)) {
            throw new NotFoundException("No entry or class found for '$id'");
        }

        if (array_key_exists($id, $this->resolvedEntries)) {
            return $this->resolvedEntries[$id];
        }

        $value = $this->definitions[$id];
        if ($value instanceof \Closure) {
            $value = $value($this);
        }

        $this->resolvedEntries[$id] = $value;
        return $value;
    }

    /**
     * Returns true if the container can return an entry for the given identifier.
     * Returns false otherwise.
     *
     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @return bool
     */
    public function has($id)
    {
        return array_key_exists($id, $this->definitions) || array_key_exists($id, $this->resolvedEntries);
    }
}

Enter fullscreen mode Exit fullscreen mode

Let's create the class that will implement the NotFoundExceptionInterface

<?php

namespace DevCoder\DependencyInjection\Exception;

use Psr\Container\NotFoundExceptionInterface;

/**
 * Class NotFoundException
 * @package DevCoder\DependencyInjection\Exception
 */
class NotFoundException extends \InvalidArgumentException implements NotFoundExceptionInterface
{
}

Enter fullscreen mode Exit fullscreen mode

How to use ?

// services.php
<?php

use Psr\Container\ContainerInterface;

return [
    'database.host' => '127.0.0.1',
    'database.port' => null,
    'database.name' => '',
    'database.user' => 'root',
    'database.password' => null,
    'google.key' => 'YQ4FcwaXD165Xm72lx53qzzNzkz7AUUN',
    PDO::class => static function (ContainerInterface $container) {
        return new PDO(
            sprintf('mysql:host=%s;dbname=%s;', $container->get('database.host'), $container->get('database.name')),
            $container->get('database.user'),
            $container->get('database.password'),
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    },
];
Enter fullscreen mode Exit fullscreen mode
<?php

use DevCoder\DependencyInjection\Container;

$services = require 'services.php';
$container = new Container($services);

var_dump($container->get(PDO::class));
// object(PDO)[18]


var_dump($container->get('google.key'));
// YQ4FcwaXD165Xm72lx53qzzNzkz7AUUN
Enter fullscreen mode Exit fullscreen mode

Ideal for small project
Simple and easy!
https://github.com/devcoder-xyz/php-dependency-injection

How can we improve this object?

  • add autowire option to Auto resolution of constructor parameters

Top comments (5)

Collapse
 
dreadfulcode profile image
ƊŗęДdϝul Ȼʘɗɇ

Was this code even tested?

Declaration of App\Container::has($id) must be compatible with Psr\Container\ContainerInterface::has(string $id): bool in ...

This absolutely does not work. This tutorial is more of a service locator pattern with a PSR-11 container.

What not to do.

Collapse
 
fadymr profile image
F.R Michel

This code has been tested successfully! You are using a newer version of Psr\Container which introduced a return type of 'bool'. This did not exist at the time this tutorial was created! This is a tutorial and will remain a tutorial!

Collapse
 
dreadfulcode profile image
Info Comment hidden by post author - thread only accessible via permalink

Why isn't the version of the PSR container specified, if that's the case? This tutorial frustrates more than helps.

public function has($id)

Needs a return type. Are you sure you want to keep this up?

This code is broken. And fraught with errors. It is nothing like PHP-DI.

Collapse
 
dreadfulcode profile image
ƊŗęДdϝul Ȼʘɗɇ

PDO:class

You mean

PDO::class?

Collapse
 
fadymr profile image
F.R Michel

yes, thanks

Some comments have been hidden by the post's author - find out more