silexphp / Silex-WebProfiler

MIT License
210 stars 61 forks source link

Calling Debug::enable() disables var-dumper support in WebProfiler. #95

Closed jlayt closed 6 years ago

jlayt commented 8 years ago

Calling Debug::enable() disables the var-dumper support in WebProfiler. This is due to the following code in WebProfileServiceProvider:

$app['profiler.templates_path.debug'] = function () {
    foreach (spl_autoload_functions() as $autoloader) {
        if (!is_array($autoloader) || !method_exists($autoloader[0], 'findFile')) {
            continue;
        }
        if ($file = $autoloader[0]->findFile('Symfony\Bundle\DebugBundle\DebugBundle')) {
            return dirname($file).'/Resources/views';
        }
    }
};

Calling Debug::enable() replaces the autoloader with DebugClassLoader which lacks a findFile() method, so the template is never found and the data collector never added, kind-of defeating the whole purpose!

dagomar commented 8 years ago

I have the same issue.

jlayt commented 8 years ago

A slightly hacky workaround in the WebProfileServiceProvider class is to change the above code to be:

$app['profiler.templates_path.debug'] = function () {
    foreach (spl_autoload_functions() as $autoloader) {
        if (!is_array($autoloader) || !is_array($autoloader[0]->getClassLoader()) || !method_exists($autoloader[0]->getClassLoader()[0], 'findFile')) {
            continue;
        }

        if ($file = $autoloader[0]->getClassLoader()[0]->findFile('Symfony\Bundle\DebugBundle\DebugBundle')) {
           return dirname($file).'/Resources/views';
        }
    }
};

The real fix should be in DebugClassLoader though.

CarsonF commented 7 years ago

Why not use reflection here, like is done for the other paths?

$app['profiler.templates_path.debug'] = function () {
    $r = new \ReflectionClass(DebugBundle::class);
    return dirname($r->getFileName()).'/Resources/views';
};
jlayt commented 7 years ago

Nice idea, just tried it, but DebugBundle extends Bundle which requires Symfony\Component\DependencyInjection to be installed, which normally isn't for Silex, so the reflection throws an exception. The best fix is for DebugClassLoader to get a findFile method. Ideally the twig would actually live in the Debug component instead, but that would probably break stuff.

CarsonF commented 7 years ago

Thanks @jlayt, I thought there might be something I was missing :smile:

jlayt commented 7 years ago

A far neater workaround, doing it exactly the same way as 'profiler.templates_path' gets set:

$this['profiler.templates_path.debug'] = function () {
    $r = new \ReflectionClass('Symfony\Bundle\DebugBundle\DependencyInjection\Configuration');
    return dirname(dirname($r->getFileName())).'/Resources/views';
};
CarsonF commented 7 years ago

@jlayt Wouldn't that require ConfigurationInterface from symfony/config (which is not a required dependency)?

macintoshplus commented 7 years ago

Hi, I have the same issue. But the panel "Debug" is loaded if the debug bundle is installed "symfony/debug-bundle".

But, the panel is empty. How to work ?

Mingun commented 7 years ago

After about a five hours digging a code I made it works. For this need doing some steps:

  1. Adding to project package symfony/debug-bundle:

    composer require symfony/debug-bundle --dev

    This is need for template files for Debug panel. No other resources from it not using, only templates from vendor\symfony\debug-bundle\Resources\views\Profiler.

  2. As @jlayt says, in file vendor\silex\web-profiler\Silex\Provider\WebProfilerServiceProvider.php need change defining service 'profiler.templates_path.debug' to either the same way as 'profiler.templates_path' (need installed package symfony/config thought):

       $app['profiler.templates_path.debug'] = function () {
           $r = new \ReflectionClass('Symfony\Bundle\DebugBundle\DependencyInjection\Configuration');
    
           return dirname(dirname($r->getFileName())).'/Resources/views';
       };

    or tuning current implementation:

       $app['profiler.templates_path.debug'] = function () {
            // This code cannot be simplified as all classes in the bundle depend
            // on packages that are not required by Silex
            foreach (spl_autoload_functions() as $autoloader) {
                if (!is_array($autoloader) || !is_array($autoloader[0]->getClassLoader()) || !method_exists($autoloader[0]->getClassLoader()[0], 'findFile')) {
                    continue;
                }
    
                if ($file = $autoloader[0]->getClassLoader()[0]->findFile('Symfony\Bundle\DebugBundle\DebugBundle')) {
                    return dirname($file).'/Resources/views';
                }
            }
       };

    This is need for find template files from step 1. Now Debug panel appears in Web Profiler, but it always empty. So we need step 3:

  3. In file vendor\symfony\http-kernel\EventListener\DumpListener.php change method getSubscribedEvents() to this:

    use Symfony\Component\HttpKernel\KernelEvents;
    
    // ...
    
       public static function getSubscribedEvents()
       {
           // Register early to have a working dump() as early as possible
           $events = array(KernelEvents::REQUEST => array('configure', 1024));
           if (class_exists(ConsoleEvents::class)) {
               $events[ConsoleEvents::COMMAND] = array('configure', 1024);
           }
    
           return $events;
       }

    We added listener to event KernelEvents::REQUEST. Now dumps will be appears in Debug panel. Global function dump() will refer to service 'var_dumper.data_collector', which doing all work.

Note, that dumps from twig templates do not appear in Debug panel, because they working directly through HtmlDumper.

Yes, this is ugly hack but it works... for a while.