V8-archive: Cannot resolve custom extension class

Created on 21 Dec 2018  Â·  17Comments  Â·  Source: directus/v8-archive

Hi,
I'm creating an extension class which will be called from Directus hook. Some code logic is omitted for simplicity.
public/extensions/custom/hooks/presentations/AfterCreatePresentation.php

<?php

namespace Directus\Custom\Hooks\Presentations;

use Directus\Application\Application;
use Directus\Mail\Message;
use function Directus\send_mail_with_layout;
use Directus\Services\UsersService;

class AfterCreatePresentation
{
    protected $container = null;

    public function __invoke($data = null)
    {
        $this->container = Application::getInstance()->getContainer();
        $userService = new UsersService($this->container);
        $users = $userService->findAll();

        foreach ($users['data'] as $user) {
            if ($user['email_notifications'] == true){
                send_mail_with_layout(
                  ...
                );
            }
        }
    }
}

And the hook is registered in config/api.php as:

...
    'hooks' => [
        'actions' => [
            'item.create.presentations' => new \Directus\Custom\Hooks\Presentations\AfterCreatePresentation()
        ],
        'filters' => [],
    ],
...

However the following error is being thrown:

2018/12/20 14:32:07 [error] 10498#10498: *123657 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught Error: Class 'Directus\Custom\Hooks\Presentations\AfterCreatePresentation' not found in /var/www/api.elisiondesign.cz/config/api.php:114
Stack trace:
#0 /var/www/api.elisiondesign.cz/src/web.php(43): require()
#1 /var/www/api.elisiondesign.cz/public/index.php(3): require('/var/www/api.el...')
#2 {main}
  thrown in /var/www/api.elisiondesign.cz/config/api.php on line 114" while reading response header from upstream, client: 89.102.32.192, server: api.elisiondesign.cz, request: "GET /server/ping HTTP/1.1", upstream: "fastcgi://unix:/run/php/php7.0-fpm.sock:", host: "api.elisiondesign.cz", referrer: "https://app.elisiondesign.cz/"

It works perfectly on my system MacOS, but when deployed to production (Ubuntu) it throws the error. I was tying to figure out some differences between the system and the most important is that Ubuntu is case-sensitive system, while MacOs is not. This would explain the not found problem. So I've tried to put the file in the folders named with uppercase /Custom/Hooks/Presentations to help the autoloader find the class in the namespace, but with no effect.

NOTE: The example hook for 'products' throws the same error when enabled.

  • OS: [Ubuntu 16.04]
  • Web Server: [nginx/1.14.1 ]
  • PHP Version: [ 7.0.32]
  • Database: [ MySQL 5.7.24]
  • API [2.0.12]
enhancement documentation

Most helpful comment

We agree. @rijkvanzanten is starting in on the 7.1 refactor in a few days, so it'd be great to get as much input as possible into how the community thinks we should streamline the extensions, Docs, code structure — and therefore the development process.

All 17 comments

I don't want to give any conclusion but I am start to believe it's because of the case sensitive nature of ubuntu.

I will try this on a ubuntu machine and see if I can reproduce this.

Any update on this @WellingGuzman?

No update. I haven't worked on this yet.

Hi, got any update on this? :)
I would like to get this resolved, cause sadly it's blocking us in any further development 😞 Tell me if you need any help or reach me on slack.
Thanks!

Hey @WellingGuzman,
I did a bit of investigation and the problem appears to be in the build branch only. On the Ubuntu server, I've checked out from the build branch to master, did composer install and in /extenstions foldernpm run build and the hooks classes are working perfectly.
Checking back to build again breaks the api though.

@honzabilek4 Could it be that the autoload.php file doesn't read your newly created file? The autoload file is generated using authoritative class maps which doesn't read the actual file system but instead relies on a pre-made map of all the files that (should) be there. If you added a new file after that and try to load it in using Composer's autoload, it might fail.

Could you try installing the dependencies using composer install -a in your test and see if it still works?

Well, yeah I've figured it out in the process 😆. This fixes the problem. I understand that this option might make the entire application faster, but it's not very convenient for deploying extensions.
Maybe we should mention it in the docs?

Gotcha! If it messes up deploying extensions, it's not a suitable way of building the API. I wish Composer has a way to have the best of both worlds?

I believe the composer dump-autoload should works in this case to, to update the whole autoloader.

@honzabilek4 @WellingGuzman Should I create the bundle without the -a optimization flag to prevent this from happening?

I closed this ticket with some notes about reinstalling the composer.json file.

I am going to add a note on update it when there's a composer.json

@rijkvanzanten I don't know what actually install -a does, but to me it seems running composer dump-autoload should do it, as the problem here seems to be an incompatible autoloader. So I believe it depends always on where it was built and where is going to be used, if those are incompatible the result will be the same.

@WellingGuzman Normally, composer will read the file system and read the files based on that. The -a flag will create a file listing all other files, so it doesn't have to read the file system on runtime. This will speed up the API pretty drastically, but also means that it doesn't "see" new files added

Since the composer.json file is missing in the build branch, it's not possible to run the mentioned commands in this branch. You have to create the file manually, which adds more overhead to the initial configuration.
I'm imagining being a newcomer trying to develop a new extension class, it might be pretty confusing to do so:

  1. I'll create a new extension and upload it to custom extensions folder
  2. Class is not found and the whole API breaks, so I spend some amount of time trying to find out why it works only on localhost.
  3. I discover this issue in Github (or some note in docs), so I'll try to run the composer commands, but composer.json not found.
  4. Have to manually copy the file and run composer install.
  5. Finally the API is back again.

That's seems to be a pretty tedious process. I'm also not sure how to avoid this down time without redeploying the entire api instance.

I think it’s important to make the extension development workflow more streamlined, so it’s easy to understand for other developers. Some additional info in the docs might help as well.

We agree. @rijkvanzanten is starting in on the 7.1 refactor in a few days, so it'd be great to get as much input as possible into how the community thinks we should streamline the extensions, Docs, code structure — and therefore the development process.

I will close this in favor of https://github.com/directus/directus/issues/2273. As soon as that one is closed the issue will be also resolved. Any new related inconvenience should be discussed there to keep the discussion in one place.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

magikstm picture magikstm  Â·  3Comments

rijkvanzanten picture rijkvanzanten  Â·  3Comments

cdwmhcc picture cdwmhcc  Â·  3Comments

benhaynes picture benhaynes  Â·  4Comments

ondronix picture ondronix  Â·  3Comments