The Mystery of the Missing Breakpoints
Occasionally I see people mentioned that Xdebug does not stop at certain breakpoints. This tends to relate to multi-line if conditions, or if/else conditions without braces ({ and }). Take for example the following artificial code sample:
1 <?php
2 $a = true;
3 $b = true;
4 $c = true;
5 $d = false;
6
7 if (
8 $a
9 && $b
10 && ( $c || $d )
11 )
12 {
13 echo "It's true!";
14 }
If you set a breakpoint at either line 7, 11, or 12, you'll find that these are ignored. But why is that?
In order to investigate we employ vld, a tool that I wrote that can show PHP's internal bytecode (oparrays containing opcodes). They read very much like assembly code. For the above snippet, the vld dump looks as follows (after redacting useless information):
function name: (null)
number of ops: 19
compiled vars: !0 = $a, !1 = $b, !2 = $c, !3 = $d
line #* E I O op return operands
---------------------------------------------------
2 0 E > EXT_STMT
1 ASSIGN !0, <true>
3 2 EXT_STMT
3 ASSIGN !1, <true>
4 4 EXT_STMT
5 ASSIGN !2, <true>
5 6 EXT_STMT
7 ASSIGN !3, <false>
8 8 EXT_STMT
9 > JMPZ_EX ~8 !0, ->11
9 10 > BOOL ~8 !1
11 > > JMPZ_EX ~8 ~8, ->15
10 12 > > JMPNZ_EX ~9 !2, ->14
13 > BOOL ~9 !3
14 > BOOL ~8 ~9
15 > > JMPZ ~8, ->18
13 16 > EXT_STMT
17 ECHO 'It%27s+true%21'
15 18 > > RETURN 1
The first column is the line number that PHP associates with each opcode, and you can see that there is no opcode for lines 7, 11, and 12. Additionally, Xdebug can only break on the EXT_STMT opcode, which you can see is only present for lines 8 and 13 in the logic section of the script. If a breakpoint is set on a line without an EXT_STMT opcode, Xdebug will not be able to interrupt the script on that line.
Sometimes it can be confusing where PHP thinks there is a line of code, especially since OPcache starts optimising more and more things out. In our example, it could really only leave line 13 around, as the rest is static. It doesn't quite do that yet however.
It is certainly frustrating that Xdebug cannot always stop where you want it to, but IDEs do have a possibility to interrogate where Xdebug could stop through a private DBGp commandxcmd_get_executable_lines. This can only be done when the script, or rather, a specific function is already running.
The DBGp protocol has provisions for signalling to IDEs whether it has resolved a breakpoint when a function gets entered into. Xdebug does currently not implement this functionality yet, but a ticket for it is scheduled for implementation (likely for Xdebug 3.0).
Life Line
If we must show off a little…
In more info innocent times, the hat riots caused some spankings...
100 Years Ago Men and Boys Fought on the Streets of New York Over Wearing Straw Hats Past Summer | The New York Public Library
https://www.nypl.org/blog/2022/09/23/straw-hat-riots-nycI hiked 15.5km in 3h12m14s
📷 Mushroom Village
🚩 Weg van den Prins Willemsberg, Ellecom, Nederland
I walked 2.4km in 59m34s
📷 National Cycle Network 6
🚩 Watford, United Kingdom
I walked 7.6km in 1h54m50s
📷 Tufted Duck Pair
🚩 Outer Circle, City of Westminster, United Kingdom
RE: https://phpc.social/@Xdebug/115662135830755552
I have just released Xdebug 3.5.0!
In the next few weeks I will create some content (text, and perhaps video) highlighting some new features in more detail.
Please share it with the world!
The master branch is now for Xdebug 3.6, targetting PHP 8.6
Back to -dev
Tweak release instructions a little
Go with 3.5.0
Tweak message IDs and severities for control socket log entries
I walked 8.1km in 1h26m40s
I walked 1.1km in 9m28s
I walked 8.5km in 1h30m56s
My whisky of the month for December 2025, is a 15yo Aultmore bottled by Cadenhead's.
Fixed off-by-one error in address length name for control socket on L…
Merged pull request #1050





Shortlink
This article has a short URL available: https://drck.me/brk-ea9