Category
php-in-php
Problem
Output buffering (ob_start, ob_get_contents, ob_end_flush, implicit flush, echo into active buffer) is implemented twice: VM uses ext/standard/VmOb.php / VmObOutput.php, while JIT/AOT embeds ~1050 lines of LLVM stack management in lib/JIT/Builtin/ObOutputRuntime.php (__phpc_ob_* globals, libc fwrite, buffer caps). This is classic PHP-in-PHP debt — behavior belongs in ext/standard, not a second C-like runtime in LLVM IR.
php-src reference
ext/standard/output.c — php_ob_* stack, handlers, flush
main/output.c — SAPI output layer interaction
Repro (VM vs JIT must match)
<?php
ob_start();
echo 'hello';
$buf = ob_get_contents();
ob_end_clean();
var_dump($buf);
echo ob_get_level(), "\n";
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/vm.php repro.php'
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/jit.php repro.php'
Zend: string(5) "hello", level 0.
Done when: identical on VM and JIT/AOT after migration.
Nested buffers + ob_implicit_flush smoke:
<?php
ob_start(); ob_start(); echo 'x'; var_dump(ob_get_level()); ob_end_flush(); ob_end_flush();
Scope (this repo)
| Area |
Path |
Action |
| Stdlib SSOT |
ext/standard/VmOb.php, VmObOutput.php, ob_*.php |
Single ob stack implementation |
| JIT bridge |
ext/standard/JitOb*.php (extend) |
Route JIT calls to PHP helpers |
| Delete/shrink |
lib/JIT/Builtin/ObOutputRuntime.php, ObOutput.php |
Remove LLVM ob stack |
| Echo lowering |
lib/JIT.php ob echo paths |
Call PHP bridge instead of __phpc_ob_echo_* |
| Related |
lib/JIT/Builtin/ObGzhandlerJitRuntime.php |
Pairs with #9091 (handler callback) |
Do not reintroduce lib/AOT/runtime/phpc_ob.c logic — shrink C, grow PHP.
Done when (php-src-strict)
Verification
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && vendor/bin/phpunit --filter Ob'
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/jit.php test/compliance/cases/stdlib/ob_start_basic.phpt'
Links
Category
php-in-phpProblem
Output buffering (
ob_start,ob_get_contents,ob_end_flush, implicit flush, echo into active buffer) is implemented twice: VM usesext/standard/VmOb.php/VmObOutput.php, while JIT/AOT embeds ~1050 lines of LLVM stack management inlib/JIT/Builtin/ObOutputRuntime.php(__phpc_ob_*globals, libcfwrite, buffer caps). This is classic PHP-in-PHP debt — behavior belongs inext/standard, not a second C-like runtime in LLVM IR.php-src reference
ext/standard/output.c—php_ob_*stack, handlers, flushmain/output.c— SAPI output layer interactionRepro (VM vs JIT must match)
Zend:
string(5) "hello", level0.Done when: identical on VM and JIT/AOT after migration.
Nested buffers +
ob_implicit_flushsmoke:Scope (this repo)
ext/standard/VmOb.php,VmObOutput.php,ob_*.phpext/standard/JitOb*.php(extend)lib/JIT/Builtin/ObOutputRuntime.php,ObOutput.phplib/JIT.phpob echo paths__phpc_ob_echo_*lib/JIT/Builtin/ObGzhandlerJitRuntime.phpDo not reintroduce
lib/AOT/runtime/phpc_ob.clogic — shrink C, grow PHP.Done when (php-src-strict)
VmObOutput)ObOutputRuntime.phpdeleted or <50-line ABI shim; PR cites lines removedob_*compliance PHPTs green on JIT/AOTmake bootstrap-selfhost-vm-driver-execute-probestill green if echo/ob touched on spineVerification
Links