Search code examples
phpsymfonycomposer-phppsr-4

Error: The autoloader expected class ... to be defined in file. inSymfony 3.4


For the update to Symfony 4.0, I added "_default" and "AppBundle" to services.yml and verified the operation, but the following error occurred.
I have two bundles and want to achieve automatic wiring for each.
Is there anything wrong with it?
https://symfony.com/doc/4.0/service_container/3.3-di-changes.html

ErrorCode

The autoloader expected class "App\Sp\AppBundle\AppBundle" to   
  be defined in file "/home/vagrant/Symfony2/vendor/composer/../../src/App/Sp/AppBundle/AppBundle.php". The file was found but the class was not in it, the class name or namespace probably has a typo.  

services.yml

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    AppBundle\:
        resource: '../../src/App/Sp/AppBundle/*'
        exclude: '../../src/App/Sp/AppBundle/{Entity,Repository,AppBundle.php}'

    CommonBundle\:
        resource: '../../src/App/Sp/CommonBundle/*'
        exclude: '../../src/App/Sp/CommonBundle/{Entity,Repository}'

AppBundle.php

<?php
namespace App\Sp\AppBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AhiSpAdminBundle extends Bundle
{
}

Solution

  • Composer

    The composer.json PSR-4 autoloader in Symfony Flex by default applies a namespace for App from the /src path. The App namespace will apply to all of the subdirectories and files within the src/ directory.

    composer.json

        "autoload": {
            "psr-4": {
                "App\\": "src/"
            }
        },
    

    Symfony Naming Conventions

    Symfony naming conventions also require the *Bundle.php file to reside in a namespace of the same name [sic] and [sic]. This is comprised of the vendor or category name (Sp) and the namespace (AhiSpAdminBundle) to make SpAhiSpAdminBundle.

    | Namespace Bundle       | Class Name     |
    |------------------------|----------------|
    | Acme\Bundle\BlogBundle | AcmeBlogBundle |
    | Acme\BlogBundle        | AcmeBlogBundle |
    
    // src/Acme/BlogBundle/AcmeBlogBundle.php
    namespace App\Acme\BlogBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    
    class AcmeBlogBundle extends Bundle
    {
    }
    

    PSR-4 Autoloading

    Since the namespace is App\Sp\AhiSpAdminBundle, the filename and directory needs to be changed to match the namespace and Symfony bundle class name conventions respectively to src/Sp/AhiSpAdminBundle/SpAhiSpAdminBundle.php.

    Rename the file to match the class name for the PSR-4 autoloader.

    mv src/Sp/AppBundle/AppBundle.php src/Sp/AppBundle/SpAhiSpAdminBundle.php
    

    Rename directory to match the namespace for the PSR-4 autoloader.

    mv src/Sp/AppBundle src/Sp/AhiSpAdminBundle
    

    Symfony Services

    Fix the namespace and class name of your bundle to adhere to the Symfony naming conventions.

    // src/Sp/AhiSpAdminBundle/SpAhiSpAdminBundle.php
    namespace App\Sp\AhiSpAdminBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    
    class SpAhiSpAdminBundle extends Bundle
    {
      //...
    }
    

    Then update your services definitions to match the PSR-4 pathing that is compatible with Symfony.

    # config/services.yaml
    services:
        _defaults:
            autowire: true
            autoconfigure: true
            public: false
    
        App\Sp\AhiSpAdminBundle\:
            resource: '../src/Sp/AhiSpAdminBundle/*'
            exclude: '../src/Sp/AhiSpAdminBundle/{Entity,Repository,SpAhiSpAdminBundle.php}'
    
        App\Sp\CommonBundle\:
            resource: '../src/Sp/CommonBundle/*'
            exclude: '../src/Sp/CommonBundle/{Entity,Repository}'
    

    Cleaning Up

    Generate the autoloader files.

    composer dump-autoload
    

    Clear and Warmup the Syfmony Cache.

    rm -rf var/cache/dev
    bin/console --env=dev cache:warmup
    

    If needed repeat the process for the CommonBundle namespace, class name, and filename of src/Sp/CommonBundle/SpCommonBundle.php or restructure following the naming conventions to achieve the desired end-results.

    // src/Sp/CommonBundle/SpCommonBundle.php
    namespace App\Sp\CommonBundle;
    
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    
    class SpCommonBundle extends Bundle
    {
      //...
    }
    

    While the vendor/category name (Sp) is optional in the bundle class name, with non-descriptive names like AdminBundle and CommonBundle, it is best-practice to include the category in the bundle name, as to ensure there are no naming conflicts and to better differentiate between them when debugging and unit-testing.