Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[VarDumper] Exception Caster Crashes on Twig Template Inspection #27921

Closed
@kiler129

Description

@kiler129

Symfony version(s) affected: 4.1 (maybe as low as 3.x)

Description
While collecting information for profiler exceptions and errors are converted to arrays. One of the errors in my log crashes ExceptionCaster, since it contains stacktrace frame with class referencing abstract class and in the same time without object.

ExceptionCaster blindly assumes that if something is a child of Twig_Template/Twig\Template it can be instantiated. However in reality I stepped into case where it's not true and stack trace contains direct reference to abstract class Vendor\Custom\TwigTemplate (which extends \Twig_Template) with method name referencing __toString(). ExceptionCaster attempts to initialize object of this class which leads to complete hard to debug crash.

How to reproduce
So far I'm unable to provide reproducer, since even stepping around the code with XDebug crashes PHP without any errors. The last frame received by \Symfony\Component\VarDumper\Caster\ExceptionCaster::castFrameStub contains reference class set as base_template_class and call to __toString(). The only special thing about this class is it contains call to @trigger_error(..., E_USER_DEPRECATED) within __toString().

The easiest way to reproduce this which I was able to find was to start with fresh 4.1 installation. Then:

  1. Configure base_template_class to point to custom class, e.g. base_template_class: \App\FooTwigBase
  2. Create custom base class:
abstract class FooTwigBase extends \Twig_Template
{
    public function __toString()
    {
        \trigger_error('Message reprimanding users to not cast templates to string etc...', \E_USER_DEPRECATED); //This is crucial part of the story!

        //In actual code I actually return template code
        return parent::__toString();
    }
}
  1. Execute (string)$te->loadTemplate('base.html.twig'); from e.g. controller action
  2. Scratch your head:
    image

Possible Solution
The easiest solution is to ensure no code calls loadTemplate() but recommended load(). This is however not possible since in some cases template code must be available for use (don't even ask...). This will mask the problem since is_subclass_of() in caster will never return true with abstract class, however I think the proper fix is actually simple. Another hack is to never ever trigger any errors in __toString, but this makes monitoring internal deprecations impossible.

I understand this is a massive edge-case from my side, however I think fixing it will be very easy from upstream.
\Symfony\Component\VarDumper\Caster\ExceptionCaster::castFrameStub contains the following code:

$template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class']));

When replaced with the following code safely skips problematic frame:

$template = null;
if (isset($f['object'])) {
    $template = $f['object'];
} elseif ((new \ReflectionClass($f['class']))->isInstantiable()) {
    $template = unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class']));
}
if (isset($template)) {
    $ellipsis = 0;
    //...

//Edit: provided way to reproduce

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions