Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EasyAdmin is not compatible with RoadRunner PHP app server #5591

Closed
speller opened this issue Jan 24, 2023 · 5 comments · May be fixed by #6209
Closed

EasyAdmin is not compatible with RoadRunner PHP app server #5591

speller opened this issue Jan 24, 2023 · 5 comments · May be fixed by #6209

Comments

@speller
Copy link

speller commented Jan 24, 2023

Describe the bug

RoadRunner is a powerful replacement to the standard FPM which keeps PHP processes running. This makes PHP apps lightning-fast. The only drawback is that all PHP code must be stateless and not rely on global and static variables, all objects must be destroyed after the request. EasyAdmin seems to be not following these requirements and admin pages fail randomly with the following error:

LogicException:
Unable to add global "ea" as the runtime or the extensions have already been initialized.

  at vendor/twig/twig/src/Environment.php:769
  at Twig\Environment->addGlobal('ea', object(AdminContext))
     (vendor/easycorp/easyadmin-bundle/src/EventListener/AdminRouterSubscriber.php:87)
  at EasyCorp\Bundle\EasyAdminBundle\EventListener\AdminRouterSubscriber->onKernelRequest(object(RequestEvent), 'kernel.request', object(TraceableEventDispatcher))
     (vendor/symfony/event-dispatcher/Debug/WrappedListener.php:115)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(object(RequestEvent), 'kernel.request', object(TraceableEventDispatcher))
     (vendor/symfony/event-dispatcher/EventDispatcher.php:206)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners(array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.request', object(RequestEvent))
     (vendor/symfony/event-dispatcher/EventDispatcher.php:56)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch(object(RequestEvent), 'kernel.request')
     (vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:127)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch(object(RequestEvent), 'kernel.request')
     (vendor/symfony/http-kernel/HttpKernel.php:139)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:74)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:184)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (vendor/baldinof/roadrunner-bundle/src/Http/KernelHandler.php:44)
  at Baldinof\RoadRunnerBundle\Http\KernelHandler->handle(object(Request))
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:97)
  at Baldinof\RoadRunnerBundle\Http\Runner->getResponse(object(Generator), 'Baldinof\\RoadRunnerBundle\\Http\\KernelHandler::handle()')
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:75)
  at Baldinof\RoadRunnerBundle\Http\Runner->handle(object(Request))
     (vendor/baldinof/roadrunner-bundle/src/Integration/Doctrine/DoctrineORMMiddleware.php:61)
  at Baldinof\RoadRunnerBundle\Integration\Doctrine\DoctrineORMMiddleware->process(object(Request), object(Runner))
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:97)
  at Baldinof\RoadRunnerBundle\Http\Runner->getResponse(object(Generator), 'Baldinof\\RoadRunnerBundle\\Integration\\Doctrine\\DoctrineORMMiddleware::process()')
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:83)
  at Baldinof\RoadRunnerBundle\Http\Runner->handle(object(Request))
     (vendor/baldinof/roadrunner-bundle/src/Http/MiddlewareStack.php:37)
  at Baldinof\RoadRunnerBundle\Http\MiddlewareStack->handle(object(Request))
  at Generator->current()
     (vendor/baldinof/roadrunner-bundle/src/Worker/Worker.php:111)
  at Baldinof\RoadRunnerBundle\Worker\Worker->start()
     (vendor/baldinof/roadrunner-bundle/src/Runtime/Runner.php:27)
  at Baldinof\RoadRunnerBundle\Runtime\Runner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/app/vendor/autoload_runtime.php')
     (public/index.php:5)                

To Reproduce
Try using easyadmin under RR.

It would be nice if EA will fix this.

@ksn135
Copy link
Contributor

ksn135 commented Jan 24, 2023

👍

@kiler129
Copy link
Contributor

kiler129 commented Jan 27, 2023

That isn't so much about EAB but Twig, or more so how your application uses it. EAB registers kernel.request event (\EasyCorp\Bundle\EasyAdminBundle\EventListener\AdminRouterSubscriber::onKernelRequest) and simply adds a global Twig variable. This can only happen before extensions and runtime are initialized (\Twig\ExtensionSet::initExtensions). Something is triggering this before onKernelRequest happens in your app, which means that EAB cannot add global variable. What's crucial, Twig will let you change the variable, despite the misleading addGlobal prefix. However, adding new globals is impossible once the environment initializes.

From my experience this will cause problems with a lot of libraries, as adding Twig globals in onKernelRequest is pretty standard. I would start from looking what causes the init in your case. As a dirty workaround you can initialize Twig global variable ea with a null before the extension set is compiled. You can even do that with a custom twig extension or event listener... but this is a hack ;)

@javiereguiluz
Copy link
Collaborator

@speller thanks for reporting this, but I'm afraid we can't do anything on our side. We don't use RoadRunner and we don't have resources to try it or debug this.

Luckily, according to @kiler129 message, this could be not an issue with EasyAdmin itself but more related to Twig global variables. I hope you can find a solution, even if it's a bit hackish. Sorry!

@AleksSem
Copy link

AleksSem commented Dec 4, 2023

@javiereguiluz probably EA shouldn't use twig globals to store context data

@GromNaN
Copy link

GromNaN commented Mar 18, 2024

Related issue twigphp/Twig#4007

I think we need to modify the way the global variable is declared. The Twig environment should not be initialized differently depending on the request.

public function getGlobals(): array
{
$context = $this->adminContextProvider->getContext();
// when there's an admin context, make it available in all templates as a short named variable
return null === $context ? [] : ['ea' => $context];
}

The ea variable can be replaced with a Twig function that would to the same as AdminContextProvider::getContext().

return null !== $currentRequest ? $currentRequest->get(EA::CONTEXT_REQUEST_ATTRIBUTE) : null;

This function signature would require the current request from the global app instead of dependency injection of RequestStack:

# src/Resources/views/crud/field/array.html.twig
- {% if ea.crud.currentAction == 'detail' %}
+ {% if ea_context(app.request).crud.currentAction == 'detail' %}

@javiereguiluz Can you re-open this issue please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants