DEV Community

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

Posted on • Edited 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!

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