Zubair Mohsin

Why Laravel requires `psr/container` package?

From the About section of psr/container package on GitHub:

This repository holds all interfaces related to PSR-11 (Container Interface). Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container.

If we look at the code in this repository we can see that there are only three interface files.

  • ContainerInterface
  • ContainerExceptionInterface
  • NotFoundExceptionInterface

Let's take a look at ContainerInterface interface file:

<?php

declare(strict_types=1);

namespace Psr\Container;

/**
 * Describes the interface of a container that exposes methods to read its entries.
 */
interface ContainerInterface
{
    /**
     * Finds an entry of the container by its identifier and returns it.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
     * @throws ContainerExceptionInterface Error while retrieving the entry.
     *
     * @return mixed Entry.
     */
    public function get(string $id);

    /**
     * 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(string $id): bool;
}

It has two method signatures. get() and has(). Any class that implements that interface must have definitions for these two methods. Let's see if Laravel's container has these methods.

Laravel Container

I am assuming every Laravel Developer knows that there's a Container. It's simply a class somewhere in the framework code. Let's perform a search in our code editor. We find two files named Container.

  • Illuminate\Contracts\Container\Container.php , this is an interface
  • Illuminate\Container\Container , this is an implementation of above interface

If we look at the above mentioned Container interface file we can see that Laravel is extending the ContainerInterface from psr/container package and also adds some method signatures of its own.

<?php

namespace Illuminate\Contracts\Container;

use Closure;
use Psr\Container\ContainerInterface;

interface Container extends ContainerInterface
{
    /**
     * Determine if the given abstract type has been bound.
     *
     * @param  string  $abstract
     * @return bool
     */
    public function bound($abstract);
}

Now let's take a look at Laravel's Container implementation class.

<?php

namespace Illuminate\Container;

use Illuminate\Contracts\Container\Container as ContainerContract;

class Container implements ContainerContract
{
    /**
     *  {@inheritdoc}
     */
    public function get($id)
    {
        try {
            return $this->resolve($id);
        } catch (Exception $e) {
            if ($this->has($id) || $e instanceof CircularDependencyException) {
                throw $e;
            }

            throw new EntryNotFoundException($id, $e->getCode(), $e);
        }
    }

    /**
     *  {@inheritdoc}
     */
    public function has($id)
    {
        return $this->bound($id);
    }

   /**
     * Determine if the given abstract type has been bound.
     *
     * @param  string  $abstract
     * @return bool
     */
    public function bound($abstract)
    {
        return isset($this->bindings[$abstract]) ||
            isset($this->instances[$abstract]) ||
            $this->isAlias($abstract);
    }
}

We can see that this class implements get() and has() method from ContainerInterface of psr/container package.

But, Why?

The real question here is, why go through all this trouble? Laravel team could have just made these interfaces and they wouldn't have to add another dependency to the framework?

PHP-FIG

PHP-FIG stands for PHP Framework Interop Group.

From FAQ section of website, aims of PHP-FIG:

  • The idea behind the group is for project representatives to talk about the commonalities between our projects and find ways we can work together. Our main audience is each other, but we’re very aware that the rest of the PHP community is watching. If other folks want to adopt what we’re doing they are welcome to do so, but that is not the aim. Nobody in the group wants to tell you, as a programmer, how to build your application.

To understand it better, consider the following:

  • Wouldn't it better if all PHP frameworks would follow a common definition of Container class? PHP-FIG has a recommended definition of the Container class and Laravel, Symfony and Yii ( to name a few frameworks ) follow this recommendation.

PSR

PSR stands for PHP Standards Recommendation. That's what PHP-FIG does, just provide Standards Recommendations. Any project ( framework ) is welcome to adopt this standard.

You can find the list of all PHP Standard Recommendations in numerical order here.

The numbers concatenated to PSR like PSR-4 and PSR-11 just represent the order in which these standards were submitted/approved.

Conclusion

In order to comply with PHP-FIG provided PSR-11 Container Interface recommendation, Laravel requires the psr/container package.

Interesting facts about psr/container in Laravel context

  • Pull request for making Laravel's container compliant to PSR-11 Container Interface was opened by Marco Aurélio Deleu on 29 June, 2017 and was approved by Taylor Otwell.
  • It was added in Laravel v5.5
  • Laravel Container didn't have get() and has() method before compliance with PSR-11 Container Interface.
  • make() and bound() methods were available alternatives on Laravel Container.

I hope you enjoyed this post. Next, we will see how Laravel uses psr/simple-cache package. You can follow me on Twitter or join my newsletter for more content.