Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.8] Updated the container for better PSR-11 ContainerInterface compliance #25678

Closed
wants to merge 1 commit into from

Conversation

Shawty81
Copy link

I think the current implementation of PSR-11 breaks the contract. The interface only describes the has() and get() methods but the has() returns false on autowired classes.

The docs for PSR-11 for the has() method state:

Returns true if the container can return an entry for the given identifier. Returns false otherwise.

Because the container can autowire classes it think it should return true in case of a class that it can autowire because it's able to return an entry for the given identifier even if it's unknown.

For example I've made a composer package that can run independently from any framework, as long as you provide a PSR-11 compliant Container. Currently the only way to get my code to work with the Laravel Container is to use the make() method as I refuse to bind hundreds (if not thousands) of classes from the multiple Laravel repositories I maintain and their respective required composer packages. As the make() method is not defined in the PSR and I can't use the has() method, this means the Laravel Container breaks the contract and I have to add some Laravel specific code.

I've made a simplified version of a Factory class to state whats wrong with the current implementation

<?php

namespace Vendor\Package;

class Factory {

    private $container;

    public function __construct(\Psr\Container\ContainerInterface $container)
    {

        $this->container = $container;

    }

    public function get($param)
    {

        // Do some stuff to generate a class name
        $class = $param;

        if ($this->container instanceof \Illuminate\Container\Container) {
            if (!class_exists($class)) {
                throw new ClassNotFoundException();
            }

            return $this->container->make($class);
        }
        else {
            if (!$this->container->has($class)) {
                throw new ClassNotFoundException();
            }

            return $this->container->get($class);
        }

    }

}

After the made changes the Factory doesn't require the Laravel specific code and will look like this:

<?php

namespace Vendor\Package;

class Factory {

    private $container;

    public function __construct(\Psr\Container\ContainerInterface $container)
    {

        $this->container = $container;

    }

    public function get($param)
    {

        // Do some stuff to generate a class name
        $class = $param;

        if (!$this->container->has($class)) {
            throw new ClassNotFoundException();
        }

        return $this->container->get($class);
    }

}

@laurencei laurencei changed the title Updated the container for better PSR-11 ContainerInterface compliance [5.8] Updated the container for better PSR-11 ContainerInterface compliance Sep 18, 2018
@laurencei
Copy link
Contributor

This looks like it is breaking one of the HTTP tests? Or it is Travis playing up?

@deleugpn
Copy link
Contributor

Relevant for this issue: #21335, #21327, laravel/ideas#803 and #19822.

As I've said, the PSR-11 text was ambiguous and led to the current implementation. AFAIK, class_exists will return true for abstract class, interfaces and traits, which are not auto wireable.

@taylorotwell
Copy link
Member

It's not true that just because a class exists we could necessarily resolve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants