Skip to content

feat(dom): add PHP 8.5 DOM compliance foundation - #654

Draft
Guikingone wants to merge 76 commits into
illegalstudio:mainfrom
Guikingone:feat/php-dom-compliance
Draft

feat(dom): add PHP 8.5 DOM compliance foundation#654
Guikingone wants to merge 76 commits into
illegalstudio:mainfrom
Guikingone:feat/php-dom-compliance

Conversation

@Guikingone

@Guikingone Guikingone commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

This draft checkpoint implements the PHP 8.5 DOM/libxml foundation and the
current DOM behavior surface against the frozen php-src 8.5.8 contract.

The branch currently includes:

  • a source-locked, statically linked elephc-dom bridge for libxml2 2.15.3
    and bundled Lexbor 2.7.0;
  • generated PHP 8.5.8 reflection/operation manifests and compiler-side
    declarations, lowering, wrapper materialization, ownership, and structured
    exception transport;
  • legacy and modern document/node APIs, serialization, parsing, validation,
    XInclude, C14N, selectors, collections, token lists, namespaces, iterators,
    stream-wrapper integration, XPath callbacks, and native wrapper lifecycle;
  • DTD named-node maps, entities, notations, tree mutation, DOMXPath and
    Dom\XPath, including exact dynamic argument contracts and callback arrays;
  • runtime-class dispatch for native methods and virtual properties selected
    through mixed or heterogeneous wrapper unions, with feature detection that
    links every latent DOM branch.

The normative specification is locked at
docs/specs/php-dom-compliance.md. Detailed progress and reproducible gates
are maintained in .plans/php-dom-compliance.md.

Tracks #622.

Checkpoint status

This PR is intentionally a draft and is not yet ready to merge.

  • current published checkpoint: ccde8612b1
  • 603 frozen operation keys
  • 564 explicit bridge routes
  • 39 unimplemented SimpleXML/interoperability keys remaining:
    • 27 functions or methods
    • 12 SimpleXML object handlers
  • the frozen DOM/libxml/SimpleXML PHPT ledgers remain pending final zero-exclusion replay
  • final macOS ARM64, Linux ARM64, and Linux x86_64 evidence remains open
  • final same-commit LOCK consensus from GLM 5.2, Kimi K2.7, and MiniMax M3
    remains open

The draft will be updated in place as those gates close.

Current checkpoint validation

  • cargo test --lib ir_lower::tests::internal_extensions
    • 19 passed
  • cargo test --test codegen_tests mixed_dispatch_with_dom_candidates_links_required_runtime
    • passed
  • focused XPath dynamic-type, variadic mutation, coercion-order, and heap-debug regressions
    • passed
  • cargo check --tests
    • passed
  • exact CLI reproduction from the previous cross-suite linker failure
    • compiled and ran with Row:1:Ada
  • assembly-comment alignment checks
    • passed
  • git diff --check
    • passed

The replacement complete CI run for ccde8612b1 is active. Local Linux Docker
execution remains unavailable on the current macOS host, so the complete
supported-target matrix will be provided by CI before this draft is marked ready.

@github-actions github-actions Bot added area:codegen Touches target-aware assembly or backend lowering. area:magician Touches eval, include execution, or elephc-magician. area:runtime Touches runtime helpers, GC, ownership, or bridge runtimes. scope:multi-area Touches more compiler areas than the automatic area-label cap. size:xl Very large pull request that needs deliberate review planning. type:feature Introduces new user-visible behavior or capabilities. labels Jul 30, 2026
@Guikingone Guikingone self-assigned this Jul 30, 2026
@Guikingone
Guikingone force-pushed the feat/php-dom-compliance branch from 50347ae to 19ca0cd Compare August 21, 2026 12:14
@nahime0

nahime0 commented Sep 7, 2026

Copy link
Copy Markdown
Member

Integration with #913 (ext/xml + ext/xmlwriter on catalog libxml2)

#913 lands ext/xml and ext/xmlwriter on libxml2 2.15.3 supplied by the curated native catalog: package libxml2 (src/native_deps/catalog.rs, recipe src/native_deps/recipes/libxml2.rs), pinning the very same tarball this branch vendors (libxml2-2.15.3.tar.xz, sha256 78262a6e…). The recipe also compiles an Elephc-owned C shim (src/native_deps/recipes/libxml2_shim.c, versioned elephc_libxml2_v1_* entry points over the parser-context and entity internals) and archives it next to libxml2.a. elephc native add libxml2 materializes both, and the compiler links them whenever the elephc_xml bridge is planned.

As this branch stands, crates/elephc-dom builds its own libxml2 in build.rs (cmake on the vendored tarball, cargo:rustc-link-lib=static=xml2), so the libxml2 objects end up inside libelephc_dom.a. Once both are on main, a program that uses DOM and xml_*/XMLWriter would carry two builds of libxml2 (cmake vs. autoconf, different feature flags) in one link: duplicate definitions at best, DOM and ext/xml disagreeing on encodings/entities at worst. So, when rebasing on main after #913:

  1. Drop the vendored libxml2 and the libxml2 half of crates/elephc-dom/build.rs. Lexbor is a different story (PHP bundles it) and can stay vendored or become its own catalog package later.
  2. Require the catalog package. In src/pipeline/backend.rs, next to the elephc_xmlNativeRequirement::package("libxml2") splice (and curl's), add the same for elephc_dom. The resolver deduplicates, so a program using both bridges links libxml2.a once. examples/*/elephc.toml for DOM examples declare libxml2 = "2.15.3".
  3. Bind libxml2 the way crates/elephc-xml does: hand-written extern "C" declarations of libxml2's public, pointer-opaque API without linking at cargo build time (the curl pattern; unresolved symbols are fine in a staticlib), and everything that needs libxml2 struct internals goes through the shim. DOM will need more than the parser accessors: extend libxml2_shim.c with the accessors you need (elephc_libxml2_v1_*, or a v2 family) and bump the recipe revision, rather than compiling a second C layer against a second copy of the headers. If parts of native/*.c must remain C (the Lexbor adapters, selectors), keep them free of libxml2 struct access or compile them in the recipe as an additional shim archive.
  4. Feature flags of the catalog build: --disable-shared --enable-static --without-python --without-zlib --with-iconv --without-icu --without-readline --without-history --without-modules, everything else at libxml2's defaults (HTML, XPath, XInclude, schemas, RelaxNG, C14N, xmlreader, pattern, xmlwriter, SAX1 all on). If DOM needs something different (zlib for .gz documents, legacy APIs), request it as a recipe revision, not a private build.
  5. Platform libraries: -lm -lpthread are already on the Linux link; on Apple targets -liconv comes from the new BridgeStaticlib.apple_libraries field (&["iconv"] on elephc_xml) — add it to the elephc_dom entry too.
  6. Tests and CI: reuse the harness pieces feat(xml): complete ext/xml and ext/xmlwriter in AOT and eval() on catalog libxml2 #913 adds instead of new ones — tests/codegen/support/xml_native.rs (structural discovery of the artifact, skip_without_xml_native, ELEPHC_TEST_REQUIRE_XML_NATIVE=1 to fail loudly in CI), the managed-archive provider in runner.rs, scripts/ci/libxml2_lib_dir.sh + ELEPHC_XML_LIBXML2_LIB_DIR for crate unit tests, the native install --locked --manifest-path examples/xml/elephc.toml steps and caches in ci.yml.
  7. Expect textual conflicts on the shared infrastructure both branches touch: Cargo.toml/Cargo.lock, .config/nextest.toml, .github/workflows/ci.yml, src/linker/bridges.rs, src/linker/mod.rs, src/lib.rs/src/main.rs, src/ir_lower/context.rs, src/ir_lower/tests/mod.rs, src/types/checker/builtins/mod.rs, tests/codegen/mod.rs, tests/codegen/support/{compiler,projects,runner}.rs, tests/error_tests.rs, tests/builtin_parity_tests.rs.
  8. Symbol catalog: XML_* constants and the XMLParser/XMLWriter classes live in the shared contract crate (catalog_xml.rs, catalog_classes.rs, catalog_constants.rs, modules Xml/Xmlwriter). The LIBXML_* constants and DOM/SimpleXML classes should be catalogued the same way (note feat: Termwind-facing DOM HTML subset for HtmlRenderer::parse #896 also introduces LIBXML_* constants for a Termwind-only HTML subset, which is meant to be replaced by this PR).

Net effect: one pinned libxml2 for every XML-family extension, built once per target by the catalog, with the shim as the single place where struct layouts are known.

@Guikingone
Guikingone force-pushed the feat/php-dom-compliance branch from 5ebdf37 to aa192bd Compare September 9, 2026 17:57
@Guikingone
Guikingone force-pushed the feat/php-dom-compliance branch from c123a3e to b245e04 Compare September 10, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:codegen Touches target-aware assembly or backend lowering. area:magician Touches eval, include execution, or elephc-magician. area:runtime Touches runtime helpers, GC, ownership, or bridge runtimes. scope:multi-area Touches more compiler areas than the automatic area-label cap. size:xl Very large pull request that needs deliberate review planning. type:feature Introduces new user-visible behavior or capabilities.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants