View Issue Details

IDProjectCategoryView StatusLast Update
0002191XdebugStep Debuggingpublic2023-07-31 13:07
Reporterderick Assigned Toderick  
PrioritynormalSeverityminorReproducibilityhave not tried
Status closedResolutionfixed 
Target Version3.2devFixed in Version3.2dev 
Summary0002191: Add warning when opcache is loaded after xdebug
Description

This came from https://github.com/xdebug/vscode-php-debug/issues/919 where breakpoints where not resolved.

The full explanation:


I spend some time delving into this, and at first I could not reproduce this. On my side (Linux, PHP 8.1/8.2) with php -S the behaviour was always right, with the breakpoints being resolved for each request through the dev server.

When I had another good look at your phpinfo() output PDF file, I noticed:

Zend Engine v4.2.8, Copyright (c) Zend Technologies
    with Xdebug v3.2.2, Copyright (c) 2002-2023, by Derick Rethans
    with Zend OPcache v8.2.8, Copyright (c), by Zend Technologies

This has OPcache loaded after Xdebug, which the documentation says you shouldn't do:

Zend Opcache

    Can be loaded together with Xdebug, but it is not 100% compatible.

    Load Xdebug after Opcache in php.ini for better compatibility. When running php -v or when looking at phpinfo(); output, Xdebug should be listed below Opcache.

And indeed, when I switched the loading order of the two zend extensions from:

XDEBUG_MODE=debug XDEBUG_TRIGGER=yes php -n -d zend_extension=opcache -d zend_extension=xdebug -S localhost:9112 -t /tmp

to

XDEBUG_MODE=debug XDEBUG_TRIGGER=yes php -n -d zend_extension=xdebug -d zend_extension=opcache -S localhost:9112 -t /tmp

I could reproduce this issue.

Both Xdebug and OPcache override PHP's "compile a file" handler.

Xdebug's uses this so that it can analyse newly loaded files for lines of code that can have breakpoints on it, so that it then can then resolve them. Before doing it's magic, it calls the already present handler.

OPcache uses it to see if a file that is being parsed for a second time, and it is in its cache, it doesn't compile it again by not calling the already present handler.

If OPcache is first loaded, and then Xdebug, the following happens:

  • opcache replaces "compile file" with "opcache_compile_file" (and remembers the previous one, "php_compile_file")
  • xdebug replaces "compile file" with "xdebug_compile_file" (and remembers the previous one, "opcache_compile_file")

This means that when PHP runs the "compile this file" logic, it first calls xdebug_compile_file, which then calls opcache_compile_file and all is well.

If OPcache is loaded last, the process is reversed:

  • xdebug replaces "compile file" with "xdebug_compile_file" (and remembers the previous one, "php_compile_file")
  • opcache replaces "compile file" with "opcache_compile_file" (and remembers the previous one, "xdebug_compile_file")

When PHP then runs the "compile this file" logic, it calls opcache_compile first. This checks whether it has seen the file already, and if not, calls the previous handler (xdebug_compile_file), but if it has seen the file already (the second request through a php -S server) it does not call the previous compile file handler. Normally, that is what you want, as compiling files is expensive. However, because it does not call the previous file handler, that means that xdebug_compile_file does not get run, which in turn means it doesn't know anything about which lines of code can have breakpoints on them.

This is not something that Xdebug can fix.

There are workarounds:

  • Disable OPcache by setting opcache.enable=0
  • Don't load OPcache at all, by commenting out the line in ext-opcache.ini.
  • Make sure to load Xdebug after OPcache.

Curiously, your phpinfo() output allegedly already shows the right order (/opt/homebrew/etc/php/8.2/conf.d/ext-opcache.ini, /opt/homebrew/etc/php/8.2/conf.d/xdebug.ini), but I can not tell if your PHP actually loads them in the order — the phpinfo() output indicates that it doesn't — or whether there is another place where the extension gets loaded (sometimes launch.json adds the -dzend_extension=xdebug part. Personally, I usually name the xdebug.ini file 99-xdebug.ini to enforce the sorting order — it is also possible that OSX's file system does not support an actual ordering here. If that is the case, you should create one ini file which loads both extensions:

zend_extension=opcache
zend_extension=xdebug
TagsNo tags attached.
Operating System
PHP Version8.2.0-8.2.9

Activities

Issue History

Date Modified Username Field Change
2023-07-31 11:12 derick New Issue
2023-07-31 13:07 derick Assigned To => derick
2023-07-31 13:07 derick Status new => closed
2023-07-31 13:07 derick Resolution open => fixed
2023-07-31 13:07 derick Fixed in Version => 3.2dev
2023-07-31 13:07 derick Note Added: 0006614