PHP-DI Can't inject created class with constructor parameters

1.9k views Asked by At

I try to make a project with PHP-DI but i have a problem.

This is the trace:

trace

Here is the code:

Container class:

$this->containerBuilder->addDefinitions([
        'Configuration' => DI\Object('Hetwan\Core\Configuration')->constructor($this->getRootDir().'/app/config/config.yml'),
        'Database' => DI\object('Hetwan\Core\Database')
]);

Configuration class:

namespace Hetwan\Core;

use Symfony\Component\Yaml\Yaml;

class Configuration
{   
private $attrs;

public function __construct($configFilePath)
{
    $this->attrs = Yaml::parse(file_get_contents($configFilePath));
}

public function get($attrPath)
{
    $el = $this->attrs;

    foreach (explode('.', $attrPath) as $attr)
    {
        if (!isset($el[$attr]))
            throw new ConfigurationException("Unable to get '{$attrPath}'.\n");

        $el = $el[$attr];
    }

    return $el;
}
}

Database class:

namespace Hetwan\Core;

use Hetwan\Core\Configuration;

class Database
{
/**
 * @Inject
 * @var Configuration
 */
private $config;

private $entityManager;

public function create($entitiesPath)
{
    $dbParameters = [
        'driver' => 'pdo_mysql',
        'user' => $config->get('database.user'),
        'password' => $config->get('database.password'),
        'dbname' => $config->get('database.name')
    ];

    $this->entityManager = EntityManager::create($dbParameters, Setup::createAnnotationMetadataConfiguration([$entitiesPath], false));
}

public function getEntityManager()
{
    return $this->entityManager;
}
}

I can acces to $container->get('Configuration') without any problem and it works.

But at the creation of Database class i think PHP-DI try to recreate an instance of Configuration and i don't know why because an singleton instance is already here.

Thank you for the help!

1

There are 1 answers

2
IMSoP On BEST ANSWER

The annotation @var Configuration is likely to be interpreted as a class name, based on the file's namespace and use declarations, not the name of a service in the container.

Without any other clues, the automatic DI will presumably look for a service matching that fully-qualified class name, i.e. Hetwan\Core\Configuration. However, your service is registered as Configuration, so doesn't match. So the DI falls back to trying to construct an instance automatically using new \Hetwan\Core\Configuration(); but fails, because the constructor has a required parameter.

I suspect what you need to do is register your service with the fully-qualified name, like this:

$this->containerBuilder->addDefinitions([
        'Hetwan\Core\Configuration' => DI\Object('Hetwan\Core\Configuration')
            ->constructor($this->getRootDir().'/app/config/config.yml')
]);

Then when it goes to look for an instance of \Hetwan\Core\Configuration it will find that service, rather than trying to run the constructor.

The PHP-DI manual shows how you can simplify this using the magic constant ::class to:

use Hetwan\Core\Configuration;

$this->containerBuilder->addDefinitions([
        Configuration::class => DI\Object()
            ->constructor($this->getRootDir().'/app/config/config.yml')
]);

Alternatively, you can tell PHP-DI which service to inject, rather than having it guess based on the type of the property, according to the documentation on annotations:

/**
 * @Inject("Configuration")
 */
private $config;