Category
stdlib · php-src-strict · regression on closed #7159
Problem
When a Throwable is constructed manually inside a user function (not thrown), getTrace() must include the immediate caller frame (Zend: array(1) from probe()). VM returns array(0) for Exception, Error, and TypeError.
CLI top-level new Exception() may show array(0) on both engines; the parity gap is the function-nested construct path used by real code and tests.
php-src reference
Repro (failure today — verified 2026-06-19)
test/repro-maintainer/throwable_trace_matrix.php:
<?php
declare(strict_types=1);
function probe(string $class): void
{
$e = new $class('probe');
foreach (['getTrace', 'getTraceAsString', '__toString'] as $m) {
echo $class, '::', $m, ' ';
try {
$r = '__toString' === $m ? (string) $e : $e->$m();
echo is_array($r) ? 'array(' . count($r) . ')' : 'string', "\n";
} catch (Throwable $t) {
echo get_class($t), "\n";
}
}
}
probe(Exception::class);
probe(Error::class);
probe(TypeError::class);
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh
php test/repro-maintainer/throwable_trace_matrix.php
php bin/vm.php test/repro-maintainer/throwable_trace_matrix.php
'
| Class |
Method |
Zend PHP 8.2 |
VM today |
Exception |
getTrace |
array(1) |
array(0) ❌ |
Error |
getTrace |
array(1) |
array(0) ❌ |
TypeError |
getTrace |
array(1) |
array(0) ❌ |
| all |
getTraceAsString |
string |
string ✅ |
| all |
__toString |
string |
string ✅ |
Scope (PHP-in-PHP)
| Path |
Work |
lib/VM/Builtin/ExceptionGetTrace.php |
synthesize caller frame when trace empty and object not thrown |
lib/VM/ExceptionTrace.php |
shared trace builder |
lib/VM/Builtin/ExceptionConstruct.php (or ctor hook) |
capture constructing frame at manual new |
| JIT/AOT |
mirror VM when lowering getTrace() |
No new C runtime — read frames from VM call stack metadata.
Done when
Verification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php test/repro-maintainer/throwable_trace_matrix.php'
Related
Category
stdlib· php-src-strict · regression on closed #7159Problem
When a
Throwableis constructed manually inside a user function (not thrown),getTrace()must include the immediate caller frame (Zend:array(1)fromprobe()). VM returnsarray(0)forException,Error, andTypeError.CLI top-level
new Exception()may showarray(0)on both engines; the parity gap is the function-nested construct path used by real code and tests.php-src reference
Zend/zend_exceptions.c— trace capture onobject_init_ex/getTrace()Zend/zend_execute.c— frame push at call sitesRepro (failure today — verified 2026-06-19)
test/repro-maintainer/throwable_trace_matrix.php:ExceptiongetTracearray(1)array(0)❌ErrorgetTracearray(1)array(0)❌TypeErrorgetTracearray(1)array(0)❌getTraceAsStringstringstring✅__toStringstringstring✅Scope (PHP-in-PHP)
lib/VM/Builtin/ExceptionGetTrace.phplib/VM/ExceptionTrace.phplib/VM/Builtin/ExceptionConstruct.php(or ctor hook)newgetTrace()No new C runtime — read frames from VM call stack metadata.
Done when
getTrace array(1)on VM (php-src-strict)test/compliance/cases/stdlib/throwable_manual_construct_trace.phpt./script/ci-fast.sh --filter throwable_manual_construct_tracegreenVerification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php test/repro-maintainer/throwable_trace_matrix.php'Related