user7941334
user7941334

Reputation:

Symfony 4: DependencyInjection Component - Uncaught ReflectionException: Class does not exist

I'm trying to implement the DependencyInjection Component from Symfony (version 4.0.0) into my project. For this I followed the following simple steps, in order to understand the autowiring process:

But I keep receiving the following error:

( ! ) Fatal error: Uncaught ReflectionException: Class does not exist in <path-to-my-project-root>/vendor/symfony/dependency-injection/ContainerBuilder.php on line 1051
( ! ) ReflectionException: Class does not exist in <path-to-my-project-root>/vendor/symfony/dependency-injection/ContainerBuilder.php on line 1051
Call Stack
#   Time    Memory  Function    Location
1   0.0160  354632  {main}( )   .../index.php:0
2   0.0166  357928  require_once( '<path-to-my-project-root>/bootstrap.php' ) .../index.php:7
3   0.0872  1752040 Symfony\Component\DependencyInjection\ContainerBuilder->get( )  .../bootstrap.php:16
4   0.0872  1752040 Symfony\Component\DependencyInjection\ContainerBuilder->doGet( )    .../ContainerBuilder.php:522
5   0.0872  1752816 Symfony\Component\DependencyInjection\ContainerBuilder->createService( )    .../ContainerBuilder.php:555
6   0.0873  1752928 __construct ( ) .../ContainerBuilder.php:1051

The error occurs at this line (1051) in ContainerBulder::createService method, because $definition->getClass() returns NULL:

private function createService(Definition $definition, array &$inlineServices, $id = null, $tryProxy = true) {
    // Line 1051:    
    $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));

    //..
}

In the chapter Automatic Service Loading in services.yaml, as I understood it, by using only those settings without any other settings in services.yaml, the DI container will know how to create an instance of MyController. Maybe I'm wrong?...

Could you please take a second to help me? Thank you very much.


My project consists of the following structure and files:

Project structure

bootstrap.php
composer.json
config/
    services.yaml
src/
    Controller/
        MyController
vendor/
    symfony/
        config/
        dependency-injection/
        filesystem/
        polyfill-mbstring/
        yaml/

composer.json

"require": {
    "php": ">=5.5.0",
    "symfony/dependency-injection": "^4.0",
    "symfony/config": "^4.0",
    "symfony/yaml": "^4.0",
},
"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
}

bootstrap.php

<?php

use App\Controller\MyController;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

require '../vendor/autoload.php';

$container = new ContainerBuilder();

$fileLocator = new FileLocator(__DIR__ . '/config');
$loader = new YamlFileLoader($container, $fileLocator);
$loader->load('services.yaml');

$myController = $container->get(MyController::class);

var_dump($myController);

config/services.yaml

services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        public: false       # Allows optimizing the container by removing unused services; this also means
                            # fetching services directly from the container via $container->get() won't work.
                            # The best practice is to be explicit about your dependencies anyway.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/*'
        exclude: '../src/{Entity,Migrations,Tests}'

src/Controller/MyController

<?php

namespace App\Controller;

class MyController {

    public function myAction() {
        echo 'Hello from MyController.';
    }

}

Update 1:

After container compilation with compile()

$loader->load('services.yaml');
$container->compile();

I received the following error:

( ! ) Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "App\Controller\MyController". in <my-project-path>/vendor/symfony/dependency-injection/ContainerBuilder.php on line 950
( ! ) Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "App\Controller\MyController". in <my-project-path>/vendor/symfony/dependency-injection/ContainerBuilder.php on line 950
Call Stack
#   Time    Memory  Function    Location
1   0.1512  357648  {main}( )   .../index.php:0
2   0.1675  361056  require_once( '<my-project-path>/bootstrap.php' )   .../index.php:7
3   3.5621  2582624 Symfony\Component\DependencyInjection\ContainerBuilder->get( ???, ??? ) .../bootstrap.php:18
4   3.5621  2582624 Symfony\Component\DependencyInjection\ContainerBuilder->doGet( ???, ???, ??? )  .../ContainerBuilder.php:522

I checked the $container->definitions array following the compile() call. I realised that all services (including App\Controller\MyController) saved in definitions list prior to compile() where removed from the array by the compilation process.

More of it: In $compiler->passConfig->log I found these entries (after compilation step):

[0] string  "Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "Psr\Container\ContainerInterface"; reason: private alias."
[1] string  "Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "Symfony\Component\DependencyInjection\ContainerInterface"; reason: private alias."
[2] string  "Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass: Inlined service "App\Service\MyService" to "App\Controller\MyController"."
[3] string  "Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "App\Controller\MyController"; reason: unused."
[4] string  "Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass: Removed service "App\Service\MyService"; reason: unused."

Upvotes: 4

Views: 4477

Answers (1)

Cerad
Cerad

Reputation: 48893

I went ahead and setup a fresh project.

There are two issues with your setup.

First you do need to compile the container before using it:

// bootstrap.php
$loader->load('services.yaml');

$container->compile();

$myController = $container->get(MyController::class);

And then you need to make your controller service public before pulling it from the container:

// services.yaml
App\:
    resource: 'src/*'
    exclude: 'src/{Entity,Migrations,Tests}'

App\Controller\:
    resource: 'src/Controller/*'
    public: true

This is basically what the framework does.

Upvotes: 13

Related Questions