Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ jobs:
docker run --rm "$IMAGE" php -v
# tolerate release tags with or without a leading "v"
docker run --rm "$IMAGE" php -v | grep -F "PHP Debugger v${DEBUGGER_REF#v}"
docker run --rm "$IMAGE" php -m | grep -ix xdebug
docker run --rm "$IMAGE" php -r 'exit(extension_loaded("xdebug") ? 0 : 1);'
docker run --rm "$IMAGE" php -m | grep -ix php_debugger
docker run --rm "$IMAGE" php -r 'exit(extension_loaded("php_debugger") ? 0 : 1);'
# the php_debugger.* function alias must resolve, not just
# the xdebug-compat surface
docker run --rm "$IMAGE" php -r 'exit(function_exists("php_debugger_info") ? 0 : 1);'
Expand Down
12 changes: 1 addition & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -484,11 +484,9 @@ jobs:
if [[ "${{ matrix.arch }}" == "x86_64" ]]; then
# Run x86_64 binary using Rosetta
arch -x86_64 ./sapi/cli/php -m | grep -q php_debugger
arch -x86_64 ./sapi/cli/php -m | grep -q xdebug
arch -x86_64 ./sapi/cli/php -v | grep -q "PHP Debugger"
else
./sapi/cli/php -m | grep -q php_debugger
./sapi/cli/php -m | grep -q xdebug
./sapi/cli/php -v | grep -q "PHP Debugger"
fi

Expand All @@ -499,7 +497,6 @@ jobs:
# Run x86_64 binary using Rosetta
arch -x86_64 ./sapi/cli/php -r "
assert(extension_loaded('php_debugger'), 'php_debugger not loaded');
assert(extension_loaded('xdebug'), 'xdebug compat alias not loaded');
assert(function_exists('xdebug_break'), 'xdebug_break missing');
assert(function_exists('xdebug_info'), 'xdebug_info missing');
assert(function_exists('php_debugger_info'), 'php_debugger_info missing');
Expand All @@ -510,7 +507,6 @@ jobs:
else
./sapi/cli/php -r "
assert(extension_loaded('php_debugger'), 'php_debugger not loaded');
assert(extension_loaded('xdebug'), 'xdebug compat alias not loaded');
assert(function_exists('xdebug_break'), 'xdebug_break missing');
assert(function_exists('xdebug_info'), 'xdebug_info missing');
assert(function_exists('php_debugger_info'), 'php_debugger_info missing');
Expand Down Expand Up @@ -610,15 +606,13 @@ jobs:
run: |
cd ~/php-src
./sapi/cli/php -m | grep -q php_debugger
./sapi/cli/php -m | grep -q xdebug
./sapi/cli/php -v | grep -q "PHP Debugger"

- name: Test extension functionality
run: |
cd ~/php-src
./sapi/cli/php -r "
assert(extension_loaded('php_debugger'), 'php_debugger not loaded');
assert(extension_loaded('xdebug'), 'xdebug compat alias not loaded');
assert(function_exists('xdebug_break'), 'xdebug_break missing');
assert(function_exists('xdebug_info'), 'xdebug_info missing');
assert(function_exists('php_debugger_info'), 'php_debugger_info missing');
Expand Down Expand Up @@ -738,11 +732,7 @@ jobs:
Write-Error "php_debugger not in module list"; exit 1
}

if (-not (& $php -m | Select-String -Pattern "xdebug" -Quiet)) {
Write-Error "xdebug not in module list"; exit 1
}

& $php -r "assert(extension_loaded('php_debugger'), 'php_debugger not loaded'); assert(extension_loaded('xdebug'), 'xdebug compat alias not loaded'); assert(function_exists('xdebug_info'), 'xdebug_info missing'); echo 'All tests passed!' . PHP_EOL;"
& $php -r "assert(extension_loaded('php_debugger'), 'php_debugger not loaded'); assert(function_exists('xdebug_info'), 'xdebug_info missing'); echo 'All tests passed!' . PHP_EOL;"
if ($LASTEXITCODE -ne 0) { Write-Error "PHP tests failed"; exit 1 }

- name: Package artifact
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/static-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,13 @@ jobs:
run: |
cd ~/php-src
./sapi/cli/php -m | grep -q php_debugger
./sapi/cli/php -m | grep -q xdebug
./sapi/cli/php -v | grep -q "PHP Debugger"

- name: Smoke test — extension_loaded
run: |
cd ~/php-src
./sapi/cli/php -r "
assert(extension_loaded('php_debugger'), 'php_debugger not loaded');
assert(extension_loaded('xdebug'), 'xdebug compat alias not loaded');
echo 'extension_loaded: OK' . PHP_EOL;
"

Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ PHP Debugger maintains compatibility with Xdebug's debug mode:

| Feature | PHP Debugger | Xdebug |
|------------------------------------|------------------------------------------------------------------------------|--------|
| `extension_loaded("xdebug")` | ✅ true | ✅ true |
| `extension_loaded("xdebug")` | ❌ false by default, ✅ true with<br/>`php_debugger.report_xdebug_module=1` | ✅ true |
| `extension_loaded("php_debugger")` | ✅ true | ❌ false |
| `xdebug.*` INI settings | ✅ works | ✅ works |
| `xdebug_break()` | ✅ works | ✅ works |
Expand All @@ -190,6 +190,26 @@ You can also use the new names — they work alongside the Xdebug ones:

The session cookie is named after the trigger you used: `PHP_DEBUGGER_SESSION_START` and `PHP_DEBUGGER_CONFIG` set a `PHP_DEBUGGER_SESSION` cookie, while `XDEBUG_SESSION_START` and `XDEBUG_CONFIG` keep setting `XDEBUG_SESSION`. Both cookie names are accepted as a trigger, and either stop trigger clears both.

### Reporting the `xdebug` module

By default PHP Debugger does **not** register itself under the module name
`xdebug`, so `extension_loaded("xdebug")` returns `false`. Tools such as
Composer and PHPUnit use that check to detect Xdebug and then restart PHP with
the extension disabled — a restart that fails here, because there is no
`xdebug` Zend extension for them to unload.

Everything else keeps working regardless: `xdebug.*` INI settings, the
`XDEBUG_*` environment variables and triggers, and the `xdebug_*()` functions.

If you do need `extension_loaded("xdebug")` to report `true` (for a tool that
gates a feature on it rather than trying to disable it), set:

```ini
php_debugger.report_xdebug_module = 1
```

The setting defaults to `0`.

## Requirements

- PHP 8.2, 8.3, 8.4, or 8.5
Expand Down
4 changes: 2 additions & 2 deletions docker/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ RUN set -eux; \
make -j"$(nproc)"; \
make install; \
php -v; \
php -m | grep -ix xdebug; \
php -r 'exit(extension_loaded("xdebug") ? 0 : 1);'
php -m | grep -ix php_debugger; \
php -r 'exit(extension_loaded("php_debugger") ? 0 : 1);'

FROM php:${BASE_TAG}

Expand Down
4 changes: 2 additions & 2 deletions docker/Dockerfile.debian
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ RUN set -eux; \
cp -a /usr/lib/apache2/modules/libphp* /opt/php-debugger/apache2-modules/; \
fi; \
php -v; \
php -m | grep -ix xdebug; \
php -r 'exit(extension_loaded("xdebug") ? 0 : 1);'
php -m | grep -ix php_debugger; \
php -r 'exit(extension_loaded("php_debugger") ? 0 : 1);'

FROM php:${BASE_TAG}

Expand Down
3 changes: 3 additions & 0 deletions run-xdebug-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,9 @@ function write_information(array $user_tests, $phpdbg): void
'tidy' => ['tidy.clean_output=0'],
'zlib' => ['zlib.output_compression=Off'],
'xdebug' => ['xdebug.mode=off','xdebug.start_with_request=default','xdebug.log_level=20'],
// the module only reports itself as "xdebug" when
// php_debugger.report_xdebug_module is on, so key off both names
'php_debugger' => ['xdebug.mode=off','xdebug.start_with_request=default','xdebug.log_level=20'],
];

foreach ($info_params_ex as $ext => $ini_overwrites_ex) {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ typedef struct _xdebug_library_settings_t {
/* Logging settings */
char *log; /* Filename to log protocol communication to */
zend_long log_level; /* Log level XDEBUG_LOG_{ERR,WARN,INFO,DEBUG} */

/* Whether to also register an "xdebug" module alias, so that
* extension_loaded("xdebug") returns true */
zend_bool report_xdebug_module;
} xdebug_library_settings_t;

void xdebug_init_library_globals(xdebug_library_globals_t *xg);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ static void xdebug_print_settings(void)
zend_module_entry *module;
zend_ini_entry *ini_entry;
int module_number;
zend_string *name = zend_string_init("xdebug", 6, 0);
zend_string *name = zend_string_init("php_debugger", sizeof("php_debugger") - 1, 0);

module = zend_hash_find_ptr(&module_registry, name);
zend_string_release(name);
Expand Down
14 changes: 14 additions & 0 deletions tests/base/report_xdebug_module-001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
report_xdebug_module: xdebug module is not reported by default
--FILE--
<?php
var_dump(extension_loaded('php_debugger'));
var_dump(extension_loaded('xdebug'));
var_dump(ini_get('xdebug.report_xdebug_module'));
var_dump(ini_get('php_debugger.report_xdebug_module'));
?>
--EXPECT--
bool(true)
bool(false)
string(1) "0"
string(1) "0"
12 changes: 12 additions & 0 deletions tests/base/report_xdebug_module-002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
report_xdebug_module: xdebug.report_xdebug_module=1 re-enables the module alias
--INI--
xdebug.report_xdebug_module=1
--FILE--
<?php
var_dump(extension_loaded('php_debugger'));
var_dump(extension_loaded('xdebug'));
?>
--EXPECT--
bool(true)
bool(true)
14 changes: 14 additions & 0 deletions tests/base/report_xdebug_module-003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
report_xdebug_module: php_debugger.report_xdebug_module=1 re-enables the module alias
--INI--
php_debugger.report_xdebug_module=1
--FILE--
<?php
var_dump(extension_loaded('php_debugger'));
var_dump(extension_loaded('xdebug'));
var_dump(ini_get('xdebug.report_xdebug_module'));
?>
--EXPECT--
bool(true)
bool(true)
string(1) "1"
27 changes: 18 additions & 9 deletions xdebug.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ PHP_INI_BEGIN()
#endif
STD_PHP_INI_ENTRY("xdebug.path_mapping", "0", PHP_INI_ALL, OnUpdateBool, settings.library.path_mapping, zend_xdebug_globals, xdebug_globals)

STD_PHP_INI_BOOLEAN("xdebug.report_xdebug_module", "0", PHP_INI_SYSTEM, OnUpdateBool, settings.library.report_xdebug_module, zend_xdebug_globals, xdebug_globals)

STD_PHP_INI_ENTRY("xdebug.log", "", PHP_INI_ALL, OnUpdateString, settings.library.log, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_ENTRY("xdebug.log_level", XLOG_DEFAULT, PHP_INI_ALL, OnUpdateLong, settings.library.log_level, zend_xdebug_globals, xdebug_globals)

Expand Down Expand Up @@ -304,6 +306,9 @@ static const zend_ini_entry_def php_debugger_ini_entries[] = {
PHP_INI_ENTRY_EX("php_debugger.control_socket", "default", PHP_INI_ALL, OnUpdatePhpDebuggerCtrlSocket, display_control_socket)
#endif
STD_PHP_INI_ENTRY("php_debugger.path_mapping", "0", PHP_INI_ALL, OnUpdatePhpDebuggerBool, settings.library.path_mapping, zend_xdebug_globals, xdebug_globals)

STD_PHP_INI_BOOLEAN("php_debugger.report_xdebug_module", "0", PHP_INI_SYSTEM, OnUpdatePhpDebuggerBool, settings.library.report_xdebug_module, zend_xdebug_globals, xdebug_globals)

STD_PHP_INI_ENTRY("php_debugger.log", "", PHP_INI_ALL, OnUpdatePhpDebuggerString, settings.library.log, zend_xdebug_globals, xdebug_globals)
STD_PHP_INI_ENTRY("php_debugger.log_level", XLOG_DEFAULT, PHP_INI_ALL, OnUpdatePhpDebuggerLong, settings.library.log_level, zend_xdebug_globals, xdebug_globals)
/* Variable display settings */
Expand Down Expand Up @@ -456,9 +461,19 @@ PHP_MINIT_FUNCTION(xdebug)
ZEND_INIT_MODULE_GLOBALS(xdebug, php_xdebug_init_globals, php_xdebug_shutdown_globals);
REGISTER_INI_ENTRIES();

/* Register "xdebug" as a module alias so extension_loaded('xdebug') still works.
* Uses a separate dummy module entry to avoid double-free in module_registry cleanup. */
{
/* register filter constants for backwards compatibility */
xdebug_filter_register_constants(INIT_FUNC_ARGS_PASSTHRU);

/* Register php_debugger.* INI aliases pointing to the same storage as xdebug.* */
zend_register_ini_entries(php_debugger_ini_entries, module_number);

/* Optionally register "xdebug" as a module alias, so that
* extension_loaded('xdebug') reports true. This is off by default: tools such
* as Composer and PHPUnit detect the module and then try to restart PHP
* without it, which fails because there is no "xdebug" zend_extension to
* remove. Uses a separate dummy module entry to avoid double-free in
* module_registry cleanup. */
if (XG(settings.library.report_xdebug_module)) {
static zend_module_entry xdebug_compat_module_entry = {0};
xdebug_compat_module_entry.name = "xdebug";
xdebug_compat_module_entry.version = XDEBUG_VERSION;
Expand All @@ -470,12 +485,6 @@ PHP_MINIT_FUNCTION(xdebug)
zend_string_release(alias_name);
}

/* register filter constants for backwards compatibility */
xdebug_filter_register_constants(INIT_FUNC_ARGS_PASSTHRU);

/* Register php_debugger.* INI aliases pointing to the same storage as xdebug.* */
zend_register_ini_entries(php_debugger_ini_entries, module_number);

/* Locking in mode as it currently is */
if (!xdebug_lib_set_mode(XG(settings.library.requested_mode))) {
xdebug_lib_set_mode("debug");
Expand Down
Loading