Skip to content

fix: handle trigger correctly#59

Merged
carlos-granados merged 3 commits into
php-debugger:mainfrom
carlos-granados:fix/check-trigger-correctly
May 8, 2026
Merged

fix: handle trigger correctly#59
carlos-granados merged 3 commits into
php-debugger:mainfrom
carlos-granados:fix/check-trigger-correctly

Conversation

@carlos-granados

Copy link
Copy Markdown
Collaborator

When doing the initial check to see if we needed to connect, we were not doing it correctly, which meant that in some cases it would try to connect when it shouldn't. And it was not correctly saving the trigger value. This PR fixes these problems

Solves one of the existing XFAIL tests, which was a real bug

Comment thread src/debugger/com.c Outdated
}

static int xdebug_handle_start_session()
int xdebug_handle_start_session()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to make this function available to be called outside of this file

Comment thread src/debugger/com.c
) {
if (found_trigger_value) {
xdebug_update_ide_key(found_trigger_value);
if (EXPECTED(XG_BASE(observer_active))) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have already tried to connect and found out that we are not connected, we don't need to try here again

'variables_order' => 'PGCS',
'xdebug.log' => $xdebugLogFileName, 'xdebug.log_level' => 10,
'xdebug.control_socket' => 'off', 'xdebug.path_mapping' => 'off',
'xdebug.on_demand_debugging_enabled' => 1

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually not needed for this test, once we fix the issue, it works fine even without this setting

Comment thread xdebug.c Outdated
xdebug_lib_start_upon_error() ||
getenv("XDEBUG_SESSION_START") != NULL ||
getenv("PHP_DEBUGGER_SESSION_START") != NULL
xdebug_handle_start_session() ||

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to call this function which correctly checks if we need to start the session or not

Comment thread xdebug.c Outdated
int debug_requested = !xdebug_lib_never_start_with_request() && !xdebug_should_ignore() && (
xdebug_lib_start_with_request() ||
xdebug_lib_start_with_trigger(NULL) ||
xdebug_lib_start_upon_error() ||

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to try to connect if this is set

Comment thread xdebug.c Outdated
xdebug_lib_start_with_request() ||
xdebug_lib_start_with_trigger(NULL) ||
xdebug_lib_start_upon_error() ||
getenv("XDEBUG_SESSION_START") != NULL ||

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to try to connect just because any of these are set. This is properly handled by xdebug_handle_start_session

Comment thread xdebug.c
xdebug_lib_start_with_trigger(&found_trigger_value)
);

if (found_trigger_value) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have found a trigger value, we need to update the ide_key with it

@carlos-granados carlos-granados force-pushed the fix/check-trigger-correctly branch from d59f69a to d716581 Compare April 19, 2026 13:02
Comment thread src/lib/headers.c
if (XG_LIB(headers)) {
switch (op) {
case SAPI_HEADER_ADD:
xdebug_log(XLOG_CHAN_DEBUG, XLOG_DEBUG, "Adding header '%s'.", h->header);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the xdebug_get_headers is no longer available, we need a way to see the headers that have been set. We add a log entry here that we can later use to check them

--FILE--
<?php
var_dump( xdebug_get_headers( ) );
require_once __DIR__ . '/../utils.inc';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using the xdebug_get_headers function which is no longer available, we log the header in the xdebug log and then print it to confirm it's working. Previously this tests would not have worked because we were not calling xdebug_handle_start_session in RINIT, so the cookie was not being set

@carlos-granados carlos-granados force-pushed the fix/check-trigger-correctly branch 6 times, most recently from 1d5a2f3 to 6a00a52 Compare April 22, 2026 16:28
@carlos-granados carlos-granados force-pushed the fix/check-trigger-correctly branch from 6a00a52 to 54050b9 Compare May 5, 2026 16:10
@carlos-granados

Copy link
Copy Markdown
Collaborator Author

@claude review

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @carlos-granados's task in 4m 13s —— View job


Code Review

Read the diff, the call sites in xdebug.c RINIT, xdebug_debug_init_if_requested_at_startup in src/debugger/com.c, the trigger plumbing in src/lib/lib.c, and src/lib/headers.c. Notes below.

Notable issues

1. Risk of duplicate side effects when on_demand_debugging_enabled=1 and the early connect fails

xdebug_handle_start_session() is no longer a pure probe — it has side effects (logs "Found 'XDEBUG_SESSION_START' ENV variable", calls xdebug_setcookie(), updates XG_DBG(ide_key)). It is now called in xdebug.c:631, and it is also still called from src/debugger/com.c:902 inside xdebug_debug_init_if_requested_at_startup.

The two calls only collapse safely when:

  • We connect successfully → second call short-circuits at the xdebug_is_debug_connection_active() check (com.c:891), or
  • debug_requested = falseXG_BASE(observer_active) is set to false (xdebug.c:656) and the new if (EXPECTED(XG_BASE(observer_active))) guard at com.c:899 skips the second call.

But when debug_requested = true, early connect fails, and on_demand_debugging_enabled=1, observer_active stays true (xdebug.c:654-660 only flips statement_handler_enabled). On the first PHP statement, com.c:902 calls xdebug_handle_start_session() again — emitting a duplicate Found 'XDEBUG_SESSION_START' log line and a duplicate Set-Cookie (and correspondingly, a duplicate Adding header 'Set-Cookie: …' log). No test in this PR exercises that combination (XDEBUG_SESSION_START + on_demand_debugging_enabled=1 + early-connect-failure), so it is silently broken. Worth either guarding the second call (e.g., remember in RINIT that we already handled the session) or adding a test that pins down the expected behavior. Fix this →

2. Set-Cookie is emitted even when the shared secret doesn't match

In xdebug_handle_start_session (com.c:803-862), the xdebug_setcookie call (com.c:831, 842, 850) happens before the shared-secret guard at com.c:856-859 zeros activate_session. The newly de-XFAIL-ed test confirms the new behavior: start_with_request_trigger_session_start_shared_secret-001.phpt now expects Adding header 'Set-Cookie: XDEBUG_SESSION=foobar' even though the trigger value doesn't match the configured shared secret and the session is not activated.

This is a pre-existing wart, but the PR makes it newly observable (and is now baked into a test as expected behavior). Worth thinking about: a caller can supply any XDEBUG_SESSION_START value and have the cookie planted on the response, even though authentication failed. If a downstream user reaches a page where the secret is configured to accept that value (or where the secret is later removed), the stored cookie would now activate the session without re-authentication. Consider gating the setcookie on either !xdebug_lib_has_shared_secret() or moving the shared-secret check before the cookie write.

3. Inconsistent header logging

src/lib/headers.c:51 only logs SAPI_HEADER_ADD. SAPI_HEADER_REPLACE (com.c:54-66) is silent, even though it ends up in XG_LIB(headers) the same way. If the goal is to give tests a way to observe all headers (now that xdebug_get_headers is gone from non-develop mode), the REPLACE branch should log too — otherwise tests that exercise header('X: y', true) will silently miss output. Fix this →

Smaller observations

  • Duplicated ide_key update logic. xdebug.c:638-644 open-codes "free old key, dup new key" — exactly what xdebug_update_ide_key() (com.c:795-801) already does. Now that you've made xdebug_handle_start_session non-static, consider exposing xdebug_update_ide_key the same way and replacing the open-coded block with a call.

  • Trailing newlines stripped. tests/debugger/bug01782.phpt and tests/debugger/remote_log1.phpt lose their final newline (\ No newline at end of file in the diff). Style nit but easy to fix.

  • Test brittleness against PHP's default Content-type. 13 test expectations now pin Adding header 'Content-type: text/html; charset=UTF-8'.. If the SAPI's default content-type or charset emission changes (or if a test ever sets default_charset differently), these will all need rebaselining together. Not blocking, just a maintenance cost worth noting.

  • Commit message contains # Conflicts: markers. The commit body still has the auto-generated rebase markers:

    # Conflicts:
    #       src/debugger/com.c
    # Conflicts:
    #       xdebug.c
    

    Worth amending these out before merge — they leak rebase mechanics into history.

  • Comment above debug_requested is now slightly out of date. xdebug.c:627-629 still talks about "xdebug_break() can initiate connections without triggers" and lists getenv checks that are no longer present. The block reads OK but the bulleted check list should be tightened to match the new three-call form.

Looks good

  • Hoisting the xdebug_handle_start_session() call into RINIT is the right call: the env-var existence check it replaced (getenv("XDEBUG_SESSION_START") != NULL || getenv("PHP_DEBUGGER_SESSION_START") != NULL) skipped the shared-secret gate entirely, which is exactly the bug that XFAIL-ed start_with_request_trigger_session_start_shared_secret-001.phpt. Correct fix in principle.
  • EXPECTED(XG_BASE(observer_active)) guard in xdebug_debug_init_if_requested_at_startup is a meaningful overhead win when RINIT decided not to debug — well aligned with the project's "near-zero overhead when no client" goal.
  • Removing on_demand_debugging_enabled=1 from the shared-secret test (now that the RINIT path correctly rejects it) is the right cleanup.
  • Removing the standalone xdebug_lib_start_upon_error() and bare getenv() checks from debug_requested is correct: errors are caught via the error-handler path (xdebug_debug_init_if_requested_on_error), not the observer, so eager activation in RINIT is unnecessary.

  • Read the PR diff
  • Examine xdebug.c RINIT changes
  • Examine src/debugger/com.c / com.h changes
  • Examine src/lib/headers.c log addition
  • Review test changes
  • Post review
    — branch: fix/check-trigger-correctly

@carlos-granados carlos-granados force-pushed the fix/check-trigger-correctly branch from 54050b9 to 2f48e74 Compare May 8, 2026 13:49
@carlos-granados

Copy link
Copy Markdown
Collaborator Author

@claude updated, review again

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @carlos-granados's task in 2m 41s —— View job


Re-review

I read commit 2f48e74 ("Updates after Claude code review") on top of d8bb2d1, plus the surrounding code in src/debugger/com.c, src/lib/headers.c, xdebug.c, and src/debugger/debugger.{c,h}. The previous review's concerns are all addressed; notes below.

Previously raised — status

# Concern Status
1 Duplicate side effects when on_demand_debugging_enabled=1 + early connect fails (RINIT and statement-handler call to xdebug_handle_start_session) ✅ Fixed via memoization: start_session_checked / start_session_result on XG_DBG, reset in xdebug_debugger_rinit (debugger.c:896-897). Second call short-circuits at com.c:811-813.
2 Set-Cookie emitted even when shared-secret doesn't match ✅ Fixed. All three branches now gate xdebug_setcookie (and xdebug_update_ide_key, and the "Found …" debug log) on !xdebug_lib_has_shared_secret() (com.c:834,849,863). The de-XFAIL'd test reflects this — the Set-Cookie line is gone from the expected output.
3 SAPI_HEADER_REPLACE branch silent ✅ Fixed. headers.c:65 now logs in the REPLACE branch too.
4 Duplicated ide_key update logic in xdebug.c ✅ Fixed. xdebug_update_ide_key is now non-static (declared in com.h:72) and used at both xdebug.c:637 and com.c:927.
5 Trailing newlines stripped from bug01782.phpt / remote_log1.phpt ✅ Fixed.
6 Test brittleness: hard-coded text/html; charset=UTF-8 ✅ Fixed. All affected tests now use Content-type: %s.
7 # Conflicts: markers in commit message ✅ Fixed. Clean commit history now.
8 Stale comment above debug_requested ✅ Fixed. New block at xdebug.c:617-627 accurately enumerates the three checks and the xdebug_break() caveat.

Notes on the new memoization

The cache approach is the right call — keeps xdebug_handle_start_session idempotent across the RINIT and statement-handler call sites without forcing the caller to track state. Two small things worth a glance:

  • Type mismatch. start_session_checked is zend_bool and assigned false/true; start_session_result is int and stores the activate flag. Fine, just inconsistent naming/typing for two adjacent fields. Could collapse to one tri-state int (-1 unchecked, 0/1 result) if you ever want to tighten, but no functional issue.

  • Reset point. Reset only happens in xdebug_debugger_rinit (debugger.c:896). That's the right place for per-request lifecycle, but it ties this debugger-specific cache to the debugger init path. If the debugger rinit is ever skipped for a request where RINIT still calls xdebug_handle_start_session, the cache could carry over. Not currently reachable — xdebug_debugger_rinit runs whenever the mode is debug, and xdebug_handle_start_session only matters in debug mode — but worth being aware of if mode dispatch changes.

Minor: behavior change in the third branch

In the XDEBUG_CONFIG branch (com.c:860-870), session_start_found = 1 is now set before the XG_DBG(ide_key) && *XG_DBG(ide_key) && !SG(headers_sent) check. That means if XDEBUG_CONFIG is set and a shared secret is configured, but ide_key is empty or headers were already sent, the "Not activating through legacy method because xdebug.trigger_value is set" warning will now fire — where previously it wouldn't have (because activate_session would have been 0 regardless of the secret). The new behavior is arguably more accurate (the user did attempt legacy activation, and the secret check did gate it), but it's a quietly observable change. No test exercises this combination, so it's not blocking, just noting.

Looks good

  • Memoization in xdebug_handle_start_session is clean and the per-branch shared-secret gating is much tidier than the post-hoc if (activate_session && xdebug_lib_has_shared_secret()) activate_session = 0; it replaced.
  • Exposing xdebug_update_ide_key and reusing it eliminates the duplicated free/dup pattern.
  • Test refactors away from xdebug_get_headers (which is gone in non-develop mode) toward log-line assertions are a sensible substitute and clearly motivated by the new headers.c log line.
  • Switching pinned text/html; charset=UTF-8 to %s defuses a class of cross-PHP-version brittleness.

Nothing blocking from my side. LGTM.

  • Examine recent changes since previous review
  • Check whether previous review concerns are addressed
  • Analyze updated code paths
  • Post review findings
    — branch: fix/check-trigger-correctly

@carlos-granados

Copy link
Copy Markdown
Collaborator Author

@claude updated, new review

@claude

claude Bot commented May 8, 2026

Copy link
Copy Markdown

Claude finished @carlos-granados's task in 1m 12s —— View job


Re-review (commit b35aa7d)

I read the diff between 2f48e74 (the previous review's HEAD) and b35aa7d, plus the surrounding code in src/debugger/com.c, src/debugger/debugger.{c,h}, and the call sites in xdebug.c.

Changes since last review

The only change is the tri-state consolidation suggested in my previous "Notes on the new memoization" — start_session_checked (zend_bool) and start_session_result (int) collapsed into a single int start_session_result with -1 = unchecked, 0/1 = checked result. Three files, three insertions, six deletions.

Verification

  • debugger.h:38 — single field, sentinel documented in the trailing comment. Clean.
  • debugger.c:896 — reset to -1 in xdebug_debugger_rinit. Replaces the prior pair of assignments.
  • com.c:811 — short-circuit changed to XG_DBG(start_session_result) != -1. Correct: -1 is the only "unchecked" sentinel and any other value is a previously-cached 0 or 1.
  • com.c:879 — stores activate_session (always 0 or 1 from the branches above) — never writes -1 accidentally.

I grepped for all references to both old and new field names; nothing else in the tree touches them, so no stale callers.

The minor caveat from my last review still stands (cache reset is tied to xdebug_debugger_rinit, so the cache could carry over if that path is ever skipped while RINIT still calls xdebug_handle_start_session) — but it's a hypothetical, not currently reachable, and doesn't get worse with this change.

Verdict

Clean follow-up. Nothing else jumps out. LGTM.

  • Pull diff since previous review (2f48e74..b35aa7d)
  • Verify tri-state consolidation is correct
  • Confirm no stale references to removed start_session_checked field
  • Post review
    — branch: fix/check-trigger-correctly

@carlos-granados carlos-granados force-pushed the fix/check-trigger-correctly branch from b35aa7d to 73c0596 Compare May 8, 2026 16:06
@carlos-granados carlos-granados merged commit 99bc51d into php-debugger:main May 8, 2026
16 of 17 checks passed
@carlos-granados carlos-granados deleted the fix/check-trigger-correctly branch May 8, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants