[PHP] Generate extension manifest validator - #3590
Closed
adamziel wants to merge 0 commit into
Closed
Conversation
adamziel
force-pushed
the
codex/spx-extension-assets-api
branch
from
May 1, 2026 23:59
51cfce5 to
290b2ce
Compare
adamziel
force-pushed
the
codex/spx-extension-manifest-ajv-validator
branch
from
May 2, 2026 02:12
007948d to
786fe13
Compare
adamziel
added a commit
that referenced
this pull request
May 3, 2026
## What it does Adds manifest-declared sidecar files to the PHP extension loading API introduced in #3566, and folds in #3590's generated AJV validator for extension manifests. A manifest can now describe files and directories that must be staged before PHP starts, alongside the selected `.so` artifact. Each node's final VFS path is `vfsRoot + vfsPath`: ```json { "name": "spx", "artifacts": [ { "phpVersion": "8.4", "sourcePath": "spx.so", "extraFiles": { "vfsRoot": "/internal/shared/spx", "nodes": [ { "vfsPath": "data", "type": "directory" }, { "vfsPath": "ui/index.html", "sourcePath": "ui/index.html" } ] } } ] } ``` ## Rationale #3255 needs SPX to load more than a WASM side module. The extension also expects a writable data directory and web UI assets such as HTML, CSS, and JS files. This PR keeps that work focused on the loader contract. It does **not** rebuild PHP 8.2, ship SPX artifacts, or claim that SPX loads today. A follow-up can use this manifest shape when adding or rebuilding SPX artifacts. ## Integration For most consumers nothing changes structurally — manifest sources slot into the same `extensions` array as bundled extension names: ```ts await loadNodeRuntime('8.4', { extensions: [ 'intl', 'xdebug', { source: { format: 'manifest', manifestUrl: 'https://example.com/spx/manifest.json' } }, ], }); ``` For hand-rolled Emscripten setups, `resolvePHPExtension` + `withResolvedPHPExtensions` in `@php-wasm/universal` are the lower-level pair. ## Implementation `PHPExtensionManifest` accepts `extraFiles` at two levels: - manifest-level `extraFiles` for files shared by every artifact - artifact-level `extraFiles` for PHP-version-specific files Each group has a `vfsRoot` (the absolute VFS prefix) and an array of `nodes` with a `vfsPath`, an optional `type: 'file' | 'directory'`, and a `sourcePath` (the URL to fetch, only required for files). The resolver joins `vfsRoot + vfsPath` into absolute VFS paths and returns: ```ts { files: { '/internal/shared/spx/ui/index.html': bytes }, directories: ['/internal/shared/spx/data'], } ``` The resolver validates untrusted manifest JSON with a standalone AJV validator generated from the public `PHPExtensionManifest` TypeScript type. Sidecar files are fetched relative to the manifest URL or inline manifest `baseUrl`, with manifest-level and selected artifact-level nodes fetched in parallel through a shared concurrency limit. Caller-supplied `extraFiles` on `PHPExtensionInstallOptions` use the same absolute-path shape (`{ files, directories? }`) — no `targetPath` indirection. External extension manifests remain JSPI-only. ## Breaking changes - Manifest field renames: `artifact.file` → `artifact.sourcePath`; `extraFiles.targetPath` → `vfsRoot`; `extraFiles.files`/`extraFiles.directories` → unified `nodes` array with `vfsPath`/`type`/`sourcePath`. - `PHPExtensionExtraFiles` (caller-supplied sidecar files) is now flat absolute VFS paths under `files` and `directories`. The previous `targetPath + relative paths` shape is gone. - `sha256` fields removed from sources and artifacts. - `ResolvePHPExtensionOptions` is no longer exported (use `PHPExtensionInstallOptions & { phpVersion: string }`). ## Testing instructions \`\`\`bash npm exec nx -- test php-wasm-universal --testFile=load-extension.spec.ts npm exec nx -- typecheck php-wasm-universal npm exec nx -- lint php-wasm-universal npm exec nx -- build php-wasm-universal cd packages/php-wasm/node && npx vitest run src/test/with-php-extensions.spec.ts --config vite.jspi.config.ts npm exec nx -- typecheck php-wasm-node npm exec nx -- lint php-wasm-node npm exec nx -- typecheck php-wasm-web \`\`\`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stacks on #3580.
Replaces the handwritten extension manifest shape checks with a standalone AJV validator generated from the
PHPExtensionManifestTypeScript type, following the same committed-validator pattern used by the Blueprints package.Why
The manifest API is now more than a small object shape, and keeping validation logic handwritten would make the type, docs, schema, and runtime checks drift over time.
This keeps the type as the source of truth while retaining the explicit VFS path traversal guard as semantic validation.
Testing
npm exec nx -- run php-wasm-universal:build:extension-manifest-schemanpm exec nx -- test php-wasm-universal --testFile=load-extension.spec.tsnpm exec nx -- typecheck php-wasm-universalnpm exec nx -- lint php-wasm-universalnpm exec nx -- build php-wasm-universal