Living roadmap β edit this issue as phases complete. Link PRs here. Check boxes when milestones land on master.
Repository: PurHur/php-compiler
Target: Compile and run a small normal PHP web app (multi-file, forms, headers, optional AOT CGI/FastCGI deploy)βnot just echo "Hello".
Issue index: #46 β#868 . Latest audit: 2026-05-23 batch 58 (#866 β#868 ; expanded #72 /#75 ; de-dupe #98 /#174 /#47 ; bisect #866 /#867 ).
Phase model (north star)
Delivery order for the compile-a-small-web-app goal:
Phase
Name
GitHub labels (typical)
Milestone
0
Foundation
phase-0:Foundation
Local/Docker CI (#436 β
, #501 β
), phpc CLI + phpc init (#312 β
, #632 β
), Docker 22.04 (#73 β
), docs (#245 β
, #48 , #727 phpc-json, #808 gate matrix), JIT compliance (#98 , #717 β
, #728 β
), inventory gate (#765 ), self-host probe CI (#829 , #830 , #868 )
1
Language
phase-2:language
OOP VM/JIT β
/ native AOT link (#568 β
, #752 β
), typed props (#767 , #169 ), includes (#54 β
VM, #475 JIT), ::class (#740 ), return types (#205 )
2
Stdlib
phase-4:stdlib
Web builtins: headers, JSON, sessions (#64 ), htmlspecialchars (#124 β
), path guards (#117 , #704 β
), regex (#93 β
, #787 preg_quote β
), redirects (#634 β
), templates (#275 β
extract), rmdir (#759 β
), unlink (#748 β
), rename (#772 β
), touch (#843 β
)
3
Web AOT
phase-3:aot, phase-1:web-runtime
phpc build --project link β
(#752 ), execute (#764 , #848 β#849 , #866 β#867 , #831 β#834 , #784 , #807 ), deploy (#609 β
, #718 β
), runtime includes (#623 β
), VM/AOT CGI (#50 β
, #665 β
), FastCGI (#173 ), debug CLI (#774 , #792 , #847 , #75 )
4
Polish
phase-5:reference-app
MiniWebApp VM β
(#539 ) β gates default-on (#641 β
, #664 β
, #674 β
) β AOT execute+HTTP (#676 , #747 , #478 , #833 ) β docs (#655 β
, #445 , #696 , #753 , #801 ) + stale-copy gates (#766 , #802 , #809 ) + smoke accuracy
Label prefixes (phase-2:language, etc.) are historical GitHub labels β they do not always match the phase number in this table.
Current phase
Active β Phase 4 Polish (AOT execute + HTTP): phpc build --project examples/003-MiniWebApp links on harness (#752 β
, #812 β
). Native .phpc/bin/app runs but returns empty stdout for home/hello routes β #764 (not a link failure).
Bisect ladder (smallest β largest):
Testing: isset_object_property_array.phpt AOT execute green (resolveAppName, #764)Β #848 β isset_object_property_array.phpt execute (resolveAppName)
Testing: aot phpt miniwebapp_render_home β resolveAppName + layout include (#764)Β #867 β miniwebapp_render_home.phpt (resolveAppName + one-hop layout include)
AOT: $_SERVER reads in runtime-included templates (layout.php SCRIPT_NAME, #764)Β #866 β $_SERVER read inside included layout.php
AOT: private method call chain before runtime include (layout_title_branch, #764)Β #846 + Testing: layout_title_branch.phpt β wire existing AOT fixture dir (#807, #784)Β #832 β private method call β layout partial chain
AOT: global user function called from class method (contact validation pattern, #764)Β #831 β global fn from class method (contact validation)
AOT: renderApiStatus json_encode + header in class method (#764)Β #849 β JSON api/status branch in class method
AOT: rejectContactInput early-return path in Router::dispatch (404 branch, #764)Β #834 β reject early-return path
AOT: MiniWebApp native binary CLI execute emits empty stdout (post-link)Β #764 β full 003 integration
Track
Issues
AOT execute (blocker)
#764 , #848 , #867 , #866 , #846 , #831 , #832 , #834 , #849 , #784 , #807
AOT execute tests
#785 , #773 , #775 / #791 β
, #747 (canonical), #485 (umbrella)
Smoke / messaging
#809 , #833 , #766 , #802 , #801 , #753
Test infra
#790 , #454
DevEx / debug
#847 --list-units, #774 , #746 , #792 , #808
Self-host (stretch)
#829 , #830 , #868 , #816 β
HTTP / CGI AOT
#610 (canonical), #478 (umbrella), #682
CI hygiene
#765 , #754 , #737 , #98
In parallel β Phase 1 Language: #205 , #475 , #145 , #740 , #169 , #767 , #195 .
In parallel β Phase 0 Foundation: #98 JIT compliance; #113 host php-parser. Canonical CI: ./script/ci-local.sh / make test-docker β #394 GHA disabled.
Stretch (orthogonal): Self-host #540 β
β docs/bootstrap-selfhost.md .
Next 3 priorities
Current state (2026-05-23, batch 58)
Area
Status
Notes
VM execution
β
Strong
Compliance + RealWorld PHPT + mini_web_app_*.phpt
phpc CLI
β
serve, run, build, deploy, test, lint, init --profile miniwebapp, doctor --gates (#657 β
)
001β004 examples
β
VM/AOT (000β002, 004)
make examples-aot-smoke β 003 execute pending #764
003-MiniWebApp VM
β
#539 , PATH_INFO (#489 ), assets (#594 )
003-MiniWebApp AOT link
β
#752 β
, #812 β
003-MiniWebApp AOT execute
β
#764 β exit 0, 0 stdout bytes
AOT bisect fixtures
π
#848 isset; #867 render_home; #866 $_SERVER in include; #846 /#832 layout; #831 , #834
JSON api/status AOT
π
#849 class-method json_encode
Self-host probe CI
π
#829 wire probe; #830 inventory; #868 cast_int/isset_array_offset link
phpc build --list-units
π
#847 debug graph
Magic constants
β
#85 β
umbrella
JIT compliance root fix
β οΈ
#98 (not #174 )
examples/README AOT row
β οΈ stale
#753 cites #568
examples-aot-smoke 003
β οΈ stale skip
#809 β script still skips #568
Bootstrap inventory CI
β οΈ
#765 , #830
Deploy shell smoke
β
#718 β
(001/002)
touch() stdlib
β
#843 β
closed
Verify on harness (no GHA):
make docker-build-22
./script/docker-ci-local.sh
php script/bootstrap-inventory.php --check # #765 / #830
./script/ci-fast.sh
make web-smoke && make examples-web-smoke
# AOT bisect (#764):
./script/ci-local.sh --filter ' isset_object_property_array|miniwebapp_render_home'
cd examples/003-MiniWebApp && QUERY_STRING=route=home REQUEST_METHOD=GET ./.phpc/bin/app | wc -c
phpc doctor --gates
New issues (batch 58, #866 β#868 )
#
Title
#866
AOT: $_SERVER reads in runtime-included templates (layout.php SCRIPT_NAME, #764 )
#867
Testing: aot phpt miniwebapp_render_home β resolveAppName + layout include (#764 )
#868
Self-host: native link bootstrap-aot cast_int.php + isset_array_offset.php (Phase C)
Expanded (batch 58): #72 (closures), #75 (AOT debug symbols).
De-dupe comments (batch 58): #98 β #174 ; closed #47 β local CI; #394 GHA off.
Prior batch (57, #846 β#849 )
#
Title
#846
AOT: private method call chain before runtime include (layout_title_branch, #764 )
#847
DevEx: phpc build --project --list-units for AOT debug (#764 )
#848
Testing: isset_object_property_array.phpt AOT execute green (resolveAppName, #764 )
#849
AOT: renderApiStatus json_encode + header in class method (#764 )
Expanded (batch 57): #829 (self-host probe in CI), #830 (inventory regen workflow).
De-dupe comments (batch 57): #98 β #174 ; #485 β #747 canonical; #478 β #610 canonical.
Prior batch (56, #831 β#834 )
#
Title
#831
AOT: global user function called from class method (#764 )
#832
Testing: layout_title_branch.phpt β wire fixture (#807 , #784 )
#833
Testing: examples-web-smoke.sh --aot 003 PATH_INFO curls
#834
AOT: rejectContactInput early-return path (#764 )
CI de-duplication (canonical)
Topic
Canonical issue
Notes
Full local gate
./script/ci-local.sh, make test-docker, ./script/docker-ci-local.sh
#245 β
docs
Fast iteration
./script/ci-fast.sh, make test-fast
#436 β
JIT skipped with LLVM 9
#98 , #250 β
, #717 β
, #728 β
Not #174
LLVM 14+ upgrade
#174
After #98 green on LLVM 9
GHA workflows
#394 β
closed
Do not add .github/workflows/* for CI
MiniWebApp AOT execute
#764 , #848 β#849 , #831 β#834
Bisect then integrate
MiniWebApp AOT execute tests
#747
#485 umbrella only
ServeAotTest MiniWebApp
#610
#478 umbrella only
Self-host compile probe
#816 β
script, #829 CI wire, #830 doc commit, #868 cast/isset link
Stretch Phase 0
AOT debug graph
#847 --list-units
#746 doctor probe
Stale #568 messaging
#766 , #792 , #802 , #801 , #809 , #753
Scripts + docs
How to update
Check phase boxes when child issues close.
Update Current state after each audit.
Comment release notes when a phase completes.
Close duplicates; link blockers in Reference app: MiniWebApp runtime green (lint 0, VM serve, routes)Β #539 , Testing: MiniWebApp AOT unskip matrix after #568 (orchestrate #454 #485 #478 #610)Β #676 , AOT: MiniWebApp native binary CLI execute emits empty stdout (post-link)Β #764 .
Child issues: #46 β#868 .
Repository: PurHur/php-compiler
Target: Compile and run a small normal PHP web app (multi-file, forms, headers, optional AOT CGI/FastCGI deploy)βnot just
echo "Hello".Issue index: #46β#868. Latest audit: 2026-05-23 batch 58 (#866β#868; expanded #72/#75; de-dupe #98/#174/#47; bisect #866/#867).
Phase model (north star)
Delivery order for the compile-a-small-web-app goal:
phase-0:FoundationphpcCLI +phpc init(#312 β , #632 β ), Docker 22.04 (#73 β ), docs (#245 β , #48, #727 phpc-json, #808 gate matrix), JIT compliance (#98, #717 β , #728 β ), inventory gate (#765), self-host probe CI (#829, #830, #868)phase-2:language::class(#740), return types (#205)phase-4:stdlibhtmlspecialchars(#124 β ), path guards (#117, #704 β ), regex (#93 β , #787 preg_quote β ), redirects (#634 β ), templates (#275 β extract),rmdir(#759 β ),unlink(#748 β ),rename(#772 β ),touch(#843 β )phase-3:aot,phase-1:web-runtimephpc build --projectlink β (#752), execute (#764, #848β#849, #866β#867, #831β#834, #784, #807), deploy (#609 β , #718 β ), runtime includes (#623 β ), VM/AOT CGI (#50 β , #665 β ), FastCGI (#173), debug CLI (#774, #792, #847, #75)phase-5:reference-appLabel prefixes (
phase-2:language, etc.) are historical GitHub labels β they do not always match the phase number in this table.Current phase
Active β Phase 4 Polish (AOT execute + HTTP):
phpc build --project examples/003-MiniWebApplinks on harness (#752 β , #812 β ). Native.phpc/bin/appruns but returns empty stdout for home/hello routes β #764 (not a link failure).Bisect ladder (smallest β largest):
isset_object_property_array.phptexecute (resolveAppName)miniwebapp_render_home.phpt(resolveAppName + one-hop layout include)$_SERVERread inside includedlayout.phpapi/statusbranch in class method--list-units, #774, #746, #792, #808In parallel β Phase 1 Language: #205, #475, #145, #740, #169, #767, #195.
In parallel β Phase 0 Foundation: #98 JIT compliance; #113 host php-parser. Canonical CI:
./script/ci-local.sh/make test-dockerβ #394 GHA disabled.Stretch (orthogonal): Self-host #540 β β docs/bootstrap-selfhost.md.
Next 3 priorities
--list-unitsdebugCurrent state (2026-05-23, batch 58)
mini_web_app_*.phptphpcCLIinit --profile miniwebapp,doctor --gates(#657 β )make examples-aot-smokeβ 003 execute pending #764$_SERVERin include; #846/#832 layout; #831, #834json_encodephpc build --list-unitsVerify on harness (no GHA):
New issues (batch 58, #866β#868)
Expanded (batch 58): #72 (closures), #75 (AOT debug symbols).
De-dupe comments (batch 58): #98 β #174; closed #47 β local CI; #394 GHA off.
Prior batch (57, #846β#849)
Expanded (batch 57): #829 (self-host probe in CI), #830 (inventory regen workflow).
De-dupe comments (batch 57): #98 β #174; #485 β #747 canonical; #478 β #610 canonical.
Prior batch (56, #831β#834)
CI de-duplication (canonical)
./script/ci-local.sh,make test-docker,./script/docker-ci-local.sh./script/ci-fast.sh,make test-fast.github/workflows/*for CI--list-unitsHow to update
Child issues: #46β#868.