PHP 8.4 improves Closure Naming for simplified debugging

PHP Performance

In applications that use closures excessively, understanding stack traces as part of the debugging experience has historically been complicated by the fact that the names of closures did not include the source location.

In a PHP stack trace, whenever a frame was represented by a closure, it only contained the reference {closure} and the namespace the closure was declared in, leading to all closures within a namespace looking identical.

Take this line of code, acting as a sort of wrapper trampoline for measuring execution time of a block of code in Shopware 6:

$response = Profiler::trace(
    'twig-rendering',
    fn () => $this->render($view, $event->getParameters(), new Response())
);

Before PHP 8.4, an exception thrown somewhere in “render” produced the following stack trace:

PHP Fatal error:  Uncaught RuntimeException in /root/index.php:32
Stack trace:
#0 /root/index.php(27): Shopware\Storefront\Controller\StorefrontController->render()
#1 /root/index.php(40): Shopware\Storefront\Controller\StorefrontController->Shopware\Storefront\Controller\{closure}()
#2 /root/index.php(26): Shopware\Core\Profiling\Profiler::trace()
#3 /root/index.php(18): Shopware\Storefront\Controller\StorefrontController->renderStorefront()
#4 /root/index.php(10): Shopware\Storefront\Controller\ErrorController->error()
#5 /root/index.php(7): Symfony\Component\HttpKernel\HttpKernel->handleRaw()
#6 /root/index.php(47): Symfony\Component\HttpKernel\HttpKernel->handle()
#7 {main}
  thrown in /root/index.php on line 32

When the code in StorefrontController::renderStorefront used multiple closures, this stack trace would not uniquely identify which closure was called.

With PHP 8.4, the closure name now contains the source location it was declared in:

PHP Fatal error:  Uncaught RuntimeException in /root/index.php:32
Stack trace:
#0 /root/index.php(27): Shopware\Storefront\Controller\StorefrontController->render()
#1 /root/index.php(40): Shopware\Storefront\Controller\StorefrontController->{closure:Shopware\Storefront\Controller\StorefrontController::renderStorefront():26}()
#2 /root/index.php(26): Shopware\Core\Profiling\Profiler::trace()
#3 /root/index.php(18): Shopware\Storefront\Controller\StorefrontController->renderStorefront()
#4 /root/index.php(10): Shopware\Storefront\Controller\ErrorController->error()
#5 /root/index.php(7): Symfony\Component\HttpKernel\HttpKernel->handleRaw()
#6 /root/index.php(47): Symfony\Component\HttpKernel\HttpKernel->handle()
#7 {main}
  thrown in /root/index.php on line 32

Additionally, var_dump of a closure now includes its location:

object(Closure)#3 (4) {
  ["name"]=>
  string(84) "{closure:Shopware\Storefront\Controller\StorefrontController::renderStorefront():26}"
  ["file"]=>
  string(15) "/root/index.php"
  ["line"]=>
  int(26)
  ["this"]=>
  object(Shopware\Storefront\Controller\ErrorController)#2 (0) {
  }
}

Tideways Profiler and Xdebug already had code like this, and my colleague Tim contributed it back to PHP core for inclusion in 8.4.

This change will improve Tideways and other APMs/Exception Tracking tools in places where they use PHP backtrace functionality. Just like in Tideways exception tracking here:

Closure naming simplified debugging

An AI would never be able to dive this deep into technical topics surrounding PHP performance! Follow us on LinkedIn or X and subscribe to our newsletter to get the latest posts.

Let’s dive in and find out what is causing performance bottlenecks! Without Tideways, you’re likely to fish in murky waters attempting to figure it out. Try our free trial today and be enlightened.

About the author

  • Benjamin

Benjamin
Founder & CEO

I am the founder and CEO of Tideways. I started the company over 10 years ago with the mission to move the PHP ecosystem forward, starting with performance. As managing director, I work across product, strategy, and the day-to-day of building a developer-focused SaaS business.

I’m a core contributor to the Doctrine open-source project and a founding board member of the PHP Foundation, which reflects my long-standing commitment to the PHP ecosystem. I particularly enjoy working at the intersection of application performance, developer experience, and the open-source community that makes PHP what it is. Outside of work, I enjoy board games, hiking, and coffee.