Skip to content

[PHP] Batch 3: optional ZIP, curl, and intl extensions#894

Closed
brandonpayton wants to merge 17 commits into
integration/kd-6nz-php-phpt-platform-fixesfrom
integration/kd-6nz-php-extensions
Closed

[PHP] Batch 3: optional ZIP, curl, and intl extensions#894
brandonpayton wants to merge 17 commits into
integration/kd-6nz-php-phpt-platform-fixesfrom
integration/kd-6nz-php-extensions

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 12, 2026

Copy link
Copy Markdown
Member

Purpose

Stacked Batch 3 integration PR for the PHP extension work. It is based on the
still-open Batch 2 platform/ABI branch, so the extension layer remains
independently reviewable on the exact ABI-19 runtime it was built against.

Constituent PRs were reviewed and repaired individually, then rebase-merged
here in serial PHP-recipe order. Their original GitHub PRs are MERGED; this
aggregate remains open and must not merge into Batch 2 or main without
Brandon's explicit approval.

Per-constituent ready-to-merge CI was intentionally skipped under the agreed
batching plan. Focused evidence supports each review decision; the full
ready-to-merge gate runs once on this final aggregate tree.

Constituent review ledger

#647 — reproducible libzip package and PHP ZIP extension — incorporated

  • Summary: Adds resolver-owned libzip 1.11.4 and PHP's opt-in zip.so side module, with STORE/DEFLATE behavior through normal Node and browser runtime paths.
  • Review changes: Replaced hand-maintained source/configuration lists and checkout-local state with upstream CMake, explicit wasm32/musl facts, exact outputs, reproducibility/path checks, required-symbol validation, and a full-member final-Wasm import audit. Limited PHP configure answers to APIs verified by that archive; added safe browser artifact serving, kernel-VFS mounting, and real Chromium behavior. Rewrote stale merge history as two purpose-led commits while retaining mho22 attribution.
  • View: Accept after repair. The extension now follows the package resolver and VFS/runtime contracts. Optional crypto, BZIP2, LZMA, and Zstandard support remains outside this wasm32 package.
  • Focused evidence: Two distinct clean builds produced byte-identical four-file output sets; all 114 members and 15 final imports were accounted for. A clean ABI-19 PHP build produced exactly six outputs with all 139 zip.so environment imports resolved. Focused Vitest passed 19/19; selected Node ZIP PHPTs passed 13/13; dedicated real Chromium passed 1/1 with a DEFLATE close/reopen through the kernel VFS.
  • Current Batch 3 commits: 0aaab1226, 6aee4687a. Original PR [PHP] Ship the libzip-backed ZIP extension #647 is MERGED.

#648 — reproducible libcurl/curl packages and PHP curl extension — incorporated

  • Summary: Builds verified curl 8.11.1 library/program transactions and publishes PHP's opt-in curl.so, keeping base PHP curl-free while exercising normal dlopen, fork replay, and kernel networking paths.
  • Review changes: Replaced persistent build state and stale dependency metadata with clean source extraction, deterministic PIC/reproducibility flags, relocatable libcurl.pc, truthful dependencies/host tools, exact outputs, required-symbol checks, all-170-member linking, complete final-import audits, and producer-path rejection. Added package CLI coverage plus Node and browser PHP tests, including a fork-child HTTP request to a guest loopback listener. Rebuilt old merge history as two purpose-led commits with Yannick Decat attribution.
  • View: Accept after repair. The package and side module now follow resolver, dlopen, VFS, and Node/browser contracts. HTTPS certificate flows, arbitrary remote endpoints, and external host-listener bridging were not part of the focused evidence.
  • Focused evidence: Clean libcurl produced exactly 14 files and a 15-import closure; clean curl produced its 15-import closure and passed 2/2 package tests. PHP produced seven ABI-19 artifacts; curl.so passed static closure and 5/5 Node tests. Real Chromium passed 1/1 and all eleven asserted PHP scenarios, including forked guest-loopback HTTP.
  • Batch commits: 1f369181e, ec8b9e318. Reviewed and landed trees both equal 556d557446d5c688c535fbf58f2a474163917665. Original PR [PHP] Ship the libcurl-backed curl extension #648 is MERGED.

#839 — reproducible ICU/libc++ packages and optional PHP intl — incorporated

  • Summary: Adds relocatable ICU4C and PIC libc++ packages and ships PHP's opt-in intl.so with its matching icu.dat runtime closure, while keeping base PHP ICU-free. Shared Node/browser host logic supports fail-closed C++ side-module linking, process-owned exception-tag identity, and TLS-safe fork replay.
  • Review changes: Rebuilt the stale two-commit branch as a purpose-led 12-commit serial on the exact Batch 3 tip while preserving original attribution. The repair links all 70 intl objects without masked build failures; makes ICU/libc++ outputs reproducible and relocatable; rejects producer paths and unsafe manifest, dependency, archive, and output paths; declares non-Wasm runtime files; selects one complete local, fetched, or installed artifact tier; and materializes icu.dat through the normal package/VFS closure. Host repair proves weak self-imports are module-defined before using a trampoline, fails genuinely missing imports eagerly, owns pointer-width-correct C++ tags per process, restores validated TLS bases across fork replay, and exposes the pthread boundary truthfully. The final VFS repair handles positive partial writes, fails on no progress, provides sufficient Node image capacity, verifies staged bytes, and routes browser-demo writers through the shared implementation. The aggregate host gate then exposed that the initial dlopen lock incorrectly rejected the second concurrent pthread fork; follow-up ba4fb0db0 preserves dlopen exclusion with an exclusive writer plus counted fork readers.
  • View: Accept after repair. The result fixes package, host-runtime, VFS, and Node/browser contracts instead of relying on cache scanning or PHP-specific host overrides. Base PHP remains independent of ICU, while loading intl selects one complete and matching runtime closure.
  • Focused evidence: Two clean ICU revision-6 and libc++ revision-7 builds from distinct roots were byte-identical; producer-path scans were clean, and relocated ICU pkg-config metadata compiled and linked a consumer. A fresh exact-tree PHP build produced all nine declared files byte-for-byte, linked all 70 intl objects, resolved the final import closure, and retained ABI 19. Pre-final host dylink/resolver/fork coverage passed 52/52; package manifest coverage passed 116 tests; resolver/runtime metadata passed 14; VFS helpers passed 5/5. On the exact integrated candidate, focused closure/VFS/dylink/fork/PHP hello+curl passed 90/90, Node intl passed 5/5, and the complete real-Chromium PHP spec passed 2/2. The initial broad host run exposed one real concurrent-fork regression plus two rootfs/lazy-exec fixture failures. After ba4fb0db0, fork/dlopen focus passed 5/5 and full host reached 1,251 passes with 2 expected failures and 95 skips; only the same two fixture failures remain, and both reproduce identically on exact pre-PHP: ship intl as an optional ICU-backed side module #839 base ec8b9e318. The exact ba4fb0db0 final-head core rerun passed kernel 1,135/1,135, libc 303 with 20 documented XFAIL and one known FLAKE-PASS but no unexpected outcomes, and the ABI consistency check. Real Chromium then ran the two-thread, 16-round concurrent-fork guest at the same head: exit 0, expected PASS output, empty stderr, and no console, page, request, or HTTP errors.
  • Remaining limits: The final Batch 3 aggregate package/browser gate and manual ./run.sh browser verification remain. Reproducibility builds ran on macOS/aarch64; the Linux host-tool path retains package-CI evidence. Pointer-width i64 cases are synthetic rather than a memory64 end-to-end runtime. Pthread-side dynamic loading remains explicitly unsupported. Performance was not measured. A separate TeX Live-only direct VFS writer is outside this PHP landing and is not claimed fixed.
  • Batch commits: PHP: ship intl as an optional ICU-backed side module #839 rebase-landed as 3c097d617, 6351dca50, 54a5e43c1, 5ed6e0ee7, a2a439c38, dde62ef75, 0c0b8b3a0, 42d45e237, 41e809ce5, 254bbeecf, 48bc91ec9, and e6d080f1c; reviewed and landed trees both equal 149bb568e0cc6d733fe9e6af17f6fc3db3b86964. Aggregate gate follow-up: ba4fb0db0. Original PR PHP: ship intl as an optional ICU-backed side module #839 is MERGED.

Batch 2 browser dependency

Batch 2 advanced from 84f086975 to 6d93e09d5 with two reviewed browser
repairs. The PHP page now imports only the canonical 63-file rootfs closure and
runs all PHPT sections/tests on one worker-owned VFS using the current
initFromImage/spawnFromVfs contract. Typed worker RPCs stage/restore
generated scripts without returning the live VFS SAB to the main thread.
Focused tests passed 13/13; real Chromium passed Zend/tests/001.phpt and a
two-test FILE-to-CLEAN persistence run. #647 was patch-equivalently rebased
onto that base before #648 and #839 landed.

Verification status

  • All three constituents received the focused evidence recorded above; their landed trees match the reviewed source trees exactly.
  • The final Batch 3 aggregate gate is still incomplete on head ba4fb0db0: host regression repair, focused PHP Chromium, browser assets, exact-head kernel/libc/ABI evidence are recorded above; canonical/manual browser and the remaining package work remain.
  • No performance claim is made.

Landing mechanics

brandonpayton and others added 2 commits July 12, 2026 04:12
PHP's ZIP extension needs a reproducible libzip package, but the original hand-maintained source and configuration lists could drift from the pinned release and retained checkout-local build state.

Build pinned libzip 1.11.4 with its upstream CMake graph, explicit wasm32/musl platform facts, and resolver-provided zlib. Publish only the archive, public headers, and relocatable pkg-config file; verify required APIs, link every archive member into a Kandelo executable, audit its actual Wasm imports, and reject producer paths.

Validation: two clean builds in distinct directories produced byte-identical declared outputs; each validated 114 archive members and the same 15 allowed Wasm imports. Resolver manifest parsing and host-tool consistency checks also passed.

Remaining limits: this package is wasm32-only and enables STORE/DEFLATE without optional crypto, BZIP2, LZMA, or Zstandard backends. It remains unpublished until the aggregate batch lands.

Co-authored-by: mho22 <yannick@capsules.codes>
The PHP package did not expose ZipArchive, and its cross-compile link probes could falsely hide newer libzip APIs when unrelated static dependencies failed the whole-program probe.

Depend on the resolver-built libzip package, build zip.so as a normal opt-in PHP side module, and preseed only the APIs verified by that exact archive. Keep the required rand, srand, and remove definitions exported from PHP main. Browser coverage serves safe basename-only PHP artifacts and mounts zip.so into the kernel-owned VFS instead of relying on host filesystem access.

Validation on the exact ABI 19 serial tree: a clean build produced exactly six declared PHP artifacts with no producer paths; php.wasm and php-fpm.wasm report ABI 19; all 139 zip.so environment imports resolve through PHP or host memory. The focused Vitest suite passed 19/19, real Chromium passed 1/1, and 13 selected upstream ext/zip PHPTs passed on Node with no skips.

Remaining limits: zip.so is opt-in and inherits libzip's wasm32 STORE/DEFLATE-only package boundary. A browser-PHPT attempt built its VFS but stopped before PHP because the general demo imports an unrelated full shell catalog and less.wasm was absent; only the dedicated Chromium ZIP result is claimed. The full gate remains the aggregate batch PR's responsibility.

Co-authored-by: mho22 <yannick@capsules.codes>
@brandonpayton
brandonpayton force-pushed the integration/kd-6nz-php-extensions branch from 7a76fd9 to 6aee468 Compare July 12, 2026 08:13
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Phase B-1 matrix build status — pr-894-staging

ABI v19. 71 built, 0 failed, 71 total.

Package Arch Status Sha
icu wasm32 built 89ba9e6e
libcurl wasm32 built 608a2cd0
libcxx wasm32 built b4e54a56
libcxx wasm64 built 3787dc5e
libiconv wasm32 built 99619c53
libpng wasm32 built c11fe7a3
libxml2 wasm32 built d37e55b0
libzip wasm32 built 1548aa2c
openssl wasm32 built 3bef3b47
openssl wasm64 built dd55f594
sqlite wasm32 built acaab402
sqlite wasm64 built d8c4bd83
zlib wasm32 built c2644c11
zlib wasm64 built 0a92e1fd
bc wasm32 built a59b6b69
bzip2 wasm32 built 949ff502
coreutils wasm32 built 39a3cc51
curl wasm32 built e8f5ddda
dash wasm32 built 3d50460e
diffutils wasm32 built 85b92dde
dinit wasm32 built 221fe8bc
fbdoom wasm32 built 47c37ec5
file wasm32 built 15fcd9b9
findutils wasm32 built b9c0c6e3
gawk wasm32 built f6930ef0
git wasm32 built 0012c03b
grep wasm32 built 93876756
gzip wasm32 built 6599b79a
hello wasm32 built 65f94017
kandelo-sdk wasm32 built eebd138c
kernel wasm32 built b697326f
less wasm32 built e5fd4d44
lsof wasm32 built c6f6fc76
m4 wasm32 built 93e9875c
make wasm32 built 49c532f3
mariadb wasm32 built bd8b7ee5
mariadb wasm64 built 7b3053c7
modeset wasm32 built 80aa3f6a
msmtpd wasm32 built 5bca0f0b
nano wasm32 built ad03e346
ncurses wasm32 built e67397eb
netcat wasm32 built fb699a1f
nginx wasm32 built 2c296da1
php wasm32 built 8e2b6815
posix-utils-lite wasm32 built 3830adcc
ruby wasm32 built bf41a46b
sed wasm32 built 43edede7
spidermonkey wasm32 built 608a829f
tar wasm32 built 2d39220c
tcl wasm32 built d0336feb
unzip wasm32 built 990b360f
userspace wasm32 built 1f3f8b39
vim wasm32 built 678b22ef
wget wasm32 built f0503b24
xz wasm32 built 7ef7ea7e
zip wasm32 built 55ee3121
zstd wasm32 built db024284
bash wasm32 built e5c92cbf
mariadb-test wasm32 built cbb09e63
mariadb-vfs wasm32 built bfeb6b22
mariadb-vfs wasm64 built a351a119
nethack wasm32 built 5df5f220
node wasm32 built 6e2568ea
spidermonkey-node wasm32 built e9571a92
vim-browser-bundle wasm32 built f74d5f17
nethack-browser-bundle wasm32 built 86775d63
rootfs wasm32 built d7a30a3f
shell wasm32 built aa96fffe
lamp wasm32 built aadd9846
node-vfs wasm32 built ff759795
wordpress wasm32 built e676ab95

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

brandonpayton and others added 2 commits July 12, 2026 04:14
The original #648 recipe reused a persistent checkout, emitted absolute cache paths in libcurl.pc, and let the curl program declare a libcurl resolver dependency that it did not consume. That made source builds vulnerable to stale state, cache collisions, and non-relocatable metadata.

Build curl 8.11.1 from its verified source in a fresh resolver transaction with explicit zlib and OpenSSL inputs, PIC and reproducibility flags, and fatal configure option checking. Upstream curl has no gettext/NLS option, so the recipe documents that fact instead of retaining a no-op flag. Library and program outputs remain separate, and the recipe audits the exact output set, required API symbols, every archive member, linked Wasm imports, and producer-path hygiene.

Validation: build-deps host-tool consistency passed; clean libcurl and curl source resolves passed; the 170-member linked smoke and CLI each closed over 15 allowed Wasm imports; the library package contained exactly 14 files; libcurl.a remained SHA-256 12881c39281ce300b819ffc7b3444865ff3cf3fc00bf4e7f975c6f43df9763ae; curl.wasm remained SHA-256 d85d11e77c863fd6990b83e4d3b9d92a59e1608c9232142e0ce885cd490c06f7; and the two packaged curl Vitest cases passed.

The standalone curl runtime coverage proves dependency identity and file:// transfer. HTTP and real-browser behavior are exercised through PHP in the following commit; this commit does not claim an external host-bridge networking test.

Co-authored-by: Yannick Decat <yannick@capsules.codes>
PHP needs curl without baking it into the base executable or bypassing the package and dlopen contracts. The original #648 artifact targeted an older PHP/ABI graph and did not establish current Node/browser parity, side-module closure, or a source-reproducible libcurl input.

Advance the ABI-19 PHP recipe to revision 12, declare the resolver-built libcurl package, configure ext/curl as a shared module, compile its measured object set, and link the PIC libcurl archive into curl.so while resolving libc, zlib, OpenSSL, and PHP symbols against php.wasm. Keep base PHP curl-free. Add static import-closure checks and runtime coverage that mounts and loads the same side module on Node and in a real browser, including dlopen replay across fork and a real HTTP exchange over the kernel-owned loopback network.

Validation: the final PHP source resolve produced all seven declared ABI-19 outputs; curl.so was 711962 bytes with SHA-256 26ce8003e614f8960a0df152f4faad8bb399ce4529031b97b3200b315e9a7e77; every PHP output passed the local/cache/temp path-leak scan; five focused Node Vitest cases passed, including the static closure and forked HTTP request; and the real Chromium Playwright test passed all eleven PHP browser scenarios, including curl load/version and the loopback HTTP response.

The HTTP evidence uses Kandelo guest loopback because 127.0.0.1 is kernel-owned, not an external host-listener bridge. HTTPS certificate verification and arbitrary remote endpoints were not exercised by these focused tests.

Co-authored-by: Yannick Decat <yannick@capsules.codes>
mho22 and others added 13 commits July 12, 2026 05:56
Introduce the initial ICU and PIC libc++ package outputs, C++ weak-self-import coverage, and the PHP intl side-module link path while keeping ICU data outside the base PHP executable. Follow-up commits in this serial reconcile the ABI-19 build, fail-closed linker behavior, reproducibility, runtime closure, fork replay, and host/browser validation.

This commit intentionally makes no standalone completion or validation claim.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ICU's native Stage-1 data tools must execute during the cross build. On the Nix Linux runner, link their libstdc++ and libgcc runtimes statically so Stage 2 does not depend on loader paths for host runtime shared libraries; keep the flags Linux-only because the macOS toolchain rejects them.

Linux execution evidence remains the responsibility of the later package rebuild/CI validation in this serial.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ICU previously left its common data and pkg-config files outside the declared library output contract, embedded the resolver temporary prefix in libicuuc.a, and relied on undeclared native build tools. The libcxx revision provenance also pointed at a fork commit that did not contain the published PIC recipe.

Add a backward-compatible outputs.files contract with parse, cache-key, missing-output, and accepted-output coverage. Declare ICU data/pkg-config outputs and host probes, build from a clean verified source tree, and compile the fallback data path as /usr/lib/php so clean archives are stable. Keep existing package keys unchanged when files is absent, and mark the ICU/libcxx producer revisions as unpublished Automattic work.

Validation: the four focused runtime-file xtask tests passed; build-deps check reported 10 consistent tools across four consumers; the six ICU tools matched the dev-shell versions. Two clean ICU rev5 builds produced byte-identical four archives, 197 headers, and icu.dat; resolver validation accepted all declared outputs and rewrote the three .pc prefixes to the canonical cache path. Two clean libcxx rev6 builds in distinct cache locations produced byte-identical four archives and 1,680 headers.

Scope limits: the clean producer comparisons were run on macOS/aarch64 in the repository dev shell. The repaired Linux host-tool build is not re-claimed here beyond the original PR CI evidence, and performance was not measured.
The original intl linker change synthesized delayed stubs for every missing
env function. That can hide real ABI gaps on unexecuted paths, while name-only
self-import detection can also mistake an imported-function re-export for a
definition and recurse through its own trampoline.

Preserve Batch 2 transactional and fork-replay behavior. Supply a trampoline
only when the export section proves the same-name function index is
module-defined, keep main-module interposition first, and leave every other
unresolved import as an eager instantiation failure. At this point in the
serial, provide the wasm-exceptions C++ tag required by the intl side module.

Validation: host type generation passed; host/test/dylink.test.ts passed 37/37,
including genuine self-definition, missing import, imported-function re-export,
C++ tag, rollback, and fork-replay cases. The built ABI-19 intl.so had zero
unresolved env/GOT symbols under this rule, and all four real PHP intl
Node/kernel cases passed after the stricter index check.

Scope limits: this intermediate commit creates the C++ tag at the side-module
linker boundary. The later process-state replay commit in this same landing
serial deliberately supersedes that ownership model with one process-owned tag
and adds cross-side identity coverage. Browser behavior is validated later;
performance was not measured.
The original recipe was based on an older PHP tree, linked into the package source directory, and masked make failures with `|| true`. On the reconciled ABI-19 recipe that could publish a partial module: the first strict attempt exposed that only one newline-delimited object had been consumed.

Preserve PHP 8.3.15, GNU libiconv, the Batch 2 extensions, forced shared-libc symbols, and fork instrumentation. Add ICU/libcxx as direct resolver dependencies, build every generated shared_objects_intl target without invoking the unsupported libtool link, then link all PIC objects and declared ICU/libcxx archives into OUT_DIR/intl.so. Reserve PHP revision 13 for the serial zip(11), curl(12), intl(13) Batch 3 order. Make the Node data fixture prefer an explicit resolved ICU file and restrict its cache fallback to the current ICU version/revision.

Validation: a clean ABI-19 source resolve built 70 intl PIC objects and a 5,860,812-byte side module; php, php-fpm, opcache, phar, and zend_test were byte-identical across two clean temporary builds. The link consumed both libcxx PIC archives, and static inspection found all 3,040 intl imports resolved by PHP, the side module, or linker-owned objects. The focused Node/kernel suite passed 4/4: base PHP excludes intl, explicit loading succeeds, Locale reads icu.dat, and Collator applies locale rules.

Scope limits: this evidence is for #839 on the ABI-19 Batch 2 base. The final #647 -> #648 -> #839 serial tree and real browser path are validated separately before recommendation. This adds no kernel/process ABI shape, so no ABI bump or snapshot change is included; performance was not measured.
Instantiation alone did not prove that the host supplied the C++ exception tag with the expected payload signature, and synthetic fork-state tests did not prove that ICU common-data state survives replay of a real PHP side module.

Make the WAT fixture throw and catch an i32 payload through env.__cpp_exception. Add a PHP case that loads intl and icu.dat before pcntl_fork, calls an ICU-backed Locale API in the replayed child, calls Locale and Collator again in the parent, and verifies wait/exit status.

Validation: with the ABI-19 sysroot present, host/test/dylink.test.ts passed 37/37. packages/registry/php/test/php-intl.test.ts passed 5/5 against the resolver-built PHP/intl/ICU artifacts; the fork-replay case completed in both branches and reaped a zero-status child.

Scope limits: this commit records Node/kernel evidence. The equivalent Chromium path is added and run only on the exact landed #647 -> #648 -> #839 serial tree; it is not claimed here.
libc++abi archive members retained the absolute checkout path because both PIC
and non-PIC CMake builds lacked compiler prefix maps. ICU metadata was likewise
rewritten from the resolver temporary prefix to one producer cache path, so
fetched or moved archives still pointed back to the build machine.

Map the entire worktree, including assembled LLVM sources, both build variants,
the smoke source, and sysroots, to /usr/src/kandelo in libcxx compiler paths and
advance it to revision 7. Rewrite ICU pkg-config prefix fields to
${pcfiledir}/../.. and advance it to revision 6, keeping headers and libraries
relative to each extracted package tree.

Validation: two libcxx revision-7 builds from distinct checkout/output roots
produced byte-identical PIC and non-PIC archives, and producer-path scans were
clean. Two ICU revision-6 builds from distinct roots produced byte-identical
declared archives and icu.dat. After moving an ICU prefix, pkg-config resolved
the relocated include/library paths and a consumer compile/link succeeded.

Scope limits: the reproducibility and relocation checks ran on macOS/aarch64 in
the repository dev shell. Linux host-tool execution, the final PHP package,
Node/browser runtime behavior, and aggregate gates are validated by later
commits; performance was not measured.
Problem: program manifests could describe only executable outputs, leaving
required data blobs outside cache keys, archives, local mirrors,
fetched-artifact validation, and VFS installation metadata. Existing archive
collection could also follow external/cyclic symlinks, and unvalidated
package/output paths could escape or collide in the mirror.

Design: add a program-only runtime_files contract with normalized artifact and
absolute guest paths, mode metadata, length-framed cache-key fields, exact
nested-artifact lookup, collision checks, and regular-file validation on
build/cache/fetch paths. Archive staging dereferences only contained symlinks
and rejects escapes, cycles, specials, empty declared trees, and incomplete
closures. Local and fetched materialization use one package-scoped mirror
layout and a structured repository-build query.

Compatibility: manifests with no runtime_files retain their historical cache
keys. The previously additive library outputs.files section is versioned and
length-framed now, before its first published consumer, while older output
sections retain their encoding for cache compatibility.

Focused evidence: pkg_manifest tests passed 112/112; archive_stage tests passed
8/8; runtime-filtered tests passed 19/19; delimiter framing, fetched/source
mirror parity, incomplete-archive fallback/fetch-only failure, nested paths,
collisions, and symlink safety are covered. The full xtask run reached 336
passed and the same 7 pre-existing malformed-Wasm/filename fixture failures
observed on the base.

Limits: shell helper syntax and host resolver precedence passed, but the full
aggregate package build, published-archive round trip, full host/libc gate, and
browser gate have not run. The metadata helper is a repository
build/materialization API, not a guest or runtime host API.
Problem: replay instantiated a fresh side module without restoring its live TLS-base global, and each module could receive a different C++ exception-tag identity. Pthread workers also have separate instances, tables, and tags, so allowing them to race process dlopen or inherit the process archive could corrupt replay.

Design: keep process-owned pointer-width-correct exception/longjmp tags, archive and validate each module's exact positive TLS base, restore only that global over the fork-copied live TLS bytes, and arbitrate process dlopen against pthread fork with a host-private atomic record. Pthread dlopen now fails through dlerror; pthread fork returns ENOTSUP once the process archive is nonempty.

Focused evidence: host declaration generation passed; dylink plus binary-resolver Vitest ran 33 tests passed with 16 compiler-dependent skips; the compiled fork/dlopen replay file ran 3/3, including mutated C++ thread_local state plus throw/catch and the real pthread boundary.

Limits: the i64 cases exercise 64-bit pointer encoding only, not a memory64 end-to-end runtime. The full host, libc, ABI-classifier, and real browser aggregate gates have not yet run. The archive-size change is intended to remain host-private and transient; the ABI check is still required before landing.
Problem: intl.so needs an ICU data blob at runtime, but the original branch relied on cache scanning and host-only path overrides. That made local, fetched, Node, VFS, and browser executions observe different closures and allowed a present extension to run without its matching data.

Design: PHP copies the exact resolver-selected ICU data into its program archive as a declared runtime file at /usr/lib/php/icu.dat. Repository image/test builders query structured package metadata for the guest path and mode, fail when intl exists without its closure, and exercise the same resolver-selected bytes on Node and browser. The PHP build exports __tls_base for fork-safe side-module replay and tracks the loader source as a cache-key input.

Focused evidence: runtime metadata resolved to php/icu.dat, /usr/lib/php/icu.dat, mode 0644; the local mirror is byte-identical to both reproducible ICU rev6 outputs; shell syntax, host declarations, and the PHP browser Vite bundle passed. The dev middleware served all 30,782,896 ICU bytes and returned 400 for dot, encoded-dot, and malformed-percent artifact paths. The base-PHP focused case passed against an ABI-19 base-manifest image.

Limits: the four intl runtime cases intentionally remain unvalidated on the stale cached intl.so, which the repaired host correctly rejects because it does not export mutable __tls_base. A fresh PHP rebuild, artifact inspection, Node intl/fork tests, real Chromium run, and full aggregate gates are still required. The focused base-manifest rootfs omitted unrelated lazy package entries and is not canonical browser evidence.
Reject unsafe manifest and dependency path components before registry, cache, and archive interpolation, and make package.toml's top-level schema fail closed so a misspelled runtime_files declaration cannot disappear silently.

Expose every program output and runtime file in structured runtime metadata. Resolve the complete set from one local, fetched, or installed-package root, allowing whole-tier fallback while refusing mixed partial closures. Route the PHP Node, VFS, and browser consumers through that selected closure.

Validation:
- pkg_manifest focused suite: 116 passed
- runtime metadata focused test: 1 passed
- binary resolver/runtime metadata Vitest: 14 passed
- live build-deps manifest check: passed
- host declaration build: passed
- PHP browser Vite production build: passed with ABI-19 kernel/rootfs fixtures
- ABI snapshot/version check: passed
- full xtask suite: 342 passed; the same 7 pre-existing archive/program fixture failures remain

The full host, libc, PHPT, and manual browser suites were not run for this focused repair. Performance was not measured.
Make the shared VFS image writer honor positive partial writes until all bytes are staged, reject zero/negative/invalid progress, and close its descriptor on every exit path. Route the browser-demo wrappers through that shared implementation.

Give the Node PHP intl fixture a 256 MiB rebased filesystem before adding the 30.8 MB ICU data file and verify the staged bytes by size and SHA-256. Preserve the browser fixture's proven 256 MiB growable restore and byte-check its injected side-module/runtime fixtures without re-reading the large PHP executable on every invocation.

Validation:
- focused VFS image-helper tests: 5 passed
- exact ABI-19 PHP intl Node suite: 5 passed, including ICU data and fork replay
- exact ABI-19 Chromium intl/fork case: 1 passed
- host declaration build: passed
- PHP browser Vite production build: passed

The full browser app typecheck was attempted and remains blocked by pre-existing unrelated type errors. Its production build was attempted and remains blocked by absent coreutils/node/nc binary assets in this isolated worktree. The full host, libc, and manual browser suites were not run. Performance was not measured.
The #839 side-module repair gave each pthread fork exclusive ownership of the process dlopen lock. In the existing two-thread, 16-round concurrent-fork fixture, one thread therefore received ENOTSUP and left its peer blocked at the next barrier.

Encode the unchanged host-private slot as an exclusive negative dlopen writer or a positive pthread-fork reader count. Concurrent empty-archive pthread forks can now snapshot in parallel, while dlopen remains excluded until every parent completes unwind, SYS_FORK, and rewind. Keep child reset, archive rejection, error releases, and ownership diagnostics explicit.

Validation: the exact pre-#839 base passed the concurrent fixture while the prior aggregate timed out. On this repair, fork-from-thread plus dlopen/fork replay passed 5/5. Full host Vitest reached 1,251 passes, 2 expected failures, and 95 skips; its only two failures are the lazy-rootfs memory64/posix-utils fixtures reproduced on the exact base. Host declarations and the ABI consistency check passed. Browser behavior was not rerun for this host-only arbitration follow-up, and performance was not measured.
brandonpayton pushed a commit that referenced this pull request Jul 13, 2026
PHP intl needs reproducible ICU and libc++ inputs plus fail-closed C++ side-module linking before the host/guest replay contract can advance safely.

Build PIC ICU/libc++ outputs without producer paths, declare non-Wasm runtime files explicitly, complete the initial intl.so link path, reject unresolved C++ imports, and cover exceptions and fork replay. This compatible setup intentionally precedes ABI 36; the next commit owns the incompatible replay-memory and pthread-fork contract, while the following PHP commit owns final ICU runtime-data materialization.

Source-era package and focused runtime evidence is retained as review provenance, but the final ABI-38 artifact rebuild and complete Node/browser gate apply at the super-batch tip.

Source-PR: #839
Source-PR: #894
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brandonpayton added a commit that referenced this pull request Jul 13, 2026
Side-module replay-control memory and pthread fork arbitration change the host/guest continuation contract and unsupported-operation behavior. Advance ABI 35 to 36 and regenerate ABI evidence atomically.

Source-PR: #839

Source-PR: #894
brandonpayton added a commit that referenced this pull request Jul 13, 2026
intl.so needs the exact resolver-selected ICU data blob at runtime. Cache scanning and host-only path overrides made local, fetched, Node, VFS, and browser executions observe different closures and could leave a present extension without matching data.

Copy the selected ICU data into the PHP program archive as declared /usr/lib/php/icu.dat state, keep general runtime files in one artifact tier, query structured package metadata from image/test builders, and reject incomplete VFS staging. The PHP consumers now follow the ABI-36 side-module replay contract established by the preceding epoch.

Source-era evidence covers reproducible ICU/libc++ outputs, runtime metadata and byte identity, complete PHP artifacts, focused Node/browser intl behavior, and fail-closed stale-artifact handling. Those results are provenance rather than a claim about an ABI-19 artifact; final ABI-38 archives and broad gates remain the super-batch tip's responsibility.

Pthread-side dynamic loading remains truthfully unsupported.

Source-PR: #839
Source-PR: #894
@brandonpayton

Copy link
Copy Markdown
Member Author

Merged PR #907 supersedes this PHP package wave through the curated commits now on main (75472887, 2c3f1f84, 00f88349, 1af1cd21, and 7479f6e0). This head was reconstructed rather than merged verbatim.

@brandonpayton
brandonpayton deleted the integration/kd-6nz-php-extensions branch July 13, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants