skip to Main Content

I’m on a Laravel project using new-ish versions of PHP, Laravel and Composer 2, as of this writing. I added a new app/Traits/MyTrait.php file beside several existing trait files but unfortunately Composer absolutely will not detect the new file. I’m getting this error:

Trait 'AppTraitsMyTrait' not found

Similar to:

Laravel Custom Trait Not Found

Here is the general layout of the code:

# app/Traits/MyTrait.php:
<?php

namespace AppTraits;

trait MyTrait {
    // ...
}
# app/Notifications/MyBaseClass.php:
<?php

namespace AppNotifications;

use AppTraitsMyTrait;

class MyBaseClass
{
    use MyTrait;

    // ...
}
# app/Notifications/MyChildClass.php
<?php

namespace AppNotifications;

class MyChildClass extends MyBaseClass
{
    // ...
}

The weird thing is that this code runs fine in my local dev, but no matter what I try, it won’t work when deployed to the server while running in a Docker container. I’ve tried everything I can think of like saving "optimize-autoloader": true in composer.json and running composer dump-autoload -o during deployment, but nothing fixes it:

https://getcomposer.org/doc/articles/autoloader-optimization.md

I’m concerned that this inheritance permutation may not have been tested properly by Composer or Laravel, so this may be a bug in the tools. If worse comes to worse, I’ll try these (potentially destructive) workarounds:

  • Calling composer dump-autoload -o (greatly slows deployment, as this is a large project, and so far doesn’t seem to fix it anyway)
  • Deleting via rm vendor/composer/autoload_classmap.php, rm vendor/composer/autoload_psr4.php and/or rm vendor/composer/autoload_namespaces.php (or similar) in the vendor folder before each deployment to force Composer to rebuild.
  • Deleting via rm -rf vendor

The sinister part about this is that we must have full confidence in our deploy process. We can’t hack this in our server dev environments by manually deleting stuff like vendor and then have it fail in the production deploy because Composer tripped over stale data in its vendor folder. My gut feeling is that this is exactly what’s happening, perhaps due to an upgrade from Composer 1 to Composer 2 or version change or stale cache files from work in recent months.

Even a verification like "this minimal sample project deployed to Docker works for us" would help to narrow this down thanks.

Edit: this is a useful resource on how the Composer autoloader works: https://jinoantony.com/blog/how-composer-autoloads-php-files

2

Answers


  1. Chosen as BEST ANSWER

    The problem turned out to be caused by the container/filesystem on AWS being case-sensitive, but my local dev environment on macOS being case-insensitive.

    My original trait (kept secret) ended with URL in its name, but I was including its path as, and using it in the base class as, Url.

    So this issue had nothing to do with traits, base classes or Composer. It also didn't require any modification of composer.json or the way we call it during deployment. But I think it's still best practice to have this in composer.json, I use it this way in local dev too currently (good/bad?):

        "config": {
            "optimize-autoloader": true
        },
    

    The real problems here (industry-wide) are:

    • Vague error messages
    • Lack of effort by code to drill down and find actual causes (by attempting to load as case-insensitive and returning a warning when found, for example)
    • Lack of action items for the user (have you checked the case? checked that the file exists? checked file permissions? etc etc, written into the error message itself, with perhaps a link to a support page/forum)

    It wasn't convenient to ssh into the server (by design). So to troubleshoot, I temporarily committed this onto my branch:

    # app/Http/Controllers/TestController.php
    class TestController extends Controller
    {
        public function test()
        {
            return response('<pre>' . 
                '# /var/www/html/vendor/composer/autoload_classmap.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_classmap.php') . "n" .
                '# /var/www/html/vendor/composer/autoload_files.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_files.php') . "n" .
                '# /var/www/html/vendor/composer/autoload_namespaces.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_namespaces.php') . "n" .
                '# /var/www/html/vendor/composer/autoload_psr4.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_psr4.php') . "n" .
                '# /var/www/html/vendor/composer/autoload_real.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_real.php') . "n" .
                '# /var/www/html/vendor/composer/autoload_static.php' . "n" . file_get_contents('/var/www/html/vendor/composer/autoload_static.php') . "n"
            );
        }
    }
    
    # routes/api.php
    Route::get('/test', 'TestController@test');
    

    Then deployed without merging in GitLab and compared the response to the error in AWS Cloudwatch, which is when the typo jumped out.

    Then I removed the temporary commit with:

    git reset --soft HEAD^
    

    And force-pushed my branch with:

    git push --force-with-lease
    

    So was able to solve this without affecting our CI/CD setup or committing code permanently to the develop or master branches.

    I've been doing this for a lot of years, and even suspected a case-sensitivity issue here, but sometimes we're just too close to the problem. If you're knee-deep in code and about to have an anxiety attack, it helps to have another set of eyes review your thought process with you from first principles.

    I also need to figure out how to run my local Docker containers as case-sensitive as well, to match the server (since that's the whole point of using Docker containers in the first place).


  2. I had the same problem and it was related to my file name. I had put it in lowercase at the beginning, that is: apiResponser.php. I added some changes and renamed my file to ApiResponser.php and sent it to production, but … oh, oh!

    I had the same problem.

    The only way it worked for me was, do the git name replacement:

    📦 git mv app/Traits/apiResponser.php app/Traits/ApiResponser.php
    

    This way I was able to solve. I understand that you have solved it in
    another way, however this may help another developer. 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search