From c44017e44fe64e7660d29d5cbe1a987e7dc89405 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 12 Nov 2026 22:16:34 +0100 Subject: [PATCH 01/10] Update PHP 8.6 ABI to 20261112 8.6.0RC7 and final release have a new ABI version. --- .gitlab/generate-common.php | 2 +- .../ci/alpine_compile_extension/docker-compose.yml | 8 ++++---- dockerfiles/ci/bookworm/docker-compose.yml | 4 ++-- dockerfiles/ci/centos/7/docker-compose.yml | 4 ++-- dockerfiles/ci/windows/docker-compose.yml | 6 +++--- loader/dd_library_loader.c | 4 ++-- tooling/bin/build-debug-artifact | 2 +- tooling/bin/generate-final-artifact.sh | 2 +- tooling/bin/generate-ssi-package.sh | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.gitlab/generate-common.php b/.gitlab/generate-common.php index 9eee552939c..6b58264837b 100644 --- a/.gitlab/generate-common.php +++ b/.gitlab/generate-common.php @@ -14,7 +14,7 @@ "8.3" => "20230831", "8.4" => "20240924", "8.5" => "20250925", - "8.6" => "20260924", + "8.6" => "20261112", ]; $all_minor_major_targets = array_keys($php_versions_to_abi); diff --git a/dockerfiles/ci/alpine_compile_extension/docker-compose.yml b/dockerfiles/ci/alpine_compile_extension/docker-compose.yml index bc7987d345d..ceb281e5576 100644 --- a/dockerfiles/ci/alpine_compile_extension/docker-compose.yml +++ b/dockerfiles/ci/alpine_compile_extension/docker-compose.yml @@ -168,9 +168,9 @@ services: x-bake: *bake args: <<: *build-base - phpVersion: 8.6.0RC3 - phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC3.tar.gz - phpSha256Hash: "a40085cef8bfb560ed3649cd83879e768ab22fe4cee467aa56b1b795c92e057d" - phpApi: 20260924 + phpVersion: 8.6.0RC7 + phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC7.tar.gz + phpSha256Hash: "68b753e1d45e1417c6874008500de66102963cf0791e8c541211e9386318722e" + phpApi: 20261112 volumes: - ../../:/app diff --git a/dockerfiles/ci/bookworm/docker-compose.yml b/dockerfiles/ci/bookworm/docker-compose.yml index 7b44b4e19b9..cc1c903bf91 100644 --- a/dockerfiles/ci/bookworm/docker-compose.yml +++ b/dockerfiles/ci/bookworm/docker-compose.yml @@ -24,8 +24,8 @@ services: <<: *build-base BUILD_BASE: ${CI_REGISTRY_IMAGE:-datadog/dd-trace-ci}:bookworm-$BOOKWORM_NEXT_VERSION phpVersion: "8.6" - phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC3.tar.gz - phpSha256Hash: "a40085cef8bfb560ed3649cd83879e768ab22fe4cee467aa56b1b795c92e057d" + phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC7.tar.gz + phpSha256Hash: "68b753e1d45e1417c6874008500de66102963cf0791e8c541211e9386318722e" php-8.5: image: ${CI_REGISTRY_IMAGE:-datadog/dd-trace-ci}:php-8.5_bookworm-$BOOKWORM_NEXT_VERSION diff --git a/dockerfiles/ci/centos/7/docker-compose.yml b/dockerfiles/ci/centos/7/docker-compose.yml index c7490e021d6..141f9eafd19 100644 --- a/dockerfiles/ci/centos/7/docker-compose.yml +++ b/dockerfiles/ci/centos/7/docker-compose.yml @@ -154,6 +154,6 @@ services: args: <<: *build-base phpVersion: "8.6" - phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC3.tar.gz - phpSha256Hash: "a40085cef8bfb560ed3649cd83879e768ab22fe4cee467aa56b1b795c92e057d" + phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC7.tar.gz + phpSha256Hash: "68b753e1d45e1417c6874008500de66102963cf0791e8c541211e9386318722e" image: '${CI_REGISTRY_IMAGE:-datadog/dd-trace-ci}:php-8.6_centos-7-$CENTOS_NEXT_VERSION' diff --git a/dockerfiles/ci/windows/docker-compose.yml b/dockerfiles/ci/windows/docker-compose.yml index 36ff04d9dc1..68130a96b24 100644 --- a/dockerfiles/ci/windows/docker-compose.yml +++ b/dockerfiles/ci/windows/docker-compose.yml @@ -91,10 +91,10 @@ services: context: . args: # Must match the tarball's top-level directory (see Dockerfile). - phpVersion: "8.6.0RC3" + phpVersion: "8.6.0RC7" vsVersion: "vs18" - phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC3.tar.gz - phpSha256Hash: "a40085cef8bfb560ed3649cd83879e768ab22fe4cee467aa56b1b795c92e057d" + phpTarGzUrl: https://downloads.php.net/~svpernova09/php-8.6.0RC7.tar.gz + phpSha256Hash: "68b753e1d45e1417c6874008500de66102963cf0791e8c541211e9386318722e" php-8.5: image: ${CI_REGISTRY_IMAGE:-datadog/dd-trace-ci}:php-8.5_windows diff --git a/loader/dd_library_loader.c b/loader/dd_library_loader.c index 8fc1c9e5690..074dbfc3327 100644 --- a/loader/dd_library_loader.c +++ b/loader/dd_library_loader.c @@ -19,14 +19,14 @@ #include "telemetry_reaper.h" #define MIN_API_VERSION 320151012 -#define MAX_API_VERSION 420260924 +#define MAX_API_VERSION 420261112 #define MAX_INI_API_VERSION MAX_API_VERSION + 1 #define PHP_70_VERSION 20151012 #define PHP_71_VERSION 20160303 #define PHP_72_VERSION 20170718 #define PHP_80_VERSION 20200930 -#define PHP_86_VERSION 20260924 +#define PHP_86_VERSION 20261112 #define MIN_PHP_VERSION "7.0" #define MAX_PHP_VERSION "8.6" diff --git a/tooling/bin/build-debug-artifact b/tooling/bin/build-debug-artifact index 542e1a8c5eb..61645287aec 100755 --- a/tooling/bin/build-debug-artifact +++ b/tooling/bin/build-debug-artifact @@ -111,7 +111,7 @@ case "$php_version" in 8.3) php_api="20230831" ;; 8.4) php_api="20240924" ;; 8.5) php_api="20250925" ;; - 8.6) php_api="20260924" ;; + 8.6) php_api="20261112" ;; *) echo "Error: unknown PHP version '$php_version'. Supported: 7.0–8.6." >&2; exit 1 ;; esac diff --git a/tooling/bin/generate-final-artifact.sh b/tooling/bin/generate-final-artifact.sh index 0bc33c53ec7..4661dfbea87 100755 --- a/tooling/bin/generate-final-artifact.sh +++ b/tooling/bin/generate-final-artifact.sh @@ -13,7 +13,7 @@ tmp_folder_final=$tmp_folder/final architectures=(x86_64 aarch64) -php_apis=(20190902 20200930 20210902 20220829 20230831 20240924 20250925 20260924) +php_apis=(20190902 20200930 20210902 20220829 20230831 20240924 20250925 20261112) if [[ -z ${DDTRACE_MAKE_PACKAGES_ASAN:-} ]]; then php_apis+=(20151012 20160303 20170718 20180731) fi diff --git a/tooling/bin/generate-ssi-package.sh b/tooling/bin/generate-ssi-package.sh index b2e1b79b85e..8c49505e7ef 100755 --- a/tooling/bin/generate-ssi-package.sh +++ b/tooling/bin/generate-ssi-package.sh @@ -74,7 +74,7 @@ for architecture in "${architectures[@]}"; do # Products ######################## - php_apis=(20151012 20160303 20170718 20180731 20190902 20200930 20210902 20220829 20230831 20240924 20250925 20260924) + php_apis=(20151012 20160303 20170718 20180731 20190902 20200930 20210902 20220829 20230831 20240924 20250925 20261112) for php_api in "${php_apis[@]}"; do ######################## # Trace From e6470437ff7d215691199c3ccda9e58f614f8b38 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 13:19:59 +0100 Subject: [PATCH 02/10] Trigger bailouts in tests without hitting the memory limit Since PHP 8.6.0RC7 (php-src 3584a98bda0, CVE-2026-109618/109619), exceeding memory_limit no longer bails out immediately: the OOM is delayed to the next VM interrupt, which throws UnwindExit before raising the E_ERROR. Tests that only used OOM as a convenient bailout trigger now see an UnwindExit instead. Use DDTrace\Testing\trigger_error(..., E_ERROR), which bails out on all PHP versions independently of the allocator. Co-Authored-By: Claude Opus 5.5 --- tests/ext/DDTrace/RaisesFatalError.php | 4 +--- .../ignores_fatal_errors.phpt | 4 +--- tests/ext/fibers/fiber_observer_bailout.phpt | 17 ++++++++--------- .../fatal_errors_ignored_in_shutdown.phpt | 9 ++------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/ext/DDTrace/RaisesFatalError.php b/tests/ext/DDTrace/RaisesFatalError.php index 28c1f721587..ad5d50f9c31 100644 --- a/tests/ext/DDTrace/RaisesFatalError.php +++ b/tests/ext/DDTrace/RaisesFatalError.php @@ -1,7 +1,5 @@ --ENV-- DD_TRACE_AUTO_FLUSH_ENABLED=0 DD_TRACE_LOG_LEVEL=info,startup=off @@ -19,6 +17,6 @@ echo "Request start" . PHP_EOL; --EXPECTF-- [ddtrace] [warning] [%d] Error raised in autoloaded file %s_files_api.php: %s(): Failed opening '%s_files_api.php' for inclusion %s on line %d Calling a function that does not exist... -[ddtrace] [warning] [%d] Error raised in autoloaded file %s: Allowed memory size of 20971520 bytes exhausted %s on line %d +[ddtrace] [warning] [%d] Error raised in autoloaded file %s: Fatal error in autoloaded file in %s on line %d Request start [ddtrace] [info] [%d] Flushing trace of size 1 to send-queue for %s diff --git a/tests/ext/fibers/fiber_observer_bailout.phpt b/tests/ext/fibers/fiber_observer_bailout.phpt index 277de4a684c..dc637fb8bd0 100644 --- a/tests/ext/fibers/fiber_observer_bailout.phpt +++ b/tests/ext/fibers/fiber_observer_bailout.phpt @@ -3,7 +3,6 @@ Test fiber observing with bailout --SKIPIF-- --INI-- -memory_limit=100M fatal_error_backtraces=0 --ENV-- DD_TRACE_AUTO_FLUSH_ENABLED=0 @@ -22,7 +21,7 @@ function outer($fiber) { function inFiber() { Fiber::suspend(); - str_repeat("\0", 1 << 31); + DDTrace\Testing\trigger_error("Fatal error in fiber", E_ERROR); } DDTrace\trace_function("outer", function() {}); @@ -42,22 +41,22 @@ outer($fiber); // make something on the main stack the current observed frame ?> --EXPECTF-- -Fatal error: Allowed memory size of %d bytes exhausted %s in %s on line %d +Fatal error: Fatal error in fiber in %s on line %d inFiber posthook spans(\DDTrace\SpanData) (1) { - fiber_observer_bailout.php (fiber_observer_bailout.php, fiber_observer_bailout.php, cli) (error: Allowed memory size of %d bytes exhausted %s) + fiber_observer_bailout.php (fiber_observer_bailout.php, fiber_observer_bailout.php, cli) (error: Fatal error in fiber) _dd.p.dm => -0 _dd.p.tid => %s - error.message => Allowed memory size of %d bytes exhausted %s - error.stack => #0 %s(%d): str_repeat() + error.message => Fatal error in fiber + error.stack => #0 %s(%d): DDTrace\Testing\trigger_error() #1 [internal function]: inFiber() #2 %s(%d): Fiber->resume() #3 %s(%d): outer() #4 {main} error.type => E_ERROR - inFiber (fiber_observer_bailout.php, inFiber, cli) (error: Allowed memory size of %d bytes exhausted %s) - error.message => Allowed memory size of %d bytes exhausted %s - error.stack => #0 %s(%d): str_repeat() + inFiber (fiber_observer_bailout.php, inFiber, cli) (error: Fatal error in fiber) + error.message => Fatal error in fiber + error.stack => #0 %s(%d): DDTrace\Testing\trigger_error() #1 [internal function]: inFiber() #2 %s(%d): Fiber->resume() #3 %s(%d): outer() diff --git a/tests/ext/sandbox/fatal_errors_ignored_in_shutdown.phpt b/tests/ext/sandbox/fatal_errors_ignored_in_shutdown.phpt index e24ae7f0eee..967f119f3b3 100644 --- a/tests/ext/sandbox/fatal_errors_ignored_in_shutdown.phpt +++ b/tests/ext/sandbox/fatal_errors_ignored_in_shutdown.phpt @@ -2,10 +2,6 @@ Fatal errors are ignored in shutdown handler --DESCRIPTION-- This is how the tracer sandboxes the flushing functionality in userland ---SKIPIF-- - ---INI-- -memory_limit=2M --ENV-- DD_TRACE_LOG_LEVEL=info,startup=off DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum @@ -43,8 +39,7 @@ DDTrace\trace_function('flushTracer', function () { array_map(function($span) { echo $span['name'] . PHP_EOL; }, dd_trace_serialize_closed_spans()); - // Trigger a fatal error (hit the memory limit) - $a = str_repeat('.', 1024 * 1024 * 3); // 3MB + DDTrace\Testing\trigger_error('Fatal error while flushing', E_ERROR); echo 'You should not see this.' . PHP_EOL; var_dump(error_get_last()); }); @@ -69,5 +64,5 @@ array_sum array_sum array_sum array_sum -[ddtrace] [warning] [%d] Error raised in ddtrace's closure defined at %s:%d for flushTracer(): Allowed memory size of 2097152 bytes exhausted %s in %s on line %d +[ddtrace] [warning] [%d] Error raised in ddtrace's closure defined at %s:%d for flushTracer(): Fatal error while flushing in %s on line %d [ddtrace] [info] [%d] No finished traces to be sent to the agent From e20fc7669ef57fd5b3fd5fcb657cfcab9b6c9a56 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 13:54:13 +0100 Subject: [PATCH 03/10] Expect the php-src#23911 fix in PHP 8.6 static closure tests php-src e46e742f45e (8.6.0RC5) keys the static closure cache by called scope, so evaluating a parent's static closure through a second subclass now yields a fresh closure bound to that subclass instead of the one cached for the first. The cache is still disabled by an extended_value of -1, so the uncaching in dd_uhook_uncache_closure() keeps working. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Bob Weinand --- tests/ext/sandbox/fatal_errors_are_tracked_002.phpt | 1 + .../install_hook/trace_closure_instance_static.phpt | 4 ++-- .../trace_closure_instance_still_shared_ge_php_86.phpt | 9 ++++++--- tests/ext/sidecar_failed_request_startup.phpt | 2 +- tracer/hook/uhook.c | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/ext/sandbox/fatal_errors_are_tracked_002.phpt b/tests/ext/sandbox/fatal_errors_are_tracked_002.phpt index f60eeabc176..4ce6a7cde44 100644 --- a/tests/ext/sandbox/fatal_errors_are_tracked_002.phpt +++ b/tests/ext/sandbox/fatal_errors_are_tracked_002.phpt @@ -2,6 +2,7 @@ E_ERROR fatal errors are tracked from hitting the memory limit --SKIPIF-- += 80600) die('skip: OOM-fatal errors no longer exist on PHP 8.6+'); ?> --ENV-- DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum --INI-- diff --git a/tests/ext/sandbox/install_hook/trace_closure_instance_static.phpt b/tests/ext/sandbox/install_hook/trace_closure_instance_static.phpt index 53396a1200c..faf62528eef 100644 --- a/tests/ext/sandbox/install_hook/trace_closure_instance_static.phpt +++ b/tests/ext/sandbox/install_hook/trace_closure_instance_static.phpt @@ -65,8 +65,8 @@ DDTrace\install_hook('Hooked::method', function (DDTrace\HookData $hook) { }); (new Hooked)->method(); -// Declared in a parent method, evaluated through two subclasses: PHP 8.6.0 hands B the closure cached -// for A (php-src#23911). Its fix changes that path, so B::make() must still be fresh after the install. +// Declared in a parent method, evaluated through two subclasses: since the php-src#23911 fix (8.6.0RC5) +// the cache is keyed by called scope. B::make() must still be fresh after the install. class P { static function make() { return static function () { return static::class; }; } } class A extends P {} class B extends P {} diff --git a/tests/ext/sandbox/install_hook/trace_closure_instance_still_shared_ge_php_86.phpt b/tests/ext/sandbox/install_hook/trace_closure_instance_still_shared_ge_php_86.phpt index c9173116630..122bdbf7951 100644 --- a/tests/ext/sandbox/install_hook/trace_closure_instance_still_shared_ge_php_86.phpt +++ b/tests/ext/sandbox/install_hook/trace_closure_instance_still_shared_ge_php_86.phpt @@ -3,8 +3,8 @@ HOOK_INSTANCE caching boundaries for PHP 8.6 static closures --DESCRIPTION-- Pins the PHP 8.6 closure caching boundaries: evaluations obtained before the install stay shared, while indexed declarations in inactive closures and property hooks produce fresh closures after it. -Also pins php-src#23911: an evaluation through a second subclass is the closure cached for the first -one, with its static::. +Also pins the php-src#23911 fix (8.6.0RC5): the cache is keyed by called scope, so an evaluation through a +second subclass is a fresh closure with its own static::, while the first subclass still hits the cache. --SKIPIF-- --INI-- @@ -44,6 +44,8 @@ class B extends P {} $a = A::make(); $b = B::make(); echo "second subclass: ", var_export($a === $b, true), " ", $b(), "\n"; +$a2 = A::make(); +echo "first subclass again: ", var_export($a === $a2, true), " ", $a2(), "\n"; ?> --EXPECT-- @@ -56,4 +58,5 @@ nested iteration 1 nested hook 1 property hook: false property hook: traced -second subclass: true A +second subclass: false B +first subclass again: true A diff --git a/tests/ext/sidecar_failed_request_startup.phpt b/tests/ext/sidecar_failed_request_startup.phpt index 623b8e5b7f9..0309a8536fb 100644 --- a/tests/ext/sidecar_failed_request_startup.phpt +++ b/tests/ext/sidecar_failed_request_startup.phpt @@ -13,7 +13,7 @@ materializes $_SERVER. if (PHP_OS !== 'Linux') die('skip: Linux only'); if (PHP_VERSION_ID < 80100) die('skip: the CLI server ends failed request startups only on PHP 8.1+'); // mbstring's RINIT frees its previous detect order list since PHP 8.6, which dangles when RSHUTDOWN was skipped -if (PHP_VERSION_ID >= 80600 && extension_loaded('mbstring')) die('skip: mbstring corrupts the heap after a failed request startup on PHP 8.6+'); +if (PHP_VERSION_ID >= 80600) die('skip: mbstring corrupts the heap after a failed request startup on PHP 8.6+'); if (!function_exists('proc_open')) die('skip: proc_open required'); if (!function_exists('stream_socket_server')) die('skip: stream sockets required'); if (!getenv('TEST_PHP_EXECUTABLE')) die('skip: TEST_PHP_EXECUTABLE required'); diff --git a/tracer/hook/uhook.c b/tracer/hook/uhook.c index 4225348bc8f..2acef1a5ced 100644 --- a/tracer/hook/uhook.c +++ b/tracer/hook/uhook.c @@ -576,7 +576,7 @@ static void dd_uhook_dtor(void *data) { #define DD_ACC2_UNCACHED_LAMBDA (1u << 31) // PHP 8.6 caches static closures without captured state, so later evaluations would share the instance hook: disable it at -// the declaration. Re-verify with the php-src#23911 fix. With opcache, workers share it: on debug and -fno-strict-aliasing builds, +// the declaration. Since the php-src#23911 fix (8.6.0RC5) the cache is keyed by called scope, and -1 still disables it. With opcache, workers share it: on debug and -fno-strict-aliasing builds, // ZEND_DECLARE_LAMBDA_FUNCTION re-reads extended_value, so a racing worker may use -1 as its cache slot and crash (php-src should read it once). static void dd_uhook_uncache_closure(zend_object *closure) { zend_op_array *op_array = &((zend_function *)zend_get_closure_method_def(closure))->op_array; From 7698fdede725981cb8b2cb39ad081a5120f59d71 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 13 Nov 2026 13:31:13 +0100 Subject: [PATCH 04/10] Run ZendMM bin test without allocation profiling hooks zend_mem_block_size() returns zero for custom allocators. The new PHP 8.6 realloc boundary test therefore sees only one bin with the profiler loaded, even though realloc itself works. Disable allocation hooks only for this test. Keep the test running and leave the other profiling features enabled. Verified the RC7 test fails before this change and passes after it on arm64 NTS, including a repeated runner invocation. --- .gitlab/run_php_language_tests.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitlab/run_php_language_tests.sh b/.gitlab/run_php_language_tests.sh index b12c6da0f64..3bbe86043ca 100755 --- a/.gitlab/run_php_language_tests.sh +++ b/.gitlab/run_php_language_tests.sh @@ -32,6 +32,16 @@ mkdir -p "${CI_PROJECT_DIR}/artifacts/tests" # replace all hardcoded object ids in tests by %d as ddtrace creates its own objects php <<'PHP' Date: Fri, 13 Nov 2026 13:31:43 +0100 Subject: [PATCH 05/10] Unload memcache for PHP language tests memcache initializes $_SERVER during RINIT even with host-key prefixes disabled. With variables_order=EGPC, this creates an empty array, so Phar::webPhar() cannot use its CGI environment fallback and sends no redirect. Remove memcache.ini alongside the other interfering extensions. The RC7 test fails with memcache alone and passes with the profiler loaded after this change. Both reported failures also reproduce with a profiler built from the PR merge base. --- .gitlab/run_php_language_tests.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab/run_php_language_tests.sh b/.gitlab/run_php_language_tests.sh index 3bbe86043ca..d8efb75a459 100755 --- a/.gitlab/run_php_language_tests.sh +++ b/.gitlab/run_php_language_tests.sh @@ -7,8 +7,9 @@ unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy ALL_PROXY all_proxy # Helper to parse version strings for comparison function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; } -# Remove extensions whose mere presence pollutes test output or output -# ordering: +# Remove extensions whose mere presence changes PHP language test behavior: +# - memcache initializes $_SERVER during RINIT, breaking tests that need it +# uninitialized (e.g. ext/phar/tests/webphar_cgi_no_path_info.phpt) # - rdkafka emits CONFWARN lines, breaking Zend/tests/instantiate_all_classes.phpt # - parallel (ZTS-only) defers 'PHP Startup' diagnostics until after the # script's first output, breaking ~56 deprecation/warning-ordering tests @@ -16,6 +17,7 @@ function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4 # variant (debug for the tracer job, nts/zts for the profiler job) instead # of hardcoding the debug path. scan_dir="$(php -r 'echo PHP_CONFIG_FILE_SCAN_DIR;')" +sudo rm -f "${scan_dir}/memcache.ini" sudo rm -f "${scan_dir}/memcached.ini" sudo rm -f "${scan_dir}/rdkafka.ini" sudo rm -f "${scan_dir}/parallel.ini" From 3db55d41833d894a62fdbf8201c4aaa19b17dc5c Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 14:08:28 +0100 Subject: [PATCH 06/10] Fix loader threshold Signed-off-by: Bob Weinand --- loader/dd_library_loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/dd_library_loader.c b/loader/dd_library_loader.c index 074dbfc3327..c1527ab3e80 100644 --- a/loader/dd_library_loader.c +++ b/loader/dd_library_loader.c @@ -26,7 +26,7 @@ #define PHP_71_VERSION 20160303 #define PHP_72_VERSION 20170718 #define PHP_80_VERSION 20200930 -#define PHP_86_VERSION 20261112 +#define PHP_86_VERSION 20260924 /* the old version, including early RC so that >= works properly */ #define MIN_PHP_VERSION "7.0" #define MAX_PHP_VERSION "8.6" @@ -1002,7 +1002,7 @@ static int ddloader_api_no_check(int api_no) { zend_register_ini_entries(module_api_no <= PHP_72_VERSION ? (zend_ini_entry_def *) ini_entries_7_0_to_2 : ini_entries, mod->module_number); } - if (api_no > MAX_API_VERSION) { + if (api_no > MAX_API_VERSION || api_no == 20260924 /* early PHP 8.6 RCs */) { if (!force_load) { TELEMETRY(REASON_INCOMPATIBLE_RUNTIME, NULL, NULL, "Found incompatible runtime (api no: %d). Supported runtimes: PHP " MIN_PHP_VERSION " to " MAX_PHP_VERSION, api_no); return SUCCESS; From e3eeaebc9709a533f6a2c0473e90761f9c1239ce Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 14:32:01 +0100 Subject: [PATCH 07/10] Apply suggestion from @datadog-prod-us1-5[bot] Co-authored-by: datadog-prod-us1-5[bot] <266081015+datadog-prod-us1-5[bot]@users.noreply.github.com> --- loader/dd_library_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/dd_library_loader.c b/loader/dd_library_loader.c index c1527ab3e80..7043d7a5739 100644 --- a/loader/dd_library_loader.c +++ b/loader/dd_library_loader.c @@ -1002,7 +1002,7 @@ static int ddloader_api_no_check(int api_no) { zend_register_ini_entries(module_api_no <= PHP_72_VERSION ? (zend_ini_entry_def *) ini_entries_7_0_to_2 : ini_entries, mod->module_number); } - if (api_no > MAX_API_VERSION || api_no == 20260924 /* early PHP 8.6 RCs */) { + if (api_no > MAX_API_VERSION || module_api_no == 20260924 /* early PHP 8.6 RCs */) { if (!force_load) { TELEMETRY(REASON_INCOMPATIBLE_RUNTIME, NULL, NULL, "Found incompatible runtime (api no: %d). Supported runtimes: PHP " MIN_PHP_VERSION " to " MAX_PHP_VERSION, api_no); return SUCCESS; From c5633bc3f859995f07f8194a72adfd239d7619cc Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 14:45:51 +0100 Subject: [PATCH 08/10] xfail PHP 8.6 language tests dumping injected trace headers ext/soap/tests/gh23686 (new in 8.6.0RC4) and sapi/cli/tests/gh16137_duplicate_headers.phpt (new in 8.6.0RC5) dump the outgoing request headers, which include the x-datadog-*, traceparent and tracestate headers injected by ddtrace. Co-Authored-By: Claude Opus 5.5 --- dockerfiles/ci/xfail_tests/8.6.list | 29 ++++++++++++++++++++++++++++ dockerfiles/ci/xfail_tests/README.md | 6 ++++++ 2 files changed, 35 insertions(+) diff --git a/dockerfiles/ci/xfail_tests/8.6.list b/dockerfiles/ci/xfail_tests/8.6.list index 9725131b56f..76adae8adbb 100644 --- a/dockerfiles/ci/xfail_tests/8.6.list +++ b/dockerfiles/ci/xfail_tests/8.6.list @@ -131,6 +131,34 @@ ext/readline/tests/libedit_callback_handler_remove_001.phpt ext/simplexml/tests/bug51615.phpt ext/soap/tests/bugs/bug77088.phpt ext/soap/tests/soap_qname_crash.phpt +ext/soap/tests/gh23686/header-from-stringable.phpt +ext/soap/tests/gh23686/header-just-lf-separator.phpt +ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt +ext/soap/tests/gh23686/import-authorization-basic.phpt +ext/soap/tests/gh23686/import-authorization-bearer.phpt +ext/soap/tests/gh23686/import-authorization-digest.phpt +ext/soap/tests/gh23686/import-cookies.phpt +ext/soap/tests/gh23686/import-keep-authorization.phpt +ext/soap/tests/gh23686/import-keep-cookies.phpt +ext/soap/tests/gh23686/import-keep-proxy-auth.phpt +ext/soap/tests/gh23686/import-proxy-authorization.phpt +ext/soap/tests/gh23686/import-repeated-authorization.phpt +ext/soap/tests/gh23686/import-repeated-cookies.phpt +ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt +ext/soap/tests/gh23686/redact-array-headers.phpt +ext/soap/tests/gh23686/redact-not-first.phpt +ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt +ext/soap/tests/gh23686/schema-authorization-basic.phpt +ext/soap/tests/gh23686/schema-authorization-bearer.phpt +ext/soap/tests/gh23686/schema-authorization-digest.phpt +ext/soap/tests/gh23686/schema-cookies.phpt +ext/soap/tests/gh23686/schema-keep-authorization.phpt +ext/soap/tests/gh23686/schema-keep-cookies.phpt +ext/soap/tests/gh23686/schema-keep-proxy-auth.phpt +ext/soap/tests/gh23686/schema-proxy-authorization.phpt +ext/soap/tests/gh23686/schema-repeated-authorization.phpt +ext/soap/tests/gh23686/schema-repeated-cookies.phpt +ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt ext/sockets/tests/gh21161.phpt ext/sockets/tests/socket_create_listen-nobind.phpt ext/sockets/tests/socket_import_stream-4.phpt @@ -198,6 +226,7 @@ ext/uri/tests/whatwg/parsing/path_success_null_byte.phpt ext/uri/tests/whatwg/builder/username_success_tab_newline.phpt ext/zend_test/tests/ sapi/cli/tests/bug80092.phpt +sapi/cli/tests/gh16137_duplicate_headers.phpt sapi/cli/tests/gh8827-001.phpt sapi/cli/tests/gh8827-003.phpt sapi/cli/tests/upload_2G.phpt diff --git a/dockerfiles/ci/xfail_tests/README.md b/dockerfiles/ci/xfail_tests/README.md index 6053f01d112..dfaffb11190 100644 --- a/dockerfiles/ci/xfail_tests/README.md +++ b/dockerfiles/ci/xfail_tests/README.md @@ -186,6 +186,12 @@ Disabled on versions: `8.6`. The dumped request headers include the injected `x-datadog-*`/`traceparent`/`tracestate` headers. +## `ext/soap/tests/gh23686/*.phpt` (the 28 listed in `8.6.list`), `sapi/cli/tests/gh16137_duplicate_headers.phpt` + +Disabled on versions: `8.6` (new in 8.6.0RC4/RC5, not yet in the 8.4/8.5 releases used in CI). + +The dumped request headers include the injected `x-datadog-*`/`traceparent`/`tracestate` headers. + ## `ext/standard/tests/filters/stream_filter_register_*.phpt` (the 5 listed in `8.6.list`) Disabled on versions: `8.6` (the tests are new in 8.6). From 87693f8a2daf1b5971d14b05661a84a5aded7515 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 14:52:43 +0100 Subject: [PATCH 09/10] xfail ext/phar/tests/webphar_cgi_no_path_info.phpt on PHP 8.6 With variables_order=EGPC, the $_SERVER that ddtrace materializes in RINIT is empty. Phar::webPhar() then looks up SCRIPT_NAME there instead of via sapi_getenv() and returns without redirecting. Plain PHP behaves the same with auto_globals_jit=0. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Bob Weinand --- dockerfiles/ci/xfail_tests/8.6.list | 1 + dockerfiles/ci/xfail_tests/README.md | 10 ++++++++++ tracer/collect_backtrace.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/dockerfiles/ci/xfail_tests/8.6.list b/dockerfiles/ci/xfail_tests/8.6.list index 76adae8adbb..ab1ed2c8aa8 100644 --- a/dockerfiles/ci/xfail_tests/8.6.list +++ b/dockerfiles/ci/xfail_tests/8.6.list @@ -126,6 +126,7 @@ ext/phar/tests/phar_metadata_write4.phpt ext/phar/tests/phar_oo_002.phpt ext/phar/tests/phar_oo_005.phpt ext/phar/tests/phar_oo_007.phpt +ext/phar/tests/webphar_cgi_no_path_info.phpt ext/readline/tests/libedit_callback_handler_install_001.phpt ext/readline/tests/libedit_callback_handler_remove_001.phpt ext/simplexml/tests/bug51615.phpt diff --git a/dockerfiles/ci/xfail_tests/README.md b/dockerfiles/ci/xfail_tests/README.md index dfaffb11190..ea96bb49ff4 100644 --- a/dockerfiles/ci/xfail_tests/README.md +++ b/dockerfiles/ci/xfail_tests/README.md @@ -192,6 +192,16 @@ Disabled on versions: `8.6` (new in 8.6.0RC4/RC5, not yet in the 8.4/8.5 release The dumped request headers include the injected `x-datadog-*`/`traceparent`/`tracestate` headers. +## `ext/phar/tests/webphar_cgi_no_path_info.phpt` + +Disabled on versions: `8.6` (the test is new in 8.6.0RC4). + +The test runs with `variables_order=EGPC`, so `$_SERVER` is not built unless something requests it, and then +it is an empty array. `Phar::webPhar()` reads `SCRIPT_NAME` from `$_SERVER` whenever it exists and only falls +back to `sapi_getenv()` otherwise. ddtrace materializes `$_SERVER` in RINIT for the root span, so `webPhar()` +finds no `SCRIPT_NAME` and returns without the expected redirect. Plain PHP fails the same way with +`auto_globals_jit=0`, or with `memcache.so` loaded. + ## `ext/standard/tests/filters/stream_filter_register_*.phpt` (the 5 listed in `8.6.list`) Disabled on versions: `8.6` (the tests are new in 8.6). diff --git a/tracer/collect_backtrace.c b/tracer/collect_backtrace.c index 0c48c7b98a7..a77a6fc59cf 100644 --- a/tracer/collect_backtrace.c +++ b/tracer/collect_backtrace.c @@ -622,6 +622,11 @@ void ddtrace_fetch_debug_backtrace(zval *return_value, int skip_last, int option prev_stack_frame = stack_frame; skip_frame: +#ifdef ZEND_HAS_OOM + if (ZEND_HAS_OOM()) { + break; + } +#endif if (UNEXPECTED(ZEND_CALL_KIND(call) == ZEND_CALL_TOP_FUNCTION) && !fake_frame && prev From 905c3248bbe36a85f21508fd1b3b347cfb4587b8 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 13 Nov 2026 16:02:42 +0100 Subject: [PATCH 10/10] Skip 8.6 verify jobs Signed-off-by: Bob Weinand --- .gitlab/generate-package.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/generate-package.php b/.gitlab/generate-package.php index d81489b5ee1..f0c9611574e 100644 --- a/.gitlab/generate-package.php +++ b/.gitlab/generate-package.php @@ -1365,7 +1365,7 @@ function system_tests_run_scenarios(string $scenarios): void - alpine:3.21 - alpine:3.24 # Pre-GA versions only exist as -rc tags on Docker Hub. - - IMAGE: ") ? "php:$v-rc-fpm-alpine" : "php:$v-fpm-alpine"; }, $all_minor_major_targets)), "\n" ?> + - IMAGE: ") ? "" /*"php:$v-rc-fpm-alpine"*/ : "php:$v-fpm-alpine"; }, $all_minor_major_targets))), "\n" ?> before_script: &verify_alpine_before_script - mkdir build @@ -1457,7 +1457,7 @@ function system_tests_run_scenarios(string $scenarios): void INSTALL_MODE: sury parallel: matrix: - - PHP_VERSION: + - PHP_VERSION: ") ? "" /*broken for PHP 8.6 currently*/ : $v; }, $all_minor_major_targets))), "\n" ?> IMAGE: - "debian:bullseye-slim" - "debian:bookworm-slim"