Closed fjugalde closed 2 years ago
Same issue here. When I render view from twig then I can dump string, but when I put this string in configureAssets->addHtmlContentToBody()
Same issue here. When I render view from twig then I can dump string, but when I put this string in
configureAssets->addHtmlContentToBody()
Correct!.. i can dump the render view content as string too but after that, an exception is thrown with the above message details.
Please, try to debug this error around line 77 of AdminContextListener.php. See if there's a method to check if a Twig global exists, check if it already exists and check if adding the global conditionally solves your issue. Thanks!
Hi @javiereguiluz
Sorry for the delay in replying..
I made a debug just like you mention it above and inside the method ->addGlobal(...) there is a validation that checks if the extension is initialized and if the global value to being added didn't exist.
When i run my controller, it seems that executes two times the same addGlobal(...) method. I printed a dump() in the addGlobal(...) method inside the Environment.php file:
public function addGlobal(string $name, $value)
{
dump((new \DateTime())->format('H:i:s'));
dump('initialized:'.($this->extensionSet->isInitialized()?'true':'false'), 'array_key_exists:'.(!\array_key_exists($name, $this->getGlobals())?'true':'false'));
if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) {
throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
}
sleep(2);
if (null !== $this->resolvedGlobals) {
$this->resolvedGlobals[$name] = $value;
} else {
$this->globals[$name] = $value;
}
}
NOTE: I added a sleep(2) in order to stop execution for 2 sec and be able to differentiate the dump outputs.
And just before adding global in the AdminContextListener:
dump($this->twig->getGlobals());
$this->twig->addGlobal('ea', $adminContext);
And the output is
It's seems like before AdminContextListener is executed, the Context is added before in another file, and then, when the listener is executed, tries to add it again.
But the thing is that if we check the stack trace or the execution order, when the listener is executed, the twig environment seems to be different from the original because seems that does not contains the ea context added before his execution?
It's very strange.
This bug also happens using RoadRunner:
With the extension https://github.com/baldinof/roadrunner-bundle the first entry always makes the bug, just by reloading the page it lets continue.
With the extension https://github.com/php-runtime/roadrunner-symfony-nyholm the bug is persistent, it cannot be used with easyadmin.
UPD: there is no longer works, see https://github.com/EasyCorp/EasyAdminBundle/issues/3715#issuecomment-1828630373
This bug also happens using RoadRunner:
With the extension https://github.com/baldinof/roadrunner-bundle the first entry always makes the bug, just by reloading the page it lets continue.
With the extension https://github.com/php-runtime/roadrunner-symfony-nyholm the bug is persistent, it cannot be used with easyadmin.
I use swoole webserver and k911/swoole-bundle. Have the same issue. This helped me:
services.yaml:
App\Service\Twig\Environment:
decorates: twig
parent: twig
App\Service\Twig\Environment:
namespace App\Service\Twig;
use Twig\Environment as BaseEnvironment;
class Environment extends BaseEnvironment
{
private $presetGlobals = ['ea'];
public function setPresetGlobals(array $names)
{
$this->presetGlobals = $names;
}
public function addGlobal(string $name, $value)
{
$presetGlobals = $this->presetGlobals;
if (in_array($name, $presetGlobals)) {
$ref = (new \ReflectionObject($this))->getParentClass();
$globalsProperty = $ref->getProperty('globals');
$globalsProperty->setAccessible(true);
$resolvedGlobalsProperty = $ref->getProperty('resolvedGlobals');
$resolvedGlobalsProperty->setAccessible(true);
$globals = $globalsProperty->getValue($this);
$resolvedGlobals = $resolvedGlobalsProperty->getValue($this);
foreach ($presetGlobals as $global) {
if ($name == $global) {
if ($resolvedGlobals !== null) {
$resolvedGlobals[$global] = null;
} else {
$globals[$global] = null;
}
}
}
$globalsProperty->setValue($this, $globals);
$resolvedGlobalsProperty->setValue($this, $resolvedGlobals);
}
parent::addGlobal($name, $value);
}
}
@maxkain thanks for showing that code as an example of how to fix this.
Sadly, I can't merge any of that because I don't use this kind of systems, so I will never be able to debug issues or fix/maintain this.
Also, this looks like a generic problem of Twig globals when using this kind of long-running systems. Maybe it's something that Twig could fix/improve in their core?
In any case, closing for the reasons mentioned earlier. Thanks for understanding!
@maxkain shouldn't be the issue created on Twig itself to have a mechanism to help twig being compatible with roadrunner/bref/swoole ?
For anyone having this issue on EA4
Somehow >addHtmlContentToBody() just breaks.
Instead create templates/bundles/EasyAdminBundle/layout.html.twig
And include your content like this:
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{% extends '@!EasyAdmin/layout.html.twig' %}
{% block configured_body_contents %}
{{ parent() }}
# Add custom HTML to body here
{% include 'components/logout/_logout.html.twig' %}
{% include 'components/logout/_session-expired.html.twig' %}
{% endblock %}
Last solution no longer works. There is new. But as I understand, the right solution is to put a service into globals instead of 'ea', which will fetch the data.
App\Service\Twig\Environment:
parent: twig
decorates: twig
namespace App\Service\Twig;
use EasyCorp\Bundle\EasyAdminBundle\Twig\EasyAdminTwigExtension;
use Twig\Environment as BaseEnvironment;
use Twig\Extension\GlobalsInterface;
class Environment extends BaseEnvironment
{
private $extensionsToRefreshGlobals = [EasyAdminTwigExtension::class];
public function mergeGlobals(array $context): array
{
foreach ($this->getExtensions() as $class => $extension) {
if ($extension instanceof GlobalsInterface && in_array($class, $this->extensionsToRefreshGlobals)) {
$this->refreshGlobals($extension->getGlobals());
}
}
return parent::mergeGlobals($context);
}
private function refreshGlobals(array $freshGlobals): void
{
$ref = (new \ReflectionObject($this))->getParentClass();
$globalsProperty = $ref->getProperty('globals');
$globalsProperty->setAccessible(true);
$resolvedGlobalsProperty = $ref->getProperty('resolvedGlobals');
$resolvedGlobalsProperty->setAccessible(true);
$extensionSetProperty = $ref->getProperty('extensionSet');
$extensionSetProperty->setAccessible(true);
$extensionSet = $extensionSetProperty->getValue($this);
$refExtensionSet = (new \ReflectionObject($extensionSet));
$extensionSetGlobalsProperty = $refExtensionSet->getProperty('globals');
$extensionSetGlobalsProperty->setAccessible(true);
$globals = $globalsProperty->getValue($this);
$resolvedGlobals = $resolvedGlobalsProperty->getValue($this);
$extensionSetGlobals = $extensionSetGlobalsProperty->getValue($extensionSet);
$globals = array_merge($globals, $freshGlobals);
if ($resolvedGlobals !== null) {
$resolvedGlobals = array_merge($resolvedGlobals, $freshGlobals);
}
if ($extensionSetGlobals !== null) {
$extensionSetGlobals = array_merge($extensionSetGlobals, $freshGlobals);
}
$globalsProperty->setValue($this, $globals);
$resolvedGlobalsProperty->setValue($this, $resolvedGlobals);
$extensionSetGlobalsProperty->setValue($this, $extensionSetGlobals);
}
}
UPD: I 've created fix for admin context but it is related to EA 4. So i posted it there, as more more suitable issue for it https://github.com/EasyCorp/EasyAdminBundle/issues/5986#issuecomment-1857725801
Yet another way to fix this issue: use twig extension for "ea" instead of globals, and set it in layout template like this: {% set ea = ea() %}
@maxkain I'm not sure if this will work in nested blocks and macros because you are redefining a variable in the template scope, not globally. However, I could be wrong.
UPD: I 've created fix for admin context but it is related to EA 4. So i posted it there, as more more suitable issue for it #5986 (comment)
@misterx, I have tried it. Sometimes it works, sometimes there is error: request.CRITICAL: Uncaught PHP Exception Twig\Error\RuntimeError: "An exception has been thrown during the rendering of a template ("Twig\Environment::getTemplateClass(): Argument #1 ($name) must be of type string, null given, called in ...vendor/twig/twig/src/Template.php on line 319")." at index.html.twig line 4
Describe the bug I'm getting this error when trying to exec $this-renderView(...) in the EntityCrudController. The errors it seems to be in the line 77 of AdminContextListener.php
This method is from the Environment.php file of twig. And inside this method (Line 747) there is a validation that is throwing this error in line 750:
So it seems like the global variable already exists in twig globals.
Have anyone getting this error when executes the $this-renderView(...) method?
Thanks so much in advance.
Francisco Ugalde
To Reproduce In a single controller extending from AbstractController, add a method with a Route annotation, Add a return like:
Additional Context I'm trying to generate a html content from a twig template on the crud method configureAssets(...) and to pass as param in the ->addHtmlContentToBody() method.