Problem
VM supports plain include/require (#54 tracks JIT/AOT). Real apps and 003-MiniWebApp load bootstrap files multiple times:
require_once __DIR__ . '/config.php ' ;
require_once __DIR__ . '/config.php ' ; // must not re-execute
Without once-semantics, side effects (constants, function declarations) double-run and break routers.
Goal
require_once / include_once skip already-loaded absolute paths in VM, JIT, and AOT (per-process for CLI; per-request rules for CGI — coordinate #49 ).
Implementation hints
VM (lib/VM.php)
Add includedOncePaths set on runtime/context (absolute normalized paths).
New opcodes or flags on TYPE_INCLUDE: once bit checked before executing file.
On hit: push true / no-op return per Zend semantics (document if different).
Reset policy: new request in Superglobals::populateFromEnvironment() for CGI (AOT: Per-request superglobal refresh in native binary (not compile-time bake) #201 ✅ GET; POST AOT: RuntimeSuperglobalRefreshTest dual REQUEST_BODY on one binary #257 ✅).
Compiler (lib/Compiler.php)
Distinguish Include_::TYPE_ONCE vs plain in CFG lowering from php-cfg Include_ node kinds.
JIT / AOT
Lint
Tests
test/compliance/cases/require_once_idempotent.phpt — counter in included file increments once
test/compliance/cases/require_once_dir.phpt — uses __DIR__
Acceptance criteria
docker run --rm -v " $( pwd) :/compiler" -w /compiler php-compiler:22.04-dev \
php bin/vm.php -r ' require_once "test/fixtures/once_counter.php"; require_once "test/fixtures/once_counter.php";'
Counter side effect runs once. JIT/AOT parity when #54 closes.
Verification (local / Docker only)
./script/ci-local.sh --filter require_once
Dependencies
Links
Problem
VM supports plain
include/require(#54 tracks JIT/AOT). Real apps and 003-MiniWebApp load bootstrap files multiple times:Without once-semantics, side effects (constants, function declarations) double-run and break routers.
Goal
require_once/include_onceskip already-loaded absolute paths in VM, JIT, and AOT (per-process for CLI; per-request rules for CGI — coordinate #49).Implementation hints
VM (
lib/VM.php)includedOncePathsset on runtime/context (absolute normalized paths).TYPE_INCLUDE:oncebit checked before executing file.true/ no-op return per Zend semantics (document if different).Superglobals::populateFromEnvironment()for CGI (AOT: Per-request superglobal refresh in native binary (not compile-time bake) #201 ✅ GET; POST AOT: RuntimeSuperglobalRefreshTest dual REQUEST_BODY on one binary #257 ✅).Compiler (
lib/Compiler.php)Include_::TYPE_ONCEvs plain in CFG lowering from php-cfgInclude_node kinds.JIT / AOT
php_compiler_include_once(path, already_loaded_table*)inlib/AOT/runtime/shared with VM__DIR__Lint
__DIR__concat benefit from Lint: Constant-fold __DIR__ concatenation for include/require graph in phpc lint --all #462 lint graph foldingTests
test/compliance/cases/require_once_idempotent.phpt— counter in included file increments oncetest/compliance/cases/require_once_dir.phpt— uses__DIR__Acceptance criteria
Counter side effect runs once. JIT/AOT parity when #54 closes.
Verification (local / Docker only)
Dependencies
__DIR__for relative pathsincludes[]— once table must cover manifest-listed filesconfig.phpbootstrapLinks