Skip to content

πŸ—ΊοΈ ROADMAP: Compile a small PHP web application (living document)Β #78

@PurHur

Description

@PurHur

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):

  1. Testing: isset_object_property_array.phpt AOT execute green (resolveAppName, #764)Β #848 β€” isset_object_property_array.phpt execute (resolveAppName)
  2. Testing: aot phpt miniwebapp_render_home β€” resolveAppName + layout include (#764)Β #867 β€” miniwebapp_render_home.phpt (resolveAppName + one-hop layout include)
  3. AOT: $_SERVER reads in runtime-included templates (layout.php SCRIPT_NAME, #764)Β #866 β€” $_SERVER read inside included layout.php
  4. 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
  5. AOT: global user function called from class method (contact validation pattern, #764)Β #831 β€” global fn from class method (contact validation)
  6. AOT: renderApiStatus json_encode + header in class method (#764)Β #849 β€” JSON api/status branch in class method
  7. AOT: rejectContactInput early-return path in Router::dispatch (404 branch, #764)Β #834 β€” reject early-return path
  8. 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

# Issue Why now
1 #848 β†’ #867 β†’ #866 β†’ #846 + #832 + #831 + #834 Bisect ladder before full #764
2 #747 + #790 + #809 + #833 + #849 + #785 Shared CGI env; CLI + HTTP smoke after GET green
3 #766 + #753 + #801 + #802 + #808 + #847 Retire #568 copy; docs; --list-units debug

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

  1. Check phase boxes when child issues close.
  2. Update Current state after each audit.
  3. Comment release notes when a phase completes.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions