Skip to content

feat(php): add PHP source lift kit (self-hosted) + php-language-signature#600

Merged
TSavo merged 1 commit into
mainfrom
codex/php-source-lift-kit
May 11, 2026
Merged

feat(php): add PHP source lift kit (self-hosted) + php-language-signature#600
TSavo merged 1 commit into
mainfrom
codex/php-source-lift-kit

Conversation

@TSavo
Copy link
Copy Markdown
Owner

@TSavo TSavo commented May 11, 2026

Self-hosted PHP source-language lift kit + menagerie/php-language-signature/. Mirrors PR #590 (the C# source lifter) and the merged Go/Python/TS/Zig lifters.

  • PHP lifter via nikic/php-parser → function-contract mementos over php:-namespaced ops
  • per-file php:source-unit lossless wrapper; round-trip compile + a lift(compile(lift(src))) test driving the real IR→source path (not the source-unit byte-copy fast-path)
  • arity_shape on every op spec; Refusals (not fakes) for unhandled syntax; effects on the canonical Effect wire shapes, sorted; loop CIDs via the existing canonicalizer (not rolled-own)
  • new php-source lift surface (existing php kit surface manifest untouched — the feat(csharp): add C# language lifter with RPC protocol and menagerie signature #590 lesson); version 0.1.0-draft; no menagerie/manifest.yaml entry (the recurring nit — *-language-signature catalogs don't belong in that destinations list)
  • PHP unit tests pass; signature generator deterministic; RPC initialize reports 0.1.0-draft/php-source; composer install couldn't run locally (no composer in sandbox) — CI verifies the php-parser-dependent compiler path

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 11, 2026 17:11
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Warning

Rate limit exceeded

@TSavo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 28 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c8d6044-dbb1-4df7-a42a-c1f9a35e81a9

📥 Commits

Reviewing files that changed from the base of the PR and between 86ef2f1 and 8dc3828.

📒 Files selected for processing (82)
  • Makefile
  • implementations/php/.provekit/lift/php-source/manifest.toml
  • implementations/php/composer.json
  • implementations/php/provekit-lift-php-source/bin/main.php
  • implementations/php/provekit-lift-php-source/src/EffectSet.php
  • implementations/php/provekit-lift-php-source/src/Ir.php
  • implementations/php/provekit-lift-php-source/src/PhpSourceCompiler.php
  • implementations/php/provekit-lift-php-source/src/PhpSourceLifter.php
  • implementations/php/provekit-lift-php-source/src/Rpc.php
  • implementations/php/provekit-lift-php-source/src/bootstrap.php
  • implementations/php/provekit-lift-php-source/tests/PhpSourceLifterTest.php
  • implementations/rust/provekit-cli/src/project_config.rs
  • menagerie/php-language-signature/README.md
  • menagerie/php-language-signature/generate_assets.php
  • menagerie/php-language-signature/specs/eff_io.spec.json
  • menagerie/php-language-signature/specs/eff_opaque_loop.spec.json
  • menagerie/php-language-signature/specs/eff_panic.spec.json
  • menagerie/php-language-signature/specs/eff_read.spec.json
  • menagerie/php-language-signature/specs/eff_unresolved_call.spec.json
  • menagerie/php-language-signature/specs/eff_write.spec.json
  • menagerie/php-language-signature/specs/effsig_io.spec.json
  • menagerie/php-language-signature/specs/effsig_opaque_loop.spec.json
  • menagerie/php-language-signature/specs/effsig_panic.spec.json
  • menagerie/php-language-signature/specs/effsig_read.spec.json
  • menagerie/php-language-signature/specs/effsig_unresolved_call.spec.json
  • menagerie/php-language-signature/specs/effsig_write.spec.json
  • menagerie/php-language-signature/specs/language_signature_php.spec.json
  • menagerie/php-language-signature/specs/op_add.spec.json
  • menagerie/php-language-signature/specs/op_and.spec.json
  • menagerie/php-language-signature/specs/op_assign.spec.json
  • menagerie/php-language-signature/specs/op_bitand.spec.json
  • menagerie/php-language-signature/specs/op_bitnot.spec.json
  • menagerie/php-language-signature/specs/op_bitor.spec.json
  • menagerie/php-language-signature/specs/op_bitxor.spec.json
  • menagerie/php-language-signature/specs/op_break.spec.json
  • menagerie/php-language-signature/specs/op_call.spec.json
  • menagerie/php-language-signature/specs/op_concat.spec.json
  • menagerie/php-language-signature/specs/op_continue.spec.json
  • menagerie/php-language-signature/specs/op_div.spec.json
  • menagerie/php-language-signature/specs/op_dowhile.spec.json
  • menagerie/php-language-signature/specs/op_echo.spec.json
  • menagerie/php-language-signature/specs/op_eq.spec.json
  • menagerie/php-language-signature/specs/op_exit.spec.json
  • menagerie/php-language-signature/specs/op_for.spec.json
  • menagerie/php-language-signature/specs/op_foreach.spec.json
  • menagerie/php-language-signature/specs/op_ge.spec.json
  • menagerie/php-language-signature/specs/op_gt.spec.json
  • menagerie/php-language-signature/specs/op_identical.spec.json
  • menagerie/php-language-signature/specs/op_if.spec.json
  • menagerie/php-language-signature/specs/op_index.spec.json
  • menagerie/php-language-signature/specs/op_le.spec.json
  • menagerie/php-language-signature/specs/op_lt.spec.json
  • menagerie/php-language-signature/specs/op_methodcall.spec.json
  • menagerie/php-language-signature/specs/op_mod.spec.json
  • menagerie/php-language-signature/specs/op_mul.spec.json
  • menagerie/php-language-signature/specs/op_ne.spec.json
  • menagerie/php-language-signature/specs/op_neg.spec.json
  • menagerie/php-language-signature/specs/op_not.spec.json
  • menagerie/php-language-signature/specs/op_not_identical.spec.json
  • menagerie/php-language-signature/specs/op_nullcoalesce.spec.json
  • menagerie/php-language-signature/specs/op_or.spec.json
  • menagerie/php-language-signature/specs/op_pos.spec.json
  • menagerie/php-language-signature/specs/op_print.spec.json
  • menagerie/php-language-signature/specs/op_propfetch.spec.json
  • menagerie/php-language-signature/specs/op_return.spec.json
  • menagerie/php-language-signature/specs/op_seq.spec.json
  • menagerie/php-language-signature/specs/op_shl.spec.json
  • menagerie/php-language-signature/specs/op_shr.spec.json
  • menagerie/php-language-signature/specs/op_source-unit.spec.json
  • menagerie/php-language-signature/specs/op_staticcall.spec.json
  • menagerie/php-language-signature/specs/op_staticprop.spec.json
  • menagerie/php-language-signature/specs/op_sub.spec.json
  • menagerie/php-language-signature/specs/op_ternary.spec.json
  • menagerie/php-language-signature/specs/op_throw.spec.json
  • menagerie/php-language-signature/specs/op_while.spec.json
  • menagerie/php-language-signature/specs/sort_bool.spec.json
  • menagerie/php-language-signature/specs/sort_int.spec.json
  • menagerie/php-language-signature/specs/sort_real.spec.json
  • menagerie/php-language-signature/specs/sort_stmt.spec.json
  • menagerie/php-language-signature/specs/sort_string.spec.json
  • menagerie/php-language-signature/specs/sort_unit.spec.json
  • menagerie/php-language-signature/specs/sort_value.spec.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/php-source-lift-kit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d973584508

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +302 to +306
if ($stmt instanceof \PhpParser\Node\Stmt\Break_) {
return ctor('php:break', unit_const());
}
if ($stmt instanceof \PhpParser\Node\Stmt\Continue_) {
return ctor('php:continue', unit_const());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve break/continue levels or refuse unsupported forms

This lowering drops the optional numeric level on break/continue (e.g., break 2;), so nested-loop control flow is silently changed instead of being represented or refused. In PHP code that uses multi-level exits, the lifted IR will no longer match source semantics, which can corrupt downstream analysis/rewrites. Please either encode the level in the IR or emit a refusal when $stmt->num is present.

Useful? React with 👍 / 👎.

'php:while' => new \PhpParser\Node\Stmt\While_($this->exprNode($args[0]), $this->stmtNodes($args[1])),
'php:echo' => new \PhpParser\Node\Stmt\Echo_([$this->exprNode($args[0])]),
'php:throw' => new \PhpParser\Node\Stmt\Throw_($this->exprNode($args[0])),
default => new \PhpParser\Node\Stmt\Expression($this->exprNode($term)),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle lifted statement ops in compiler instead of expr fallback

The compiler's statement switch only handles a small subset, then falls back to Expression($this->exprNode($term)). However the lifter emits statement operators like php:for, php:dowhile, php:foreach, php:break, and php:continue that are not valid expression ops in exprNode, so compiling valid lifted IR can throw InvalidArgumentException. This makes compile fail for accepted PHP programs once source-unit passthrough is unavailable (e.g., transformed/body-only IR).

Useful? React with 👍 / 👎.

@TSavo
Copy link
Copy Markdown
Owner Author

TSavo commented May 11, 2026

Review: PR #600feat(php): add PHP source lift kit (self-hosted)

Verdict: MERGE-WITH-NITS — clean self-hosted PHP source lifter that meets the #590 bar on every conformance axis; a few cosmetic asymmetries and one structural divergence (the production compile codepath is byte-copy, not PrettyPrinter) that are worth a follow-up but do not block. CI is the verifier for the unverified composer/php-parser path per the task note — prove is green; Cross-language conformance gate is still pending, merge gated on it going green.

Blocking findings

None.

Non-blocking findings

  1. PhpSourceCompiler::compileIrDocument always takes the php:source-unit byte-copy fast-path in production. Every lifted IR document begins with a source_unit_contract (Ir.php:140), and sourceUnitBytes() returns those bytes verbatim (PhpSourceCompiler.php:236-250), so the compile RPC (Rpc.php:compile_rpc) never reaches the PrettyPrinter (compileFunctionContractWithPhpParser). The PrettyPrinter path is only reachable via compileBodyTerm, which is called only from the test. Contrast feat(csharp): add C# language lifter with RPC protocol and menagerie signature #590: csharp's compile RPC takes a single IR term and runs it through the real EmitStmt/EmitCtor machine (RpcServer.cs:121, CsharpCompiler.cs:11) — no byte-copy short-circuit. The PHP behavior is defensible (the source-unit wrapper is the lossless artifact), and the body-term round-trip test (test_compile_lift_roundtrip_body_term_is_byte_identical) does drive PrettyPrinter→parse in CI, so the compiler is exercised. But the asymmetry is real — a compile of mutated IR returns stale bytes. Look at this first.

  2. PhpSourceCompiler::isUnit() (PhpSourceCompiler.php:~210)($k==='const' && !array_key_exists('value',$term)) || ($k==='const' && ($value??null)===null) relies on &&-over-|| precedence; works, but unreadable. Simplify.

  3. exprNode has no php:assign case (PhpSourceCompiler.php:~115-150) — but the lifter will produce nested php:assign(var a, php:assign(var b, …)) from $a = $b = 1; (Assign.expr is an Assign). The compiler then throws unsupported php operation in expression position: php:assign. Lifter→compiler asymmetry; either refuse nested assigns in the lifter or handle them in exprNode.

  4. PHP null literal lowers to unit_const() (sort Unit) (PhpSourceLifter.php ConstFetch handler) even in value position ($x = null;), where the slot is Value. Sort mismatch; harmless for CIDs but inconsistent. Confirm csharp/swift handle the same way before "fixing".

  5. Token fallback (liftSourceWithTokenFallback, regex-based) diverges from the php-parser path and does not run in CI (composer is installed there). Documented as "intentionally narrow." Acceptable, but it's a second, drifting lifter — flag for eventual deletion once composer is reliably present.

Conformance checklist

# Item Status Note
1 php: prefix on every op; no php:binop/php:unknown/php:skip; =====, .+ PASS ctor() enforces prefix + bans the three catch-alls (Ir.php:64-72); php:eq/php:identical/php:ne/php:not_identical/php:concat all distinct (lowerBinaryOp)
2 arity_shape on every op, sane; short-circuit RHS / ternary / ?? mark evaluation:"unevaluated"; mirrors csharp PASS op_and/op_or/op_nullcoalesce slot[1] unevaluated; op_ternary slots 1,2 unevaluated; op_seq positional arity 2; op_assign named target/value; op_call/op_methodcall args slot shape:{kind:set}
3 php:source-unit(bytes, operational_term) present AND emitted carrying full file bytes PASS op_source-unit.spec.json; source_unit_contract($path, $sourceBytes, …) uses raw file_get_contents/$source incl. <?php prologue + inline HTML (InlineHTML is skipped from the term but the bytes are intact)
4 Refuse-not-fake for unhandled AST: closures/arrow-fns, generators, try/catch, switch/match, refs, variadic/named/byref args, list(), $$x, eval/call_user_func, traits, magic methods, instanceof, clone, @, goto PASS Catch-alls: lowerStatement/lowerExpr/lowerTarget/lowerBinaryOp all throw $this->refuse(...); explicit refusals for byref returns/params, variadic, default params, named/unpack/byref args, variable-variables, dynamic calls/props/classes, traits, __* magic methods, anon classes, eval/call_user_func; Yield_/Closure/ArrowFunction/TryCatch/Switch_/Match_/List_/Instanceof_/Clone_/ErrorSuppress/Goto_ all fall through to the nodeKind catch-all refusal (no silent skip)
5 fnName provably unique — ns\fn, ns\Class::method PASS qualifyFunctionName + :: for methods; <source-unit:path> uses <> (illegal in real PHP names); PHP has no overloading, no static-vs-instance same-name
6 Effects on canonical wire shapes, sorted by sort_key() incl. secondary sort; no alloc/resolved-call PASS EffectSet: reads/writes w/ target, io, panics, unresolved_call w/ name, opaque_loop w/ loopCid:"blake3-512:…"; sortKey = "0:reads:…""6:opaque_loop:…" matching the Java/Swift Effect::sort_key; loops (while/do/for/foreach) → opaque_loop; echo/print/file_*/curl_*/printf/header/fopen/fread/fwrite/readfileio; throw/exit/die + trigger_error(…,E_USER_ERROR)panics; no alloc, calls are unresolved_call not resolved call
7 Round-trip lift(compile(lift(src))) byte/CID-identity driving the real PrettyPrinter, not the byte-copy fast-path PASS (with caveat) test_compile_lift_roundtrip_body_term_is_byte_identical lifts f, calls compileBodyTerm (→ PrettyPrinter in CI), re-lifts, asserts Jcs::encode($body) === Jcs::encode($reliftedBody). Drives the real compiler. Caveat = finding #1: the document-level compileIrDocument is byte-copy, so lift(compile(lift(src))) at doc level is trivially identical via copy
8 Determinism PASS EffectSet dedups by JCS key + sorts; liftPaths sorts files SORT_STRING; fold_seq deterministic left-fold; no timestamps
9 CID-correctness — reuses provekit-ir-symbolic's canonicalizer/BLAKE3, not rolled-own PASS Ir.php / EffectSet.php use ProvekIt\Canonicalizer\Jcs + Blake3 (the existing classes, bootstrap.php requires them directly); cid_of_json = 'blake3-512:' . Blake3::hash(Jcs::encode(...)). No new JCS/BLAKE3 — Supra omnia, rectum satisfied
10 Wiring — composer adds nikic/php-parser; existing php manifest not repointed; new php-source manifest w/ working command + version 0.1.0-draft; no .provekit/ci/accepted/self-contract changes; no php-language-signature entry in menagerie/manifest.yaml PASS composer.json adds "nikic/php-parser": "^5.0" + ProvekIt\\LiftPhpSource\\ autoload + test script entry; implementations/php/.provekit/lift/php/manifest.toml untouched (empty diff); new php-source/manifest.tomlcommand = ["php","provekit-lift-php-source/bin/main.php","--rpc"], version 0.1.0-draft; no accepted/self-contract diffs; menagerie/manifest.yaml diff empty (matches csharp-language-signature which also has no entry)
11 Existing PHP project still builds/tests UNVERIFIED LOCALLY no composer in sandbox per task note. composer wiring is plausible — only additive (new autoload key, new require, new test script line); CI composer install && composer test is the verifier
12 --rpc server initialize/lift/compile/shutdown match the lift-plugin protocol; initialize reports version 0.1.0-draft, dialect php-source PASS Rpc.php: initialize{name, version:"0.1.0-draft", protocol_version:"provekit-lift/1", dialect:"php-source", capabilities:{authoring_surfaces:["php-source"], ir_version:"v1.1.0", emits_signed_mementos:false}} — mirrors SwiftSourceRPC.initializeResult(); lift checks surface, returns ir-document; compile returns compiled-formula; shutdown → null; line-delimited JSON-RPC 2.0; test_initialize_declares_php_source_draft covers it
13 cargo test -p provekit-bug-zoo --test smoke still passes with new php-source surface N/A IN THIS WORKTREE no provekit-bug-zoo crate exists in implementations/rust/ on this branch — item stale. The only Rust change is KNOWN_SURFACES += "php-source" in provekit-cli/src/project_config.rs plus its unit-test assertion — clean and minimal

The one thing to look at first

Finding #1 — confirm with the maintainer that the compile RPC returning byte-copied source for a real lifted document is the intended PHP design (vs #590's term-in / compile-out). If a future caller mutates IR and round-trips through compile, they get stale bytes. Not a blocker; a follow-up issue at most.

Comparison to #590's bar

Meets it. Same shape: self-hosted lifter + IR↔source compiler + --rpc JSON-RPC server + 0.1.0-draft manifest + a *-language-signature menagerie (68 specs) + KNOWN_SURFACES registration, no menagerie/manifest.yaml entry, no self-contract pins. PHP refuses more aggressively than csharp (every unhandled node → Refusal{kind,function,line,reason}), which is the right instinct. The two deltas from #590: (a) the byte-copy compile fast-path (finding #1), (b) the token-fallback second lifter for composer-less environments (finding #5) — csharp has no analog because dotnet is always present. Neither is disqualifying.

— reviewed against origin/main...d973584, worktree /tmp/pk-600-review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a self-hosted PHP source lift kit (php-source) and a corresponding PHP language-signature menagerie catalog, mirroring the existing pattern for other source lifters (RPC initialize/lift/compile, lossless php:source-unit, deterministic signatures/specs, and unit tests).

Changes:

  • Introduces implementations/php/provekit-lift-php-source (RPC server, lifter, compiler, effects tracking) plus a PHP test suite.
  • Adds menagerie/php-language-signature/ with sorts/ops/effects/effect-signatures and a language_signature_php index.
  • Wires the new surface into the Rust CLI (KNOWN_SURFACES) and adds a make test-php target + Composer dependency on nikic/php-parser.

Reviewed changes

Copilot reviewed 81 out of 82 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Makefile Adds test-php target and help text entry.
implementations/rust/provekit-cli/src/project_config.rs Registers php-source as a known surface and asserts it in tests.
implementations/php/composer.json Adds PHP-parser dependency and php-source test script; adds PSR-4 mapping for the new kit.
implementations/php/.provekit/lift/php-source/manifest.toml Declares the php-source lift surface manifest (RPC command + capabilities).
implementations/php/provekit-lift-php-source/bin/main.php RPC entrypoint (--rpc required).
implementations/php/provekit-lift-php-source/src/bootstrap.php Bootstraps vendor autoload + local includes for the kit.
implementations/php/provekit-lift-php-source/src/EffectSet.php Canonical/deduped effect collection + deterministic sorting.
implementations/php/provekit-lift-php-source/src/Ir.php IR helpers + function-contract/source-unit contract constructors + CID helpers.
implementations/php/provekit-lift-php-source/src/PhpSourceCompiler.php IR → PHP compilation (php-parser pretty-printer with fallback).
implementations/php/provekit-lift-php-source/src/PhpSourceLifter.php PHP source → IR lifter (php-parser frontend with token fallback).
implementations/php/provekit-lift-php-source/src/Rpc.php JSON-RPC dispatcher implementing initialize/lift/compile/shutdown.
implementations/php/provekit-lift-php-source/tests/PhpSourceLifterTest.php End-to-end tests for lift/refusals/effects/round-trip/initialize/signature shapes.
menagerie/php-language-signature/README.md Describes the PHP language signature scope and intent.
menagerie/php-language-signature/generate_assets.php Deterministic generator for all PHP language-signature spec assets.
menagerie/php-language-signature/specs/eff_io.spec.json Effect op spec: IO.
menagerie/php-language-signature/specs/eff_opaque_loop.spec.json Effect op spec: opaque loop.
menagerie/php-language-signature/specs/eff_panic.spec.json Effect op spec: panic/termination.
menagerie/php-language-signature/specs/eff_read.spec.json Effect op spec: read.
menagerie/php-language-signature/specs/eff_unresolved_call.spec.json Effect op spec: unresolved call.
menagerie/php-language-signature/specs/eff_write.spec.json Effect op spec: write.
menagerie/php-language-signature/specs/effsig_io.spec.json Effect-signature: Io.
menagerie/php-language-signature/specs/effsig_opaque_loop.spec.json Effect-signature: Loop.
menagerie/php-language-signature/specs/effsig_panic.spec.json Effect-signature: Panic.
menagerie/php-language-signature/specs/effsig_read.spec.json Effect-signature: Read.
menagerie/php-language-signature/specs/effsig_unresolved_call.spec.json Effect-signature: Call.
menagerie/php-language-signature/specs/effsig_write.spec.json Effect-signature: Write.
menagerie/php-language-signature/specs/language_signature_php.spec.json Top-level PHP language signature index (sorts/ops/effects).
menagerie/php-language-signature/specs/op_add.spec.json Op spec: php:add.
menagerie/php-language-signature/specs/op_and.spec.json Op spec: php:and.
menagerie/php-language-signature/specs/op_assign.spec.json Op spec: php:assign.
menagerie/php-language-signature/specs/op_bitand.spec.json Op spec: php:bitand.
menagerie/php-language-signature/specs/op_bitnot.spec.json Op spec: php:bitnot.
menagerie/php-language-signature/specs/op_bitor.spec.json Op spec: php:bitor.
menagerie/php-language-signature/specs/op_bitxor.spec.json Op spec: php:bitxor.
menagerie/php-language-signature/specs/op_break.spec.json Op spec: php:break.
menagerie/php-language-signature/specs/op_call.spec.json Op spec: php:call.
menagerie/php-language-signature/specs/op_concat.spec.json Op spec: php:concat.
menagerie/php-language-signature/specs/op_continue.spec.json Op spec: php:continue.
menagerie/php-language-signature/specs/op_div.spec.json Op spec: php:div.
menagerie/php-language-signature/specs/op_dowhile.spec.json Op spec: php:dowhile.
menagerie/php-language-signature/specs/op_echo.spec.json Op spec: php:echo.
menagerie/php-language-signature/specs/op_eq.spec.json Op spec: php:eq.
menagerie/php-language-signature/specs/op_exit.spec.json Op spec: php:exit.
menagerie/php-language-signature/specs/op_for.spec.json Op spec: php:for.
menagerie/php-language-signature/specs/op_foreach.spec.json Op spec: php:foreach.
menagerie/php-language-signature/specs/op_ge.spec.json Op spec: php:ge.
menagerie/php-language-signature/specs/op_gt.spec.json Op spec: php:gt.
menagerie/php-language-signature/specs/op_identical.spec.json Op spec: php:identical.
menagerie/php-language-signature/specs/op_if.spec.json Op spec: php:if.
menagerie/php-language-signature/specs/op_index.spec.json Op spec: php:index.
menagerie/php-language-signature/specs/op_le.spec.json Op spec: php:le.
menagerie/php-language-signature/specs/op_lt.spec.json Op spec: php:lt.
menagerie/php-language-signature/specs/op_methodcall.spec.json Op spec: php:methodcall.
menagerie/php-language-signature/specs/op_mod.spec.json Op spec: php:mod.
menagerie/php-language-signature/specs/op_mul.spec.json Op spec: php:mul.
menagerie/php-language-signature/specs/op_ne.spec.json Op spec: php:ne.
menagerie/php-language-signature/specs/op_neg.spec.json Op spec: php:neg.
menagerie/php-language-signature/specs/op_not_identical.spec.json Op spec: php:not_identical.
menagerie/php-language-signature/specs/op_not.spec.json Op spec: php:not.
menagerie/php-language-signature/specs/op_nullcoalesce.spec.json Op spec: php:nullcoalesce.
menagerie/php-language-signature/specs/op_or.spec.json Op spec: php:or.
menagerie/php-language-signature/specs/op_pos.spec.json Op spec: php:pos.
menagerie/php-language-signature/specs/op_print.spec.json Op spec: php:print.
menagerie/php-language-signature/specs/op_propfetch.spec.json Op spec: php:propfetch.
menagerie/php-language-signature/specs/op_return.spec.json Op spec: php:return.
menagerie/php-language-signature/specs/op_seq.spec.json Op spec: php:seq.
menagerie/php-language-signature/specs/op_shl.spec.json Op spec: php:shl.
menagerie/php-language-signature/specs/op_shr.spec.json Op spec: php:shr.
menagerie/php-language-signature/specs/op_source-unit.spec.json Op spec: php:source-unit.
menagerie/php-language-signature/specs/op_staticcall.spec.json Op spec: php:staticcall.
menagerie/php-language-signature/specs/op_staticprop.spec.json Op spec: php:staticprop.
menagerie/php-language-signature/specs/op_sub.spec.json Op spec: php:sub.
menagerie/php-language-signature/specs/op_ternary.spec.json Op spec: php:ternary.
menagerie/php-language-signature/specs/op_throw.spec.json Op spec: php:throw.
menagerie/php-language-signature/specs/op_while.spec.json Op spec: php:while.
menagerie/php-language-signature/specs/sort_bool.spec.json Sort spec: Bool.
menagerie/php-language-signature/specs/sort_int.spec.json Sort spec: Int.
menagerie/php-language-signature/specs/sort_real.spec.json Sort spec: Real.
menagerie/php-language-signature/specs/sort_stmt.spec.json Sort spec: Stmt.
menagerie/php-language-signature/specs/sort_string.spec.json Sort spec: String.
menagerie/php-language-signature/specs/sort_unit.spec.json Sort spec: Unit.
menagerie/php-language-signature/specs/sort_value.spec.json Sort spec: Value.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +880 to +885
$parent = is_dir($candidate) ? $candidate : dirname($candidate);
$realParent = realpath($parent);
if ($realParent === false) {
return null;
}
$resolved = $realParent . DIRECTORY_SEPARATOR . basename($candidate);
Comment on lines +67 to +68
$fileResult = $this->liftSource($source, $file);
$result['ir'] = array_merge($result['ir'], $fileResult['ir']);
Comment on lines +37 to +41
} catch (\JsonException $e) {
write_rpc(error_response(null, -32700, 'PARSE_ERROR: ' . $e->getMessage()));
} catch (\Throwable $e) {
write_rpc(error_response($request['id'] ?? null, -32603, $e->getMessage()));
}
Comment on lines +154 to +158
private function targetNode(array $term): object
{
$node = $this->exprNode($term);
return $node;
}
Comment on lines 6 to 13
"psr-4": {
"Provekit\\Ir\\": "provekit-ir-symbolic/src/Ir/",
"Provekit\\Canonicalizer\\": "provekit-ir-symbolic/src/Canonicalizer/",
"Provekit\\ClaimEnvelope\\": "provekit-ir-symbolic/src/ClaimEnvelope/",
"Provekit\\ProofEnvelope\\": "provekit-ir-symbolic/src/ProofEnvelope/",
"Provekit\\SelfContracts\\": "provekit-self-contracts/slabs/"
"Provekit\\SelfContracts\\": "provekit-self-contracts/slabs/",
"ProvekIt\\LiftPhpSource\\": "provekit-lift-php-source/src/"
}
…ture menagerie

A self-hosted PHP source-language lift kit, mirroring PR #590 (the C# source
lifter) and the merged Go/Python/TypeScript/Zig lifters: a PHP program using
nikic/php-parser that lifts PHP function definitions into ProvekIt function-contract
mementos over a php:-namespaced operation algebra, plus menagerie/php-language-signature/,
plus a round-trip compiler. Built to the conventions the C#/Go/Java/Python/TS/Zig
review rounds settled: namespaced op CIDs, arity_shape on every op spec, a
php:source-unit lossless wrapper actually emitted, loud Refusals for unhandled
syntax (no php:unknown/skip catch-all), provably-unique qualified fnName, effects
on the canonical Effect wire shapes sorted by sort_key(), BLAKE3/JCS loop CIDs via
the existing per-language canonicalizer (not a rolled-own one), a lift(compile(lift(src)))
round-trip test driving the real IR->source compiler path (not just the source-unit
byte-copy fast-path), deterministic JSON, version 0.1.0-draft. The existing php kit
surface manifest is untouched; the source lifter gets its own php-source surface.
PHP unit tests pass; signature generator deterministic; RPC initialize reports 0.1.0-draft/php-source; composer install couldn't run locally (no composer in sandbox) — CI verifies the php-parser-dependent compiler path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TSavo TSavo force-pushed the codex/php-source-lift-kit branch from d973584 to 8dc3828 Compare May 11, 2026 17:53
@TSavo TSavo merged commit b6b84e5 into main May 11, 2026
17 of 19 checks passed
TSavo added a commit that referenced this pull request May 12, 2026
…#596/#598/#599/#600

Per-kit changes:
- rust/provekit-cli: extend KNOWN_SURFACES anchor test to assert ruby-source, csharp-source, swift-source, zig-source are all registered (were in the array, not in the test)
- Makefile: wire test-ruby and test-php into test-all so the new lift kits run under ci (boy-scout -- flagged in #598 review)
- typescript-language-signature/mint.sh: replace find with explicit enumeration to match c11/rust pattern and remove portability footgun (PR #595 nit #3)
- ruby/ruby_source.rb: add instance_variable_get, instance_variable_set, const_get, const_set to METAPROGRAMMING_CALLS (PR #598 nit #4)
- php/PhpSourceCompiler.php: simplify isUnit() to remove precedence-reliant double-condition (PR #600 nit #2)

Already swept on origin/main (codex/lift-kit-nits-cleanup, empty cherry-picks confirmed):
- effect sort-key alignment: 5:unresolved_call -> 5:unresolved: (ruby + swift)
- csharp-source backfill in KNOWN_SURFACES
- swift: emit Refusal for unparseable function signatures + ThrowStmt API fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TSavo added a commit that referenced this pull request May 12, 2026
…#596/#598/#599/#600

Per-kit changes:
- rust/provekit-cli: extend KNOWN_SURFACES anchor test to assert ruby-source, csharp-source, swift-source, zig-source are all registered (were in the array, not in the test)
- Makefile: wire test-ruby and test-php into test-all so the new lift kits run under ci (boy-scout -- flagged in #598 review)
- typescript-language-signature/mint.sh: replace find with explicit enumeration to match c11/rust pattern and remove portability footgun (PR #595 nit #3)
- ruby/ruby_source.rb: add instance_variable_get, instance_variable_set, const_get, const_set to METAPROGRAMMING_CALLS (PR #598 nit #4)
- php/PhpSourceCompiler.php: simplify isUnit() to remove precedence-reliant double-condition (PR #600 nit #2)

Already swept on origin/main (codex/lift-kit-nits-cleanup, empty cherry-picks confirmed):
- effect sort-key alignment: 5:unresolved_call -> 5:unresolved: (ruby + swift)
- csharp-source backfill in KNOWN_SURFACES
- swift: emit Refusal for unparseable function signatures + ThrowStmt API fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TSavo added a commit that referenced this pull request May 12, 2026
…599/#600 (#632)

* chore(lift-kits): sweep accumulated MERGE-WITH-NITS nits from PRs #595/#596/#598/#599/#600

Per-kit changes:
- rust/provekit-cli: extend KNOWN_SURFACES anchor test to assert ruby-source, csharp-source, swift-source, zig-source are all registered (were in the array, not in the test)
- Makefile: wire test-ruby and test-php into test-all so the new lift kits run under ci (boy-scout -- flagged in #598 review)
- typescript-language-signature/mint.sh: replace find with explicit enumeration to match c11/rust pattern and remove portability footgun (PR #595 nit #3)
- ruby/ruby_source.rb: add instance_variable_get, instance_variable_set, const_get, const_set to METAPROGRAMMING_CALLS (PR #598 nit #4)
- php/PhpSourceCompiler.php: simplify isUnit() to remove precedence-reliant double-condition (PR #600 nit #2)

Already swept on origin/main (codex/lift-kit-nits-cleanup, empty cherry-picks confirmed):
- effect sort-key alignment: 5:unresolved_call -> 5:unresolved: (ruby + swift)
- csharp-source backfill in KNOWN_SURFACES
- swift: emit Refusal for unparseable function signatures + ThrowStmt API fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ruby-language-signature): regen op_source-unit.spec.json with notes field

The generator `generate_assets.py` was updated to emit a `notes` field on
the `source-unit` op spec but the committed file was never regenerated.
CI gate at Makefile:606 (`generate_assets.py --check`) caught the staleness.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: add composer to conformance job apt block (fixes test-php exit 127)

test-php runs `composer install && composer test`; the conformance job
installed php-cli but never composer, so the command was not found.
Adds the `composer` Ubuntu apt package to the shared system-deps step
so make test-all (and make ci) can reach it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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