diff --git a/.config/nextest.toml b/.config/nextest.toml
index f3630d3ca2..f6d597b64d 100644
--- a/.config/nextest.toml
+++ b/.config/nextest.toml
@@ -6,3 +6,52 @@ failure-output = "immediate-final"
final-status-level = "slow"
slow-timeout = { period = "60s", terminate-after = 1 }
status-level = "slow"
+
+# `cargo nextest archive` captures the test binaries + the `elephc` bin, but NOT
+# the bridge staticlibs — the codegen test runner links compiled PHP programs
+# against libelephc_*.a at run time (via `ld`), so nextest can't discover them.
+# Include them explicitly so archived shards can run on a machine that never
+# rebuilt the workspace. Built into target/debug by `cargo build $BRIDGE_CRATES`
+# in the archive job before archiving.
+[[profile.ci.archive.include]]
+path = "debug/libelephc_tls.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_pdo.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_crypto.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_phar.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_tz.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_image.a"
+relative-to = "target"
+[[profile.ci.archive.include]]
+path = "debug/libelephc_web.a"
+relative-to = "target"
+
+# Emit a machine-readable JUnit report for every `--profile ci` run. This is
+# purely additive (it writes `target/nextest/ci/junit.xml` and changes no test
+# outcome or console output), so the native codegen/non-codegen jobs are
+# unaffected. The Windows codegen no-regression gate parses this file per shard
+# to recover the exact set of failing test names (see the `windows-codegen-parity`
+# job in `.github/workflows/ci.yml` and `scripts/windows_codegen_gate_check.py`).
+[profile.ci.junit]
+path = "junit.xml"
+
+# The `ir_backend_parity` first-class-callable case bundles 15 legacy-vs-EIR
+# parity programs (several link the PCRE staticlib), each compiled and run twice.
+# It legitimately runs ~65-70s, just over the global 60s cap, so it needs a
+# longer slow-timeout to avoid spurious termination on loaded runners.
+[[profile.default.overrides]]
+filter = 'test(parity_function_first_class_callable_dispatch)'
+slow-timeout = { period = "180s", terminate-after = 1 }
+
+[[profile.ci.overrides]]
+filter = 'test(parity_function_first_class_callable_dispatch)'
+slow-timeout = { period = "180s", terminate-after = 1 }
diff --git a/.github/traffic/clones-badge.svg b/.github/traffic/clones-badge.svg
index cd129e5bdd..797ff53762 100644
--- a/.github/traffic/clones-badge.svg
+++ b/.github/traffic/clones-badge.svg
@@ -1 +1 @@
-cloners: 14051 cloners 14051
\ No newline at end of file
+cloners: 14246 cloners 14246
\ No newline at end of file
diff --git a/.github/traffic/clones.json b/.github/traffic/clones.json
index 7fbe899c6f..6c089d3ef5 100644
--- a/.github/traffic/clones.json
+++ b/.github/traffic/clones.json
@@ -1,5 +1,5 @@
{
- "total_uniques": 14051,
+ "total_uniques": 14246,
"daily": {
"2026-03-16": 0,
"2026-03-17": 0,
@@ -103,6 +103,15 @@
"2026-06-23": 48,
"2026-06-24": 31,
"2026-06-25": 39,
- "2026-06-26": 43
+ "2026-06-26": 43,
+ "2026-06-27": 24,
+ "2026-06-28": 36,
+ "2026-06-29": 25,
+ "2026-06-30": 18,
+ "2026-07-01": 13,
+ "2026-07-02": 18,
+ "2026-07-03": 17,
+ "2026-07-04": 22,
+ "2026-07-05": 22
}
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 94a63038ea..705d414c2b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,6 +6,12 @@ on:
pull_request:
branches: [main]
+# Cancel superseded runs for the same ref (e.g. when a branch is force-pushed or
+# rebased) so stale jobs stop consuming runners the moment a newer commit lands.
+concurrency:
+ group: ci-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
env:
CARGO_TERM_COLOR: always
BRIDGE_CRATES: >-
@@ -18,23 +24,13 @@ env:
-p elephc-web
jobs:
- non-codegen-tests:
- name: Build & Non-Codegen Tests (${{ matrix.platform.name }})
- runs-on: ${{ matrix.platform.runner }}
+ # Compile each platform once, then let that platform's tests start as soon as
+ # its archive is uploaded. This avoids a matrix-wide barrier where Linux tests
+ # wait for a queued or slower macOS archive job.
+ build-archive-macos-aarch64:
+ name: Build & Archive Tests (macos-aarch64)
+ runs-on: macos-14
timeout-minutes: 60
- strategy:
- fail-fast: false
- matrix:
- platform:
- - name: macos-aarch64
- runner: macos-14
- nextest_jobs: 1
- - name: linux-x86_64
- runner: ubuntu-24.04
- nextest_jobs: 1
- - name: linux-aarch64
- runner: ubuntu-24.04-arm
- nextest_jobs: 1
steps:
- uses: actions/checkout@v4
@@ -48,19 +44,74 @@ jobs:
~/.cargo/git
~/.cargo/registry
target
- key: rust-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
+ key: rust-macos-aarch64-${{ hashFiles('Cargo.lock') }}
restore-keys: |
- rust-${{ matrix.platform.name }}-
+ rust-macos-aarch64-
- name: Install cargo-nextest
uses: taiki-e/install-action@nextest
- - name: Install native test dependencies (macOS)
- if: runner.os == 'macOS'
+ - name: Install native test dependencies
run: brew install pcre2
- - name: Install native test dependencies (Linux)
- if: runner.os == 'Linux'
+ - name: Check (no warnings)
+ shell: bash
+ run: |
+ set -o pipefail
+ cargo build 2>&1 | tee "$RUNNER_TEMP/cargo-build.log"
+ ! grep -i warning "$RUNNER_TEMP/cargo-build.log"
+
+ - name: Build bridge/native support crates
+ # The codegen test runner links compiled programs against bridge
+ # staticlibs: libelephc_tls.a (https:// / TLS), libelephc_pdo.a (PDO),
+ # libelephc_crypto.a (hash / HMAC family), libelephc_phar.a (PHAR),
+ # libelephc_tz.a (DateTimeZone introspection), libelephc_image.a
+ # (GD / Exif / Imagick / Gmagick / Cairo), and libelephc_web.a. Build
+ # them before archiving so the archive can capture the .a files (see the
+ # archive include list in .config/nextest.toml).
+ run: cargo build $BRIDGE_CRATES
+
+ - name: Archive test binaries
+ run: cargo nextest archive --profile ci --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst"
+
+ - name: Run doctests
+ # Doctests cannot run from a nextest archive, so run them here where the
+ # workspace is already built.
+ run: cargo test --workspace --doc
+
+ - name: Upload test archive
+ uses: actions/upload-artifact@v4
+ with:
+ name: nextest-archive-macos-aarch64
+ path: ${{ runner.temp }}/nextest-archive.tar.zst
+ retention-days: 1
+ if-no-files-found: error
+
+ build-archive-linux-x86_64:
+ name: Build & Archive Tests (linux-x86_64)
+ runs-on: ubuntu-24.04
+ timeout-minutes: 60
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache Rust build state
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cargo/git
+ ~/.cargo/registry
+ target
+ key: rust-linux-x86_64-${{ hashFiles('Cargo.lock') }}
+ restore-keys: |
+ rust-linux-x86_64-
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
@@ -86,35 +137,31 @@ jobs:
# staticlibs: libelephc_tls.a (https:// / TLS), libelephc_pdo.a (PDO),
# libelephc_crypto.a (hash / HMAC family), libelephc_phar.a (PHAR),
# libelephc_tz.a (DateTimeZone introspection), libelephc_image.a
- # (GD / Exif / Imagick / Gmagick / Cairo), and libelephc_web.a.
- # Build all of them up front so every CI platform validates native support
- # crates before integration tests need them.
+ # (GD / Exif / Imagick / Gmagick / Cairo), and libelephc_web.a. Build
+ # them before archiving so the archive can capture the .a files (see the
+ # archive include list in .config/nextest.toml).
run: cargo build $BRIDGE_CRATES
- - name: Run non-codegen tests
- run: cargo nextest run --profile ci --workspace -E 'not binary(codegen_tests)' --no-fail-fast --retries 1 --flaky-result pass -j ${{ matrix.platform.nextest_jobs }}
+ - name: Archive test binaries
+ run: cargo nextest archive --profile ci --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst"
- name: Run doctests
+ # Doctests cannot run from a nextest archive, so run them here where the
+ # workspace is already built.
run: cargo test --workspace --doc
- codegen-tests:
- name: Codegen Tests (${{ matrix.platform.name }} ${{ matrix.shard }}/16)
- runs-on: ${{ matrix.platform.runner }}
- timeout-minutes: 75
- strategy:
- fail-fast: false
- matrix:
- platform:
- - name: macos-aarch64
- runner: macos-14
- nextest_jobs: 1
- - name: linux-x86_64
- runner: ubuntu-24.04
- nextest_jobs: 1
- - name: linux-aarch64
- runner: ubuntu-24.04-arm
- nextest_jobs: 1
- shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+ - name: Upload test archive
+ uses: actions/upload-artifact@v4
+ with:
+ name: nextest-archive-linux-x86_64
+ path: ${{ runner.temp }}/nextest-archive.tar.zst
+ retention-days: 1
+ if-no-files-found: error
+
+ build-archive-linux-aarch64:
+ name: Build & Archive Tests (linux-aarch64)
+ runs-on: ubuntu-24.04-arm
+ timeout-minutes: 60
steps:
- uses: actions/checkout@v4
@@ -128,19 +175,14 @@ jobs:
~/.cargo/git
~/.cargo/registry
target
- key: rust-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
+ key: rust-linux-aarch64-${{ hashFiles('Cargo.lock') }}
restore-keys: |
- rust-${{ matrix.platform.name }}-
+ rust-linux-aarch64-
- name: Install cargo-nextest
uses: taiki-e/install-action@nextest
- - name: Install native test dependencies (macOS)
- if: runner.os == 'macOS'
- run: brew install pcre2
-
- - name: Install native test dependencies (Linux)
- if: runner.os == 'Linux'
+ - name: Install native test dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
@@ -154,18 +196,271 @@ jobs:
tzdata \
zlib1g-dev
+ - name: Check (no warnings)
+ shell: bash
+ run: |
+ set -o pipefail
+ cargo build 2>&1 | tee "$RUNNER_TEMP/cargo-build.log"
+ ! grep -i warning "$RUNNER_TEMP/cargo-build.log"
+
- name: Build bridge/native support crates
# The codegen test runner links compiled programs against bridge
# staticlibs: libelephc_tls.a (https:// / TLS), libelephc_pdo.a (PDO),
# libelephc_crypto.a (hash / HMAC family), libelephc_phar.a (PHAR),
# libelephc_tz.a (DateTimeZone introspection), libelephc_image.a
- # (GD / Exif / Imagick / Gmagick / Cairo), and libelephc_web.a.
- # Build all of them up front so every CI platform validates native support
- # crates before integration tests need them.
+ # (GD / Exif / Imagick / Gmagick / Cairo), and libelephc_web.a. Build
+ # them before archiving so the archive can capture the .a files (see the
+ # archive include list in .config/nextest.toml).
run: cargo build $BRIDGE_CRATES
+ - name: Archive test binaries
+ run: cargo nextest archive --profile ci --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst"
+
+ - name: Run doctests
+ # Doctests cannot run from a nextest archive, so run them here where the
+ # workspace is already built.
+ run: cargo test --workspace --doc
+
+ - name: Upload test archive
+ uses: actions/upload-artifact@v4
+ with:
+ name: nextest-archive-linux-aarch64
+ path: ${{ runner.temp }}/nextest-archive.tar.zst
+ retention-days: 1
+ if-no-files-found: error
+
+ non-codegen-tests-macos-aarch64:
+ name: Non-Codegen Tests (macos-aarch64)
+ needs: build-archive-macos-aarch64
+ runs-on: macos-14
+ timeout-minutes: 30
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: brew install pcre2
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-macos-aarch64
+ path: ${{ runner.temp }}
+
+ - name: Run non-codegen tests
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'not binary(codegen_tests)' \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
+
+ non-codegen-tests-linux-x86_64:
+ name: Non-Codegen Tests (linux-x86_64)
+ needs: build-archive-linux-x86_64
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils \
+ build-essential \
+ file \
+ libbz2-dev \
+ libpcre2-dev \
+ libssl-dev \
+ pkg-config \
+ tzdata \
+ zlib1g-dev
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-linux-x86_64
+ path: ${{ runner.temp }}
+
+ - name: Run non-codegen tests
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'not binary(codegen_tests)' \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
+
+ non-codegen-tests-linux-aarch64:
+ name: Non-Codegen Tests (linux-aarch64)
+ needs: build-archive-linux-aarch64
+ runs-on: ubuntu-24.04-arm
+ timeout-minutes: 30
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils \
+ build-essential \
+ file \
+ libbz2-dev \
+ libpcre2-dev \
+ libssl-dev \
+ pkg-config \
+ tzdata \
+ zlib1g-dev
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-linux-aarch64
+ path: ${{ runner.temp }}
+
+ - name: Run non-codegen tests
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'not binary(codegen_tests)' \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
+
+ codegen-tests-macos-aarch64:
+ name: Codegen Tests (macos-aarch64 ${{ matrix.shard }}/16)
+ needs: build-archive-macos-aarch64
+ runs-on: macos-14
+ timeout-minutes: 40
+ strategy:
+ fail-fast: false
+ matrix:
+ shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: brew install pcre2
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-macos-aarch64
+ path: ${{ runner.temp }}
+
- name: Run codegen test shard
- run: cargo nextest run --profile ci --test codegen_tests --partition hash:${{ matrix.shard }}/16 --no-fail-fast --retries 1 --flaky-result pass -j ${{ matrix.platform.nextest_jobs }}
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'binary(codegen_tests)' \
+ --partition hash:${{ matrix.shard }}/16 \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
+
+ codegen-tests-linux-x86_64:
+ name: Codegen Tests (linux-x86_64 ${{ matrix.shard }}/16)
+ needs: build-archive-linux-x86_64
+ runs-on: ubuntu-24.04
+ timeout-minutes: 40
+ strategy:
+ fail-fast: false
+ matrix:
+ shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils \
+ build-essential \
+ file \
+ libbz2-dev \
+ libpcre2-dev \
+ libssl-dev \
+ pkg-config \
+ tzdata \
+ zlib1g-dev
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-linux-x86_64
+ path: ${{ runner.temp }}
+
+ - name: Run codegen test shard
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'binary(codegen_tests)' \
+ --partition hash:${{ matrix.shard }}/16 \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
+
+ codegen-tests-linux-aarch64:
+ name: Codegen Tests (linux-aarch64 ${{ matrix.shard }}/16)
+ needs: build-archive-linux-aarch64
+ runs-on: ubuntu-24.04-arm
+ timeout-minutes: 40
+ strategy:
+ fail-fast: false
+ matrix:
+ shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils \
+ build-essential \
+ file \
+ libbz2-dev \
+ libpcre2-dev \
+ libssl-dev \
+ pkg-config \
+ tzdata \
+ zlib1g-dev
+
+ - name: Download test archive
+ uses: actions/download-artifact@v4
+ with:
+ name: nextest-archive-linux-aarch64
+ path: ${{ runner.temp }}
+
+ - name: Run codegen test shard
+ run: |
+ cargo nextest run --profile ci \
+ --archive-file "$RUNNER_TEMP/nextest-archive.tar.zst" \
+ --workspace-remap . \
+ -E 'binary(codegen_tests)' \
+ --partition hash:${{ matrix.shard }}/16 \
+ --no-fail-fast --retries 1 --flaky-result pass \
+ -j 1
image-api-sync:
name: Image API stubs in sync
@@ -195,6 +490,26 @@ jobs:
steps:
- uses: actions/checkout@v4
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache Rust build state
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cargo/git
+ ~/.cargo/registry
+ target
+ key: rust-builtins-docs-${{ hashFiles('Cargo.lock') }}
+ restore-keys: |
+ rust-builtins-docs-
+
+ - name: Build the builtin docs exporter
+ # The docs generator reads the single-source `builtin!` registry via the
+ # `gen_builtins` binary (`extract.py` prefers the prebuilt binary at
+ # target/debug/gen_builtins), so it must be built before regeneration.
+ run: cargo build --bin gen_builtins
+
- name: Regenerate builtins documentation
# Re-run the generator; the committed Markdown pages and JSON registry
# must come back byte-identical. A diff means someone changed a builtin
@@ -221,22 +536,43 @@ jobs:
name: Build & Test
runs-on: ubuntu-latest
needs:
- - non-codegen-tests
- - codegen-tests
+ - build-archive-macos-aarch64
+ - build-archive-linux-x86_64
+ - build-archive-linux-aarch64
+ - non-codegen-tests-macos-aarch64
+ - non-codegen-tests-linux-x86_64
+ - non-codegen-tests-linux-aarch64
+ - codegen-tests-macos-aarch64
+ - codegen-tests-linux-x86_64
+ - codegen-tests-linux-aarch64
- image-api-sync
- builtins-docs-sync
+ - windows-pe-cross-compile
+ - windows-codegen-gate
if: always()
steps:
- name: Verify test jobs
run: |
- test "${{ needs.non-codegen-tests.result }}" = "success"
- test "${{ needs.codegen-tests.result }}" = "success"
+ test "${{ needs.build-archive-macos-aarch64.result }}" = "success"
+ test "${{ needs.build-archive-linux-x86_64.result }}" = "success"
+ test "${{ needs.build-archive-linux-aarch64.result }}" = "success"
+ test "${{ needs.non-codegen-tests-macos-aarch64.result }}" = "success"
+ test "${{ needs.non-codegen-tests-linux-x86_64.result }}" = "success"
+ test "${{ needs.non-codegen-tests-linux-aarch64.result }}" = "success"
+ test "${{ needs.codegen-tests-macos-aarch64.result }}" = "success"
+ test "${{ needs.codegen-tests-linux-x86_64.result }}" = "success"
+ test "${{ needs.codegen-tests-linux-aarch64.result }}" = "success"
test "${{ needs.image-api-sync.result }}" = "success"
test "${{ needs.builtins-docs-sync.result }}" = "success"
+ test "${{ needs.windows-pe-cross-compile.result }}" = "success"
+ test "${{ needs.windows-codegen-gate.result }}" = "success"
benchmark:
name: Benchmark Suite
runs-on: macos-14 # Apple Silicon runner
+ # The benchmark suite does a full release build and only tracks trends, so it
+ # runs on pushes to main rather than on every pull request.
+ if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
@@ -262,3 +598,248 @@ jobs:
path: |
benchmark-results.json
benchmark-results.md
+
+ windows-pe-cross-compile:
+ name: Windows PE Cross-Compile Tests
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+ env:
+ # Silence Wine's diagnostic chatter so it never pollutes captured stdout/stderr.
+ WINEDEBUG: -all
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache Rust build state
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cargo/git
+ ~/.cargo/registry
+ target
+ key: rust-windows-pe-${{ hashFiles('Cargo.lock') }}
+ restore-keys: |
+ rust-windows-pe-
+
+ - name: Install MinGW-w64 cross-compiler
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils-mingw-w64-x86-64 \
+ gcc-mingw-w64-x86-64 \
+ file
+
+ - name: Install Wine
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends wine64 wine
+ # On ubuntu-24.04 the wine64 package only ships the internal
+ # /usr/lib/wine/wine64 loader; the callable binary on PATH is `wine`
+ # (dispatches to the 64-bit loader since wine32/i386 is not installed
+ # and is not needed for x86_64-only PE binaries).
+ command -v wine64 >/dev/null 2>&1 && wine64 --version || wine --version
+
+ - name: Initialize Wine prefix
+ run: |
+ wineboot --init || true
+ wineserver --wait || true
+
+ - name: Check (no warnings)
+ shell: bash
+ run: |
+ set -o pipefail
+ cargo build 2>&1 | tee "$RUNNER_TEMP/cargo-build.log"
+ ! grep -i warning "$RUNNER_TEMP/cargo-build.log"
+
+ - name: Run Windows PE tests
+ run: cargo test --test codegen_tests -- windows_pe --nocapture
+
+ - name: Cross-compile hello-world
+ run: |
+ echo ' /tmp/hello.php
+ cargo run -- --target windows-x86_64 /tmp/hello.php
+ file /tmp/hello.exe
+ # Verify it is a valid PE32+ executable
+ file /tmp/hello.exe | grep -q "PE32+ executable (console) x86-64"
+ # Verify imports include kernel32
+ x86_64-w64-mingw32-objdump -x /tmp/hello.exe | grep -q "KERNEL32.dll"
+
+ - name: Run hello-world under Wine
+ run: |
+ WINE_BIN=wine64
+ command -v wine64 >/dev/null 2>&1 || WINE_BIN=wine
+ "$WINE_BIN" /tmp/hello.exe > /tmp/hello.out
+ cat /tmp/hello.out
+ grep -q "Hello from Windows!" /tmp/hello.out
+
+ - name: Cross-compile arithmetic test
+ run: |
+ echo ' /tmp/arith.php
+ cargo run -- --target windows-x86_64 /tmp/arith.php
+ file /tmp/arith.exe | grep -q "PE32+ executable"
+
+ - name: Cross-compile function call test
+ run: |
+ echo ' /tmp/func.php
+ cargo run -- --target windows-x86_64 /tmp/func.php
+ file /tmp/func.exe | grep -q "PE32+ executable"
+
+ - name: Cross-compile loop test
+ run: |
+ echo ' /tmp/loop.php
+ cargo run -- --target windows-x86_64 /tmp/loop.php
+ file /tmp/loop.exe | grep -q "PE32+ executable"
+
+ - name: Cross-compile string concatenation test
+ run: |
+ echo ' /tmp/concat.php
+ cargo run -- --target windows-x86_64 /tmp/concat.php
+ file /tmp/concat.exe | grep -q "PE32+ executable"
+
+ # Windows codegen parity MEASUREMENT + no-regression GATE (unified, sharded 16x).
+ # Runs the full codegen suite cross-compiled to windows-x86_64 and executed under
+ # Wine, then does two things from that single run:
+ # 1. MEASURE (informational): emits passed / failed / parity% for the shard to
+ # the job summary, so the overall Windows parity picture stays visible.
+ # 2. GATE (blocking): fails the shard iff any test in the curated allow-list
+ # (`tests/codegen/support/windows_codegen_allowlist.txt`, the tests that
+ # currently PASS on Windows) failed. Tests NOT in the allow-list -- the known
+ # failures AND any brand-new / native-only fixtures -- never fail the gate, so
+ # Windows parity can only improve, never regress.
+ # The nextest run step is `continue-on-error: true` so ordinary (non-allow-listed)
+ # failures do not fail the job; only the post-run gate step decides pass/fail via
+ # `actual_failures ∩ allow_list`. The aggregating `windows-codegen-gate` job (which
+ # needs all 16 shards) is the single entry wired into the `test` gate.
+ # Interpreting a "pass": a real Windows pass OR a graceful skip (harness skip when
+ # MinGW/Wine are missing, or a raw-asm exit-harness fixture that cannot target
+ # Windows). A crashing `.exe` cannot stall a shard: the `ci` profile's 60s
+ # slow-timeout kills and reports a hung binary as a failure. Refresh the allow-list
+ # as parity grows (see docs/compiling/targets.md, "Windows codegen parity gate").
+ windows-codegen-parity:
+ name: Windows Codegen Parity + Gate (${{ matrix.shard }}/16)
+ runs-on: ubuntu-24.04
+ timeout-minutes: 75
+ strategy:
+ fail-fast: false
+ matrix:
+ shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+ env:
+ # Cross-compile + run every codegen fixture as windows-x86_64 (via Wine).
+ ELEPHC_TEST_TARGET: windows-x86_64
+ # Silence Wine's diagnostic chatter so it never pollutes captured stdout/stderr.
+ WINEDEBUG: -all
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache Rust build state
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cargo/git
+ ~/.cargo/registry
+ target
+ key: rust-windows-parity-${{ hashFiles('Cargo.lock') }}
+ restore-keys: |
+ rust-windows-parity-
+
+ - name: Install cargo-nextest
+ uses: taiki-e/install-action@nextest
+
+ - name: Install native test dependencies (Linux)
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils \
+ build-essential \
+ file \
+ libbz2-dev \
+ libpcre2-dev \
+ libssl-dev \
+ pkg-config \
+ tzdata \
+ zlib1g-dev
+
+ - name: Install MinGW-w64 cross-compiler
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ binutils-mingw-w64-x86-64 \
+ gcc-mingw-w64-x86-64 \
+ file
+
+ - name: Install Wine
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends wine64 wine
+ # On ubuntu-24.04 the wine64 package only ships the internal
+ # /usr/lib/wine/wine64 loader; the callable binary on PATH is `wine`
+ # (dispatches to the 64-bit loader since wine32/i386 is not installed
+ # and is not needed for x86_64-only PE binaries).
+ command -v wine64 >/dev/null 2>&1 && wine64 --version || wine --version
+
+ - name: Initialize Wine prefix
+ run: |
+ wineboot --init || true
+ wineserver --wait || true
+
+ - name: Build bridge/native support crates
+ # Match the native codegen-tests job so bridge-linking fixtures build their
+ # host staticlibs up front instead of triggering serialized on-demand builds
+ # mid-shard.
+ run: cargo build $BRIDGE_CRATES
+
+ - name: Run codegen test shard under windows-x86_64
+ # `--no-fail-fast` so the shard measures every fixture. `continue-on-error`
+ # is on the STEP (not the job): ordinary test failures -- including the
+ # ~1874 known Windows failures -- must not fail the job here, because the
+ # gate step below is the sole arbiter of pass/fail. nextest still writes its
+ # JUnit report (`target/nextest/ci/junit.xml`, configured in
+ # `.config/nextest.toml`) even when tests fail, which the gate step parses.
+ id: nextest
+ continue-on-error: true
+ run: cargo nextest run --profile ci --test codegen_tests --partition hash:${{ matrix.shard }}/16 --no-fail-fast --retries 1 --flaky-result pass
+
+ - name: Windows codegen no-regression gate + parity summary
+ # Compute regressions = actual_failures ∩ allow_list from THIS shard's JUnit
+ # report. Emits passed / failed / parity% to the job summary (measurement),
+ # then exits non-zero -- failing the shard -- iff any allow-listed test
+ # regressed. Non-allow-listed failures (known failures + brand-new tests)
+ # are ignored, so this never blocks native-only fixtures.
+ run: |
+ python3 scripts/gen_windows_codegen_allowlist.py gate \
+ --allowlist tests/codegen/support/windows_codegen_allowlist.txt \
+ --junit target/nextest/ci/junit.xml \
+ --shard "${{ matrix.shard }}/16"
+
+ - name: Upload shard JUnit report
+ # Retained so a maintainer can refresh the allow-list from a real parity run
+ # by feeding these 16 reports to `gen_windows_codegen_allowlist.py generate`.
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: windows-codegen-junit-${{ matrix.shard }}
+ path: target/nextest/ci/junit.xml
+ if-no-files-found: warn
+
+ # Aggregating gate: a single job that is green iff every sharded
+ # `windows-codegen-parity` job was green (i.e. no allow-listed Windows codegen
+ # test regressed on any shard). A matrix job's `result` in `needs` is `success`
+ # only when ALL of its shards succeeded, so this collapses the 16 shards into the
+ # one entry wired into the `test` gate's `needs` below. Kept separate from the
+ # measurement/gate shards so the `test` gate lists one dependency, not sixteen.
+ windows-codegen-gate:
+ name: Windows Codegen No-Regression Gate
+ runs-on: ubuntu-latest
+ needs:
+ - windows-codegen-parity
+ if: always()
+ steps:
+ - name: Verify no allow-listed Windows codegen regressions
+ run: |
+ echo "windows-codegen-parity result: ${{ needs.windows-codegen-parity.result }}"
+ test "${{ needs.windows-codegen-parity.result }}" = "success"
diff --git a/.gitignore b/.gitignore
index 22450ffd17..0285daba46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,14 @@ __pycache__/
.DS_Store
# Internal planning docs (specs/plans) — kept locally, not tracked
docs/superpowers/
+# Subagent-driven-development scratch (briefs, reports, review diffs, ledger) — local only
+.superpowers/
+# Generated archives written to the repo root when the phar-write example
+# is run from here instead of its own directory (root-anchored so the
+# tracked examples/phar-reader/app.phar fixture is unaffected)
+/hello.phar
+/note.phar
+/oop.phar
+/bundle.tar
+/bundle.tar.gz
+/bundle.tar.bz2
diff --git a/.plans/DESIGN_eir_codegen_bugs.md b/.plans/DESIGN_eir_codegen_bugs.md
new file mode 100644
index 0000000000..f923cd7ad8
--- /dev/null
+++ b/.plans/DESIGN_eir_codegen_bugs.md
@@ -0,0 +1,835 @@
+# Design: EIR Lowering / Codegen PHP-Compatibility Bug Fixes (Issues #384, #340, #377, #381, #360)
+
+Status: **Design only — no implementation.** All designs are grounded in the current
+`main` source. Each issue was reproduced locally (see "Reproduction" notes) except
+#340 and #377, whose original symptom has already been fixed on `main` by commits
+`9b5913988` (interpolation) and `00aecd00f` (release overwritten locals by storage
+type); for those two the design targets the **residual gap** that still lets
+related inputs go wrong, plus hardening tests so the regression cannot return.
+
+Active-backend constraints respected throughout:
+- No edits to `src/codegen/expr/`, `src/codegen/stmt/`, `src/codegen/builtins/`
+ (frozen legacy). New semantics go through `src/ir_lower/` and
+ `src/codegen_ir/`.
+- ARM64 **and** x86_64 lowerings for every new codegen path.
+- Every new `emitter.instruction(...)` carries a `//` comment at column 81.
+- New/edited Rust files get a `//!` module preamble and `///` docblocks on every
+ function. Leaf files stay under the 500-LOC cohesion guideline.
+- Runtime cache (`~/.cache/elephc/*.o`) must be invalidated after any runtime
+ emitter change; the design notes this where relevant.
+
+---
+
+## Issue #384 — `match` subject call loses by-reference write-back
+
+### Reproduction
+```
+function bump(&$i) { $i++; return $i; }
+$i = 0;
+echo match (bump($i)) { 1 => 'one', default => 'other' } . '|' . $i;
+```
+PHP prints `one|1`. elephc prints `one|0`.
+
+### Root cause (verified)
+The bug is **not match-specific**. The identical program with
+`echo bump($i) . '|' . $i;` also prints `1|0`, and `switch (bump($i))` prints
+`one|1`. The difference is statement vs. expression scope.
+
+`--emit-ir` (with and without `--no-ir-opt`) for both the bare call and the
+`match` form shows the second read of `$i` lowered as
+`v6: I64 = const_i64 0` — i.e. **the AST-level constant propagator already
+folded `$i` to `0` before EIR lowering**. The generated assembly
+(`/tmp/t384b.s:4073`) correctly passes `sub x0, x29, #120` (the address of
+`$i`'s slot) to `_fn_bump`, and `_fn_bump` correctly does
+`ldr x0, [x9]` / `str x0, [x9]` through that pointer — so the *runtime*
+write-back works. The folded `0` for the later read simply never observes it.
+
+The folding happens in `src/optimize/propagate/expr.rs:52`:
+
+```rust
+ExprKind::BinaryOp { left, op, right } => ExprKind::BinaryOp {
+ left: Box::new(propagate_expr(*left, env)),
+ op,
+ right: Box::new(propagate_expr(*right, env)), // <-- same env
+},
+```
+
+The right operand is propagated with the **same `env`** as the left. A
+`FunctionCall` left operand (`bump($i)`) returns `None` from
+`expr_local_writes` ("may write anything"), but that write-set is consulted
+only at **statement** boundaries (`stmt_local_writes`). Inside one expression
+the propagator never invalidates variables that the left subexpression may
+have mutated. `ExprKind::Match` (`propagate_expr` at line 111) propagates the
+subject and each arm with the same `env` too, so the arms see the pre-subject
+value of `$i`.
+
+`switch` works only because the `echo '|' . $i;` is a *separate statement*
+after the switch: `stmt_local_writes(switch_stmt)` returns `None` (the
+subject contains a call), so the next statement starts with a fully
+invalidated env.
+
+### Spec
+- **PHP behavior**: By-reference parameter mutation is a visible side effect
+ on the caller's variable. Any subsequent read of that variable — in the
+ same expression or a later statement — must observe the mutated value.
+ Evaluation order is left-to-right for `BinaryOp`, subject-before-arms for
+ `match`, condition-before-body for `??`/`?:`, callee-before-args for calls.
+- **elephc current**: Constant propagation within a single expression reuses
+ one `env` across all subexpressions, so a by-ref call followed by a read of
+ the mutated variable in the same expression folds to the stale constant.
+- **Fix target**: Intra-expression propagation must invalidate every variable
+ that a *may-write* subexpression can mutate, *before* propagating the next
+ subexpression in source/evaluation order. This is the general fix; `match`
+ is one beneficiary.
+
+### Architecture
+
+**Primary file**: `src/optimize/propagate/expr.rs` (rewrite of
+`propagate_expr` to thread a *write-invalidate* accumulator through
+evaluation order).
+
+New helper in the same file (or a small new `src/optimize/propagate/order.rs`
+if `expr.rs` would exceed the soft 500-LOC limit after the change — current
+`expr.rs` is 439 LOC, so an in-place edit is preferred):
+
+```rust
+/// Returns the set of locals a subexpression may write, or `None` when it
+/// may write anything (conservative). Mirrors `expr_local_writes` but is
+/// kept in sync with the order in which `propagate_expr` visits children.
+fn expr_may_write_locals(expr: &Expr) -> Option>
+```
+
+`propagate_expr` gains an internal recursive form that threads a mutable
+`ConstantEnv`:
+
+```rust
+fn propagate_expr_ordered(expr: Expr, env: &mut ConstantEnv) -> Expr
+```
+
+For each composite variant, after propagating the left/first child with the
+current `env`, compute `expr_may_write_locals(child)`:
+- `Some(set)` → remove every name in `set` from `env` before propagating the
+ next child.
+- `None` → clear `env` entirely (unknown writes) before propagating the next
+ child.
+
+Variants that must invalidate between children, in evaluation order:
+- `BinaryOp { left, op, right }` — invalidate after `left`, before `right`.
+ (PHP evaluates `left` then `right` for all binary ops including `&&`/`||`,
+ which already short-circuit in `propagate_expr` via their own arms — those
+ keep their existing handling and just additionally invalidate.)
+- `Match { subject, arms, default }` — invalidate after `subject`; each arm
+ body is propagated in its own fresh env (arms are mutually exclusive, so a
+ write in arm 1 must not be visible to arm 2).
+- `Assignment { value, result_target, prelude, conditional_value_temp, .. }`
+ — the `prelude` (by-ref setup) and `value` already mutate the target; keep
+ existing handling but invalidate the target name after the value is
+ propagated, before any `result_target` read.
+- `NullCoalesce { value, default }`, `Ternary { .. }`,
+ `ShortTernary { .. }` — invalidate after the condition/value, before the
+ alternative (the alternative only runs when the first didn't, but
+ conservatively the first may have written before deciding).
+- `Pipe { value, callable }` — invalidate after `value`.
+- `PreIncrement`/`PostIncrement`/`PreDecrement`/`PostDecrement` (as
+ expression operands) — invalidate the named variable after the operand,
+ before any surrounding continuation. (These already propagate as their own
+ variant; the change is that a *parent* binary/match invalidates after them.)
+- Function/method/constructor call variants (`FunctionCall`, `MethodCall`,
+ `NullsafeMethodCall`, `StaticMethodCall`, `ExprCall`, `ClosureCall`,
+ `NewObject`, `NewDynamic`, `NewDynamicObject`, `NewScopedObject`) — these
+ already return `None` from `expr_local_writes`; the ordered propagator
+ treats them as `None` writers and clears the env for any following sibling.
+
+**Optimizer effect modeling** (`src/optimize/effects.rs` and
+`src/optimize/effects/calls.rs`): no change required — `function_call_effect`
+already sets `with_side_effects()`, which is what makes
+`expr_local_writes` return `None`. The fix only changes *propagation*, not
+*effect modeling*.
+
+**Tests**: `src/optimize/tests/propagate/straight_line.rs` and
+`src/optimize/tests/propagate/loops/foreach_loops.rs` get new unit tests
+asserting the AST after propagation keeps the second read as a `Variable`
+(not a literal) when a preceding call may write it. Codegen regression test
+in `tests/codegen/callables/language_features.rs` (or a new
+`tests/codegen/by_ref/intra_expr_writeback.rs`) with the issue's exact
+program plus the bare-call and switch variants.
+
+### Test plan
+| # | PHP source | Expected | Covers |
+|---|---|---|---|
+| 1 | `function bump(&$i){$i++;return $i;} $i=0; echo match(bump($i)){1=>'one',default=>'other'}.'|'.$i;` | `one\|1` | Issue #384 exact |
+| 2 | `function bump(&$i){$i++;return $i;} $i=0; echo bump($i).'|'.$i;` | `1\|1` | Bare call (same root cause) |
+| 3 | `function bump(&$i){$i++;return $i;} $i=0; switch(bump($i)){case 1: echo 'one'; break; default: echo 'other';} echo '|'.$i;` | `one\|1` | Switch (must keep working) |
+| 4 | `function add(&$a,$v){$a[]=$v;return count($a);} $a=[]; echo add($a,1).$a[0];` | `11` | Array append by-ref + read in same expr |
+| 5 | `$i=0; echo ($i++).'|'.$i;` | `0\|1` | Post-increment in same expression |
+| 6 | `function b(&$x){$x=5;return $x;} $i=0; $r = b($i) + $i; echo $r;` | `10` | Binary op + by-ref |
+| 7 | `function b(&$x){$x=5;return 1;} $i=0; echo match(b($i)){1=>$i,default=>0};` | `5` | Match arm reads mutated var |
+| 8 | `$i=5; echo $i . ($i=0) . $i;` | `500` | Assignment mid-expression (PHP eval order) |
+
+Edge: `echo bump($i) . bump($i);` with `$i=0` → PHP: `12` (first call makes
+`$i=1` returns `1`; second makes `$i=2` returns `2`). Validates that repeated
+calls in the same expression each invalidate.
+
+### Risk assessment
+- **High-value risk**: over-invalidation could suppress *legitimate* folding
+ and regress optimized output size/speed. Mitigation: the invalidation only
+ fires for `None`/non-empty write-sets; pure subexpressions (literals,
+ variable reads, pure builtins) return `Some(empty)` and don't invalidate.
+- **Short-circuit ops**: `&&`/`||`/`??` only evaluate the right side
+ conditionally; invalidating before the right side is still safe (the right
+ side runs only when the left didn't write, so clearing is conservative, not
+ wrong). Verify with `$i=1; $r = ($i=0) && ($i=2); echo $i;` → PHP `0`.
+- **Regression watch**: existing propagation tests in
+ `src/optimize/tests/propagate/**` and `tests/codegen/optimizer/**` must stay
+ green. The `--no-ir-opt` IR for the issue program must show the second `$i`
+ read as `load_local`, not `const_i64`.
+
+---
+
+## Issue #340 — String interpolation with array access emits corrupted output
+
+### Reproduction
+The issue's exact input `echo "{$a['x']}";` now compiles and prints `ok` on
+`main` (fixed by `9b5913988 fix(lexer): support complex and simple string
+interpolation forms`). Residual gaps found while grounding this design:
+
+1. **Deprecated `${...}` form** (PHP 8.2 deprecation, still functional):
+ `echo "${a['x']}";` → PHP prints `ok` (with deprecation notice); elephc
+ emits the literal text `${a['x']}` (the `${` form is never recognized —
+ `src/lexer/literals/strings.rs:140` only matches `{$`).
+2. **Heredoc with a `}` inside a quoted key**: works for `"{$a['x']}"` but
+ the brace/quote scanner in `capture_braced_expr` (line 289) is hand-rolled;
+ it does not understand PHP string *escapes* inside the embedded string
+ (only raw `\\` and matching quote). A key like `"{$a['x\'']}"` would
+ confuse the depth counter. PHP rejects this as a parse error, so elephc
+ should too — but it currently mis-captures rather than erroring cleanly.
+
+The original `{{['x']}` symptom was the `{$a['x']}` form before
+`9b5913988`; the design below locks in the fix and covers the residual
+`${...}` and escape-balancing gaps.
+
+### Spec
+- **PHP behavior**: Inside a double-quoted string or heredoc body:
+ - `{$expr}` — complex interpolation; `$expr` is any PHP expression that
+ *starts with `$`* (a variable, `$this`, `${...}`, or a callable/array
+ access chain on one). The braces close at the matching `}`, with
+ balanced nested braces and string literals skipped verbatim.
+ - `${name}` (deprecated since PHP 8.2) — simple variable lookup of
+ `$name`; PHP emits `E_DEPRECATED` and treats it as `$name` (not an
+ arbitrary expression; `${a['x']}` is special: PHP reads it as
+ `$a['x']` for legacy compatibility — see PHP's `ZEND_COMPILE_*`
+ `${...}` rule).
+- **elephc current**: `{$expr}` is handled by `capture_braced_expr` +
+ `tokenize_fragment` (re-lexes the captured text as `prop}`.
+ `${name}` is not recognized at all (emitted literally).
+- **Fix target**:
+ 1. Keep `{$expr}` handling as-is (regression-protected by new tests).
+ 2. Add `${name}` (deprecated) handling: recognize `${` at the start of an
+ interpolation, emit a deprecation warning through the existing warning
+ channel (`src/types/warnings/`), and parse the contents as a variable
+ name with optional single-level `[offset]` (matching PHP's legacy
+ `${a['x']}` semantics, which is *not* a full expression — only
+ `$var` or `$var[offset]`).
+ 3. Harden `capture_braced_expr` to reject unbalanced/unterminated inputs
+ with the existing `"Unterminated complex interpolation '{$...}'"` error
+ rather than silently mis-capturing.
+
+### Architecture
+
+**Primary file**: `src/lexer/literals/strings.rs`.
+
+- In `interpolate` (line 112), add a new arm before the `Some('$') =>` arm:
+
+ ```rust
+ Some('$') if input.peek_nth(1) == Some('{') => {
+ input.advance_escape(); // consume '$'
+ input.advance_escape(); // consume '{'
+ let inner = capture_dollar_brace_var(input, span)?;
+ has_interpolation = true;
+ // emit a deprecation warning via the lexer's warning sink
+ // ... build token stream for `$var` or `$var[offset]` ...
+ push_interp_part(&mut tokens, &mut current, part, span);
+ }
+ ```
+
+- New function `capture_dollar_brace_var` in the same file: parses only
+ `$name`, `$name[offset]` (with `$name`, integer, or bareword offset —
+ reusing `append_simple_offset_key`), and the closing `}`. Anything more
+ complex (e.g. `${a->prop}`, `${func()}`) is a PHP parse error; emit a
+ `CompileError` with a clear message rather than mis-capturing. This keeps
+ the new code under the 500-LOC guideline (the file is currently ~722 LOC
+ and a *single cohesive feature* — string scanning — so it already lives
+ above the soft limit legitimately; adding ~40 LOC stays within the
+ "cohesive leaf" exception).
+
+- Deprecation warning: the lexer does not currently emit warnings; the
+ existing `CompileError` is error-only. Wire a `DiagnosticSink`-style
+ warning through the lexer's return path, or defer the warning to the type
+ checker by tagging the token with a `DeprecatedInterpolation` marker.
+ **Preferred minimal approach**: emit the warning during
+ `src/types/warnings/expr_reads.rs` (which already walks interpolation
+ expression nodes) by detecting a new `ExprKind::DeprecatedDollarBrace`
+ wrapper. Add that wrapper in `src/parser/ast/expr.rs`, parse it in
+ `src/parser/expr/prefix_complex.rs`'s interpolation-token consumer, and
+ lower it in `src/ir_lower/expr/mod.rs` as the underlying `$var[offset]`.
+
+- `capture_braced_expr` hardening: after the capture loop, if `depth != 0`
+ is already caught (EOF → error). Add an additional check: if the captured
+ `inner` re-lex fails (`tokenize_fragment` returns an error), propagate the
+ error instead of pushing a malformed part.
+
+**Parser**: `src/parser/expr/prefix_complex.rs` — the interpolation token
+stream consumer already handles `Variable` + `ArrayAccess`; the new
+`DeprecatedDollarBrace` node is a single new variant consumed there.
+
+**EIR lowering**: `src/ir_lower/expr/mod.rs:90` dispatch — add
+`ExprKind::DeprecatedDollarBrace { .. } => lower_deprecated_dollar_brace(...)`
+that simply delegates to `lower_array_access` / `lower_expr(Variable)`.
+No new EIR op needed.
+
+**No codegen change** — interpolation lowers to normal `Op::StrInterpolate`
+plus the inner expression's ops, which already work.
+
+### Test plan
+| # | PHP source | Expected | Covers |
+|---|---|---|---|
+| 1 | `$a=['x'=>'ok']; echo "{$a['x']}";` | `ok` | Original issue (regression lock) |
+| 2 | `$a=['x'=>'ok']; echo "{$a["x"]}";` | `ok` | Double-quoted key inside braces |
+| 3 | `$a=[['x'=>'deep']]; echo "{$a[0]['x']}";` | `deep` | Nested array access |
+| 4 | `$a=['x'=>'ok']; echo "${a['x']}";` | `ok` (+ deprecation warning) | `${...}` legacy form |
+| 5 | `$a=['k'=>1]; echo "${a['k']}";` | `1` | `${...}` int value |
+| 6 | `$a=['x'=>'ok']; echo "pre {$a['x']} post";` | `pre ok post` | Interpolation with surrounding text |
+| 7 | `$a=['x'=>'ok']; echo "{$a['x']}{$a['x']}";` | `okok` | Two interpolations in one string |
+| 8 | `echo "{$a['x']}";` with `$a` undefined | PHP: empty + notice | Undefined-key interpolation (elephc may fatal; document) |
+| 9 | Lexer error test: `echo "{$a['x']";` (unbalanced) | `Unterminated complex interpolation` | Hardened error path |
+
+Error tests go in `tests/error_tests/` (new `interpolation.rs`); codegen
+tests extend `tests/codegen/strings/interpolation_and_hashes.rs`.
+
+### Risk assessment
+- **Deprecation warning plumbing** is the most invasive part. If wiring a
+ warning through the lexer is too costly, the minimal alternative is to
+ *accept* `${name}` silently (matching the *value* PHP produces) and defer
+ the deprecation notice to a follow-up. This still fixes the corruption
+ (literal `${a['x']}` output) without the warning sink.
+- **Parse-error parity**: PHP's `${...}` accepts only a narrow subset; any
+ deviation must produce a clean error, not silent mis-capture. New error
+ tests (#9) guard this.
+- **Regression watch**: existing interpolation tests in
+ `tests/codegen/strings/interpolation_and_hashes.rs` and
+ `tests/parser_tests/**` must stay green.
+
+---
+
+## Issue #377 — Numeric loop variable that becomes float leaks heap
+
+### Reproduction
+On current `main` the exact issue program (`$i = 0; $i = $i + 1.0` for 2M
+iterations) runs to completion with the correct result `1999999000000` in
+both debug and release (verified locally). Commit `00aecd00f fix(ir): release
+overwritten locals by storage type` closed the leak for the common path.
+
+**Residual gap verified during grounding**: the slot-widening logic in
+`src/ir/builder.rs:385 widened_local_storage_type` promotes
+`Int → Float` storage to `Mixed` (boxed). The cleanup load in
+`src/ir_lower/context.rs:594 release_stored_local_value` then loads the slot
+as `Heap(Mixed)` and calls `Op::Release` even on the very first reassignment,
+when the slot still holds a *raw unboxed int* (the `store_local v1 slot[1]`
+in the loop preheader stores an `I64` into the not-yet-widened slot). The
+runtime `__rt_decref_mixed` (in
+`src/codegen/runtime/arrays/decref_mixed.rs:42`) treats the raw int bits as
+a "pointer" and skips the decref because `0`/small ints are below
+`_heap_buf`. This is *safe* (no crash) but means a raw-int slot can be
+released as Mixed without effect, while a subsequently-stored boxed float
+*is* released correctly on iteration 2+. The net leak on `main` is at most
+one cell per widening transition — small, but it generalizes to other
+type-widening transitions (`Bool → Float`, `Int → Str`, etc.) where the
+first reassignment releases a non-boxed old value as if it were boxed.
+
+### Spec
+- **PHP behavior**: Reassigning a variable that changes its runtime type
+ (int → float, int → string, etc.) must free the previous value's storage
+ exactly once. No leak across long loops.
+- **elephc current**: `release_stored_local_value` uses the slot's
+ *widened storage type* (`local_php_type(slot)`) to load and release the
+ previous occupant. When the previous occupant was stored before the
+ slot was widened (raw int/bool), the release is a no-op for that
+ transition; for transitions into refcounted storage it can leak the first
+ old value.
+- **Fix target**: Release the previous occupant using the *type the slot
+ held when the previous store happened*, not the post-widening storage
+ type. Concretely: track the previous logical type (`previous_type`
+ captured at `store_local:617`) and use it to decide whether the old value
+ is refcounted *before* the widening. If `previous_type` is non-refcounted
+ (`Int`/`Bool`/`Float`/`Void`), skip the release entirely — there is no
+ heap cell to free.
+
+### Architecture
+
+**Primary file**: `src/ir_lower/context.rs`.
+
+Change `release_stored_local_value` (line 594) to take the *previous logical
+type* as an argument, and skip the release when that type does not need
+lifetime tracking:
+
+```rust
+fn release_stored_local_value_with_previous_type(
+ &mut self,
+ name: &str,
+ slot: LocalSlotId,
+ previous_type: PhpType,
+ span: Option,
+) {
+ if !Ownership::php_type_needs_lifetime_tracking(&previous_type) {
+ return; // old occupant was a raw scalar; nothing to free
+ }
+ let previous = self.load_local_storage(name, slot, previous_type, span);
+ crate::ir_lower::ownership::release_if_owned(self, previous, span);
+}
+```
+
+Update the three call sites in `store_local` (lines 633, 642, 656) and the
+epilogue path (line 948) to pass `previous_type` (or `local_php_type(slot)`
+for the epilogue, which is correct there because the epilogue runs after all
+stores have widened the slot to its final type and boxed every occupant).
+
+**No EIR-op change**, no runtime change, no codegen change. This is purely a
+lowering-side type-accuracy fix.
+
+**Tests**: `src/ir_lower/tests/ownership.rs` — add a unit test that builds a
+function with an `Int`-then-`Float` store to the same slot and asserts the
+EIR contains exactly one `release` (for the boxed float on iteration 2+),
+not a release of the raw int. End-to-end test in
+`tests/codegen/runtime_gc/` (new `type_widening_loop.rs`) running the issue
+program at 2M iterations with an assertion on the result and a *memory-debug*
+run (`ELEPHC_HEAP_DEBUG=1`) asserting no leak is reported.
+
+### Test plan
+| # | PHP source | Expected | Covers |
+|---|---|---|---|
+| 1 | `$acc=0.0; for($i=0;$i<2000000;$i=$i+1.0){$acc=$acc+$i;} echo $acc;` | `1999999000000` | Issue #377 exact (2M, no OOM) |
+| 2 | `$acc=0; for($i=0;$i<2000000;$i=$i+1){$acc+=$i;} echo $acc;` | `1999999000000` | Pure int loop (no widening, must not regress) |
+| 3 | `$s=''; for($i=0;$i<100000;$i=$i+1){$s=$s.'x';} echo strlen($s);` | `100000` | Int→string reassignment, no leak |
+| 4 | `for($i=0;$i<100000;$i++){ $x = ($i%2==0) ? 1 : 1.5; } echo $x;` | `1.5` | Alternating int/float reassignment |
+| 5 | `$x=1; $x=2.0; $x=3; echo $x;` | `3` | Single int→float→int transition |
+| 6 | `ELEPHC_HEAP_DEBUG=1` run of #1 | no leak report | Heap-debug instrumentation |
+
+### Risk assessment
+- **Risk**: skipping the release for a non-refcounted `previous_type` could
+ *under*-free if a previous store actually boxed the value (e.g. a prior
+ path stored a Mixed into the slot before this straight-line store). The
+ `previous_type` captured at line 617 is the *logical* type from the last
+ `set_local_type`, which is updated on every store, so it tracks what was
+ actually stored. The epilogue path keeps the conservative storage-type
+ release as a backstop.
+- **Regression watch**: `tests/codegen/runtime_gc/**`, `tests/codegen/arrays/**`,
+ and the existing ownership unit tests must stay green. Run with
+ `ELEPHC_HEAP_DEBUG=1` locally on tests #1–#4.
+
+---
+
+## Issue #381 — `foreach` over arrays visits appended elements during value iteration
+
+### Reproduction
+```
+$a = [1, 2];
+foreach ($a as $v) { echo $v; if ($v === 1) $a[] = 3; }
+echo '|' . count($a);
+```
+PHP: `12|3`. elephc: `123|3`.
+
+### Root cause (verified)
+`src/codegen_ir/lower_inst/iterators.rs:1183 lower_indexed_iter_next_aarch64`
+(and the x86_64 twin at line 1202) re-reads the array length **every
+iteration** from the live array header:
+
+```
+load_at_offset_scratch array_reg, [offset-0], "x9" ; reload source pointer
+load_at_offset index_reg, [offset-8] ; cursor
+add index_reg, index_reg, #1
+emit_load_from_address len_reg, array_reg, 0 ; <-- fresh length each iter
+cmp index_reg, len_reg
+```
+
+PHP snapshots the array's length (and, for associative arrays, the entry
+count) at the moment `foreach` begins iterating; appends during the loop
+body do not extend the iteration. The current lowering treats the array as
+live and re-reads its current length, so an append inside the body is
+visible to the next `IterNext`.
+
+The hash path (`__rt_hash_iter_next`, line 1225) is a runtime helper that
+already advances a cursor against a snapshot taken at `IterStart` (the hash
+iterator stores its own end pointer), so the hash case does **not** have this
+bug — only the indexed-array fast path does.
+
+### Spec
+- **PHP behavior**: `foreach ($arr as $v)` captures the array's element count
+ at loop entry. Appends (`$arr[] = …`), `$arr[count($arr)] = …`, and
+ `array_push` inside the body do not add new elements to the iteration.
+ Modifications to *existing* elements that change values are visible (PHP's
+ semantics are nuanced, but the *count* is frozen). Unset of the current
+ element is handled separately (PHP keeps a copy of the current value).
+- **elephc current**: indexed-array `IterNext` re-reads the length each
+ iteration → appends are visited.
+- **Fix target**: `IterStart` for an indexed array must snapshot the array's
+ initial length into the iterator state. `IterNext` compares the cursor
+ against the **snapshotted** length, not the live array header.
+
+### Architecture
+
+**Primary file**: `src/codegen_ir/lower_inst/iterators.rs`.
+
+1. **Add a length slot to the iterator state**:
+ - In `src/codegen_ir/value_placement.rs`, bump
+ `ITERATOR_STATE_BYTES` from `64` to `72` (one extra 8-byte word).
+ - In `src/codegen_ir/lower_inst/iterators.rs`, add
+ `const ITER_LENGTH_OFFSET_DELTA: usize = 64;` after
+ `ITER_VALUE_ADDR_OFFSET_DELTA` (line 30). This slot is only used by
+ the indexed-array path; the hash path keeps using
+ `__rt_hash_iter_next` and ignores it.
+
+2. **`lower_iter_start`** (line 48): for `IteratorSourceKind::Indexed { .. }`,
+ after storing the source pointer and the initial cursor (`-1`), also load
+ the array's current length (`emit_load_from_address(emitter, result_reg,
+ source_reg, 0)`) and store it at `offset - ITER_LENGTH_OFFSET_DELTA`.
+ This is the snapshot. ARM64 and x86_64 paths each get a 2-instruction
+ addition with column-81 comments.
+
+3. **`lower_indexed_iter_next_aarch64`** (line 1183) and
+ `lower_indexed_iter_next_x86_64` (line 1202): replace the fresh length
+ load with a load from the snapshot slot:
+ - Remove: `emit_load_from_address(len_reg, array_reg, 0)`
+ - Add: `load_at_offset(emitter, len_reg, offset - ITER_LENGTH_OFFSET_DELTA)`
+ The `array_reg` load is still needed because the value loader
+ (`load_current_array_value_*`) reads the element from `[array_reg,
+ element_offset]`. Keep that load.
+
+4. **Dynamic iterable/mixed indexed paths**
+ (`initialize_dynamic_iterable_iterator`,
+ `initialize_dynamic_mixed_iterator`): these already store the cursor;
+ add the same length snapshot for the indexed branch. The hash and object
+ branches leave the length slot uninitialized (the indexed `IterNext` is
+ the only reader, and the dynamic dispatch routes hash/object to their own
+ `IterNext` lowering, so an uninitialized length slot is never read on
+ those paths).
+
+5. **By-reference foreach**: PHP's `foreach ($a as &$v)` does **not** use the
+ by-value length snapshot for indexed arrays; appended elements remain
+ visible to the live iteration. Preserve that by making `IterNext` consult
+ the `IterStart` by-ref flag and read the live array length on indexed by-ref
+ paths. Verify with a regression test.
+
+**No EIR-op change**, no `src/ir_lower/stmt/mod.rs` change. The EIR
+`Op::IterStart`/`Op::IterNext` keep their existing operand lists; the
+length snapshot is a codegen-internal detail of the indexed-array lowering.
+
+**Runtime cache**: the iterator state size changes from 64 to 72 bytes. This
+is a frame-layout change internal to each function; no runtime `.o` is
+affected, so `~/.cache/elephc/*.o` does **not** need clearing. (Document
+this in the PR description anyway.)
+
+### Test plan
+| # | PHP source | Expected | Covers |
+|---|---|---|---|
+| 1 | `$a=[1,2]; foreach($a as $v){echo $v; if($v===1)$a[]=3;} echo '|'.count($a);` | `12\|3` | Issue #381 exact |
+| 2 | `$a=[1,2,3]; foreach($a as $v){echo $v; $a[]=9;} echo '|'.count($a);` | `123\|6` | Multiple appends |
+| 3 | `$a=[1,2]; foreach($a as &$v){echo $v; if($v===1)$a[]=3;} echo '|'.count($a);` | `123\|3` | By-ref foreach live length |
+| 4 | `$a=range(1,1000); $c=0; foreach($a as $v){$c++; $a[]=$v;} echo $c;` | `1000` | Large loop with appends, exact count |
+| 5 | `$a=[1,2,3]; foreach($a as $k=>$v){echo "$k=$v,"; $a[]=$k;} echo count($a);` | `0=1,1=2,2=3,6` | Key+value foreach with appends |
+| 6 | `$a=['a'=>1,'b'=>2]; foreach($a as $k=>$v){echo "$k=$v,"; $a['c']=3;} echo count($a);` | `a=1,b=2,3` | Hash foreach (already correct, lock in) |
+| 7 | `$a=[1,2]; foreach($a as $v){echo $v; unset($a[0]);}` | PHP: `12` | Unset during foreach (current behavior, document) |
+
+Codegen tests in `tests/codegen/arrays/foreach_snapshot.rs` (new file, with
+module preamble).
+
+### Risk assessment
+- **Frame-size change** (64→72 bytes per iterator): the
+ `allocates_iter_start_value_as_iterator_state` unit test in
+ `src/codegen_ir/value_placement.rs` hard-codes the slot offset; update it.
+ Any other test asserting iterator state size must be updated.
+- **Uninitialized length slot** on hash/object paths: confirm via the
+ validator that no `IterNext` on those paths reads
+ `ITER_LENGTH_OFFSET_DELTA`. Add a debug assertion in
+ `lower_indexed_iter_next_*` that the source kind is `Indexed` (already
+ guaranteed by the dispatch in `lower_iter_next` line 124).
+- **Regression watch**: all existing `tests/codegen/arrays/foreach*.rs` and
+ `src/ir_lower/tests/arrays.rs` tests. Run the foreach key-write tests
+ specifically (`tests/codegen/arrays/foreach_key_write.rs`) since they
+ exercise the same iterator state layout.
+
+---
+
+## Issue #360 — Array elements cannot be passed to by-reference parameters
+
+### Reproduction
+```
+function bump(&$x) { $x++; }
+$a = [5];
+bump($a[0]);
+echo $a[0];
+```
+PHP: `6`. elephc: `parameter $x must be passed a variable` (compile error).
+
+### Root cause (verified)
+Two distinct gaps:
+
+1. **Checker rejects valid lvalues**:
+ - `src/types/checker/functions/call_validation.rs:332-347` rejects any
+ by-ref argument that is not `ExprKind::Variable(_)`:
+ ```rust
+ if sig.ref_params.get(param_idx).copied().unwrap_or(false)
+ && !matches!(arg.kind, ExprKind::Variable(_))
+ { return Err("... must be passed a variable"); }
+ ```
+ - The same check is duplicated in
+ `src/types/checker/functions/resolution/mod.rs:293-308` and `:527-542`
+ for already-resolved callees.
+ - PHP allows as by-ref targets: `$var`, `$a[idx]`, `$a[]` (append),
+ `$a[$i]`, `$o->prop`, `$$var`, and `$a[idx][idx2]` (nested). PHP 8
+ also accepts `$a[]` (append) as a write target for by-ref.
+
+2. **EIR lowering has no by-ref array-element path**:
+ - `src/ir_lower/expr/mod.rs:4315 lower_by_ref_array_arg_with_signature`
+ only handles the *array-widening* case (converting `Array(Int)` to
+ `Array(Mixed)` before a Mixed ref param). It does **not** pass the
+ element's address.
+ - For a plain `$a[0]` argument to a by-ref param, `lower_arg_with_signature`
+ (line 4240) falls through to `lower_expr(arg)`, which emits an
+ `Op::ArrayGet` (a *read*) and passes the value. The codegen
+ `plan_ref_arg_writebacks` (`src/codegen_ir/lower_inst.rs:5194`) only
+ plans writebacks for **Mixed-typed parameters** with scalar sources, so
+ an `Int`-typed element passed to an `Int` by-ref param has no writeback
+ and no address passing — even if the checker accepted it, the mutation
+ would be lost.
+
+### Spec
+- **PHP behavior**: A by-reference parameter accepts any lvalue: a
+ variable, an array element (`$a[0]`, `$a['k']`, `$a[$i]`, `$a[]`), a
+ nested array element, or an object property. The callee mutates the
+ *caller's storage*. For a packed array with unboxed scalar elements, the
+ callee mutates the element slot in place; for a Mixed/assoc array, the
+ callee mutates the boxed cell. `$a[]` as a by-ref target appends a new
+ element (default `null`) and passes its address.
+- **elephc current**: Checker rejects non-variable lvalues; lowering has no
+ element-address path.
+- **Fix target**:
+ 1. Checker accepts `ExprKind::ArrayAccess { array: Variable, .. }` (and
+ nested) and `ExprKind::PropertyAccess { .. }` as by-ref targets, with
+ the existing "must be passed a variable" error retained only for
+ non-lvalue expressions (literals, calls, binary ops, constants).
+ 2. EIR lowering passes the *address* of the array element (or property
+ slot) to the callee, and the codegen writeback path copies any boxed
+ cell back into the array element after the call.
+
+### Architecture
+
+This is the largest of the five fixes. It splits into the checker change
+(small, unblocks the error) and the lowering/codegen change (substantial,
+actually makes the mutation work).
+
+**Phase A — Checker (unblock the error)**
+
+Files: `src/types/checker/functions/call_validation.rs`,
+`src/types/checker/functions/resolution/mod.rs`.
+
+Introduce a shared predicate so the three duplicated checks stay in lockstep
+(per the AGENTS.md "do not maintain parallel tables" rule):
+
+```rust
+// in src/types/checker/functions/mod.rs or a new lvalues.rs
+pub(super) fn is_by_ref_lvalue(expr: &Expr) -> bool {
+ match &expr.kind {
+ ExprKind::Variable(_) => true,
+ ExprKind::ArrayAccess { array, .. } => is_by_ref_lvalue_base(array),
+ ExprKind::PropertyAccess { object, .. } => is_by_ref_lvalue_base(object),
+ ExprKind::DynamicPropertyAccess { object, .. } => is_by_ref_lvalue_base(object),
+ _ => false,
+ }
+}
+fn is_by_ref_lvalue_base(expr: &Expr) -> bool {
+ matches!(expr.kind,
+ ExprKind::Variable(_)
+ | ExprKind::ArrayAccess { .. }
+ | ExprKind::PropertyAccess { .. }
+ | ExprKind::DynamicPropertyAccess { .. })
+}
+```
+
+Replace the three `!matches!(arg.kind, ExprKind::Variable(_))` checks with
+`!is_by_ref_lvalue(arg)`. The error message can stay "must be passed a
+variable" (PHP's own message) or be widened to "must be passed a variable or
+array element" — match PHP's wording in tests.
+
+**Phase B — EIR lowering (make it work)**
+
+The hard part: passing the address of `$a[0]`. Packed arrays in elephc store
+unboxed scalar elements inline (`Array(Int)` → contiguous 8-byte slots);
+Mixed/assoc arrays store boxed `Mixed` cells. The by-ref mechanism already
+has two paths in `src/codegen_ir/lower_inst.rs`:
+- `materialize_local_ref_arg_address` (line 5388): emits the *frame slot
+ address* of a local variable. This is what makes `bump($i)` work for
+ scalar by-ref params.
+- `materialize_temporary_ref_arg_cell` (line 5278): allocates a heap
+ ref-cell, copies the value in, passes the cell pointer, and
+ `emit_ref_arg_writebacks` copies back.
+
+For `$a[0]`:
+- **Packed scalar array**: the element lives at
+ `array_header + 16 + index * 8` (header is 16 bytes: length + capacity).
+ The address is `array_ptr + 16 + index*8`. We can compute this and pass
+ it directly — the callee reads/writes through that pointer exactly like
+ it does for a local slot. No writeback needed because the write goes
+ directly into the array storage.
+- **Mixed/assoc array**: the element is a boxed `Mixed` cell allocated on
+ the heap. The element's address is the cell pointer (read from the hash
+ bucket). Pass that pointer; the callee mutates the cell in place. Again
+ no writeback needed because the cell is shared.
+
+So the design is: compute the *element address* and pass it, mirroring
+`materialize_local_ref_arg_address` but with the address derived from the
+array pointer + index instead of a frame slot.
+
+New EIR op (so the codegen can emit target-specific address arithmetic
+without the lowering having to know element byte sizes):
+
+```rust
+// src/ir/instr.rs
+ArrayElementAddr, // operands: [array_ptr, index]; result: I64 pointer
+PropSlotAddr, // operands: [object_ptr, prop_offset]; for Phase B2
+```
+
+`ArrayElementAddr` is `Heap(Array) + Int → I64`. It emits:
+- ARM64: load array ptr, load index, compute `ptr + 16 + index*8` (packed)
+ or call `__rt_hash_element_addr(ptr, key)` for assoc.
+- x86_64: same arithmetic with the x86_64 register conventions.
+
+The lowering (`src/ir_lower/expr/mod.rs`) gets a new by-ref arg path:
+
+```rust
+fn lower_by_ref_element_arg(
+ ctx: &mut LoweringContext<'_, '_>,
+ array: &Expr,
+ index: &Expr,
+ span: Span,
+) -> ValueId {
+ // For a packed array with statically-known element type, emit
+ // Op::ArrayElementAddr(array_ptr, index). For a Mixed/assoc array,
+ // emit the hash-element-address runtime helper. Append targets
+ // ($a[]) lower to "address of the next slot after auto-grow".
+}
+```
+
+`lower_arg_with_signature` (line 4240) gains, before the `lower_expr(arg)`
+fallthrough:
+
+```rust
+if sig.ref_params.get(index).copied().unwrap_or(false) {
+ if let ExprKind::ArrayAccess { array, index: idx } = &arg.kind {
+ return lower_by_ref_element_arg(ctx, array, idx, arg.span);
+ }
+ if let ExprKind::PropertyAccess { .. } = &arg.kind {
+ return lower_by_ref_property_arg(ctx, arg); // Phase B2
+ }
+}
+```
+
+**Codegen**: `src/codegen_ir/lower_inst.rs` — add `Op::ArrayElementAddr =>`
+to the dispatch (line ~155) routing to a new
+`src/codegen_ir/lower_inst/arrays.rs::lower_array_element_addr` (or reuse
+`arrays.rs` if it has a natural home). This emits the address arithmetic
+per target, with column-81 comments on every instruction.
+
+**Append target `$a[]`**: this is a by-ref write target in PHP. Lower it as
+"ensure capacity for one more, return address of the new slot." Reuse the
+existing array-grow runtime helpers (`__rt_array_push_*`). The new element
+is initialized to `null`/`0` before the callee runs.
+
+**Writeback**: for the direct-address path, **no writeback** is needed — the
+callee writes through the pointer into the array storage. The existing
+`plan_ref_arg_writebacks` Mixed-cell writeback path is bypassed for these
+args (the source value is an `ArrayElementAddr` result, not a `LoadLocal`,
+so `local_ref_arg_source` fails and `materialize_temporary_ref_arg_cell` is
+skipped via a new guard that recognizes the element-address opcode).
+
+**Phase B2 — Object properties** (`$o->prop` by-ref): out of scope for this
+fix unless trivial; the issue only mentions array elements. The
+`is_by_ref_lvalue` predicate accepts properties so the checker doesn't
+reject them, but the lowering emits a clear `unsupported` diagnostic for
+property by-ref until a follow-up. Document this in `docs/php/`.
+
+**Tests**:
+- `tests/error_tests/callables.rs`: update the existing tests at lines 232,
+ 244, 257 that assert "must be passed a variable" — those pass non-lvalue
+ args (literals/calls) and must *still* error. Add new tests asserting
+ `$a[0]`, `$a['k']`, `$a[]`, `$a[$i]` are *accepted*.
+- `tests/codegen/callables/by_ref_array_element.rs` (new): the issue program
+ plus nested arrays, append targets, and Mixed-element arrays.
+
+### Test plan
+| # | PHP source | Expected | Covers |
+|---|---|---|---|
+| 1 | `function bump(&$x){$x++;} $a=[5]; bump($a[0]); echo $a[0];` | `6` | Issue #360 exact |
+| 2 | `function bump(&$x){$x++;} $a=['k'=>5]; bump($a['k']); echo $a['k'];` | `6` | String key |
+| 3 | `function bump(&$x){$x++;} $a=[1,2,3]; bump($a[1]); echo implode(',',$a);` | `1,3,3` | Mid-array element |
+| 4 | `function bump(&$x){$x+=10;} $a=[[1],[2]]; bump($a[0][0]); echo $a[0][0];` | `11` | Nested array element |
+| 5 | `function bump(&$x){$x++;} $a=[]; bump($a[]); echo $a[0];` | `1` | Append target (`$a[]`) |
+| 6 | `function s(&$x){$x='changed';} $a=['k'=>'orig']; s($a['k']); echo $a['k'];` | `changed` | String value by-ref |
+| 7 | `function add(&$arr,$v){$arr[]=$v;} $a=[1]; add($a,2); echo implode(',',$a);` | `1,2` | Array by-ref + append (existing path, lock in) |
+| 8 | `function b(&$x){$x++;} b(5);` | error "must be passed a variable" | Non-lvalue still rejected |
+| 9 | `function b(&$x){$x++;} b(foo());` | error | Call result rejected |
+| 10 | `function b(&$x){$x++;} $a=[1]; b($a[0]+1);` | error | Binary op rejected |
+
+### Risk assessment
+- **Highest risk** of the five issues: introduces a new EIR op and a new
+ codegen path for element-address arithmetic on two targets. Element byte
+ sizes (8 for scalars, 16 for TaggedScalar, cell-pointer indirection for
+ Mixed) must be exactly right per `PhpType::stack_size` and the array
+ header layout in `src/codegen/runtime/arrays/`.
+- **Array grow during by-ref**: if the callee appends to the same array it
+ received an element address for, the array may reallocate and invalidate
+ the passed pointer. PHP does not guarantee stability here either (a by-ref
+ array param that grows can move the element), so matching PHP's "best
+ effort" is acceptable. Document.
+- **Mixed-element address**: for `Array(Mixed)`, the element is a boxed
+ cell; passing the cell pointer is correct. For `AssocArray`, the hash
+ bucket's value cell address is needed; a runtime helper
+ `__rt_hash_element_addr(hash, key)` is the clean approach (mirror
+ `__rt_hash_iter_next`'s lookup).
+- **Regression watch**: every existing by-ref test in
+ `tests/codegen/callables/**`, `tests/ir_backend_parity/cases.rs` (the
+ `parity_*_by_ref_*` tests at lines 1518, 1540, 1560), and
+ `tests/error_tests/callables.rs`. The frozen legacy backend must *not* be
+ touched; its by-ref path stays as-is (it may already support array
+ elements — verify parity but do not change it).
+- **Runtime cache**: if a new runtime helper (`__rt_hash_element_addr`) is
+ added, `~/.cache/elephc/*.o` must be cleared before the next compile
+ (`rm -rf ~/.cache/elephc`). Note this in the PR.
+
+---
+
+## Cross-cutting notes
+
+- **Order of implementation**: #340 and #377 are small, low-risk, and can
+ land first (regression locks + residual fixes). #384 is medium (optimizer
+ propagation change). #381 is small-medium (iterator state slot). #360 is
+ large (new EIR op + codegen path); land it last and consider splitting
+ into "Phase A checker accept" and "Phase B lowering" PRs so PHP programs
+ that only need the checker to stop erroring can progress sooner (Phase A
+ alone makes `bump($a[0])` *compile* but the mutation is still lost —
+ document this clearly).
+- **Test policy**: each issue's tests span the required four surfaces
+ (lexer/parser/codegen/error) where applicable, plus an `examples/` entry
+ per AGENTS.md. Suggested examples:
+ - `examples/by_ref/main.php` exercising #384 and #360,
+ - `examples/foreach_snapshot/main.php` for #381,
+ - `examples/interpolation/main.php` for #340,
+ - `examples/type_widening_loop/main.php` for #377.
+- **Docs**: update `docs/php/functions.md` (by-ref parameters), the
+ interpolation section of `docs/php/strings.md`, the foreach section of
+ `docs/php/control-structures.md`, and the optimizer section of
+ `docs/internals/the-optimizer.md` to describe intra-expression
+ write-invalidation.
+- **CI**: rely on the sharded codegen matrix for the full ARM64/x86_64
+ coverage; locally run only the focused filters named in each issue's test
+ plan during implementation.
diff --git a/.plans/eir-00-overview.md b/.plans/eir-00-overview.md
deleted file mode 100644
index 617260585d..0000000000
--- a/.plans/eir-00-overview.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# elephc IR — Overview and Vision
-
-**Document version:** 2026-05-12
-**Author:** Architecture proposal for elephc
-**Target series:** v0.24.x (introduction), v0.25.x+ (optimization passes)
-
----
-
-## Goal
-
-Introduce a **domain-specific intermediate representation** (called **EIR — elephc IR**) between the AST-level optimizer and the assembly emitter, so that:
-
-1. A real **register allocator** can see the entire function and avoid the per-expression spill/reload pattern that currently caps throughput.
-2. **Instruction scheduling**, **CSE**, **LICM**, **peephole over wider windows**, and **inlining** become possible without retrofitting them onto an AST walker that has no notion of basic blocks or value identity.
-3. The codegen pipeline gains a **clean boundary** between *semantic* lowering (AST → IR, preserves PHP semantics) and *physical* lowering (IR → ASM, preserves performance).
-4. The educational character of the project is preserved: ASM is still hand-emitted, every instruction still commented at column 81, every emitter file still readable line-by-line.
-
-## Non-goals
-
-- **Do not** replace the hand-written ASM backend with a third-party crate (Cranelift, LLVM, etc.). EIR is ours.
-- **Do not** redesign `PhpType`, the type checker, the parser, or the runtime. EIR consumes the existing semantic model.
-- **Do not** rewrite the AST-level optimizer (`src/optimize/`) up front. AST-level folding, propagation, and DCE remain; IR-level optimizations are added on top, not in place.
-- **Do not** ship register allocation in the first PR. The first deliverable is a 1:1 lowering with **zero behavior change**.
-- **Do not** introduce a generic IR (SSA-CFG with abstract value semantics like LLVM/CLIF). EIR is **PHP-specific**: it has `MixedBox`, `ArrayCowEnsureUnique`, `Fatal`, ownership state — operations LLVM and Cranelift would never have.
-
-## Why a custom IR over Cranelift
-
-Decided already in the design discussion preceding these plans. Recap:
-
-- **Identity**: the project's value proposition is the educational, fully hand-rolled toolchain. Cranelift dissolves that.
-- **PHP semantics**: Mixed boxing, COW, ownership lattice, exact eval order, fatal vs throw, `__rt_*` runtime calls — all hostile to a generic optimizing IR. They are first-class in EIR.
-- **Migration cost**: Cranelift migration is 6–9 months of refactor work with no visible features. EIR Phase 1 alone is 4–6 weeks and unlocks subsequent optimization phases.
-- **Reversibility**: if we ever want Cranelift, EIR → CLIF is a much smaller hop than AST → CLIF.
-
-## Architecture
-
-```
-PHP source
- → Lexer
- → Parser
- → Magic constants
- → Conditional compilation
- → Resolver
- → NameResolver
- → Constant folding (AST)
- → Type checker / warnings
- → Optimizer passes (AST) ◄── unchanged
- → AST → EIR lowering ◄── NEW
- → EIR passes:
- • validation
- • effect annotation finalization
- • (later) peephole / CSE / LICM / register allocation
- → EIR → ASM emission ◄── replaces direct AST → ASM
- → assembler / linker
- → binary
-```
-
-Two new modules will be introduced under `src/`:
-
-- `src/ir/` — EIR types, builder, validator, printer, passes
-- `src/codegen_ir/` — the new IR-consuming backend (renamed once stable; see Phase 5)
-
-The current `src/codegen/` keeps emitting assembly during the migration. A feature flag selects which pipeline runs. When EIR reaches parity, the legacy path is removed.
-
-## Tech stack
-
-- Rust (existing toolchain, no new dependencies)
-- No external crates required for Phases 1–6
-- Insta or hand-rolled snapshot tests for IR pretty-printer (decide in Phase 02; prefer no new dep)
-- Existing `as` + `ld` test infrastructure remains the final correctness gate
-
-## Phases and deliverables
-
-Each phase is **independently shippable**. After every phase the test suite must pass with `cargo test -- --include-ignored` and no regression in the benchmark harness.
-
-| Phase | Plan | Deliverable | Visible to users? |
-|-------|------|-------------|-------------------|
-| 1 | [01](eir-01-design-spec.md) | EIR design specification document | No |
-| 2 | [02](eir-02-ir-module-skeleton.md) | `src/ir/` module: types, instructions, builder, validator, printer | No |
-| 3 | [03](eir-03-ast-to-ir-lowering.md) | AST → EIR lowering, no optimizations, full test parity through `--emit-ir` | Yes (`--emit-ir` flag) |
-| 4 | [04](eir-04-ir-to-asm-backend.md) | EIR → ASM backend producing equivalent assembly to current codegen | No (parity check only) |
-| 5 | [05](eir-05-switchover-behind-flag.md) | `--ir-backend` flag, then default switchover, then legacy codegen removed | Yes (perf neutral) |
-| 6 | [06](eir-06-linear-scan-register-allocator.md) | Linear-scan register allocator, first real perf gain | Yes (~15–25% perf on compute) |
-| 7 | [07](eir-07-peephole-and-local-opts.md) | IR-level peephole, dead store elimination, identity ops | Yes (~5–10% more) |
-| 8 | [08](eir-08-cse-licm-inlining.md) | CSE, LICM, inlining of small functions | Yes (~10–20% more on loops) |
-| 9 | [09](eir-09-legacy-cleanup.md) | Remove legacy path, consolidate docs, finalize internals chapter | No (cleanup) |
-
-## Definition of done (per phase)
-
-- All existing tests pass (`cargo test`, `cargo test -- --include-ignored`)
-- New tests cover the phase's added surface
-- Benchmark harness shows no regression (or shows the expected gain for Phases 6–8)
-- `cargo build` clean, zero warnings
-- Linux x86_64 and Linux ARM64 verified via `scripts/test-linux-*.sh`
-- `docs/internals/` updated where the change is user-visible internally (e.g., `the-codegen.md`, new `the-ir.md`)
-- Commit history follows project conventions (`feat:`, `refactor:`, no `Co-Authored-By`)
-
-## Risks and mitigations
-
-| Risk | Likelihood | Mitigation |
-|------|-----------|------------|
-| EIR design too abstract / not PHP-shaped | High if rushed | Phase 01 must spec each instruction by walking 10+ real codegen sites |
-| Phase 4 fails to produce byte-identical assembly | High | Phases 4 and 5 do *not* require byte-identical assembly, only semantically equivalent (same `compile_and_run` output). Snapshot tests on stdout, not on `.s` |
-| Phase 3 PR becomes a 20k-line monster | Very high without discipline | Lower AST → IR per AST family in separate sub-tasks within Phase 3 (literals, locals, arithmetic, calls, control flow, classes, etc.) |
-| Register allocator interacts badly with ABI helpers | Medium | Phase 6 starts with caller-saved-only scratch allocation, then expands. ABI helpers in `src/codegen/abi/` are reused, not reinvented |
-| Ownership/refcount semantics drift during IR lowering | High | Encode ownership in EIR as explicit ops (`Acquire`/`Release`/`Move`/`Borrow`). Validator rejects unbalanced cleanup paths |
-| Performance gain is smaller than predicted | Medium | Benchmark before/after every optimization phase. If Phase 6 alone delivers <10% on compute benchmarks, stop and diagnose before continuing to Phase 7 |
-| Educational value lost to abstraction | Low if disciplined | EIR is *added* between AST and ASM; the ASM emitter retains every comment. New `docs/internals/the-ir.md` teaches IR. The pipeline story actually gets better |
-
-## Out of scope
-
-- Generic IR features (linear scan SSA destruction with phi nodes — we use block parameters instead)
-- Cross-module optimization (we don't have separate compilation yet)
-- Profile-guided optimization
-- SIMD vectorization (later, post-1.0 if at all)
-- A textual IR parser for round-tripping `.eir` files (printer is for tests/debug only, not bidirectional)
-- WebAssembly backend (post-1.0 product track, see ROADMAP v1.2.x)
-
-## Relationship to existing optimizer
-
-`src/optimize/` operates on the AST and stays there. AST-level folding and propagation are good at:
-
-- Constant folding of pure arithmetic on literal subtrees
-- Dead branch elimination in `if (false)`
-- Reachability/control-flow normalization
-- Alias-aware scalar propagation across statement boundaries
-
-These are kept. EIR-level optimizations target what AST-level can't see:
-
-- **Liveness** of values across an entire function
-- **Value identity** for CSE (two `array_get %a, 0` calls produce the same SSA value; AST nodes are structural but not value-identified)
-- **Basic-block dominance** for LICM
-- **Register placement** for elimination of redundant moves and spills
-- **Instruction scheduling** for pipelining
-
-## Relationship to ROADMAP
-
-- The external plan series was written against an older roadmap shape where register allocation, peephole optimization, inlining, tail-call optimization, and deeper DCE/propagation were standalone performance bullets.
-- This repository's `ROADMAP.md` has already been reconciled: **v0.24.x** covers EIR introduction and register allocation, **v0.25.x** covers EIR optimization passes, and **v0.26.x** covers performance closure, legacy cleanup, and 0.x stabilization.
-- Treat `ROADMAP.md` as the source of truth for release placement; treat these `.plans/eir-*` files as the execution detail behind those roadmap bullets.
-
-## Open design questions (decided here)
-
-These are decided in this proposal, not deferred:
-
-- **SSA form**: SSA-lite with block parameters. No phi nodes. Block params are easier to construct and lower than phi-based SSA, at no real cost for our scale.
-- **Value naming**: `ValueId` is a u32 index into a per-function value table. Values are SSA: defined exactly once.
-- **CFG representation**: Functions own a `Vec`, each block owns a `Vec` and one terminator. Blocks reference each other by `BlockId(u32)`.
-- **Types in IR**: minimal — `I64`, `F64`, `Str`, `Heap`, `Void`. Heap subkind (`Array`/`Hash`/`Object`/`Mixed`/`Iterable`/`Union`) is carried as metadata on operations, not in the IR type itself, because the runtime handles them uniformly via heap headers.
-- **Ownership**: tracked in EIR as explicit ops (`Acquire`, `Release`, `Move`, `Borrow`). The validator checks for balance along all paths.
-- **PHP-specific ops**: yes, first-class. `MixedBox`, `ArrayCowEnsureUnique`, `Fatal(msg_idx)`, `RuntimeCall`, `BuiltinCall` are distinct from generic `Call`.
-- **Effects**: each instruction carries effect bits (Pure, ReadsHeap, ReadsGlobal, ReadsFs, WritesHeap, WritesGlobal, WritesFs, MayThrow, MayFatal, MayDeoptimize). Set at builder time, refined by validator.
-
-## What success looks like
-
-After Phase 9:
-
-- The compiler still ships PHP-correct programs.
-- Benchmarks show **40–70% performance improvement** on compute-heavy workloads (function calls, tight loops, arithmetic-heavy programs) compared to pre-EIR baseline.
-- `docs/internals/` has a new chapter (`the-ir.md`) explaining EIR with the same pedagogical care as `the-codegen.md`.
-- The codebase passes all gates: tests, ignored tests, Docker Linux tests, benchmark harness, zero compiler warnings.
-- A future migration to Cranelift, if ever desired, is a 6–8 week project instead of 6–9 months — because EIR is the hard part.
diff --git a/.plans/eir-01-design-spec.md b/.plans/eir-01-design-spec.md
deleted file mode 100644
index ea0c8fb469..0000000000
--- a/.plans/eir-01-design-spec.md
+++ /dev/null
@@ -1,662 +0,0 @@
-# Phase 01 — EIR Design Specification
-
-> **For agentic workers:** This phase produces *documentation only*. No code is written. Phase 02 implements the spec defined here.
-
-**Goal:** Produce a complete written specification of elephc IR (EIR) — types, instructions, terminators, effects, ownership semantics, and validation rules — covering every existing AST node the codegen currently lowers.
-
-**Architecture:** SSA-form CFG with block parameters. PHP-specific instructions (Mixed boxing, COW, fatal, runtime calls) are first-class. Effects are explicit metadata, not implicit ordering.
-
-**Tech Stack:** Markdown. No code. Output is a single doc that Phase 02 implements verbatim.
-
----
-
-## File Structure
-
-The output of this phase is one document:
-
-- Create: `docs/internals/the-ir.md` — the canonical EIR specification (replaces nothing; new doc)
-
-This repository now has `docs/internals/the-ir.md` and a `docs/README.md` link. Later phases should update `docs/internals/the-codegen.md` when the backend transition becomes real.
-
----
-
-## Task 1: Specify EIR types
-
-**Files:**
-- Create: `docs/internals/the-ir.md` (section: "Types")
-
-- [ ] **Step 1: Write the types section**
-
-```markdown
-## Types
-
-EIR uses a minimal type lattice. Type-level distinctions that the runtime
-treats uniformly (e.g., Array vs Hash, Object vs Mixed) are *not* separate
-IR types — they are carried as metadata on operations.
-
-| EIR type | Storage | Maps from `PhpType` |
-|----------|---------|---------------------|
-| `I64` | 1 integer register | `Int`, `Bool`, `Pointer`, `Resource`, `Callable` |
-| `F64` | 1 float register | `Float` |
-| `Str` | pair `(ptr, len)`, 2 registers | `Str` |
-| `Heap` | 1 integer register (pointer to heap header) | `Array(_)`, `AssocArray{..}`, `Object(_)`, `Mixed`, `Iterable`, `Union(_)`, `Buffer(_)` |
-| `Void` | zero registers | `Void`, `Never` |
-
-Notes:
-- `Bool` and `Int` share `I64` storage. The PHP-level distinction is preserved
- via type metadata on the producing operation (`def_php_type`), not via a
- separate IR type. This avoids per-operation duplication of arithmetic
- opcodes.
-- `Str` is two registers everywhere it appears, matching the existing
- `(ptr, len)` ABI from `src/codegen/abi/registers.rs`.
-- `Heap` is uniform pointer-to-header. The runtime's `__rt_decref_any` uses
- the heap header to dispatch by kind. Operations that need the kind
- (e.g., `ArrayGet` vs `HashGetStr`) take the kind as an immediate
- attribute.
-
-PHP-level type information is preserved on each `Value`'s metadata
-(`Value.php_type: PhpType`) for diagnostics, validator checks, and to
-inform passes that care (e.g., `MixedBox` cannot apply to a value whose
-PHP type is already `Mixed`).
-```
-
-- [ ] **Step 2: Self-review for completeness**
-
-Ensure each variant of `PhpType` (Int, Float, Str, Bool, Void, Never, Iterable, Mixed, Array, AssocArray, Buffer, Callable, Object, Packed, Pointer, Resource, Union) maps to exactly one EIR type. Cross-reference `src/types/model.rs`.
-
----
-
-## Task 2: Specify `Value`, `BasicBlock`, `Function`, `Module`
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "Module structure")
-
-- [ ] **Step 1: Write the module structure section**
-
-````markdown
-## Module structure
-
-### `ValueId` and `Value`
-
-Values are SSA: each `ValueId` is defined exactly once. A `ValueId` is a
-`u32` index into the owning function's value table.
-
-```rust
-pub struct Value {
- pub ir_type: IrType,
- pub php_type: PhpType,
- pub def: ValueDef,
- pub ownership: Ownership,
-}
-
-pub enum ValueDef {
- BlockParam { block: BlockId, index: u16 },
- Instruction { block: BlockId, index: u32 },
-}
-
-pub enum Ownership {
- NonHeap, // I64/F64/Void scalars; never need release
- Owned, // refcounted; this value owns +1 refcount
- Borrowed, // refcounted; this value does not own a refcount
- MaybeOwned, // refcounted; ownership joins across CFG merges
-}
-```
-
-`Ownership` mirrors `HeapOwnership` from `src/codegen/context.rs` but is
-attached to *every* SSA value, not just locals.
-
-### `BasicBlock`
-
-```rust
-pub struct BasicBlock {
- pub id: BlockId,
- pub params: Vec, // block parameters (SSA-lite)
- pub instructions: Vec, // indices into function's instruction pool
- pub terminator: Terminator,
-}
-```
-
-Blocks have a single terminator at the end and may have parameters at the
-top. Branch arguments carry SSA values into the destination block,
-replacing phi nodes.
-
-### `Function`
-
-```rust
-pub struct Function {
- pub name: String,
- pub params: Vec,
- pub return_type: IrType,
- pub return_php_type: PhpType,
- pub blocks: Vec,
- pub values: Vec,
- pub instructions: Vec,
- pub locals: Vec, // stack slots for PHP locals
- pub entry: BlockId,
- pub source_signature_ref: Option,
- pub flags: FunctionFlags, // is_main, is_method, is_closure, etc.
-}
-
-pub struct LocalSlot {
- pub name: String, // PHP variable name; "" for synthetic
- pub php_type: PhpType,
- pub kind: LocalKind, // PhpVariable, Hidden, Static, Global
-}
-```
-
-### `Module`
-
-```rust
-pub struct Module {
- pub functions: Vec,
- pub class_methods: Vec, // flattened class methods
- pub data: DataPool, // string literals, runtime tables
- pub extern_decls: Vec,
- pub target: Target,
-}
-```
-
-A Module is one compilation unit. The runtime (`__rt_*` routines) is
-*not* in the Module — it lives outside, exactly as today.
-````
-
-- [ ] **Step 2: Cross-check field names against existing types**
-
-Walk `src/types/`, `src/parser/ast/`, and `src/codegen/context.rs` and verify field names match the spelling already used (e.g., `php_type` matches `PhpType`, `class_id` matches `ClassInfo.class_id`).
-
----
-
-## Task 3: Specify the Instruction set
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "Instructions")
-
-This is the largest task. Specify every opcode by walking the existing codegen sites that produce it. Each opcode entry MUST include:
-
-- Operands and result type
-- Effects (Pure / Reads* / Writes* / MayThrow / MayFatal / MayDeoptimize)
-- Lowering target (which `__rt_*` routine or inline ASM pattern)
-- AST node(s) that produce it
-
-- [ ] **Step 1: Write the literals/locals/globals section**
-
-```markdown
-### Literals and locals
-
-| Op | Operands | Result | Effects | Lowers to |
-|----|----------|--------|---------|-----------|
-| `ConstI64(i64)` | — | `I64` | Pure | `mov reg, #imm` (or constant pool for large values) |
-| `ConstF64(f64)` | — | `F64` | Pure | adr + ldr from data section |
-| `ConstStr(string_id)` | — | `Str` | Pure | adr to label + immediate length |
-| `ConstNull` | — | `I64` | Pure | `mov reg, #0` |
-| `LoadLocal(slot_id)` | — | (slot.ir_type) | Reads(local) | `ldr reg, [x29, #-off]` |
-| `StoreLocal(slot_id)` | val | `Void` | Writes(local) | `str reg, [x29, #-off]` |
-| `LoadGlobal(name_id)` | — | (decl.ir_type) | Reads(global) | adr + ldr |
-| `StoreGlobal(name_id)` | val | `Void` | Writes(global) | adr + str |
-```
-
-- [ ] **Step 2: Write the arithmetic/bitwise/comparison section**
-
-```markdown
-### Scalar arithmetic and bitwise
-
-All scalar ops operate on `I64` or `F64` operands matching the op's domain.
-
-| Op | Operands | Result | Effects | Lowers to (ARM64) |
-|----|----------|--------|---------|--------------------|
-| `IAdd(a, b)` | I64, I64 | I64 | Pure | `add` |
-| `ISub(a, b)` | I64, I64 | I64 | Pure | `sub` |
-| `IMul(a, b)` | I64, I64 | I64 | Pure | `mul` |
-| `ISDiv(a, b)` | I64, I64 | I64 | MayFatal (div by zero in PHP modes that fatal) | `sdiv` |
-| `ISMod(a, b)` | I64, I64 | I64 | MayFatal | `sdiv`+`msub` |
-| `INeg(a)` | I64 | I64 | Pure | `neg` |
-| `IBitAnd/Or/Xor/Not(a,b)/a` | I64 (1 or 2) | I64 | Pure | `and`/`orr`/`eor`/`mvn` |
-| `IShl(a, b)` | I64, I64 | I64 | Pure | `lsl` |
-| `IShrA(a, b)` | I64, I64 | I64 | Pure | `asr` (PHP `>>` is arithmetic) |
-| `FAdd/FSub/FMul/FDiv(a, b)` | F64, F64 | F64 | Pure | `fadd`/`fsub`/`fmul`/`fdiv` |
-| `FNeg(a)` | F64 | F64 | Pure | `fneg` |
-| `FPow(a, b)` | F64, F64 | F64 | Pure (libc) | `bl pow` |
-
-### Comparison
-
-| Op | Operands | Result | Effects |
-|----|----------|--------|---------|
-| `ICmp(predicate, a, b)` | I64, I64 | I64 (0/1) | Pure |
-| `FCmp(predicate, a, b)` | F64, F64 | I64 (0/1) | Pure |
-| `StrCmpEq(a, b)` | Str, Str | I64 (0/1) | Pure (calls `__rt_str_eq`) |
-| `PhpLooseEq(a, b)` | any, any | I64 (0/1) | MayDeoptimize (object comparison may invoke __toString) |
-| `PhpIdentical(a, b)` | any, any | I64 (0/1) | Pure (type-tag aware) |
-| `Spaceship(a, b)` | any, any | I64 | as PhpLooseEq |
-
-`predicate` is `Eq`, `Ne`, `Slt`, `Sle`, `Sgt`, `Sge` for integers and the
-float equivalents (`Olt`, ...) for floats, mapping to PHP's signed
-comparison semantics.
-```
-
-- [ ] **Step 3: Write the conversion / cast section**
-
-```markdown
-### Conversions
-
-| Op | From | To | Effects | Notes |
-|----|------|----|---------|-------|
-| `IToF(a)` | I64 | F64 | Pure | PHP int-to-float widening |
-| `FToI(a)` | F64 | I64 | Pure | PHP float-to-int (truncate, PHP rules) |
-| `IToStr(a)` | I64 | Str | AllocConcatBuf | calls `__rt_itoa` |
-| `FToStr(a)` | F64 | Str | AllocConcatBuf | calls `__rt_ftoa` |
-| `BoolToStr(a)` | I64 | Str | Pure | "" or "1" |
-| `StrToI(a)` | Str | I64 | Pure | calls `__rt_str_to_int` |
-| `StrToF(a)` | Str | F64 | Pure | calls `__rt_str_to_float` |
-| `MixedBox(a)` | any (non-Mixed) | Heap | AllocHeap | tags value into Mixed cell |
-| `MixedUnbox(a, expected_tag)` | Heap (Mixed) | I64/F64/Str/Heap | Pure + MayFatal | extracts payload |
-| `MixedTagOf(a)` | Heap (Mixed) | I64 | Pure | returns tag |
-| `Cast(a, to_php_type)` | any | matching IR type | as PHP cast | dispatches to specific helper |
-```
-
-- [ ] **Step 4: Write the string ops section**
-
-```markdown
-### String operations
-
-| Op | Operands | Result | Effects | Lowers to |
-|----|----------|--------|---------|-----------|
-| `StrConcat(a, b)` | Str, Str | Str | AllocConcatBuf | `__rt_str_concat` |
-| `StrLen(a)` | Str | I64 | Pure | inline `mov reg, len` |
-| `StrCharAt(a, i)` | Str, I64 | Str (1-char) | MayFatal (oob), AllocConcatBuf | `__rt_str_char_at` |
-| `StrPersist(a)` | Str | Str | AllocHeap (idempotent) | `__rt_str_persist` |
-| `StrInterpolate(parts, vals)` | varargs | Str | AllocConcatBuf | builds in concat buf |
-```
-
-- [ ] **Step 5: Write the array/hash/object ops section**
-
-```markdown
-### Array and hash
-
-The `kind` immediate distinguishes indexed Array, hash AssocArray, and
-mixed-key payload.
-
-| Op | Operands | Result | Effects | Notes |
-|----|----------|--------|---------|-------|
-| `ArrayNew(kind, capacity)` | — | Heap | AllocHeap | calls `__rt_array_new` |
-| `ArrayLen(a)` | Heap | I64 | Pure | inline header read |
-| `ArrayGet(arr, idx)` | Heap, I64 | (element ir_type) | Reads(heap) + MayFatal? | `__rt_array_get_int` |
-| `ArraySet(arr, idx, val)` | Heap, I64, any | Void | Writes(heap), AllocHeap (COW) | `__rt_array_set_int` |
-| `ArrayPush(arr, val)` | Heap, any | Void | Writes(heap), AllocHeap | `__rt_array_push` |
-| `ArrayCowEnsureUnique(arr)` | Heap | Heap | AllocHeap (maybe) | `__rt_array_cow_ensure` |
-| `HashGetStr(h, key)` | Heap, Str | (value ir_type) | Reads(heap) + MayFatal? | `__rt_hash_get_str` |
-| `HashGetInt(h, key)` | Heap, I64 | (value ir_type) | Reads(heap) + MayFatal? | `__rt_hash_get_int` |
-| `HashSetStr/Int(h, k, v)` | Heap, key, val | Void | Writes(heap), AllocHeap | `__rt_hash_set_*` |
-| `HashKeyExists(h, k)` | Heap, key | I64 | Reads(heap) | `__rt_hash_exists_*` |
-| `IterNext(iter)` | Heap | (key, value) | Reads(heap), MayDeoptimize (Iterator::next) | `__rt_iter_next` |
-| `IterCurrent(iter)` | Heap | (key, value) | Reads(heap) | `__rt_iter_current` |
-
-### Object
-
-| Op | Operands | Result | Effects | Notes |
-|----|----------|--------|---------|-------|
-| `ObjectNew(class_id)` | — | Heap | AllocHeap | `__rt_object_alloc` then constructor call |
-| `PropGet(obj, offset, ir_type)` | Heap, immediate | ir_type | Reads(heap) | inline `ldr` |
-| `PropSet(obj, offset, val)` | Heap, immediate, any | Void | Writes(heap) | inline `str` + retain handling |
-| `VTableLookup(obj, method_id)` | Heap | I64 (fn ptr) | Reads(heap) | reads class header |
-| `InstanceOf(obj, class_id)` | Heap | I64 | Reads(heap) | `__rt_instanceof` |
-```
-
-- [ ] **Step 6: Write the calls section**
-
-```markdown
-### Calls
-
-| Op | Operands | Result | Effects | Notes |
-|----|----------|--------|---------|-------|
-| `Call(func_id, args)` | varargs | (sig return) | per callee | user-defined PHP call |
-| `IndirectCall(fn_ptr, sig, args)` | varargs | (sig return) | MayDeoptimize | closure/callable |
-| `MethodCall(obj, method_id, args)` | Heap + varargs | (sig return) | per callee | virtual dispatch |
-| `BuiltinCall(builtin, args)` | varargs | (builtin return) | per builtin (from `src/optimize/effects/builtins.rs`) | inline-emit |
-| `RuntimeCall(rt_routine, args)` | varargs | (rt return) | per routine | `__rt_*` |
-| `ExternCall(name, args)` | varargs | (extern return) | per FFI sig | direct C call |
-```
-
-Each call instruction declares its **effect summary** at lowering time
-from the existing `src/optimize/effects/` analysis. The IR keeps the
-summary; passes do not re-derive it.
-
-- [ ] **Step 7: Write the ownership/control-flow section**
-
-```markdown
-### Ownership operations
-
-These operations are *explicit* in the IR. The AST → IR builder inserts
-them. The validator checks balance along every path. The register
-allocator must not eliminate them; later passes may move them.
-
-| Op | Operands | Result | Effects | Lowers to |
-|----|----------|--------|---------|-----------|
-| `Acquire(a)` | refcounted | Void | Writes(refcount) | `bl __rt_incref` |
-| `Release(a)` | refcounted | Void | Writes(refcount), MayFatal (debug heap) | `bl __rt_decref_any` |
-| `Move(a)` | any | (same type) | Pure (transfers ownership) | no-op at codegen, validator-only |
-| `Borrow(a)` | refcounted | (same type) | Pure | no-op at codegen, validator-only |
-
-### Terminators
-
-Every basic block ends with exactly one terminator.
-
-| Term | Operands | Notes |
-|------|----------|-------|
-| `Br(target, args)` | block, list | unconditional branch |
-| `CondBr(cond, then, then_args, else_, else_args)` | I64 cond | conditional |
-| `Switch(scrutinee, cases, default)` | I64 | jump table or chain |
-| `Return(value?)` | optional value | function epilogue |
-| `Throw(value)` | Heap (exception object) | longjmp via `__rt_throw` |
-| `Fatal(msg_id)` | immediate | unrecoverable error path |
-| `Unreachable` | — | provably unreachable (after `never`-typed call) |
-```
-
-- [ ] **Step 8: Self-review the instruction set**
-
-Walk every file under `src/codegen/expr/` and `src/codegen/stmt/`. For each emission helper, ask "which EIR ops does this lower to?" — if you cannot answer, the IR is missing something or the helper is doing too much (which may be a refactor finding). Make a checklist of files visited.
-
-Files to walk (minimum):
-- `src/codegen/expr/scalars.rs`
-- `src/codegen/expr/binops/arithmetic.rs`
-- `src/codegen/expr/binops/comparison.rs`
-- `src/codegen/expr/binops/array_union.rs`
-- `src/codegen/expr/calls.rs` and `src/codegen/expr/calls/args.rs`
-- `src/codegen/expr/arrays.rs` (and sub-dir)
-- `src/codegen/expr/objects.rs` (and sub-dir)
-- `src/codegen/expr/chains.rs`
-- `src/codegen/expr/assignment.rs`
-- `src/codegen/expr/variables.rs`
-- `src/codegen/expr/ternary.rs`
-- `src/codegen/expr/compare/`
-- `src/codegen/expr/coerce.rs`
-- `src/codegen/expr/helpers.rs`
-- `src/codegen/expr/ownership.rs`
-- `src/codegen/stmt/` (all)
-- `src/codegen/builtins/` (sample 5 categories: strings, arrays, math, io, oop)
-- `src/codegen/runtime/` (sample 5 categories)
-
----
-
-## Task 4: Specify effect lattice
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "Effects")
-
-- [ ] **Step 1: Write the effects section**
-
-```markdown
-## Effects
-
-Each EIR instruction carries an immutable `Effects` bitset assigned at
-construction time. Effects model what an instruction *may* do:
-
-```rust
-bitflags! {
- pub struct Effects: u16 {
- const READS_LOCAL = 0b0000_0000_0000_0001;
- const READS_HEAP = 0b0000_0000_0000_0010;
- const READS_GLOBAL = 0b0000_0000_0000_0100;
- const READS_FS = 0b0000_0000_0000_1000;
- const WRITES_LOCAL = 0b0000_0000_0001_0000;
- const WRITES_HEAP = 0b0000_0000_0010_0000;
- const WRITES_GLOBAL = 0b0000_0000_0100_0000;
- const WRITES_FS = 0b0000_0000_1000_0000;
- const ALLOC_HEAP = 0b0000_0001_0000_0000;
- const ALLOC_CONCAT = 0b0000_0010_0000_0000;
- const MAY_THROW = 0b0000_0100_0000_0000;
- const MAY_FATAL = 0b0000_1000_0000_0000;
- const MAY_DEOPT = 0b0001_0000_0000_0000;
- const REFCOUNT_OP = 0b0010_0000_0000_0000;
- }
-}
-
-impl Effects {
- pub const PURE: Effects = Effects::empty();
- pub fn is_pure(&self) -> bool { self.is_empty() }
- pub fn may_observe(&self) -> bool {
- self.intersects(
- Effects::READS_LOCAL | Effects::READS_HEAP | Effects::READS_GLOBAL
- | Effects::READS_FS | Effects::ALLOC_HEAP
- | Effects::MAY_THROW | Effects::MAY_FATAL | Effects::MAY_DEOPT
- )
- }
- pub fn may_mutate(&self) -> bool {
- self.intersects(
- Effects::WRITES_LOCAL | Effects::WRITES_HEAP | Effects::WRITES_GLOBAL
- | Effects::WRITES_FS | Effects::REFCOUNT_OP
- )
- }
-}
-```
-
-Sources of effect data for the builder:
-- Arithmetic / comparison / scalar ops — hardcoded
-- BuiltinCall — looked up from `src/optimize/effects/builtins.rs`
-- Call / MethodCall — derived from `FunctionSig` purity flags, falling
- back to "all effects" when unknown
-- ExternCall — always conservative (`READS_HEAP | WRITES_HEAP | MAY_THROW`)
-- RuntimeCall — declared in a per-routine table maintained in the IR
- module
-
-The validator checks that operations with `MAY_DEOPT` are not silently
-reordered across `ALLOC_HEAP` instructions before passes that don't
-preserve allocation order.
-```
-
----
-
-## Task 5: Specify validator rules
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "Validator")
-
-- [ ] **Step 1: Write the validator rules section**
-
-```markdown
-## Validator
-
-The validator runs after every IR pass (cheap mode: structural;
-expensive mode: ownership + dominance). Failures are *bugs*, not
-diagnostics — they abort compilation.
-
-### Structural rules
-
-1. Every basic block ends with exactly one terminator. The terminator
- appears nowhere else in the block.
-2. Every `ValueId` is defined exactly once.
-3. Every use of a `ValueId` either (a) follows its definition in the
- same block, or (b) the defining block dominates the using block.
-4. Block parameter counts at the destination match the argument counts
- in every incoming branch.
-5. `IrType` matches between definition and every use.
-6. `entry` block has no parameters (function parameters are loaded via
- `LoadLocal` in the entry block).
-
-### Ownership rules
-
-1. Every `Owned` value reaches exactly one consuming op (`Release`,
- `Move`, or `Return`) along every CFG path from its definition.
-2. `Borrow` does not increase or decrease refcount; the source must
- outlive the borrowed value.
-3. At every CFG merge, ownership states from incoming edges must be
- compatible: identical states merge to themselves; `Owned` + `Owned`
- = `Owned`; `Borrowed` + `Borrowed` = `Borrowed`; mixed = `MaybeOwned`
- (the validator emits a runtime branch in lowering).
-4. `Return` of an `Owned` value transfers ownership to the caller;
- `Return` of `Borrowed` requires an explicit prior `Acquire`.
-
-### Effect rules
-
-1. `Pure` operations may not have side-effect dependencies via memory.
-2. Operations with `MAY_FATAL` define a control-flow effect: passes that
- reorder them must not move them past observable operations.
-3. `ALLOC_CONCAT` operations are sensitive to PHP's concat-buffer reuse
- policy: per-statement reset is preserved by the lowering, and passes
- must not reorder concat ops across statement boundaries.
-```
-
-- [ ] **Step 2: Self-review**
-
-Verify ownership rules against `src/codegen/expr/ownership.rs` and `src/codegen/context.rs::HeapOwnership`. Adjust rules if the actual codegen has cases not covered (e.g., container propagation, foreach values).
-
----
-
-## Task 6: Specify the textual format
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "Textual format")
-
-- [ ] **Step 1: Write the textual format section**
-
-````markdown
-## Textual format
-
-EIR has a printable textual format used for snapshot tests, `--emit-ir`,
-and debugging. It is **printer-only** — no parser. Format does not need
-to be machine-readable, only human-readable.
-
-Example:
-
-```eir
-function add_pair(p0: I64, p1: I64) -> I64 {
- entry:
- v0 = const_i64 0
- store_local slot[0] "result", v0
- v1 = load_local slot[0]
- v2 = iadd v1, p0 ; effects: pure
- store_local slot[0], v2
- v3 = load_local slot[0]
- v4 = iadd v3, p1
- store_local slot[0], v4
- v5 = load_local slot[0]
- return v5
-}
-
-function map_word_count(p0: Str) -> Heap[Hash] {
- entry:
- v0 = builtin_call explode " ", p0 ; effects: alloc_heap, alloc_concat
- own v0
- v1 = hash_new ; effects: alloc_heap
- own v1
- br loop(v0, 0_i64, v1)
-
- loop(arr: Heap[Array], i: I64, acc: Heap[Hash]):
- v2 = array_len arr
- v3 = icmp slt i, v2
- cond_br v3, body(arr, i, acc), exit(acc)
-
- body(arr: Heap[Array], i: I64, acc: Heap[Hash]):
- v4 = array_get arr, i ; effects: reads_heap, may_fatal
- borrow v4
- ; ... omitted ...
- v5 = iadd i, 1_i64
- br loop(arr, v5, acc)
-
- exit(acc: Heap[Hash]):
- release v0 ; arr no longer needed
- return acc ; transfer ownership
-}
-```
-
-Notes:
-- Function header shows IR type. `Heap[Hash]` displays subkind metadata.
-- Block params use `name: Type` after `block_name(`.
-- Branch arguments use the destination's param order.
-- `own`, `borrow`, `release` are the ownership ops.
-- Effects, when displayed, appear as `; effects: ...` comments.
-````
-
----
-
-## Task 7: Cross-reference Lowering catalogue
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "AST → EIR lowering catalogue")
-
-- [ ] **Step 1: Write the lowering catalogue**
-
-For every `ExprKind` and `StmtKind` variant in `src/parser/ast/expr.rs` and `src/parser/ast/stmt.rs`, give a one-paragraph lowering recipe. Example entries:
-
-```markdown
-### `ExprKind::BinaryOp { op: Add, lhs, rhs }`
-
-Lower `lhs` and `rhs` recursively. Their result types determine the op:
-
-- Both `I64` → `IAdd`
-- Both `F64` → `FAdd`
-- Mixed → coerce via `IToF` on the int side, then `FAdd`
-- `Str` + `Str` → `StrConcat` (PHP `.` is a separate op; `+` on strings
- is an array union or coerced numeric add depending on operand types,
- per PHP rules)
-
-### `StmtKind::Foreach { iter, key, value, body }`
-
-Lower `iter` → `Heap` value. Insert `IterStart`. Loop block reads
-`IterCurrent`, binds key/value to locals, runs the body, then
-`IterNext` and branches back. Exit block runs `IterEnd`. Owned-vs-
-borrowed semantics follow PHP foreach rules (by-value vs by-ref).
-```
-
-The full catalogue is mandatory and lists every variant. Step 1 produces the catalogue document; Step 2 is the review.
-
-- [ ] **Step 2: Validate completeness against AST**
-
-Run:
-```bash
-grep -E "^[[:space:]]*[A-Z][A-Za-z0-9_]*[ {,(]" src/parser/ast/expr.rs src/parser/ast/stmt.rs
-```
-Cross-check every variant has an entry. Missing entries are plan failures.
-
----
-
-## Task 8: Specify how `--emit-ir` works
-
-**Files:**
-- Modify: `docs/internals/the-ir.md` (append: "CLI surface")
-
-- [ ] **Step 1: Write the CLI section**
-
-```markdown
-## CLI
-
-`--emit-ir` (added in Phase 03) prints the EIR for the compiled program
-to stdout and exits without invoking the assembler. Useful for debugging
-and for snapshot tests.
-
-`--ir-backend` (added in Phase 05) selects the IR pipeline; default off
-during Phases 03–04, default on at the end of Phase 05, removed in
-Phase 09 when the legacy backend is deleted.
-```
-
----
-
-## Task 9: Self-review and commit
-
-- [ ] **Step 1: Read the document end-to-end**
-
-Check for:
-- Placeholders, "TBD", "fill in later"
-- Field-name inconsistencies (e.g., `ir_type` vs `irType`)
-- Op names that overlap or duplicate
-- Missing AST variants in the lowering catalogue
-- Missing effects on instructions
-
-Fix inline.
-
-- [ ] **Step 2: Commit**
-
-```bash
-git add docs/internals/the-ir.md
-git commit -m "docs: introduce EIR design specification (phase 01)"
-```
-
----
-
-## Exit criteria
-
-- `docs/internals/the-ir.md` exists and is complete
-- Every `ExprKind` and `StmtKind` has a lowering entry
-- Every EIR op has effects, operands, result type, and a lowering target
-- Validator rules cover SSA, ownership, and effects
-- Textual format example renders a non-trivial function correctly
-- No "TBD" / "TODO" / placeholder text anywhere
diff --git a/.plans/eir-02-ir-module-skeleton.md b/.plans/eir-02-ir-module-skeleton.md
deleted file mode 100644
index 82571d396c..0000000000
--- a/.plans/eir-02-ir-module-skeleton.md
+++ /dev/null
@@ -1,1741 +0,0 @@
-# Phase 02 — IR Module Skeleton
-
-> **For agentic workers:** Implement the data structures defined in Phase 01. No AST → IR lowering yet; no codegen consumer yet. The validator and printer are testable on hand-built `Module` instances. This phase produces no user-visible behavior change.
-
-**Goal:** Implement `src/ir/` containing types, instructions, values, blocks, functions, modules, a builder API, a validator, and a textual printer. Cover with unit tests.
-
-**Architecture:** Pure data structures. No interaction with the rest of the compiler. The next phases consume this module.
-
-**Tech Stack:** Rust. One new dependency: `bitflags` (already an indirect dep of common crates; verify with `cargo tree` before adding).
-
----
-
-## File Structure
-
-All new files under `src/ir/`:
-
-- Create: `src/ir/mod.rs` — module root, re-exports
-- Create: `src/ir/types.rs` — `IrType`, `IrHeapKind`, conversions from `PhpType`
-- Create: `src/ir/value.rs` — `ValueId`, `Value`, `Ownership`, value table
-- Create: `src/ir/instr.rs` — `Instruction`, opcode enum (`Op`), `InstId`
-- Create: `src/ir/effects.rs` — `Effects` bitset
-- Create: `src/ir/block.rs` — `BasicBlock`, `BlockId`, `Terminator`
-- Create: `src/ir/function.rs` — `Function`, `LocalSlot`, `LocalKind`, `FunctionParam`, `FunctionFlags`
-- Create: `src/ir/module.rs` — `Module`, `DataPool`, `ExternDecl`
-- Create: `src/ir/builder.rs` — `Builder` API for constructing IR by hand (used by tests now, by Phase 03 lowering later)
-- Create: `src/ir/validator.rs` — structural + ownership + effect checks
-- Create: `src/ir/print.rs` — textual format printer
-- Create: `src/ir/tests/mod.rs` — unit tests
-- Create: `src/ir/tests/print_test.rs` — snapshot-style tests for printer
-- Create: `src/ir/tests/validator_test.rs` — positive and negative validator cases
-- Create: `src/ir/tests/builder_test.rs` — build a couple of hand-rolled functions
-- Modify: `src/lib.rs` — add `pub mod ir;`
-- Modify: `src/main.rs` — add `mod ir;` for the binary crate module tree
-
----
-
-## Task 1: Wire up the module
-
-**Files:**
-- Create: `src/ir/mod.rs`
-- Modify: `src/lib.rs`
-- Modify: `src/main.rs`
-
-- [ ] **Step 1: Create `src/ir/mod.rs` with the module preamble and re-exports**
-
-```rust
-//! Purpose:
-//! Defines elephc IR (EIR), a CFG-based SSA-lite intermediate representation
-//! used between AST-level optimization and assembly emission.
-//!
-//! Called from:
-//! - Phase 03: `crate::ir::builder` from a new AST → EIR lowering pass
-//! - Phase 04: a new `src/codegen_ir/` backend consuming `Module`
-//!
-//! Key details:
-//! - Block parameters replace SSA phi nodes; ownership is explicit; effects
-//! are immutable bitset metadata. See `docs/internals/the-ir.md`.
-
-mod block;
-mod builder;
-mod effects;
-mod function;
-mod instr;
-mod module;
-mod print;
-mod types;
-mod validator;
-mod value;
-
-#[cfg(test)]
-mod tests;
-
-pub use block::{BasicBlock, BlockId, Terminator};
-pub use builder::Builder;
-pub use effects::Effects;
-pub use function::{Function, FunctionFlags, FunctionParam, LocalKind, LocalSlot};
-pub use instr::{InstId, Instruction, Op};
-pub use module::{DataPool, ExternDecl, Module};
-pub use print::print_module;
-pub use types::{IrHeapKind, IrType};
-pub use validator::{validate_function, validate_module, ValidationError};
-pub use value::{Ownership, Value, ValueDef, ValueId};
-```
-
-- [ ] **Step 2: Add module to crate root**
-
-```rust
-// src/lib.rs — add this line in alphabetical order with existing pub mod entries
-pub mod ir;
-```
-
-- [ ] **Step 3: Add the binary module declaration**
-
-```rust
-// src/main.rs — add this line near the other module declarations
-mod ir;
-```
-
-- [ ] **Step 4: Build to verify the empty skeleton compiles**
-
-Run: `cargo build`
-Expected: `error[E0583]: file not found for module 'types'` (and others) — this confirms the module is wired up; the missing files come next.
-
-- [ ] **Step 5: Add stub files so the build proceeds**
-
-Create each of `types.rs`, `value.rs`, `instr.rs`, `effects.rs`, `block.rs`, `function.rs`, `module.rs`, `builder.rs`, `validator.rs`, `print.rs` with only the module preamble and no items:
-
-```rust
-//! Purpose:
-//!
-//!
-//! Called from:
-//! - `crate::ir`
-//!
-//! Key details:
-//! - Implemented in phase 02.
-```
-
-Run: `cargo build`
-Expected: clean (with warnings for unused imports in `mod.rs`, acceptable for now)
-
-- [ ] **Step 6: Commit**
-
-```bash
-git add src/ir/ src/lib.rs src/main.rs
-git commit -m "feat(ir): scaffold src/ir/ module skeleton"
-```
-
----
-
-## Task 2: Implement `IrType` and `IrHeapKind`
-
-**Files:**
-- Modify: `src/ir/types.rs`
-- Test: `src/ir/tests/types_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/types_test.rs
-use crate::ir::{IrHeapKind, IrType};
-use crate::types::PhpType;
-
-#[test]
-fn maps_int_to_i64() {
- assert_eq!(IrType::from_php(&PhpType::Int), IrType::I64);
-}
-
-#[test]
-fn maps_float_to_f64() {
- assert_eq!(IrType::from_php(&PhpType::Float), IrType::F64);
-}
-
-#[test]
-fn maps_str_to_str() {
- assert_eq!(IrType::from_php(&PhpType::Str), IrType::Str);
-}
-
-#[test]
-fn maps_bool_to_i64() {
- assert_eq!(IrType::from_php(&PhpType::Bool), IrType::I64);
-}
-
-#[test]
-fn maps_array_int_to_heap_array() {
- let php_ty = PhpType::Array(Box::new(PhpType::Int));
- assert_eq!(IrType::from_php(&php_ty), IrType::Heap(IrHeapKind::Array));
-}
-
-#[test]
-fn maps_mixed_to_heap_mixed() {
- assert_eq!(IrType::from_php(&PhpType::Mixed), IrType::Heap(IrHeapKind::Mixed));
-}
-
-#[test]
-fn register_count_matches_php_type() {
- assert_eq!(IrType::I64.register_count(), 1);
- assert_eq!(IrType::F64.register_count(), 1);
- assert_eq!(IrType::Str.register_count(), 2);
- assert_eq!(IrType::Heap(IrHeapKind::Array).register_count(), 1);
- assert_eq!(IrType::Void.register_count(), 0);
-}
-```
-
-Add `mod types_test;` to `src/ir/tests/mod.rs` (creating the file if needed):
-
-```rust
-// src/ir/tests/mod.rs
-mod types_test;
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::types_test`
-Expected: `error: cannot find type IrType` etc.
-
-- [ ] **Step 3: Implement `IrType`**
-
-```rust
-// src/ir/types.rs
-//! Purpose:
-//! Defines the EIR type lattice and conversions from `PhpType`.
-//!
-//! Called from:
-//! - `crate::ir::value`, `crate::ir::builder`, lowering passes (phase 03)
-//!
-//! Key details:
-//! - Heap subkind is metadata on `IrType::Heap`; runtime treats heap values
-//! uniformly via `__rt_decref_any`.
-
-use crate::types::PhpType;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum IrType {
- I64,
- F64,
- Str,
- Heap(IrHeapKind),
- Void,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum IrHeapKind {
- Array,
- Hash,
- Object,
- Mixed,
- Iterable,
- Union,
- Buffer,
-}
-
-impl IrType {
- pub fn from_php(php: &PhpType) -> Self {
- match php {
- PhpType::Int | PhpType::Bool | PhpType::Pointer(_)
- | PhpType::Resource(_) | PhpType::Callable => IrType::I64,
- PhpType::Float => IrType::F64,
- PhpType::Str => IrType::Str,
- PhpType::Void | PhpType::Never => IrType::Void,
- PhpType::Array(_) => IrType::Heap(IrHeapKind::Array),
- PhpType::AssocArray { .. } => IrType::Heap(IrHeapKind::Hash),
- PhpType::Object(_) | PhpType::Packed(_) => IrType::Heap(IrHeapKind::Object),
- PhpType::Mixed => IrType::Heap(IrHeapKind::Mixed),
- PhpType::Iterable => IrType::Heap(IrHeapKind::Iterable),
- PhpType::Union(_) => IrType::Heap(IrHeapKind::Union),
- PhpType::Buffer(_) => IrType::Heap(IrHeapKind::Buffer),
- }
- }
-
- pub fn register_count(&self) -> usize {
- match self {
- IrType::I64 | IrType::F64 | IrType::Heap(_) => 1,
- IrType::Str => 2,
- IrType::Void => 0,
- }
- }
-
- pub fn is_refcounted(&self) -> bool {
- matches!(self, IrType::Heap(_))
- }
-
- pub fn is_float(&self) -> bool {
- matches!(self, IrType::F64)
- }
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::types_test`
-Expected: all tests pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/types.rs src/ir/tests/types_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement IrType and IrHeapKind"
-```
-
----
-
-## Task 3: Implement `Ownership` and `Value`
-
-**Files:**
-- Modify: `src/ir/value.rs`
-- Test: `src/ir/tests/value_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/value_test.rs
-use crate::ir::{IrType, Ownership, Value, ValueDef, ValueId};
-use crate::types::PhpType;
-
-#[test]
-fn ownership_merge_same_state() {
- assert_eq!(Ownership::Owned.merge(Ownership::Owned), Ownership::Owned);
- assert_eq!(Ownership::Borrowed.merge(Ownership::Borrowed), Ownership::Borrowed);
-}
-
-#[test]
-fn ownership_merge_distinct_states_yields_maybe_owned() {
- assert_eq!(Ownership::Owned.merge(Ownership::Borrowed), Ownership::MaybeOwned);
-}
-
-#[test]
-fn ownership_for_php_type_int_is_nonheap() {
- assert_eq!(Ownership::for_php_type(&PhpType::Int), Ownership::NonHeap);
-}
-
-#[test]
-fn ownership_for_php_type_array_starts_maybe_owned() {
- let ty = PhpType::Array(Box::new(PhpType::Int));
- assert_eq!(Ownership::for_php_type(&ty), Ownership::MaybeOwned);
-}
-
-#[test]
-fn value_id_is_zero_indexed() {
- let v = ValueId::from_raw(0);
- assert_eq!(v.as_raw(), 0);
-}
-```
-
-Add `mod value_test;` to `src/ir/tests/mod.rs`.
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::value_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement**
-
-```rust
-// src/ir/value.rs
-//! Purpose:
-//! Defines SSA-style `ValueId`, the owning `Value`, and the `Ownership` lattice
-//! mirroring `crate::codegen::context::HeapOwnership`.
-//!
-//! Called from:
-//! - `crate::ir::function`, `crate::ir::builder`, `crate::ir::validator`
-//!
-//! Key details:
-//! - Each `ValueId` is defined exactly once.
-//! - Ownership lattice tracks heap retention across SSA values, not just locals.
-
-use crate::ir::{block::BlockId, types::IrType};
-use crate::types::PhpType;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ValueId(u32);
-
-impl ValueId {
- pub fn from_raw(raw: u32) -> Self { Self(raw) }
- pub fn as_raw(self) -> u32 { self.0 }
-}
-
-#[derive(Debug, Clone)]
-pub struct Value {
- pub ir_type: IrType,
- pub php_type: PhpType,
- pub def: ValueDef,
- pub ownership: Ownership,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ValueDef {
- BlockParam { block: BlockId, index: u16 },
- Instruction { block: BlockId, index: u32 },
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Ownership {
- NonHeap,
- Owned,
- Borrowed,
- MaybeOwned,
-}
-
-impl Ownership {
- pub fn for_php_type(ty: &PhpType) -> Self {
- if ty.is_refcounted() || matches!(ty, PhpType::Str) {
- Ownership::MaybeOwned
- } else {
- Ownership::NonHeap
- }
- }
-
- pub fn merge(self, other: Self) -> Self {
- use Ownership::*;
- match (self, other) {
- (NonHeap, NonHeap) => NonHeap,
- (Owned, Owned) => Owned,
- (Borrowed, Borrowed) => Borrowed,
- (MaybeOwned, _) | (_, MaybeOwned) => MaybeOwned,
- (Owned, Borrowed) | (Borrowed, Owned) => MaybeOwned,
- (NonHeap, x) | (x, NonHeap) => x,
- }
- }
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::value_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/value.rs src/ir/tests/value_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement Value, ValueId, Ownership"
-```
-
----
-
-## Task 4: Implement `Effects`
-
-**Files:**
-- Modify: `Cargo.toml` (add `bitflags = "2"` only if not already present)
-- Modify: `src/ir/effects.rs`
-- Test: `src/ir/tests/effects_test.rs`
-
-- [ ] **Step 1: Check whether `bitflags` is already a dependency**
-
-Run: `cargo tree | grep bitflags`
-If present and version >= 2.0, skip adding to Cargo.toml. If absent, add to `[dependencies]` in `Cargo.toml`:
-
-```toml
-bitflags = "2"
-```
-
-- [ ] **Step 2: Write the failing test**
-
-```rust
-// src/ir/tests/effects_test.rs
-use crate::ir::Effects;
-
-#[test]
-fn pure_has_no_bits() {
- assert!(Effects::PURE.is_empty());
- assert!(Effects::PURE.is_pure());
-}
-
-#[test]
-fn reads_and_writes_are_orthogonal() {
- let r = Effects::READS_HEAP;
- let w = Effects::WRITES_HEAP;
- assert!(r.may_observe());
- assert!(!r.may_mutate());
- assert!(w.may_mutate());
- assert!(!w.may_observe());
-}
-
-#[test]
-fn combined_effects_compose() {
- let e = Effects::READS_HEAP | Effects::MAY_FATAL;
- assert!(e.contains(Effects::READS_HEAP));
- assert!(e.contains(Effects::MAY_FATAL));
-}
-```
-
-Add `mod effects_test;` to `src/ir/tests/mod.rs`.
-
-- [ ] **Step 3: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::effects_test`
-Expected: undefined symbols.
-
-- [ ] **Step 4: Implement**
-
-```rust
-// src/ir/effects.rs
-//! Purpose:
-//! Defines the immutable per-instruction effect bitset used by IR-level passes.
-//!
-//! Called from:
-//! - `crate::ir::instr` and every pass over IR instructions
-//!
-//! Key details:
-//! - Effects are assigned at builder time and are not mutated by subsequent
-//! passes. They are inferred conservatively for unknown calls.
-
-use bitflags::bitflags;
-
-bitflags! {
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
- pub struct Effects: u16 {
- const READS_LOCAL = 1 << 0;
- const READS_HEAP = 1 << 1;
- const READS_GLOBAL = 1 << 2;
- const READS_FS = 1 << 3;
- const WRITES_LOCAL = 1 << 4;
- const WRITES_HEAP = 1 << 5;
- const WRITES_GLOBAL = 1 << 6;
- const WRITES_FS = 1 << 7;
- const ALLOC_HEAP = 1 << 8;
- const ALLOC_CONCAT = 1 << 9;
- const MAY_THROW = 1 << 10;
- const MAY_FATAL = 1 << 11;
- const MAY_DEOPT = 1 << 12;
- const REFCOUNT_OP = 1 << 13;
- }
-}
-
-impl Effects {
- pub const PURE: Effects = Effects::empty();
-
- pub fn is_pure(&self) -> bool { self.is_empty() }
-
- pub fn may_observe(&self) -> bool {
- self.intersects(
- Effects::READS_LOCAL | Effects::READS_HEAP | Effects::READS_GLOBAL
- | Effects::READS_FS | Effects::ALLOC_HEAP
- | Effects::MAY_THROW | Effects::MAY_FATAL | Effects::MAY_DEOPT,
- )
- }
-
- pub fn may_mutate(&self) -> bool {
- self.intersects(
- Effects::WRITES_LOCAL | Effects::WRITES_HEAP | Effects::WRITES_GLOBAL
- | Effects::WRITES_FS | Effects::REFCOUNT_OP,
- )
- }
-}
-```
-
-- [ ] **Step 5: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::effects_test`
-Expected: pass.
-
-- [ ] **Step 6: Commit**
-
-```bash
-git add src/ir/effects.rs src/ir/tests/effects_test.rs src/ir/tests/mod.rs Cargo.toml Cargo.lock
-git commit -m "feat(ir): implement Effects bitset"
-```
-
----
-
-## Task 5: Implement `BlockId`, `BasicBlock`, `Terminator`
-
-**Files:**
-- Modify: `src/ir/block.rs`
-- Test: `src/ir/tests/block_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/block_test.rs
-use crate::ir::{BasicBlock, BlockId, Terminator, ValueId};
-
-#[test]
-fn block_id_is_zero_indexed() {
- assert_eq!(BlockId::from_raw(0).as_raw(), 0);
- assert_eq!(BlockId::from_raw(42).as_raw(), 42);
-}
-
-#[test]
-fn terminator_return_with_value() {
- let term = Terminator::Return(Some(ValueId::from_raw(7)));
- if let Terminator::Return(Some(v)) = term {
- assert_eq!(v.as_raw(), 7);
- } else {
- panic!("expected Return");
- }
-}
-
-#[test]
-fn block_construction_records_params_and_terminator() {
- let block = BasicBlock {
- id: BlockId::from_raw(0),
- params: vec![ValueId::from_raw(0)],
- instructions: vec![],
- terminator: Terminator::Return(None),
- };
- assert_eq!(block.params.len(), 1);
- assert!(matches!(block.terminator, Terminator::Return(None)));
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::block_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement**
-
-```rust
-// src/ir/block.rs
-//! Purpose:
-//! Defines basic blocks, terminators, and the typed block identifier.
-//!
-//! Called from:
-//! - `crate::ir::function`, `crate::ir::builder`, `crate::ir::validator`
-//!
-//! Key details:
-//! - Block parameters replace SSA phi nodes; every block has exactly one
-//! terminator at the end.
-
-use crate::ir::instr::InstId;
-use crate::ir::value::ValueId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub struct BlockId(u32);
-
-impl BlockId {
- pub fn from_raw(raw: u32) -> Self { Self(raw) }
- pub fn as_raw(self) -> u32 { self.0 }
-}
-
-#[derive(Debug, Clone)]
-pub struct BasicBlock {
- pub id: BlockId,
- pub params: Vec,
- pub instructions: Vec,
- pub terminator: Terminator,
-}
-
-#[derive(Debug, Clone)]
-pub enum Terminator {
- Br {
- target: BlockId,
- args: Vec,
- },
- CondBr {
- cond: ValueId,
- then_block: BlockId,
- then_args: Vec,
- else_block: BlockId,
- else_args: Vec,
- },
- Switch {
- scrutinee: ValueId,
- cases: Vec<(i64, BlockId, Vec)>,
- default: (BlockId, Vec),
- },
- Return(Option),
- Throw(ValueId),
- Fatal { message_id: u32 },
- Unreachable,
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::block_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/block.rs src/ir/tests/block_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement BasicBlock and Terminator"
-```
-
----
-
-## Task 6: Implement `Op`, `Instruction`, `InstId`
-
-**Files:**
-- Modify: `src/ir/instr.rs`
-- Test: `src/ir/tests/instr_test.rs`
-
-This is a large enum. Implement it in one pass; tests are minimal at this stage. Effect assignments per opcode are added in Task 7 via a helper.
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/instr_test.rs
-use crate::ir::{Effects, InstId, Instruction, Op, ValueId};
-
-#[test]
-fn inst_id_is_zero_indexed() {
- assert_eq!(InstId::from_raw(0).as_raw(), 0);
-}
-
-#[test]
-fn iadd_op_is_pure() {
- assert_eq!(Op::IAdd.default_effects(), Effects::PURE);
-}
-
-#[test]
-fn array_set_writes_heap_and_allocs() {
- let e = Op::ArraySet.default_effects();
- assert!(e.contains(Effects::WRITES_HEAP));
- assert!(e.contains(Effects::ALLOC_HEAP));
-}
-
-#[test]
-fn fatal_terminator_is_separate_from_op() {
- // Fatal is a Terminator, not an Op. This test guards against accidentally
- // adding it to Op.
- let names: Vec<&'static str> = vec![]; // no-op probe
- assert!(names.is_empty());
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::instr_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement the opcode enum**
-
-The full enum is large. Lay it out in the order from `docs/internals/the-ir.md` (Task 3 of Phase 01). Here is the canonical shape; copy verbatim:
-
-```rust
-// src/ir/instr.rs
-//! Purpose:
-//! Defines `Op` (the opcode enum), the `Instruction` payload, and `InstId`.
-//!
-//! Called from:
-//! - `crate::ir::builder`, lowering, validator, printer, codegen consumer
-//!
-//! Key details:
-//! - Each opcode has a single canonical effect set returned by `default_effects()`.
-//! Builders may not weaken effects; passes may not mutate them.
-
-use crate::ir::effects::Effects;
-use crate::ir::types::IrType;
-use crate::ir::value::ValueId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct InstId(u32);
-
-impl InstId {
- pub fn from_raw(raw: u32) -> Self { Self(raw) }
- pub fn as_raw(self) -> u32 { self.0 }
-}
-
-#[derive(Debug, Clone)]
-pub struct Instruction {
- pub op: Op,
- pub operands: Vec,
- pub immediate: Option,
- pub result: Option,
- pub result_type: IrType,
- pub effects: Effects,
-}
-
-#[derive(Debug, Clone)]
-pub enum Immediate {
- I64(i64),
- F64(f64),
- Str(u32), // string_id into DataPool
- LocalSlot(u32),
- GlobalName(u32),
- FunctionRef(u32),
- BuiltinRef(BuiltinId),
- RuntimeRef(RuntimeId),
- ExternRef(u32),
- ClassRef(u32),
- MethodRef { class: u32, method: u32 },
- PropOffset(u32),
- HeapKind(crate::ir::types::IrHeapKind),
- MixedTag(u8),
- CmpPredicate(CmpPredicate),
- CastTarget(IrType),
- None,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum CmpPredicate {
- Eq, Ne, Slt, Sle, Sgt, Sge,
- Olt, Ole, Ogt, Oge, // float
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct BuiltinId(pub u32);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RuntimeId(pub u32);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Op {
- // -- constants and locals --
- ConstI64, ConstF64, ConstStr, ConstNull,
- LoadLocal, StoreLocal,
- LoadGlobal, StoreGlobal,
- // -- arithmetic / bitwise --
- IAdd, ISub, IMul, ISDiv, ISMod, INeg,
- IBitAnd, IBitOr, IBitXor, IBitNot, IShl, IShrA,
- FAdd, FSub, FMul, FDiv, FNeg, FPow,
- // -- comparison --
- ICmp, FCmp, StrCmpEq, PhpLooseEq, PhpIdentical, Spaceship,
- // -- conversion --
- IToF, FToI, IToStr, FToStr, BoolToStr,
- StrToI, StrToF,
- MixedBox, MixedUnbox, MixedTagOf, Cast,
- // -- strings --
- StrConcat, StrLen, StrCharAt, StrPersist, StrInterpolate,
- // -- arrays / hashes --
- ArrayNew, ArrayLen, ArrayGet, ArraySet, ArrayPush, ArrayCowEnsureUnique,
- HashGetStr, HashGetInt, HashSetStr, HashSetInt, HashKeyExists,
- IterStart, IterNext, IterCurrent, IterEnd,
- // -- objects --
- ObjectNew, PropGet, PropSet, VTableLookup, InstanceOf,
- // -- calls --
- Call, IndirectCall, MethodCall, BuiltinCall, RuntimeCall, ExternCall,
- // -- closures --
- ClosureNew, ClosureCapture,
- // -- ownership (no-op at runtime, semantic for validator/passes) --
- Acquire, Release, Move, Borrow,
- // -- misc --
- Nop,
-}
-
-impl Op {
- pub fn default_effects(self) -> Effects {
- use Effects as E;
- use Op::*;
- match self {
- ConstI64 | ConstF64 | ConstStr | ConstNull
- | INeg | IAdd | ISub | IMul
- | IBitAnd | IBitOr | IBitXor | IBitNot | IShl | IShrA
- | FAdd | FSub | FMul | FDiv | FNeg
- | ICmp | FCmp | StrCmpEq | PhpIdentical
- | IToF | FToI | BoolToStr | StrToI | StrToF
- | StrLen | MixedTagOf
- | Move | Borrow | Nop => E::PURE,
- ISDiv | ISMod => E::MAY_FATAL,
- FPow => E::PURE, // libc-backed but pure for our purposes
- LoadLocal => E::READS_LOCAL,
- StoreLocal => E::WRITES_LOCAL,
- LoadGlobal => E::READS_GLOBAL,
- StoreGlobal => E::WRITES_GLOBAL,
- IToStr | FToStr | StrConcat | StrInterpolate => E::ALLOC_CONCAT,
- StrPersist => E::ALLOC_HEAP,
- StrCharAt => E::ALLOC_CONCAT | E::MAY_FATAL,
- MixedBox => E::ALLOC_HEAP,
- MixedUnbox => E::MAY_FATAL,
- Cast => E::MAY_FATAL | E::ALLOC_CONCAT, // worst-case until refined
- PhpLooseEq | Spaceship => E::MAY_DEOPT,
- ArrayNew | HashGetStr | HashGetInt | HashKeyExists => {
- // ArrayNew allocates; gets read heap.
- match self {
- ArrayNew => E::ALLOC_HEAP,
- HashGetStr | HashGetInt => E::READS_HEAP | E::MAY_FATAL,
- HashKeyExists => E::READS_HEAP,
- _ => unreachable!(),
- }
- }
- ArrayLen => E::READS_HEAP,
- ArrayGet => E::READS_HEAP | E::MAY_FATAL,
- ArraySet | HashSetStr | HashSetInt | ArrayPush => {
- E::WRITES_HEAP | E::ALLOC_HEAP
- }
- ArrayCowEnsureUnique => E::ALLOC_HEAP,
- IterStart | IterNext | IterCurrent => E::READS_HEAP | E::MAY_DEOPT,
- IterEnd => E::WRITES_HEAP,
- ObjectNew => E::ALLOC_HEAP | E::MAY_DEOPT, // constructor user code
- PropGet => E::READS_HEAP,
- PropSet => E::WRITES_HEAP,
- VTableLookup => E::READS_HEAP,
- InstanceOf => E::READS_HEAP,
- Call | IndirectCall | MethodCall => E::all() - E::REFCOUNT_OP, // worst-case; refined per-callsite from FunctionSig
- BuiltinCall => E::all() - E::REFCOUNT_OP, // refined per-builtin at builder time
- RuntimeCall => E::all() - E::REFCOUNT_OP, // refined per-routine
- ExternCall => E::READS_HEAP | E::WRITES_HEAP | E::MAY_THROW,
- ClosureNew => E::ALLOC_HEAP,
- ClosureCapture => E::READS_LOCAL,
- Acquire | Release => E::REFCOUNT_OP,
- }
- }
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::instr_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/instr.rs src/ir/tests/instr_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement Op enum and Instruction"
-```
-
----
-
-## Task 7: Implement `LocalSlot`, `Function`, `Module`
-
-**Files:**
-- Modify: `src/ir/function.rs`
-- Modify: `src/ir/module.rs`
-- Test: `src/ir/tests/function_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/function_test.rs
-use crate::ir::{Function, FunctionFlags, IrType, LocalKind, LocalSlot, Module};
-use crate::ir::block::{BasicBlock, BlockId, Terminator};
-use crate::types::PhpType;
-
-#[test]
-fn empty_function_has_no_blocks() {
- let f = Function::new("foo".to_string(), IrType::Void, PhpType::Void);
- assert_eq!(f.blocks.len(), 0);
- assert_eq!(f.name, "foo");
-}
-
-#[test]
-fn module_has_target_metadata() {
- let target = crate::codegen::platform::Target::new(
- crate::codegen::platform::Platform::MacOS,
- crate::codegen::platform::Arch::AArch64,
- );
- let m = Module::new(target);
- assert!(m.functions.is_empty());
- assert!(m.class_methods.is_empty());
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::function_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement `Function`**
-
-```rust
-// src/ir/function.rs
-//! Purpose:
-//! Defines IR-level functions, their local slots, parameters, and per-function flags.
-//!
-//! Called from:
-//! - `crate::ir::module`, builder, validator, codegen consumer
-//!
-//! Key details:
-//! - The entry block has no block parameters; function parameters surface via
-//! `LoadLocal` at the top of the entry block.
-
-use crate::ir::block::{BasicBlock, BlockId};
-use crate::ir::instr::Instruction;
-use crate::ir::types::IrType;
-use crate::ir::value::Value;
-use crate::types::PhpType;
-
-#[derive(Debug, Clone)]
-pub struct Function {
- pub name: String,
- pub params: Vec,
- pub return_type: IrType,
- pub return_php_type: PhpType,
- pub blocks: Vec,
- pub values: Vec,
- pub instructions: Vec,
- pub locals: Vec,
- pub entry: BlockId,
- pub flags: FunctionFlags,
-}
-
-impl Function {
- pub fn new(name: String, return_type: IrType, return_php_type: PhpType) -> Self {
- Self {
- name,
- params: Vec::new(),
- return_type,
- return_php_type,
- blocks: Vec::new(),
- values: Vec::new(),
- instructions: Vec::new(),
- locals: Vec::new(),
- entry: BlockId::from_raw(0),
- flags: FunctionFlags::default(),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct FunctionParam {
- pub name: String,
- pub ir_type: IrType,
- pub php_type: PhpType,
- pub by_ref: bool,
- pub variadic: bool,
-}
-
-#[derive(Debug, Clone)]
-pub struct LocalSlot {
- pub name: String,
- pub php_type: PhpType,
- pub kind: LocalKind,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum LocalKind {
- PhpVariable,
- Hidden,
- Static,
- Global,
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct FunctionFlags {
- pub is_main: bool,
- pub is_method: bool,
- pub is_closure: bool,
- pub is_generator: bool,
- pub is_static: bool,
-}
-```
-
-- [ ] **Step 4: Implement `Module`**
-
-```rust
-// src/ir/module.rs
-//! Purpose:
-//! Top-level IR container: functions, class methods, data pool, extern decls.
-//!
-//! Called from:
-//! - The AST → IR pass (phase 03) and the IR → ASM backend (phase 04).
-//!
-//! Key details:
-//! - The runtime (`__rt_*`) lives outside the module; only declarations and
-//! IDs of runtime routines used by the program are referenced.
-
-use crate::codegen::platform::Target;
-use crate::ir::function::Function;
-
-#[derive(Debug, Clone)]
-pub struct Module {
- pub target: Target,
- pub functions: Vec,
- pub class_methods: Vec,
- pub data: DataPool,
- pub extern_decls: Vec,
-}
-
-impl Module {
- pub fn new(target: Target) -> Self {
- Self {
- target,
- functions: Vec::new(),
- class_methods: Vec::new(),
- data: DataPool::default(),
- extern_decls: Vec::new(),
- }
- }
-}
-
-#[derive(Debug, Clone, Default)]
-pub struct DataPool {
- pub strings: Vec, // strings[idx] -> literal
- pub float_literals: Vec,
- pub global_names: Vec,
- pub function_names: Vec,
- pub class_names: Vec,
-}
-
-impl DataPool {
- pub fn intern_string(&mut self, s: &str) -> u32 {
- if let Some(idx) = self.strings.iter().position(|existing| existing == s) {
- return idx as u32;
- }
- let id = self.strings.len() as u32;
- self.strings.push(s.to_string());
- id
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ExternDecl {
- pub name: String,
- pub link_libs: Vec,
-}
-```
-
-- [ ] **Step 5: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::function_test`
-Expected: pass.
-
-- [ ] **Step 6: Commit**
-
-```bash
-git add src/ir/function.rs src/ir/module.rs src/ir/tests/function_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement Function, Module, DataPool"
-```
-
----
-
-## Task 8: Implement the `Builder`
-
-**Files:**
-- Modify: `src/ir/builder.rs`
-- Test: `src/ir/tests/builder_test.rs`
-
-The `Builder` API is the only sanctioned way to add blocks, instructions, and values to a `Function`. It maintains invariants the validator relies on.
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/builder_test.rs
-use crate::ir::{Builder, Effects, Function, IrType, Op, Terminator};
-use crate::types::PhpType;
-
-#[test]
-fn build_function_with_return() {
- let mut f = Function::new("ret_42".to_string(), IrType::I64, PhpType::Int);
- let mut b = Builder::new(&mut f);
- let entry = b.create_block_with_params(vec![]);
- b.set_entry(entry);
- b.position_at_end(entry);
- let v = b.emit_const_i64(42);
- b.terminate(Terminator::Return(Some(v)));
- assert_eq!(f.blocks.len(), 1);
- assert_eq!(f.values.len(), 1);
-}
-
-#[test]
-fn build_function_with_iadd_and_branch() {
- let mut f = Function::new("add_one".to_string(), IrType::I64, PhpType::Int);
- let mut b = Builder::new(&mut f);
- let entry = b.create_block_with_params(vec![(IrType::I64, PhpType::Int)]);
- b.set_entry(entry);
- let arg = f.blocks[0].params[0]; // param ValueId
- b.position_at_end(entry);
- let one = b.emit_const_i64(1);
- let sum = b.emit_iadd(arg, one);
- b.terminate(Terminator::Return(Some(sum)));
- assert_eq!(f.blocks[0].instructions.len(), 2);
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::builder_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement `Builder`**
-
-```rust
-// src/ir/builder.rs
-//! Purpose:
-//! Mutator API for constructing IR functions. Maintains SSA invariants
-//! that the validator depends on.
-//!
-//! Called from:
-//! - Phase 03: AST → IR lowering
-//! - Phase 02 tests: hand-built functions for validator and printer tests
-//!
-//! Key details:
-//! - The builder enforces single terminator per block via `terminate()`.
-//! - Builders fix effects at instruction-emit time using `Op::default_effects()`,
-//! refined by per-callsite information passed to `emit_call()`-style helpers.
-
-use crate::ir::block::{BasicBlock, BlockId, Terminator};
-use crate::ir::effects::Effects;
-use crate::ir::function::Function;
-use crate::ir::instr::{Immediate, InstId, Instruction, Op};
-use crate::ir::types::IrType;
-use crate::ir::value::{Ownership, Value, ValueDef, ValueId};
-use crate::types::PhpType;
-
-pub struct Builder<'f> {
- func: &'f mut Function,
- current: Option,
-}
-
-impl<'f> Builder<'f> {
- pub fn new(func: &'f mut Function) -> Self {
- Self { func, current: None }
- }
-
- pub fn set_entry(&mut self, block: BlockId) {
- self.func.entry = block;
- }
-
- pub fn create_block_with_params(
- &mut self,
- params: Vec<(IrType, PhpType)>,
- ) -> BlockId {
- let block_id = BlockId::from_raw(self.func.blocks.len() as u32);
- let mut param_value_ids = Vec::with_capacity(params.len());
- for (idx, (ir_ty, php_ty)) in params.into_iter().enumerate() {
- let value_id = ValueId::from_raw(self.func.values.len() as u32);
- self.func.values.push(Value {
- ir_type: ir_ty,
- ownership: Ownership::for_php_type(&php_ty),
- php_type: php_ty,
- def: ValueDef::BlockParam { block: block_id, index: idx as u16 },
- });
- param_value_ids.push(value_id);
- }
- self.func.blocks.push(BasicBlock {
- id: block_id,
- params: param_value_ids,
- instructions: Vec::new(),
- terminator: Terminator::Unreachable,
- });
- block_id
- }
-
- pub fn position_at_end(&mut self, block: BlockId) {
- self.current = Some(block);
- }
-
- pub fn terminate(&mut self, term: Terminator) {
- let block_id = self.current.expect("no block positioned");
- self.func.blocks[block_id.as_raw() as usize].terminator = term;
- }
-
- fn push_inst(
- &mut self,
- op: Op,
- operands: Vec,
- immediate: Option,
- result_type: IrType,
- result_php_type: PhpType,
- result_ownership: Ownership,
- effects: Effects,
- ) -> Option {
- let block_id = self.current.expect("no block positioned");
- let block_idx = block_id.as_raw() as usize;
- let inst_idx_in_block = self.func.blocks[block_idx].instructions.len() as u32;
- let result_id = if matches!(result_type, IrType::Void) {
- None
- } else {
- let value_id = ValueId::from_raw(self.func.values.len() as u32);
- self.func.values.push(Value {
- ir_type: result_type,
- php_type: result_php_type,
- def: ValueDef::Instruction { block: block_id, index: inst_idx_in_block },
- ownership: result_ownership,
- });
- Some(value_id)
- };
- let inst_id = InstId::from_raw(self.func.instructions.len() as u32);
- self.func.instructions.push(Instruction {
- op,
- operands,
- immediate,
- result: result_id,
- result_type,
- effects,
- });
- self.func.blocks[block_idx].instructions.push(inst_id);
- result_id
- }
-
- // -- convenience emitters --
-
- pub fn emit_const_i64(&mut self, val: i64) -> ValueId {
- self.push_inst(
- Op::ConstI64, vec![], Some(Immediate::I64(val)),
- IrType::I64, PhpType::Int, Ownership::NonHeap,
- Op::ConstI64.default_effects(),
- ).unwrap()
- }
-
- pub fn emit_const_null(&mut self) -> ValueId {
- self.push_inst(
- Op::ConstNull, vec![], None,
- IrType::I64, PhpType::Void, Ownership::NonHeap,
- Op::ConstNull.default_effects(),
- ).unwrap()
- }
-
- pub fn emit_iadd(&mut self, a: ValueId, b: ValueId) -> ValueId {
- self.push_inst(
- Op::IAdd, vec![a, b], None,
- IrType::I64, PhpType::Int, Ownership::NonHeap,
- Op::IAdd.default_effects(),
- ).unwrap()
- }
-
- pub fn emit_load_local(&mut self, slot_id: u32, ir_ty: IrType, php_ty: PhpType) -> ValueId {
- let own = Ownership::for_php_type(&php_ty);
- self.push_inst(
- Op::LoadLocal, vec![], Some(Immediate::LocalSlot(slot_id)),
- ir_ty, php_ty, own,
- Op::LoadLocal.default_effects(),
- ).unwrap()
- }
-
- pub fn emit_store_local(&mut self, slot_id: u32, val: ValueId) {
- self.push_inst(
- Op::StoreLocal, vec![val], Some(Immediate::LocalSlot(slot_id)),
- IrType::Void, PhpType::Void, Ownership::NonHeap,
- Op::StoreLocal.default_effects(),
- );
- }
-
- // Additional emitters are added in phase 03 as lowering needs them.
- // Pattern: one emit_* per Op variant; each pins the effect set from
- // Op::default_effects() (refinable for Call/Builtin/Runtime/Extern).
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::builder_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/builder.rs src/ir/tests/builder_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement Builder API"
-```
-
----
-
-## Task 9: Implement the validator
-
-**Files:**
-- Modify: `src/ir/validator.rs`
-- Test: `src/ir/tests/validator_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/validator_test.rs
-use crate::ir::{Builder, Function, IrType, Terminator, validate_function};
-use crate::types::PhpType;
-
-#[test]
-fn empty_function_fails_validation() {
- let f = Function::new("empty".to_string(), IrType::Void, PhpType::Void);
- assert!(validate_function(&f).is_err());
-}
-
-#[test]
-fn well_formed_function_passes() {
- let mut f = Function::new("ok".to_string(), IrType::I64, PhpType::Int);
- let mut b = Builder::new(&mut f);
- let entry = b.create_block_with_params(vec![]);
- b.set_entry(entry);
- b.position_at_end(entry);
- let v = b.emit_const_i64(7);
- b.terminate(Terminator::Return(Some(v)));
- assert!(validate_function(&f).is_ok());
-}
-
-#[test]
-fn return_type_mismatch_fails() {
- let mut f = Function::new("bad".to_string(), IrType::F64, PhpType::Float);
- let mut b = Builder::new(&mut f);
- let entry = b.create_block_with_params(vec![]);
- b.set_entry(entry);
- b.position_at_end(entry);
- let v = b.emit_const_i64(1);
- b.terminate(Terminator::Return(Some(v)));
- assert!(validate_function(&f).is_err());
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::validator_test`
-Expected: undefined symbols.
-
-- [ ] **Step 3: Implement the validator (structural only in Phase 02)**
-
-```rust
-// src/ir/validator.rs
-//! Purpose:
-//! Structural, type, and ownership validation for `Function`/`Module`.
-//!
-//! Called from:
-//! - After every IR pass; called from tests directly.
-//!
-//! Key details:
-//! - Phase 02 implements structural checks; ownership and dominance checks
-//! land in phase 03 once the AST → IR pass has more realistic IR to test.
-
-use crate::ir::block::{BlockId, Terminator};
-use crate::ir::function::Function;
-use crate::ir::module::Module;
-use crate::ir::value::ValueDef;
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ValidationError {
- NoBlocks,
- NoEntryBlock,
- BlockMissingTerminator(BlockId),
- DuplicateValueDef(u32),
- UseBeforeDef { value_raw: u32 },
- UnknownBlock(BlockId),
- BranchArgCountMismatch { target: BlockId, expected: usize, actual: usize },
- BranchArgTypeMismatch { target: BlockId, index: usize },
- ReturnTypeMismatch,
- UnreachableTerminatorIsReachable(BlockId),
-}
-
-pub fn validate_function(f: &Function) -> Result<(), ValidationError> {
- if f.blocks.is_empty() {
- return Err(ValidationError::NoBlocks);
- }
- if (f.entry.as_raw() as usize) >= f.blocks.len() {
- return Err(ValidationError::NoEntryBlock);
- }
-
- for block in &f.blocks {
- // structural: there's always a terminator field; Unreachable counts.
- // Type checks per terminator:
- match &block.terminator {
- Terminator::Return(Some(val)) => {
- let v = &f.values[val.as_raw() as usize];
- if v.ir_type != f.return_type {
- return Err(ValidationError::ReturnTypeMismatch);
- }
- }
- Terminator::Return(None) => {
- if !matches!(f.return_type, crate::ir::types::IrType::Void) {
- return Err(ValidationError::ReturnTypeMismatch);
- }
- }
- Terminator::Br { target, args } => {
- let dest = f.blocks.get(target.as_raw() as usize)
- .ok_or(ValidationError::UnknownBlock(*target))?;
- if dest.params.len() != args.len() {
- return Err(ValidationError::BranchArgCountMismatch {
- target: *target,
- expected: dest.params.len(),
- actual: args.len(),
- });
- }
- for (i, (param_id, arg_id)) in dest.params.iter().zip(args.iter()).enumerate() {
- let p = &f.values[param_id.as_raw() as usize];
- let a = &f.values[arg_id.as_raw() as usize];
- if p.ir_type != a.ir_type {
- return Err(ValidationError::BranchArgTypeMismatch {
- target: *target,
- index: i,
- });
- }
- }
- }
- // Remaining terminators: similar checks for CondBr, Switch, etc.
- // For phase 02 brevity, implement Br and Return only; phase 03
- // extends to all terminators with corresponding tests.
- _ => {}
- }
- }
-
- Ok(())
-}
-
-pub fn validate_module(m: &Module) -> Result<(), ValidationError> {
- for f in &m.functions {
- validate_function(f)?;
- }
- for f in &m.class_methods {
- validate_function(f)?;
- }
- Ok(())
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::validator_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/validator.rs src/ir/tests/validator_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement structural validator"
-```
-
----
-
-## Task 10: Implement the textual printer
-
-**Files:**
-- Modify: `src/ir/print.rs`
-- Test: `src/ir/tests/print_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir/tests/print_test.rs
-use crate::ir::{Builder, Function, IrType, Module, Terminator, print_module};
-use crate::types::PhpType;
-use crate::codegen::platform::{Arch, Platform, Target};
-
-#[test]
-fn prints_minimal_function() {
- let target = Target::new(Platform::MacOS, Arch::AArch64);
- let mut m = Module::new(target);
- let mut f = Function::new("ret_seven".to_string(), IrType::I64, PhpType::Int);
- let mut b = Builder::new(&mut f);
- let entry = b.create_block_with_params(vec![]);
- b.set_entry(entry);
- b.position_at_end(entry);
- let v = b.emit_const_i64(7);
- b.terminate(Terminator::Return(Some(v)));
- m.functions.push(f);
-
- let printed = print_module(&m);
- assert!(printed.contains("function ret_seven"));
- assert!(printed.contains("const_i64 7"));
- assert!(printed.contains("return v"));
-}
-```
-
-- [ ] **Step 2: Run test to verify it fails**
-
-Run: `cargo test --lib ir::tests::print_test`
-Expected: undefined symbol `print_module`.
-
-- [ ] **Step 3: Implement printer**
-
-```rust
-// src/ir/print.rs
-//! Purpose:
-//! Textual format for `Module` and `Function`. Used by `--emit-ir` and tests.
-//!
-//! Called from:
-//! - `Module::print()` wrapper, CLI in phase 03 (`--emit-ir`).
-//!
-//! Key details:
-//! - Printer-only; no parser. Output stability is a soft guarantee for tests.
-
-use std::fmt::Write;
-
-use crate::ir::block::Terminator;
-use crate::ir::function::Function;
-use crate::ir::instr::{Immediate, Op};
-use crate::ir::module::Module;
-use crate::ir::types::{IrHeapKind, IrType};
-
-pub fn print_module(m: &Module) -> String {
- let mut out = String::new();
- for f in &m.functions {
- print_function(&mut out, f, &m.data);
- out.push('\n');
- }
- for f in &m.class_methods {
- print_function(&mut out, f, &m.data);
- out.push('\n');
- }
- out
-}
-
-fn print_function(out: &mut String, f: &Function, data: &crate::ir::module::DataPool) {
- let _ = write!(out, "function {}(", f.name);
- for (i, p) in f.params.iter().enumerate() {
- if i > 0 { out.push_str(", "); }
- let _ = write!(out, "{}: {}", p.name, type_name(p.ir_type));
- }
- let _ = writeln!(out, ") -> {} {{", type_name(f.return_type));
- for block in &f.blocks {
- let _ = write!(out, " bb{}", block.id.as_raw());
- if !block.params.is_empty() {
- out.push('(');
- for (i, pid) in block.params.iter().enumerate() {
- if i > 0 { out.push_str(", "); }
- let v = &f.values[pid.as_raw() as usize];
- let _ = write!(out, "v{}: {}", pid.as_raw(), type_name(v.ir_type));
- }
- out.push(')');
- }
- out.push_str(":\n");
- for inst_id in &block.instructions {
- let inst = &f.instructions[inst_id.as_raw() as usize];
- out.push_str(" ");
- if let Some(r) = inst.result {
- let _ = write!(out, "v{} = ", r.as_raw());
- }
- let _ = write!(out, "{:?}", inst.op);
- for operand in &inst.operands {
- let _ = write!(out, " v{}", operand.as_raw());
- }
- if let Some(imm) = &inst.immediate {
- print_immediate(out, imm, data);
- }
- if !inst.effects.is_empty() {
- let _ = write!(out, " ; effects: {:?}", inst.effects);
- }
- out.push('\n');
- }
- out.push_str(" ");
- print_terminator(out, &block.terminator);
- out.push('\n');
- }
- out.push_str("}\n");
-}
-
-fn print_immediate(out: &mut String, imm: &Immediate, data: &crate::ir::module::DataPool) {
- match imm {
- Immediate::I64(v) => { let _ = write!(out, " {}", v); }
- Immediate::F64(v) => { let _ = write!(out, " {}", v); }
- Immediate::Str(idx) => {
- let s = data.strings.get(*idx as usize).map(|s| s.as_str()).unwrap_or("?");
- let _ = write!(out, " {:?}", s);
- }
- Immediate::LocalSlot(idx) => { let _ = write!(out, " slot[{}]", idx); }
- Immediate::GlobalName(idx) => {
- let s = data.global_names.get(*idx as usize).map(|s| s.as_str()).unwrap_or("?");
- let _ = write!(out, " global({})", s);
- }
- Immediate::FunctionRef(idx) => {
- let s = data.function_names.get(*idx as usize).map(|s| s.as_str()).unwrap_or("?");
- let _ = write!(out, " fn({})", s);
- }
- Immediate::BuiltinRef(id) => { let _ = write!(out, " builtin#{}", id.0); }
- Immediate::RuntimeRef(id) => { let _ = write!(out, " runtime#{}", id.0); }
- Immediate::ExternRef(idx) => { let _ = write!(out, " extern#{}", idx); }
- Immediate::ClassRef(idx) => {
- let s = data.class_names.get(*idx as usize).map(|s| s.as_str()).unwrap_or("?");
- let _ = write!(out, " class({})", s);
- }
- Immediate::MethodRef { class, method } => { let _ = write!(out, " method({},{})", class, method); }
- Immediate::PropOffset(off) => { let _ = write!(out, " prop@{}", off); }
- Immediate::HeapKind(k) => { let _ = write!(out, " kind={}", heap_kind_name(*k)); }
- Immediate::MixedTag(t) => { let _ = write!(out, " tag={}", t); }
- Immediate::CmpPredicate(p) => { let _ = write!(out, " pred={:?}", p); }
- Immediate::CastTarget(t) => { let _ = write!(out, " to={}", type_name(*t)); }
- Immediate::None => {}
- }
-}
-
-fn print_terminator(out: &mut String, term: &Terminator) {
- match term {
- Terminator::Br { target, args } => {
- let _ = write!(out, "br bb{}", target.as_raw());
- if !args.is_empty() { print_args(out, args); }
- }
- Terminator::CondBr { cond, then_block, then_args, else_block, else_args } => {
- let _ = write!(out, "cond_br v{}, bb{}", cond.as_raw(), then_block.as_raw());
- if !then_args.is_empty() { print_args(out, then_args); }
- let _ = write!(out, ", bb{}", else_block.as_raw());
- if !else_args.is_empty() { print_args(out, else_args); }
- }
- Terminator::Switch { scrutinee, cases, default } => {
- let _ = write!(out, "switch v{} [", scrutinee.as_raw());
- for (val, target, args) in cases {
- let _ = write!(out, "{} => bb{}", val, target.as_raw());
- if !args.is_empty() { print_args(out, args); }
- out.push_str(", ");
- }
- let _ = write!(out, "default => bb{}", default.0.as_raw());
- if !default.1.is_empty() { print_args(out, &default.1); }
- out.push(']');
- }
- Terminator::Return(Some(v)) => { let _ = write!(out, "return v{}", v.as_raw()); }
- Terminator::Return(None) => { out.push_str("return"); }
- Terminator::Throw(v) => { let _ = write!(out, "throw v{}", v.as_raw()); }
- Terminator::Fatal { message_id } => { let _ = write!(out, "fatal msg#{}", message_id); }
- Terminator::Unreachable => { out.push_str("unreachable"); }
- }
-}
-
-fn print_args(out: &mut String, args: &[crate::ir::value::ValueId]) {
- out.push('(');
- for (i, v) in args.iter().enumerate() {
- if i > 0 { out.push_str(", "); }
- let _ = write!(out, "v{}", v.as_raw());
- }
- out.push(')');
-}
-
-fn type_name(t: IrType) -> String {
- match t {
- IrType::I64 => "I64".to_string(),
- IrType::F64 => "F64".to_string(),
- IrType::Str => "Str".to_string(),
- IrType::Heap(k) => format!("Heap[{}]", heap_kind_name(k)),
- IrType::Void => "Void".to_string(),
- }
-}
-
-fn heap_kind_name(k: IrHeapKind) -> &'static str {
- match k {
- IrHeapKind::Array => "Array",
- IrHeapKind::Hash => "Hash",
- IrHeapKind::Object => "Object",
- IrHeapKind::Mixed => "Mixed",
- IrHeapKind::Iterable => "Iterable",
- IrHeapKind::Union => "Union",
- IrHeapKind::Buffer => "Buffer",
- }
-}
-```
-
-- [ ] **Step 4: Run test to verify it passes**
-
-Run: `cargo test --lib ir::tests::print_test`
-Expected: pass.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir/print.rs src/ir/tests/print_test.rs src/ir/tests/mod.rs
-git commit -m "feat(ir): implement textual format printer"
-```
-
----
-
-## Task 11: Run full test suite and verify zero regressions
-
-- [ ] **Step 1: Run full suite**
-
-Run:
-```bash
-cargo build
-cargo test
-cargo test -- --include-ignored
-```
-
-Expected: all green. No new code paths are exercised by the existing test suite — `src/ir/` is untouched by the compiler. New tests live under `src/ir/tests/`.
-
-- [ ] **Step 2: Run Docker Linux gates**
-
-Run:
-```bash
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-Expected: green.
-
-- [ ] **Step 3: `git diff --check` and final commit if anything left**
-
-Run:
-```bash
-git diff --check
-git status
-```
-
-If any whitespace or untracked files appear, fix and amend the last commit only if it is the most recent; otherwise create a new commit.
-
----
-
-## Exit criteria
-
-- `src/ir/` module compiles cleanly
-- All Phase 02 unit tests pass
-- Full suite (`cargo test -- --include-ignored`) passes
-- Docker Linux gates pass
-- Zero compiler warnings
-- All commits follow `feat(ir):` prefix
-- No file in `src/ir/` exceeds 500 LOC unless it is a cohesive leaf (see file-size policy in `CLAUDE.md`)
diff --git a/.plans/eir-03-ast-to-ir-lowering.md b/.plans/eir-03-ast-to-ir-lowering.md
deleted file mode 100644
index 1533478271..0000000000
--- a/.plans/eir-03-ast-to-ir-lowering.md
+++ /dev/null
@@ -1,872 +0,0 @@
-# Phase 03 — AST → IR Lowering
-
-> **Current status:** Historical phase plan. EIR lowering is now part of the
-> active production path, and the legacy direct AST backend is frozen. References
-> below to the legacy backend as production path are superseded by `AGENTS.md`
-> and `ROADMAP.md`.
-
-> **For agentic workers:** Build the pass that consumes a typed AST (after frontend/optimizer) and produces an EIR `Module`. No assembly is emitted; the existing AST → ASM backend remains the production path. Output is exercised by a new `--emit-ir` CLI flag and by a parallel test harness.
-
-**Goal:** Lower every `ExprKind` and `StmtKind` variant currently handled by `src/codegen/` into EIR, preserving PHP semantics. Validate the resulting module after each top-level lowering.
-
-**Architecture:** New module `src/ir_lower/` that mirrors the structure of `src/codegen/expr/` and `src/codegen/stmt/`. The lowering walks the AST top-down and feeds the EIR `Builder`. Ownership operations are inserted explicitly. Function dispatchers, class methods, variants, and main emission are mirrored.
-
-**Tech Stack:** Rust. Uses `src/ir/`, `src/parser/ast/`, `src/types/`, `src/codegen/program_usage/`. No new external dependencies.
-
----
-
-## File Structure
-
-All new files under `src/ir_lower/`:
-
-- Create: `src/ir_lower/mod.rs` — entry `pub fn lower_program(...)`
-- Create: `src/ir_lower/context.rs` — lowering context (local slot map, label counter, ownership table)
-- Create: `src/ir_lower/program.rs` — orchestration: functions, class methods, main, variants
-- Create: `src/ir_lower/function.rs` — function body lowering (entry block, param load, prologue/epilogue)
-- Create: `src/ir_lower/expr/mod.rs` — dispatcher
-- Create: `src/ir_lower/expr/literals.rs` — literals and constants
-- Create: `src/ir_lower/expr/variables.rs` — variable load/store, super-globals, statics
-- Create: `src/ir_lower/expr/arithmetic.rs` — binary arithmetic, unary, casts, conversions
-- Create: `src/ir_lower/expr/comparison.rs` — comparison, equality, spaceship
-- Create: `src/ir_lower/expr/strings.rs` — concat, interpolation, char access
-- Create: `src/ir_lower/expr/arrays.rs` — array literals, access, assignment, push
-- Create: `src/ir_lower/expr/objects.rs` — `new`, `->`, methods, `::`, `instanceof`
-- Create: `src/ir_lower/expr/calls.rs` — function calls, builtins, externs, closures, first-class callables
-- Create: `src/ir_lower/expr/control.rs` — ternary, short-circuit `&&`/`||`, null coalesce, throw, error suppress, print
-- Create: `src/ir_lower/expr/closures.rs` — closure expressions and arrow functions
-- Create: `src/ir_lower/expr/ptr_ffi.rs` — ptr_cast, buffer_new
-- Create: `src/ir_lower/expr/match_expr.rs` — `match` expression
-- Create: `src/ir_lower/stmt/mod.rs` — dispatcher
-- Create: `src/ir_lower/stmt/control_flow.rs` — `if`, `while`, `do_while`, `for`, `foreach`, `break`, `continue`, `switch`
-- Create: `src/ir_lower/stmt/exceptions.rs` — `try`, `catch`, `finally`, `throw`
-- Create: `src/ir_lower/stmt/assignments.rs` — `Assign`, `ArrayAssign`, `ArrayPush`, `PropertyAssign`, `StaticPropertyAssign`, all variants
-- Create: `src/ir_lower/stmt/declarations.rs` — `FunctionDecl`, `ClassDecl`, `EnumDecl`, `InterfaceDecl`, `TraitDecl`, `PackedClassDecl`, `ExternFunctionDecl`, etc. (most are no-ops at lowering since they were already handled by frontend; lowering records IDs)
-- Create: `src/ir_lower/stmt/includes.rs` — `Include`, `IncludeOnce*`
-- Create: `src/ir_lower/stmt/output.rs` — `Echo`, statement-form `Print`
-- Create: `src/ir_lower/ownership.rs` — helpers for inserting `Acquire`/`Release` on heap values
-- Create: `src/ir_lower/effects_lookup.rs` — looks up effect summaries for builtins/runtime calls from existing analysis
-- Create: `src/ir_lower/tests/` — integration tests (snapshot-based using the printer)
-- Modify: `src/lib.rs` — add `pub mod ir_lower;`
-- Modify: `src/main.rs` — add `mod ir_lower;` for the binary crate module tree
-- Modify: `src/cli.rs` — add `--emit-ir` parsing to `CliConfig`
-- Modify: `src/pipeline.rs` — honor `emit_ir` after frontend and optimization
-
----
-
-## Task 1: Wire the module and add `--emit-ir`
-
-**Files:**
-- Create: `src/ir_lower/mod.rs` (skeleton)
-- Modify: `src/lib.rs`
-- Modify: `src/main.rs` (module declaration)
-- Modify: `src/cli.rs` (CLI argument parser)
-- Modify: `src/pipeline.rs` (emit path)
-
-- [ ] **Step 1: Confirm CLI parser and module roots**
-
-Run: `grep -rln "pub(crate) fn parse_args\\|fn main" src/`
-In this branch, `src/cli.rs` owns argument parsing and `src/main.rs` owns the binary crate module declarations.
-
-- [ ] **Step 2: Create skeleton**
-
-```rust
-// src/ir_lower/mod.rs
-//! Purpose:
-//! Lowers a typed `Program` AST into an EIR `Module`. Preserves PHP semantics
-//! including evaluation order, ownership, and effect annotations.
-//!
-//! Called from:
-//! - `crate::pipeline::compile()` when `--emit-ir` or `--ir-backend` is set
-//!
-//! Key details:
-//! - Lowering is one pass over the AST; ownership ops are inserted explicitly.
-//! - Validation runs after every function lowering to catch builder bugs early.
-
-mod context;
-mod effects_lookup;
-mod expr;
-mod function;
-mod ownership;
-mod program;
-mod stmt;
-
-#[cfg(test)]
-mod tests;
-
-use std::collections::HashMap;
-
-use crate::codegen::platform::Target;
-use crate::ir::Module;
-use crate::parser::ast::Program;
-use crate::types::{
- ClassInfo, EnumInfo, ExternClassInfo, ExternFunctionSig, FunctionSig, InterfaceInfo,
- PackedClassInfo, PhpType, TypeEnv,
-};
-
-#[allow(clippy::too_many_arguments)]
-pub fn lower_program(
- program: &Program,
- global_env: &TypeEnv,
- functions: &HashMap,
- interfaces: &HashMap,
- classes: &HashMap,
- enums: &HashMap,
- packed_classes: &HashMap,
- extern_functions: &HashMap,
- extern_classes: &HashMap,
- extern_globals: &HashMap,
- target: Target,
-) -> Module {
- program::lower(
- program, global_env, functions, interfaces, classes, enums,
- packed_classes, extern_functions, extern_classes, extern_globals, target,
- )
-}
-```
-
-- [ ] **Step 3: Add `--emit-ir` to the CLI**
-
-Locate the existing argument parser. Add a boolean flag `--emit-ir`. When set:
-
-1. Run the full frontend (lex, parse, name-resolve, type-check, optimize).
-2. Call `ir_lower::lower_program(...)`.
-3. Call `ir::print_module(&module)`, print to stdout.
-4. Skip codegen, assembler, linker.
-5. Exit zero.
-
-Place implementation in `src/cli.rs` alongside `--emit-asm` and `--check`. Add an `emit_ir` field to `CliConfig`, extend the usage string, and reject `--emit-ir` with `--emit-asm` or `--check` because all three are mutually exclusive output modes.
-
-- [ ] **Step 4: Add an end-to-end test**
-
-```rust
-// tests/ir_emit_test.rs (or inside an existing tests/ file)
-#[test]
-fn emit_ir_prints_a_function_for_hello_world() {
- let temp = tests_common::compile_inline(
- "`. Stores to
-//! a local rebind the local to a fresh SSA value at the current program
-//! point; uses load from the most recent binding (no explicit phi insertion
-//! — block parameters at merge points handle joins).
-
-use std::collections::HashMap;
-
-use crate::ir::{BlockId, Builder, Function, IrType, ValueId};
-use crate::types::PhpType;
-
-pub struct LoweringContext<'a, 'f> {
- pub func: &'f mut Function,
- pub builder_state: BuilderState,
- // local name -> most recent SSA value bound
- pub local_bindings: HashMap,
- // local name -> slot index in func.locals
- pub local_slots: HashMap,
- pub loop_stack: Vec,
- pub handler_stack: Vec,
- pub captures: HashMap,
- pub label_counter: &'a std::sync::atomic::AtomicUsize,
-}
-
-pub struct BuilderState {
- pub current_block: Option,
-}
-
-pub struct LoopFrame {
- pub continue_block: BlockId,
- pub break_block: BlockId,
- pub continue_args_template: Vec,
- pub break_args_template: Vec,
-}
-
-pub struct HandlerFrame {
- pub catch_block: BlockId,
- pub finally_block: Option,
-}
-
-impl<'a, 'f> LoweringContext<'a, 'f> {
- pub fn declare_local(&mut self, name: &str, php_ty: PhpType) -> u32 {
- if let Some(idx) = self.local_slots.get(name) { return *idx; }
- let idx = self.func.locals.len() as u32;
- self.func.locals.push(crate::ir::LocalSlot {
- name: name.to_string(),
- php_type: php_ty,
- kind: crate::ir::LocalKind::PhpVariable,
- });
- self.local_slots.insert(name.to_string(), idx);
- idx
- }
-
- pub fn current_value_of(&self, name: &str) -> Option {
- self.local_bindings.get(name).copied()
- }
-
- pub fn rebind(&mut self, name: &str, value: ValueId) {
- self.local_bindings.insert(name.to_string(), value);
- }
-}
-```
-
-- [ ] **Step 2: Commit**
-
-```bash
-git add src/ir_lower/context.rs
-git commit -m "feat(ir_lower): add LoweringContext skeleton"
-```
-
----
-
-## Task 3: Implement function-level lowering
-
-**Files:**
-- Create: `src/ir_lower/function.rs`
-
-- [ ] **Step 1: Write the function lowering driver**
-
-The driver:
-1. Creates the entry block with no parameters.
-2. Allocates locals for each function parameter, each `static`, each `global`, each PHP local discovered (mirroring `src/codegen/functions/locals.rs` collection logic).
-3. Emits parameter `LoadLocal` ops at the top of the entry block.
-4. Walks the function body via `stmt::lower_stmt(...)`.
-5. If control falls off the end and the return type is `Void`, emits `Return(None)`; otherwise `Fatal` (mirroring PHP behavior where falling off a non-void function with declared return type is undefined / fatal in strict types).
-6. Runs `validate_function`.
-
-```rust
-//! Purpose:
-//! Lowers a single PHP function body into an IR `Function`.
-//!
-//! Called from:
-//! - `crate::ir_lower::program::lower`
-//!
-//! Key details:
-//! - Locals are pre-collected before lowering to ensure stable slot indices.
-//! - Validation runs at end-of-function.
-
-use crate::ir::{Builder, Function, FunctionFlags, FunctionParam, IrType, Terminator};
-use crate::ir_lower::context::LoweringContext;
-use crate::parser::ast::Stmt;
-use crate::types::{FunctionSig, PhpType, TypeEnv};
-
-pub fn lower_function(
- name: &str,
- sig: &FunctionSig,
- body: &[Stmt],
- global_env: &TypeEnv,
- // ... + frontend metadata maps; same signature as src/codegen/functions emit_function
-) -> Function {
- let return_ir = IrType::from_php(&sig.return_type);
- let mut func = Function::new(name.to_string(), return_ir, sig.return_type.clone());
- // Populate params:
- for p in &sig.params {
- func.params.push(FunctionParam {
- name: p.name.clone(),
- ir_type: IrType::from_php(&p.ty),
- php_type: p.ty.clone(),
- by_ref: p.by_ref,
- variadic: p.variadic,
- });
- }
- func.flags = FunctionFlags { /* derive */ ..Default::default() };
-
- // Build entry block.
- {
- let mut b = Builder::new(&mut func);
- let entry = b.create_block_with_params(vec![]);
- b.set_entry(entry);
- b.position_at_end(entry);
- // ... allocate locals, load params, lower body via stmt::lower_stmt
- }
-
- // Validate.
- crate::ir::validate_function(&func).expect("ir validation failed");
- func
-}
-```
-
-- [ ] **Step 2: Commit (incomplete; expanded in later tasks)**
-
-```bash
-git add src/ir_lower/function.rs
-git commit -m "feat(ir_lower): function-level lowering skeleton"
-```
-
----
-
-## Task 4: Lower literals and locals
-
-**Files:**
-- Create: `src/ir_lower/expr/mod.rs`
-- Create: `src/ir_lower/expr/literals.rs`
-- Create: `src/ir_lower/expr/variables.rs`
-- Test: `src/ir_lower/tests/literals_test.rs`
-
-- [ ] **Step 1: Write the failing test**
-
-```rust
-// src/ir_lower/tests/literals_test.rs
-use crate::ir_lower::tests::lower_and_print;
-
-#[test]
-fn lowers_int_literal_return() {
- let printed = lower_and_print("= 2, "expected at least 2 branches, got {br_count}");
-}
-
-#[test]
-fn lowers_break_branches_to_loop_break_block() {
- let printed = lower_and_print(" 1];"#);
- assert!(printed.contains("Hash"));
- assert!(printed.contains("hash_set_str"));
-}
-
-#[test]
-fn lowers_array_get_indexed() {
- let printed = lower_and_print(", ::, instanceof, method calls)
-
-**Files:**
-- Create: `src/ir_lower/expr/objects.rs`
-- Test: `src/ir_lower/tests/objects_test.rs`
-
-- [ ] **Step 1: Failing tests** (one per primitive shape)
-- [ ] **Step 2 / 3 / 4**: Implement.
-
-Notes:
-- `new ClassName(args)` → `ObjectNew(class_id)` + `Call(ctor)` with args.
-- `$obj->prop` → `PropGet(obj, offset)`. Offset is computed from `ClassInfo.property_layout` at lowering time (already known after type checking).
-- `$obj->method(args)` → `VTableLookup(obj, method_id)` returning a function pointer, then `IndirectCall(ptr, sig, args)`. For statically known final methods, skip the lookup and emit `Call(direct_fn_id)`.
-- `instanceof` → `InstanceOf(obj, class_id)`.
-- Static method/property accesses → direct `Call`/`LoadGlobal` on the class's static storage.
-- Nullsafe (`?->`) → conditional branch: if null, the chain result is null; else proceed.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir_lower/expr/objects.rs src/ir_lower/tests/objects_test.rs
-git commit -m "feat(ir_lower): lower objects and method calls"
-```
-
----
-
-## Task 10: Lower exception flow (`try`/`catch`/`finally`, `throw`)
-
-**Files:**
-- Create: `src/ir_lower/stmt/exceptions.rs`
-- Test: `src/ir_lower/tests/exceptions_test.rs`
-
-- [ ] **Step 1: Failing tests**
-- [ ] **Step 2 / 3 / 4**: Implement.
-
-Notes:
-- Push a `HandlerFrame` on `ctx.handler_stack` when entering `try`.
-- Operations with `MAY_THROW` effect lowered while a handler is on the stack get an *implicit edge* to the handler block. The structural validator does not check this yet (Phase 09 hardens it); for now, ensure cleanup paths run.
-- `throw expr` → `Throw(exc_value)` terminator. Builder records the value as owned at the throw point; the runtime takes ownership via `__rt_throw`.
-- `finally` runs on all exit paths (normal, throw, return, break, continue). Lower as a *cleanup block* that the handler-walk inserts before each exit terminator. Implementation strategy: lower the `finally` body once into a synthetic block, then duplicate-or-call from each exit point. PHP semantics require the finally body to run; calling is simpler than inlining.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add src/ir_lower/stmt/exceptions.rs src/ir_lower/tests/exceptions_test.rs
-git commit -m "feat(ir_lower): lower try/catch/finally and throw"
-```
-
----
-
-## Task 11: Lower assignments, ownership transfer ops
-
-**Files:**
-- Create: `src/ir_lower/stmt/assignments.rs`
-- Create: `src/ir_lower/ownership.rs`
-- Test: `src/ir_lower/tests/ownership_test.rs`
-
-- [ ] **Step 1: Failing tests**
-
-```rust
-#[test]
-fn array_local_assignment_releases_previous_owned_array() {
- let src = " **Current status:** Historical phase plan. EIR is now the default and only
-> active implementation backend. References below to keeping the legacy backend
-> working were Phase 04 parity scaffolding, not current feature-work policy.
-
-> **For agentic workers:** Build a new backend that takes an EIR `Module` and produces assembly. The goal is **semantic equivalence** to the current `src/codegen/` backend on every `tests/codegen/` fixture, NOT byte-identical assembly. Zero new optimizations.
-
-**Goal:** A new module `src/codegen_ir/` that walks EIR and emits ASM identical-in-behavior to today's AST → ASM path. After this phase, choosing the IR backend produces correct programs; choosing the legacy backend continues to work. No default switch yet (that's Phase 05).
-
-**Architecture:** Mirror the structure of `src/codegen/` but consume `Function` instances instead of AST. Reuse `src/codegen/abi/` register helpers and `src/codegen/runtime/` runtime emission unchanged. Reuse `src/codegen/data_section.rs` for the data pool.
-
-**Tech Stack:** Rust. No new dependencies.
-
----
-
-## File Structure
-
-All new files under `src/codegen_ir/`:
-
-- Create: `src/codegen_ir/mod.rs` — entry `pub fn generate_user_asm_from_ir(...)`
-- Create: `src/codegen_ir/context.rs` — backend state: ABI cursor, value→register map, label counter, emitter, frame layout
-- Create: `src/codegen_ir/frame.rs` — frame size calculation, prologue/epilogue emission (mirrors `src/codegen/functions/locals.rs` + frame emission)
-- Create: `src/codegen_ir/value_placement.rs` — value → register-or-slot mapping for the 1:1 backend (no real register allocator yet — values land in fixed slots, then move into ABI registers at uses)
-- Create: `src/codegen_ir/lower_inst.rs` — per-opcode emitter dispatcher
-- Create: `src/codegen_ir/lower_inst/arithmetic.rs` — IAdd/ISub/IMul/ISDiv/INeg/bitwise/shifts
-- Create: `src/codegen_ir/lower_inst/floats.rs` — FAdd/FSub/.../FPow
-- Create: `src/codegen_ir/lower_inst/comparison.rs` — ICmp/FCmp/StrCmp/PhpLooseEq/PhpIdentical/Spaceship
-- Create: `src/codegen_ir/lower_inst/conversion.rs` — IToF/FToI/IToStr/.../MixedBox/MixedUnbox/Cast
-- Create: `src/codegen_ir/lower_inst/strings.rs` — StrConcat/StrLen/StrCharAt/StrPersist/StrInterpolate
-- Create: `src/codegen_ir/lower_inst/arrays.rs` — ArrayNew/Get/Set/Push/CowEnsureUnique
-- Create: `src/codegen_ir/lower_inst/hashes.rs` — HashGet*/Set*/KeyExists/Iter*
-- Create: `src/codegen_ir/lower_inst/objects.rs` — ObjectNew/PropGet/PropSet/VTableLookup/InstanceOf
-- Create: `src/codegen_ir/lower_inst/calls.rs` — Call/IndirectCall/MethodCall/BuiltinCall/RuntimeCall/ExternCall, closure ops
-- Create: `src/codegen_ir/lower_inst/ownership.rs` — Acquire/Release/Move/Borrow (no-op for Move/Borrow at runtime)
-- Create: `src/codegen_ir/lower_term.rs` — Br/CondBr/Switch/Return/Throw/Fatal/Unreachable
-- Create: `src/codegen_ir/block_emit.rs` — block label naming, block ordering, jumps between blocks
-- Create: `src/codegen_ir/tests/` — integration tests using `compile_and_run`-style helpers with the IR backend
-- Modify: `src/lib.rs` — add `pub mod codegen_ir;`
-- Modify: `src/main.rs` — add `mod codegen_ir;` for the binary crate module tree
-- Modify: `src/pipeline.rs` — when the IR backend feature is requested, call `codegen_ir::generate_user_asm_from_ir(...)` instead of `codegen::generate_user_asm(...)`
-
----
-
-## Task 1: Wire the module and add `--ir-backend` CLI flag
-
-**Files:**
-- Create: `src/codegen_ir/mod.rs` (skeleton)
-- Modify: `src/lib.rs`
-- Modify: `src/main.rs` (module declaration)
-- Modify: `src/cli.rs` (CLI argument parser)
-- Modify: `src/pipeline.rs`
-
-- [ ] **Step 1: Module skeleton**
-
-```rust
-// src/codegen_ir/mod.rs
-//! Purpose:
-//! IR-consuming assembly backend. Produces functionally equivalent ASM to
-//! `src/codegen/` while reading from an EIR `Module` instead of an AST.
-//!
-//! Called from:
-//! - `crate::pipeline::compile()` when the `--ir-backend` flag is set.
-//!
-//! Key details:
-//! - Phase 04: 1:1 lowering, no optimization, no register allocation.
-//! - Phase 06: adds linear-scan register allocator.
-//! - Phase 09: replaces `src/codegen/` as the default.
-
-mod block_emit;
-mod context;
-mod frame;
-mod lower_inst;
-mod lower_term;
-mod value_placement;
-
-#[cfg(test)]
-mod tests;
-
-use crate::codegen::platform::Target;
-use crate::ir::Module;
-
-pub fn generate_user_asm_from_ir(module: &Module, _gc_stats: bool, _heap_debug: bool) -> String {
- // Implementation in Task 3.
- unimplemented!("phase 04")
-}
-```
-
-- [ ] **Step 2: CLI flag**
-
-Add `--ir-backend` (boolean) to `src/cli.rs` and thread it through `CliConfig`. When set:
-1. Run frontend + optimizer + AST → IR lowering.
-2. Call `codegen_ir::generate_user_asm_from_ir(...)`.
-3. Hand the assembly to the existing assembler/linker pipeline (unchanged).
-4. Produce a binary as usual.
-
-- [ ] **Step 3: Pipeline switch**
-
-In `src/pipeline.rs::compile`, add a branch:
-
-```rust
-let user_asm = if config.ir_backend {
- let ir = crate::ir_lower::lower_program(/* args */);
- crate::codegen_ir::generate_user_asm_from_ir(&ir, gc_stats, heap_debug)
-} else {
- crate::codegen::generate_user_asm(/* args */)
-};
-```
-
-The runtime path (`codegen::generate_runtime`) stays identical regardless of backend choice.
-
-- [ ] **Step 4: Stub test passes**
-
-```rust
-// tests/ir_backend_smoke_test.rs
-#[test]
-#[ignore]
-fn ir_backend_hello_world() {
- // Will be unignored in Task 4 once smallest emitters work.
- let out = compile_and_run_with(["--ir-backend"], ", // ValueId raw -> negative offset from x29
- pub total_slot_bytes: usize,
-}
-
-pub fn allocate(func: &Function) -> ValuePlacement {
- let mut placement = ValuePlacement {
- slot_of: HashMap::new(),
- total_slot_bytes: 0,
- };
- let mut offset: i32 = 0;
- for v in &func.values {
- let bytes = bytes_for(v.ir_type);
- if bytes == 0 { continue; }
- offset -= bytes as i32;
- let raw = match &v.def {
- crate::ir::ValueDef::BlockParam { .. } | crate::ir::ValueDef::Instruction { .. } => {
- // raw is the index in func.values
- offset_into_func_values(func, v)
- }
- };
- placement.slot_of.insert(raw, offset);
- }
- placement.total_slot_bytes = (-offset as usize).next_multiple_of(16);
- placement
-}
-
-fn bytes_for(t: IrType) -> usize {
- match t {
- IrType::I64 | IrType::F64 | IrType::Heap(_) => 8,
- IrType::Str => 16,
- IrType::Void => 0,
- }
-}
-
-fn offset_into_func_values(func: &Function, v: &crate::ir::Value) -> u32 {
- func.values.iter()
- .position(|other| std::ptr::eq(other, v))
- .map(|i| i as u32)
- .unwrap()
-}
-```
-
-- [ ] **Step 2: Implement frame emission**
-
-```rust
-// src/codegen_ir/frame.rs
-//! Purpose:
-//! Emits function prologue and epilogue using ABI helpers from `crate::codegen::abi`.
-//!
-//! Called from:
-//! - `crate::codegen_ir::context::emit_function`
-//!
-//! Key details:
-//! - Frame size = locals + value-placement slots, 16-byte aligned.
-
-use crate::codegen::abi::{self, frame};
-use crate::codegen::emit::Emitter;
-use crate::codegen::platform::Arch;
-use crate::ir::{Function, LocalKind};
-
-pub fn emit_prologue(emitter: &mut Emitter, func: &Function, frame_bytes: usize) {
- // Mirror src/codegen/functions/* prologue emission. Use abi::frame helpers.
- // ARM64:
- // sub sp, sp, #N
- // stp x29, x30, [sp, #N-16]
- // add x29, sp, #N-16
- // X86_64:
- // push rbp
- // mov rbp, rsp
- // sub rsp, #N
- abi::frame::emit_function_prologue(emitter, frame_bytes);
-}
-
-pub fn emit_epilogue(emitter: &mut Emitter, frame_bytes: usize) {
- abi::frame::emit_function_epilogue(emitter, frame_bytes);
-}
-```
-
-(Confirm helper names by reading `src/codegen/abi/frame.rs` and adjusting; the existing AST backend already has the primitives we need.)
-
-- [ ] **Step 3: Test placement**
-
-A minimal test asserts that allocation for a function with one I64 value yields one 8-byte slot.
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/codegen_ir/frame.rs src/codegen_ir/value_placement.rs src/codegen_ir/tests/frame_test.rs
-git commit -m "feat(codegen_ir): implement Phase 04 value placement and frame emission"
-```
-
----
-
-## Task 3: Implement block emission and terminators
-
-**Files:**
-- Create: `src/codegen_ir/block_emit.rs`
-- Create: `src/codegen_ir/lower_term.rs`
-- Test: `src/codegen_ir/tests/block_test.rs`
-
-- [ ] **Step 1: Failing test**
-
-```rust
-#[test]
-fn emits_label_for_each_block() {
- let asm = compile_simple_ir_to_asm(/* fn returning const 7 */);
- assert!(asm.contains(".LBB_"));
-}
-```
-
-- [ ] **Step 2 / 3: Implement**
-
-Block labels: `format!(".LBB_{fn_name}_{block_id}")`. Use `ctx.next_label(prefix)` style with a global counter so labels don't collide across functions (mirroring the existing emitter's label-counter discipline in `src/codegen/context.rs`).
-
-Block ordering: topological, with the entry block first. Fall-through optimization where possible: if the terminator of block N is `Br(N+1)`, omit the branch instruction.
-
-Terminator lowering:
-- `Br(target, args)` — move args into the target's parameter slots, then `b .LBB_target`.
-- `CondBr(cond, then, else_)` — load `cond` into a register, `cbz reg, else_label`, branch to `then_label` (or fall-through if next block is `then`).
-- `Switch` — if dense, emit a jump table from `src/codegen/runtime/data.rs` patterns; otherwise chained `cmp`+`b.eq`.
-- `Return(v)` — move `v` into the result register (`x0`/`d0`/`x1+x2` per type), branch to epilogue.
-- `Throw(v)` — call `__rt_throw` with `v` in `x0`.
-- `Fatal { message_id }` — call `__rt_fatal` with message pointer.
-- `Unreachable` — `udf #0` on ARM64 / `ud2` on x86_64.
-
-Block-parameter move scheduling: when a `Br(target, [v0, v1])` enters a block with params `[p0, p1]`, copy `v0 -> slot(p0)`, `v1 -> slot(p1)`. Handle parallel-move semantics with a topological sort + cycle break (one extra scratch register, mirroring how `src/codegen/abi/values.rs` handles register shuffles).
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/codegen_ir/block_emit.rs src/codegen_ir/lower_term.rs src/codegen_ir/tests/block_test.rs
-git commit -m "feat(codegen_ir): emit blocks, labels, and terminators"
-```
-
----
-
-## Task 4: Lower scalar arithmetic and comparison
-
-**Files:**
-- Create: `src/codegen_ir/lower_inst.rs` (dispatcher)
-- Create: `src/codegen_ir/lower_inst/arithmetic.rs`
-- Create: `src/codegen_ir/lower_inst/floats.rs`
-- Create: `src/codegen_ir/lower_inst/comparison.rs`
-- Modify: `tests/ir_backend_smoke_test.rs` to unignore the hello-world test
-
-- [ ] **Step 1: Failing test**
-
-Unignore `ir_backend_hello_world`. It expects `42`. Will fail until scalar lowering is in place.
-
-- [ ] **Step 2: Implement scalar arithmetic**
-
-Each op emits the same instruction sequence the AST backend emits today, with operands loaded from the placement slots:
-
-```
-// Op::IAdd, operands [a, b], result r:
-ldr x1, [x29, #slot(a)] // load left operand from slot
-ldr x0, [x29, #slot(b)] // load right operand from slot
-add x0, x1, x0 // add operands, result in x0
-str x0, [x29, #slot(r)] // store result to result slot
-```
-
-Each `emitter.instruction(...)` MUST have a `// ` comment at column 81 per `CLAUDE.md` policy.
-
-- [ ] **Step 3: Run smoke test**
-
-Run: `cargo test --test ir_backend_smoke_test`
-Expected: pass (prints `42`).
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/codegen_ir/lower_inst.rs src/codegen_ir/lower_inst/arithmetic.rs src/codegen_ir/lower_inst/floats.rs src/codegen_ir/lower_inst/comparison.rs tests/ir_backend_smoke_test.rs
-git commit -m "feat(codegen_ir): lower scalar arithmetic and comparison"
-```
-
----
-
-## Task 5: Lower constants, locals, conversions
-
-**Files:**
-- Create: `src/codegen_ir/lower_inst/conversion.rs`
-- Modify: `src/codegen_ir/lower_inst.rs`
-- Test: `src/codegen_ir/tests/conversions_test.rs`
-
-- [ ] **Step 1 / 2 / 3 / 4 / 5**: TDD pattern as before.
-
-`ConstI64` / `ConstF64` / `ConstStr` / `ConstNull` lower to literal-load patterns from the existing AST emitter (use the same data-section interning).
-
-`LoadLocal(slot_id)` / `StoreLocal(slot_id)` lower to `ldr`/`str` against the PHP local slot (separate from value-placement slots — locals are named, values are anonymous). `IToF` / `FToI` / `IToStr` etc. call the existing `__rt_*` routines.
-
-- [ ] **Step 6: Commit**
-
----
-
-## Task 6: Lower strings, arrays, hashes, objects
-
-Five separate commits, one per file:
-
-- [ ] **Step 1**: `lower_inst/strings.rs` — reuse `__rt_str_concat`, `__rt_str_persist`, `__rt_str_char_at` runtime routines. The emitter just sets up arguments and `bl`s.
-
-- [ ] **Step 2**: `lower_inst/arrays.rs` — reuse `__rt_array_new`, `__rt_array_get_int`, `__rt_array_set_int`, `__rt_array_push`, `__rt_array_cow_ensure`.
-
-- [ ] **Step 3**: `lower_inst/hashes.rs` — reuse `__rt_hash_*`, `__rt_iter_*`.
-
-- [ ] **Step 4**: `lower_inst/objects.rs` — reuse `__rt_object_alloc`, inline vtable lookup pattern from existing class-methods emission.
-
-- [ ] **Step 5**: Tests + commits per cluster.
-
-Read the existing emitter file (e.g., `src/codegen/expr/arrays/`) for each cluster and reproduce its assembly pattern.
-
----
-
-## Task 7: Lower calls, builtins, externs, closures
-
-**Files:**
-- Create: `src/codegen_ir/lower_inst/calls.rs`
-
-This is the most surface-area cluster. Recipe:
-
-- **`Call(func_id, args)`**:
- 1. Look up `func_id` in `module.data.function_names` → function symbol.
- 2. Use ABI helpers (`abi::values::*`) to place args in registers/stack: int args in `x0..x7`, floats in `d0..d7`, stack overflow per ABI.
- 3. Emit `bl ` (ARM64) / `call ` (x86_64).
- 4. Move result from result register into the result value's slot.
-
-- **`IndirectCall(fn_ptr, sig, args)`**: same but `blr ` / `call `.
-
-- **`MethodCall(obj, method_id, args)`**:
- 1. Load `obj` into the first arg register (`x0` ARM64, `rdi` x86_64).
- 2. `VTableLookup` produced a function-pointer SSA value earlier; emit indirect call.
- 3. Same arg placement and result handling.
-
-- **`BuiltinCall(builtin, args)`**: dispatch to the existing builtin emitters in `src/codegen/builtins/`. The IR knows which builtin, the lowering knows where to find its codegen. This is the largest opcode — *do not* re-implement each builtin; call into the existing emitter helpers with the operand values placed appropriately.
-
-- **`RuntimeCall(rt_routine, args)`**: similar to `BuiltinCall` but always inline `bl __rt_`.
-
-- **`ExternCall(name, args)`**: same as `Call` but with C-ABI conversions on strings (already handled by `src/codegen/ffi.rs` — reuse).
-
-- **`ClosureNew`**: allocate closure object on heap with captured environment. Reuse `src/codegen/functions/closures.rs` patterns.
-
-- [ ] **Step 1 / 2 / 3 / 4**: TDD pattern.
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(codegen_ir): lower calls, builtins, externs, closures"
-```
-
----
-
-## Task 8: Lower ownership ops
-
-**Files:**
-- Create: `src/codegen_ir/lower_inst/ownership.rs`
-
-- `Acquire(v)` → `bl __rt_incref` with `v` in `x0`.
-- `Release(v)` → `bl __rt_decref_any` with `v` in `x0`.
-- `Move(v)` → no-op (semantic only). Validator already checked balance.
-- `Borrow(v)` → no-op (semantic only).
-
-- [ ] **Step 1: Failing test**: a fixture that allocates an array, assigns over it (forcing release of the old), and checks `--gc-stats` reports correct alloc/free counts.
-
-- [ ] **Step 2 / 3 / 4: Implement and verify.**
-
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(codegen_ir): lower Acquire/Release ownership ops"
-```
-
----
-
-## Task 9: Function-level driver
-
-**Files:**
-- Create: `src/codegen_ir/context.rs`
-- Modify: `src/codegen_ir/mod.rs`
-
-- [ ] **Step 1: Implement `emit_function`**
-
-```rust
-// src/codegen_ir/context.rs
-pub struct BackendContext<'e> {
- pub emitter: &'e mut Emitter,
- pub placement: ValuePlacement,
- pub func: &'e Function,
- pub data: &'e mut DataSection,
-}
-
-pub fn emit_function(emitter: &mut Emitter, data: &mut DataSection, func: &Function) {
- let placement = value_placement::allocate(func);
- let locals_bytes = func.locals.iter().map(|s| s.php_type.stack_size()).sum::();
- let frame_bytes = ((locals_bytes + placement.total_slot_bytes).next_multiple_of(16)) + 16;
-
- emit_function_label(emitter, &func.name);
- frame::emit_prologue(emitter, func, frame_bytes);
- block_emit::emit_blocks(emitter, &mut BackendContext {
- emitter, placement, func, data,
- });
- // Epilogue is emitted by Return terminators.
-}
-```
-
-- [ ] **Step 2: Wire `generate_user_asm_from_ir`**
-
-```rust
-// src/codegen_ir/mod.rs (fix the unimplemented! from Task 1)
-pub fn generate_user_asm_from_ir(module: &Module, _gc_stats: bool, _heap_debug: bool) -> String {
- let mut emitter = Emitter::new(module.target);
- if module.target.arch == Arch::X86_64 {
- emitter.emit_text_prelude();
- }
- let mut data = DataSection::new();
- // Translate module.data into data_section.
- seed_data_section(&mut data, &module.data);
-
- for f in &module.functions {
- context::emit_function(&mut emitter, &mut data, f);
- }
- for f in &module.class_methods {
- context::emit_function(&mut emitter, &mut data, f);
- }
- // Interface return wrappers and main emission stay in src/codegen/ for now;
- // Phase 09 either ports them or keeps them via a shared helper.
- crate::codegen::emit_main_and_finalize_from_ir(/* ... */)
-}
-```
-
-The `emit_main_and_finalize_from_ir` is a new entry point that mirrors `src/codegen/main_emission.rs::emit_main_and_finalize` but reads the `main` function body from the IR module (the AST → IR pass produces a `main` function). Add this helper to `src/codegen/main_emission.rs` in a separate commit if its surface is too small to deserve its own file.
-
-- [ ] **Step 3: Commit**
-
-```bash
-git commit -m "feat(codegen_ir): wire generate_user_asm_from_ir end-to-end"
-```
-
----
-
-## Task 10: Parity testing — run every existing codegen test with `--ir-backend`
-
-**Files:**
-- Create: `src/codegen_ir/tests/parity.rs`
-
-- [ ] **Step 1: Replay codegen tests through `--ir-backend`**
-
-For each `tests/codegen/*.rs` test, the standard helper is `compile_and_run`. Add a sibling `compile_and_run_ir` (or a flag on the existing helper) that compiles with `--ir-backend` set. Then **mirror every test** in a new `tests/ir_backend_parity/` directory.
-
-A pragmatic approach: a single integration test file that walks the public test fixtures (where available) and asserts each `--ir-backend` output matches the `compile_and_run` (legacy) output. Where tests use inline source, the parity test re-runs the same inline source with the IR backend.
-
-The Docker Linux scripts must be run as well. Use `./scripts/test-linux-*.sh ir_backend` to filter to the new tests.
-
-- [ ] **Step 2: Iterate until parity**
-
-This is where most of Phase 04's calendar time lives. Each parity failure points at:
-- A missing or incorrectly emitted opcode
-- An ABI mismatch
-- An ownership op imbalance
-- A wrong effect annotation
-- A wrong block-parameter move sequencing
-
-Fix one failure at a time, with a focused regression test in `tests/ir_backend_parity/`.
-
-- [ ] **Step 3: Commit each fix individually**
-
-Aim for many small commits (`fix(codegen_ir): correct stack alignment for str result`, `fix(codegen_ir): release order at if-merge`, etc.). This keeps the PR review tractable and lets the team revert specific regressions if needed.
-
----
-
-## Task 11: Final parity gate
-
-- [ ] **Step 1: Run the gates**
-
-```bash
-cargo build
-cargo test # legacy backend (default)
-cargo test -- --include-ignored # legacy backend incl. SDL2 etc.
-cargo test --features ir-backend # if a feature flag is used; else use env or a separate test runner
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-The new `tests/ir_backend_parity/` corpus must pass with `--ir-backend` enabled at the binary level.
-
-- [ ] **Step 2: Benchmark parity (no regression expected, possibly small slowdown)**
-
-Run the benchmark harness with `--ir-backend` and verify there is *no major regression* compared to the legacy backend on compute benchmarks. A 0–20% slowdown is acceptable in Phase 04 — Phase 06 recovers this and more.
-
-If a >50% slowdown appears on any benchmark, stop and diagnose before claiming Phase 04 done. Likely cause: redundant slot loads (every value spilled and re-loaded). This is expected, but pathological cases (deep call chains, hot loop bodies) may need a minimal Phase 04 mitigation: keep up to 4 SSA values "pinned" in callee-saved scratch registers across the loop body. Document the mitigation in `docs/internals/the-codegen.md`.
-
-- [ ] **Step 3: Commit final benchmark output as a baseline**
-
-```bash
-git add benchmarks/ir_backend_phase4_baseline.json
-git commit -m "perf(codegen_ir): record phase 04 baseline (parity, no opt yet)"
-```
-
----
-
-## Exit criteria
-
-- Every `tests/codegen/` fixture passes with `--ir-backend` set.
-- Docker Linux gates green for `--ir-backend`.
-- Benchmark regression bounded (≤20% slowdown vs legacy on any single benchmark).
-- Legacy backend untouched and still default.
-- Zero compiler warnings.
-- Each `emitter.instruction(...)` call in `src/codegen_ir/` has a column-81 `//` comment per project policy.
diff --git a/.plans/eir-05-switchover-behind-flag.md b/.plans/eir-05-switchover-behind-flag.md
deleted file mode 100644
index b959248161..0000000000
--- a/.plans/eir-05-switchover-behind-flag.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Phase 05 - Switch EIR Backend to Default
-
-> **Current status:** Historical phase plan. The EIR switch is complete, and
-> the legacy AST backend is now frozen as a diagnostic-only fallback. Do not use
-> this plan to justify new work on the legacy backend; follow `AGENTS.md` and
-> `ROADMAP.md` instead.
-
-> **For agentic workers:** This historical phase shipped the EIR backend as the
-> user-facing default. The legacy AST backend is now frozen behind the explicit
-> `--ast-backend` diagnostic fallback while the old emitter remains in-tree. No
-> optimization work belongs in the legacy path.
-
-**Goal:** Move the default backend to EIR now that parity gates are green. Keep
-the legacy backend frozen as a diagnostic-only fallback until it is removed in
-Phase 09.
-
-**Architecture:** `src/cli.rs` owns backend selection, and `src/pipeline.rs`
-uses that selection to choose AST -> ASM or AST -> EIR -> ASM. CI should run
-the EIR backend on the normal path and keep only frozen fallback smoke coverage.
-
-**Tech Stack:** Rust, existing CLI argument parser, existing CI configuration.
-
----
-
-## File Structure
-
-No new modules. Edits to:
-
-- Modify: `src/cli.rs` - backend flags and default
-- Modify: `src/pipeline.rs` - backend selection plumbing if needed
-- Modify: `docs/internals/the-codegen.md` - document EIR as the default backend
-- Modify: `docs/internals/the-ir.md` - promote from preview language to default-backend language
-- Modify: `.github/workflows/*.yml` or equivalent - CI coverage for default EIR and legacy fallback
-- Modify: `Cargo.toml` - version bump once Phase 05 ships
-- Modify: `ROADMAP.md` - tick off Phase 05 entries
-
----
-
-## Task 1: Make EIR the default backend
-
-**Files:**
-- Modify: `src/cli.rs`
-- Modify: `src/pipeline.rs`
-- Modify: `docs/internals/the-ir.md`
-
-- [ ] **Step 1: Change backend selection defaults**
-
-In `src/cli.rs`, make EIR the default backend. Keep:
-
-- `--ir-backend`: explicit selection of the default EIR backend
-- `--ast-backend`: explicit fallback to the legacy AST backend
-
-If both flags are set, fail with:
-
-```text
-cannot use --ir-backend and --ast-backend together
-```
-
-- [ ] **Step 2: Document backend selection**
-
-Add a "Backend selection" section to `docs/internals/the-ir.md`:
-
-```markdown
-## Backend selection
-
-The compiler currently supports two backends:
-
-- EIR backend (default): lowers the AST to EIR first, then emits ASM from EIR
-- `--ast-backend`: legacy fallback that walks the AST directly and emits ASM
-
-Use `--ir-backend` to select the default explicitly. Use `--ast-backend` only
-as a temporary fallback while the legacy emitter remains in-tree.
-
-The EIR backend is feature-complete against the supported test matrix. It
-currently does not register-allocate; register allocation is a later v0.24.x
-task.
-```
-
-- [ ] **Step 3: Commit**
-
-```bash
-git add src/cli.rs src/pipeline.rs docs/internals/the-ir.md
-git commit -m "feat: switch default backend to eir"
-```
-
----
-
-## Task 2: CI default-EIR and legacy fallback coverage
-
-**Files:**
-- Modify: `.github/workflows/ci.yml`
-
-- [ ] **Step 1: Audit existing CI**
-
-Run `find .github/workflows -name '*.yml'`, or check the repository's
-equivalent CI configuration.
-
-- [ ] **Step 2: Run ordinary CI with the default EIR backend**
-
-Ensure the normal CI path runs `cargo test` without selecting the legacy
-backend. This makes EIR the default gate.
-
-- [ ] **Step 3: Add explicit legacy fallback coverage**
-
-Add a smaller explicit legacy job or matrix entry that invokes the compiler
-with `--ast-backend` for the codegen/parity surfaces that still need fallback
-coverage.
-
-- [ ] **Step 4: Add an EIR benchmark job**
-
-Benchmarks already exist. Add a job that runs them with the default EIR backend
-and stores results in `benchmarks/results/ir/`.
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add .github/workflows/ci.yml
-git commit -m "ci: make eir the default backend gate"
-```
-
----
-
-## Task 3: Mark legacy AST backend deprecated
-
-**Files:**
-- Modify: `src/cli.rs`
-- Modify: `src/codegen/mod.rs`
-- Modify: `docs/internals/the-codegen.md`
-
-- [ ] **Step 1: Emit a deprecation note**
-
-When `--ast-backend` is passed, print to stderr:
-
-```text
-warning: --ast-backend is deprecated and will be removed in v0.26.0. The EIR
-backend is now the default. See docs/internals/the-ir.md for details.
-```
-
-Only emit once per compilation. Do not fail.
-
-- [ ] **Step 2: Add `#[deprecated]` to crate-internal entry points used only by the legacy backend**
-
-Only on public-ish entry points such as `codegen::generate_user_asm` and
-`codegen::generate`. Internal helpers stay untouched.
-
-- [ ] **Step 3: Update `the-codegen.md`**
-
-At the top of `docs/internals/the-codegen.md`, explain that EIR is the default
-backend and that the legacy AST backend is documented only as a frozen
-diagnostic-only fallback implementation.
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/cli.rs src/codegen/mod.rs docs/internals/the-codegen.md
-git commit -m "chore: mark ast backend deprecated"
-```
-
----
-
-## Task 4: Release notes and version
-
-**Files:**
-- Modify: `Cargo.toml`
-- Modify: release notes location, if present
-- Modify: `ROADMAP.md`
-
-- [ ] **Step 1: Write release notes**
-
-Add a "Backend rework - what changed and why" section to the v0.24.0 release
-notes. Explain:
-
-- EIR is now the default backend
-- `--ir-backend` remains accepted as an explicit default selection
-- `--ast-backend` is the temporary legacy fallback
-- register allocation and IR optimization are still future work
-
-- [ ] **Step 2: Bump version**
-
-```toml
-[package]
-version = "0.24.0"
-```
-
-- [ ] **Step 3: Update ROADMAP**
-
-Mark EIR parity and the default switch completed under v0.24.x. Leave register
-allocation and register-pressure mitigation as the remaining v0.24.x work.
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add Cargo.toml ROADMAP.md
-git commit -m "chore: prepare v0.24 eir default"
-```
-
----
-
-## Exit Criteria
-
-- EIR backend is the default backend.
-- `--ir-backend` still works as an explicit default selection.
-- `--ast-backend` still works as a legacy fallback and warns on use.
-- Normal CI uses the EIR backend.
-- Legacy fallback has explicit targeted CI coverage.
-- Benchmark suite has an EIR baseline.
-- Documentation reflects the new default.
-- ROADMAP is up to date.
diff --git a/.plans/eir-07-peephole-and-local-opts.md b/.plans/eir-07-peephole-and-local-opts.md
deleted file mode 100644
index 09a6a6e25f..0000000000
--- a/.plans/eir-07-peephole-and-local-opts.md
+++ /dev/null
@@ -1,335 +0,0 @@
-# Phase 07 — IR-Level Peephole and Local Optimizations
-
-> **For agentic workers:** Add a set of small, local, fixed-point IR optimizations that pay off measurably. Expected gain: 5–10% on top of Phase 06.
-
-**Goal:** Implement on-the-IR optimizations that the AST-level passes cannot reach: redundant move elimination, dead store elimination, identity arithmetic folding, branch chain shortening, and per-block constant propagation.
-
-**Architecture:** Each optimization is a `Pass` over a `Function` producing a new `Function` (or mutating in place where safe). All passes run inside a fixed-point loop until no pass reports changes. Validation runs after every pass during testing.
-
-**Tech Stack:** Rust, EIR module. No new dependencies.
-
----
-
-## File Structure
-
-- Create: `src/ir_passes/peephole.rs` — peephole patterns
-- Create: `src/ir_passes/dead_inst.rs` — DCE on instructions whose result is unused and effect-free
-- Create: `src/ir_passes/dead_store.rs` — eliminate stores to locals never re-read
-- Create: `src/ir_passes/branch_simplify.rs` — chain-of-branches collapse, constant-cond folding
-- Create: `src/ir_passes/identity_fold.rs` — `x + 0`, `x * 1`, `x | 0`, `x ^ 0`, `x - x`, etc.
-- Create: `src/ir_passes/move_elimination.rs` — remove `Move`/`Borrow` no-ops that the allocator left untouched
-- Create: `src/ir_passes/pass_driver.rs` — fixed-point driver
-- Modify: `src/ir_passes/mod.rs` — re-export
-- Modify: `src/pipeline.rs` — invoke pass pipeline after lowering, before register allocation
-
----
-
-## Task 1: Pass driver
-
-**Files:**
-- Create: `src/ir_passes/pass_driver.rs`
-- Test: `src/ir_passes/tests/pass_driver_test.rs`
-
-- [ ] **Step 1: Failing test**
-
-```rust
-#[test]
-fn driver_runs_to_fixed_point() {
- let mut func = build_function_with_redundant_pattern();
- let report = run_pass_pipeline(&mut func, PassConfig::default());
- assert!(report.iterations >= 1);
- assert!(report.changed);
- assert!(no_redundant_patterns(&func));
-}
-```
-
-- [ ] **Step 2: Implement driver**
-
-```rust
-//! Purpose:
-//! Drives the fixed-point loop over IR optimization passes.
-//!
-//! Called from:
-//! - `crate::pipeline::compile()` after lowering, before register allocation
-//!
-//! Key details:
-//! - Passes report `Changed::Yes` if they modified the function; the driver
-//! re-runs the pipeline until all passes report `Changed::No` in one round.
-//! - Capped at `MAX_ITERATIONS` to prevent oscillation bugs.
-
-use crate::ir::Function;
-
-const MAX_ITERATIONS: usize = 16;
-
-#[derive(Debug, Default)]
-pub struct PassConfig {
- pub run_peephole: bool,
- pub run_identity_fold: bool,
- pub run_branch_simplify: bool,
- pub run_dead_inst: bool,
- pub run_dead_store: bool,
- pub run_move_elimination: bool,
-}
-
-impl PassConfig {
- pub fn all_phase_07() -> Self {
- Self {
- run_peephole: true,
- run_identity_fold: true,
- run_branch_simplify: true,
- run_dead_inst: true,
- run_dead_store: true,
- run_move_elimination: true,
- }
- }
-}
-
-pub struct PassReport {
- pub iterations: usize,
- pub changed: bool,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Changed { Yes, No }
-
-pub fn run_pass_pipeline(func: &mut Function, cfg: PassConfig) -> PassReport {
- let mut total_changed = false;
- for i in 0..MAX_ITERATIONS {
- let mut iter_changed = false;
- if cfg.run_move_elimination {
- iter_changed |= matches!(super::move_elimination::run(func), Changed::Yes);
- }
- if cfg.run_identity_fold {
- iter_changed |= matches!(super::identity_fold::run(func), Changed::Yes);
- }
- if cfg.run_peephole {
- iter_changed |= matches!(super::peephole::run(func), Changed::Yes);
- }
- if cfg.run_branch_simplify {
- iter_changed |= matches!(super::branch_simplify::run(func), Changed::Yes);
- }
- if cfg.run_dead_inst {
- iter_changed |= matches!(super::dead_inst::run(func), Changed::Yes);
- }
- if cfg.run_dead_store {
- iter_changed |= matches!(super::dead_store::run(func), Changed::Yes);
- }
- total_changed |= iter_changed;
- crate::ir::validate_function(func).expect("invariant broken by IR pass");
- if !iter_changed {
- return PassReport { iterations: i + 1, changed: total_changed };
- }
- }
- panic!("IR pass pipeline did not converge in {} iterations", MAX_ITERATIONS);
-}
-```
-
-- [ ] **Step 3 / 4 / 5: Test and commit**
-
-```bash
-git commit -m "feat(ir_passes): pass driver with fixed-point loop"
-```
-
----
-
-## Task 2: Identity fold
-
-**Files:**
-- Create: `src/ir_passes/identity_fold.rs`
-- Test: `src/ir_passes/tests/identity_fold_test.rs`
-
-Patterns to fold:
-
-- `IAdd(x, 0)` / `IAdd(0, x)` → `x`
-- `ISub(x, 0)` → `x`
-- `IMul(x, 1)` / `IMul(1, x)` → `x`
-- `IMul(x, 0)` / `IMul(0, x)` → `ConstI64(0)`
-- `IBitAnd(x, 0)` → `ConstI64(0)`
-- `IBitOr(x, 0)` → `x`
-- `IBitXor(x, x)` → `ConstI64(0)`
-- `IShl(x, 0)` → `x`, `IShrA(x, 0)` → `x`
-- `FMul(x, 1.0)` → `x`; do NOT fold `FAdd(x, 0.0)` (signed zero / -0.0)
-- `ICmp(Eq, x, x)` → `ConstI64(1)` if `x` is not `F64` (NaN-aware)
-
-Each pattern is one match arm.
-
-- [ ] **Step 1**: failing tests for at least 8 patterns above.
-- [ ] **Step 2 / 3 / 4**: implement and verify.
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(ir_passes): identity-fold pass"
-```
-
----
-
-## Task 3: Peephole patterns
-
-**Files:**
-- Create: `src/ir_passes/peephole.rs`
-- Test: `src/ir_passes/tests/peephole_test.rs`
-
-Patterns to match:
-
-- **Redundant load after store**: `StoreLocal(slot, v); LoadLocal(slot)` → use `v` directly.
-- **Box-unbox cancellation**: `MixedBox(v); MixedUnbox(_, tag=v.tag)` → `v`.
-- **Acquire-Release pairs with no use between**: cancel if `v` is not observed otherwise.
-- **String literal concat folding**: `StrConcat(ConstStr a, ConstStr b)` → `ConstStr "ab"` (interning into data pool).
-- **Coalesced casts**: `IToF; FToI` → identity (only when round-trip is exact, e.g., integer literals).
-
-Each pattern is one helper in `peephole.rs`. The driver calls them all and reports whether anything matched.
-
-- [ ] **Step 1: Failing tests per pattern**
-- [ ] **Step 2 / 3 / 4: Implement**
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(ir_passes): peephole pass with five core patterns"
-```
-
----
-
-## Task 4: Branch simplification
-
-**Files:**
-- Create: `src/ir_passes/branch_simplify.rs`
-- Test: `src/ir_passes/tests/branch_simplify_test.rs`
-
-Patterns:
-
-- **Constant-condition `CondBr`**: if the condition is `ConstI64(0)`, replace with `Br(else_block)`. If `ConstI64(non-zero)`, replace with `Br(then_block)`.
-- **Trivially equivalent CondBr branches**: if `then_block == else_block` (and args match), replace with `Br(then_block)`.
-- **Empty-block jump-thread**: a block that has no instructions and an unconditional `Br(next)` is collapsed: predecessors branch directly to `next`. Safe only when the block has no parameters whose values come from multiple distinct predecessor sources (otherwise parameter substitution must be applied).
-- **Dead block removal**: after simplification, blocks unreachable from the entry are removed.
-
-- [ ] **Step 1 / 2 / 3 / 4 / 5**: TDD pattern.
-
-```bash
-git commit -m "feat(ir_passes): branch simplification pass"
-```
-
----
-
-## Task 5: Dead instruction elimination
-
-**Files:**
-- Create: `src/ir_passes/dead_inst.rs`
-- Test: `src/ir_passes/tests/dead_inst_test.rs`
-
-An instruction is dead when:
-
-1. Its result is unused by any other instruction or terminator.
-2. Its effects are `PURE` or limited to `READS_LOCAL` (which is also safe to drop since no observable state changes).
-
-Iterate until fixed point. (The driver gives us fixed point for free; one pass per iteration suffices.)
-
-Patterns to be careful about:
-- Calls with unused returns may still have side effects. Check `Effects::may_mutate() || may_observe()` — if either, keep the call.
-- `Acquire`/`Release` ops change refcounts; never remove individually. The move-elimination pass handles paired removal.
-
-- [ ] **Step 1 / 2 / 3 / 4 / 5**
-
-```bash
-git commit -m "feat(ir_passes): dead instruction elimination"
-```
-
----
-
-## Task 6: Dead store elimination
-
-**Files:**
-- Create: `src/ir_passes/dead_store.rs`
-- Test: `src/ir_passes/tests/dead_store_test.rs`
-
-A `StoreLocal(slot, v)` is dead when:
-
-1. There is no `LoadLocal(slot)` reachable from this store before either another `StoreLocal(slot, _)` (no reads in between) or end-of-function.
-2. The slot is not aliased through `Global`/`Static`/`ByRef` semantics.
-
-Care:
-- Slots backing `Global` / `Static` PHP locals can be observed via `LoadGlobal`. The dead-store pass must ignore these slots.
-- Slots passed to closures via capture: also not dead — the closure may read them later.
-
-Implementation: per-slot, walk the CFG; mark stores reachable to a load as live; the remaining stores are dead.
-
-- [ ] **Step 1 / 2 / 3 / 4 / 5**
-
-```bash
-git commit -m "feat(ir_passes): dead store elimination"
-```
-
----
-
-## Task 7: Move elimination
-
-**Files:**
-- Create: `src/ir_passes/move_elimination.rs`
-- Test: `src/ir_passes/tests/move_elimination_test.rs`
-
-After register allocation, the lowering may emit `mov reg_a, reg_b` because the allocator placed both ends in different registers. Some of those can be removed:
-
-- `mov reg, reg` (same register) — always remove.
-- Paired `Acquire(v); Release(v)` with no intervening use of `v` — remove both.
-- `Move(v)` IR ops are no-ops at codegen; remove them from the IR after they've served their semantic-validation purpose. This is a "make it tidy" pass; impact on perf is negligible.
-
-This pass runs both *before* register allocation (to remove pure `Move`/`Borrow` ops that influence interval computation) and *after* (to clean up identity `mov`s the allocator left).
-
-- [ ] **Step 1 / 2 / 3 / 4 / 5**
-
-```bash
-git commit -m "feat(ir_passes): move elimination"
-```
-
----
-
-## Task 8: Integrate and benchmark
-
-**Files:**
-- Modify: `src/pipeline.rs`
-- Test: existing benchmark suite
-
-- [ ] **Step 1: Run the pipeline**
-
-```rust
-let mut func = func; // mutable
-ir_passes::pass_driver::run_pass_pipeline(&mut func, PassConfig::all_phase_07());
-let alloc = ir_passes::allocate_registers(&func, target);
-```
-
-Apply to every function in the module.
-
-- [ ] **Step 2: Full test suite**
-
-```bash
-cargo test
-cargo test -- --include-ignored
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-Expected: all green.
-
-- [ ] **Step 3: Benchmark gate**
-
-Run benchmarks. Expected: **5–10% improvement** over Phase 06 baseline on compute benchmarks, smaller (~2–3%) on benchmarks dominated by builtin calls or I/O.
-
-If less than 3% improvement, investigate which pass is paying off:
-1. Run the pipeline with `PassConfig` selectively disabling each pass.
-2. Identify the pass with the smallest contribution; ensure it's actually firing on real workloads (instrument the pass driver with per-pass change counts).
-
-- [ ] **Step 4: Commit**
-
-```bash
-git commit -m "feat(ir_passes): integrate phase 07 optimization pipeline"
-```
-
----
-
-## Exit criteria
-
-- All passes integrated and gated by validator
-- Pipeline converges in ≤16 iterations on every test fixture
-- Test suite green
-- Benchmark suite shows additional ≥3% gain over Phase 06 baseline (cumulatively, ≥18% vs Phase 04)
-- Per-pass change counts logged so future tuning has a baseline
-- Documentation: `docs/internals/the-ir.md` gains "Optimization passes" section listing each pass and what it does
diff --git a/.plans/eir-08-cse-licm-inlining.md b/.plans/eir-08-cse-licm-inlining.md
deleted file mode 100644
index 1c696bd6e5..0000000000
--- a/.plans/eir-08-cse-licm-inlining.md
+++ /dev/null
@@ -1,391 +0,0 @@
-# Phase 08 — CSE, LICM, and Inlining
-
-> **For agentic workers:** Add global IR-level optimizations: common subexpression elimination, loop-invariant code motion, and small-function inlining. Expected cumulative gain: 10–20% on loop-heavy workloads on top of Phase 07.
-
-**Goal:** Implement three optimizations that need basic-block reasoning and dominance information — operations that AST-level passes cannot do.
-
-**Architecture:** Three new IR passes plus a dominance analysis. All passes are *opt-in* via the existing `PassConfig` from Phase 07 and run in the fixed-point pipeline.
-
-**Tech Stack:** Rust, existing IR module. No new dependencies.
-
----
-
-## File Structure
-
-- Create: `src/ir_passes/dominance.rs` — dominator tree, dominance frontier, immediate-dominator table
-- Create: `src/ir_passes/cse.rs` — common subexpression elimination
-- Create: `src/ir_passes/licm.rs` — loop-invariant code motion
-- Create: `src/ir_passes/loops.rs` — natural loop detection (back edges, headers, loop bodies)
-- Create: `src/ir_passes/inliner.rs` — inline candidate analysis and inlining transformation
-- Modify: `src/ir_passes/mod.rs` — re-export
-- Modify: `src/ir_passes/pass_driver.rs` — add Phase 08 passes to `PassConfig`
-- Modify: `src/pipeline.rs` — call the inliner before the rest of the pass pipeline
-
----
-
-## Task 1: Dominance analysis
-
-**Files:**
-- Create: `src/ir_passes/dominance.rs`
-- Test: `src/ir_passes/tests/dominance_test.rs`
-
-- [ ] **Step 1: Failing test**
-
-```rust
-#[test]
-fn entry_dominates_all_blocks() {
- let f = build_diamond_cfg();
- let dom = compute_dominance(&f);
- let entry = f.entry;
- for b in &f.blocks {
- assert!(dom.dominates(entry, b.id));
- }
-}
-
-#[test]
-fn diamond_blocks_dominator_is_entry() {
- let f = build_diamond_cfg();
- let dom = compute_dominance(&f);
- let merge = f.blocks.last().unwrap().id;
- assert_eq!(dom.immediate_dominator(merge), Some(f.entry));
-}
-```
-
-- [ ] **Step 2: Implement**
-
-Use the Lengauer-Tarjan algorithm or the simpler iterative Cooper-Harvey-Kennedy method. The latter is ~30 LOC and good enough for our small CFGs.
-
-```rust
-//! Purpose:
-//! Computes the immediate-dominator table and offers dominance queries.
-//!
-//! Called from:
-//! - `crate::ir_passes::cse::run`
-//! - `crate::ir_passes::licm::run`
-//!
-//! Key details:
-//! - Cooper-Harvey-Kennedy iterative dominators; small CFG so cost is negligible.
-
-use std::collections::HashMap;
-
-use crate::ir::{BlockId, Function};
-
-pub struct Dominance {
- pub idom: HashMap>,
- pub depth: HashMap,
-}
-
-impl Dominance {
- pub fn immediate_dominator(&self, b: BlockId) -> Option {
- self.idom.get(&b).copied().flatten()
- }
- pub fn dominates(&self, a: BlockId, b: BlockId) -> bool {
- if a == b { return true; }
- let mut cur = self.idom.get(&b).copied().flatten();
- while let Some(c) = cur {
- if c == a { return true; }
- cur = self.idom.get(&c).copied().flatten();
- }
- false
- }
-}
-
-pub fn compute_dominance(func: &Function) -> Dominance {
- unimplemented!()
-}
-```
-
-- [ ] **Step 3 / 4 / 5: Test, pass, commit**
-
-```bash
-git commit -m "feat(ir_passes): dominance analysis"
-```
-
----
-
-## Task 2: Loop detection
-
-**Files:**
-- Create: `src/ir_passes/loops.rs`
-- Test: `src/ir_passes/tests/loops_test.rs`
-
-- [ ] **Step 1: Failing tests**: detect a simple `while` loop and a nested loop.
-
-- [ ] **Step 2: Implement**
-
-Identify back edges (edges `u -> v` where `v` dominates `u`). Each back edge defines a natural loop: the set of blocks from which the source `u` is reachable without going through `v`.
-
-```rust
-pub struct LoopInfo {
- pub header: BlockId,
- pub body: HashSet,
- pub back_edges: Vec<(BlockId, BlockId)>,
- pub preheader: Option, // synthesized by LICM if needed
-}
-```
-
-- [ ] **Step 3 / 4 / 5**: Test and commit.
-
----
-
-## Task 3: Common Subexpression Elimination
-
-**Files:**
-- Create: `src/ir_passes/cse.rs`
-- Test: `src/ir_passes/tests/cse_test.rs`
-
-CSE finds two instructions that produce the same value and replaces uses of the second with the first.
-
-Conservative version (per-block):
-- Within each block, hash instructions by `(op, operands, immediate)`.
-- If a later instruction matches an earlier one *and* its effects are `PURE`, replace its result with the earlier one and drop the later instruction.
-
-Global version (cross-block, dominance-aware):
-- Same hashing.
-- When two matching instructions are in different blocks, only replace if the earlier dominates the later.
-- Effects must remain `PURE` (or contain no observable bits).
-
-Start with per-block CSE; add cross-block CSE in a follow-up commit.
-
-- [ ] **Step 1**: failing tests for at least these patterns:
- - `v1 = load_local slot[a]; v2 = load_local slot[a]` → `v2` becomes `v1`. (`LoadLocal` is `READS_LOCAL`, not pure — but it is safe if no `StoreLocal slot[a]` lies between def of `v1` and use point. The pass tracks per-block stores.)
- - `v1 = iadd p0, p1; v2 = iadd p0, p1` → `v2` becomes `v1`.
- - `v1 = array_get arr, 0; v2 = array_get arr, 0` — NOT CSE'd (effect = `READS_HEAP | MAY_FATAL`); careful here.
-
-- [ ] **Step 2 / 3 / 4: Implement**
-
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(ir_passes): common subexpression elimination"
-```
-
----
-
-## Task 4: Loop-Invariant Code Motion
-
-**Files:**
-- Create: `src/ir_passes/licm.rs`
-- Test: `src/ir_passes/tests/licm_test.rs`
-
-LICM hoists `PURE` instructions out of loops when:
-1. The instruction's operands are all loop-invariant (defined outside the loop or loop-invariant themselves).
-2. The instruction has no side effects (`Effects::PURE`).
-3. The instruction dominates all loop exits *OR* it is safe to execute speculatively (always true for pure instructions).
-
-The pass needs a *preheader* block — a block on the loop's entry edge with no other predecessors — to hoist into. If the loop doesn't have one, the pass synthesizes one.
-
-Patterns this catches that AST-level passes miss:
-- Loop-invariant arithmetic: `for ($i = 0; $i < count($a); $i++) { $x = $a[0] + 1; ... }` — the `+ 1` and the `array_get a[0]` (if proven side-effect-free for the loop's array) are hoisted.
-- Loop-invariant `count($a)`: when the loop doesn't mutate `$a`, the array length read is hoisted.
-
-Care:
-- `count()` reads heap — not strictly pure. Adding a "safe within loop body" predicate based on memory dependencies is more sophisticated than the basic pass. For Phase 08, only hoist `PURE` operations. Memory-aware LICM is a v0.26 follow-up.
-
-- [ ] **Step 1: Failing tests** (3-5 patterns)
-- [ ] **Step 2 / 3 / 4: Implement**
-
-```rust
-//! Purpose:
-//! Hoists pure loop-invariant instructions out of natural loops.
-//!
-//! Called from:
-//! - `crate::ir_passes::pass_driver` when `run_licm` is enabled.
-//!
-//! Key details:
-//! - Synthesizes a preheader if the loop entry edge has multiple predecessors.
-//! - Only hoists `Effects::PURE` instructions; memory-effect-aware LICM is a
-//! v0.26 follow-up.
-
-pub fn run(func: &mut crate::ir::Function) -> super::pass_driver::Changed {
- // 1. compute dominance
- // 2. detect loops
- // 3. for each loop, find invariant instructions
- // 4. ensure preheader exists; if not, synthesize one
- // 5. move invariants to preheader, in dependency order
- unimplemented!()
-}
-```
-
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(ir_passes): loop-invariant code motion"
-```
-
----
-
-## Task 5: Small-function inliner
-
-**Files:**
-- Create: `src/ir_passes/inliner.rs`
-- Test: `src/ir_passes/tests/inliner_test.rs`
-
-The inliner picks **small callees with no recursion and no exception handlers** and substitutes their bodies into the call site. Benefits:
-
-- Eliminates call/return overhead.
-- Exposes additional CSE/LICM opportunities post-inline.
-- Allows the register allocator to see the inlined body as part of the caller's live ranges.
-
-Cost model:
-
-- **Size threshold**: callee instruction count ≤ `INLINE_THRESHOLD` (start at 24 instructions).
-- **No-recursion**: callee does not reach itself transitively in the call graph.
-- **No exception handlers**: callee has no `Throw`, no `Try`. (Inlining a function that may throw makes the caller's stack-unwinding contract more complex; defer this case.)
-- **No generator / fiber bodies**: not inlinable.
-- **No closures with hidden environment unless the captures are loop-invariant or constant**: defer to later.
-
-The pass runs *once* before the rest of the pipeline (not per fixed-point iteration), because each inlining substantially increases function size and IR pass cost.
-
-- [ ] **Step 1: Failing test**
-
-```rust
-#[test]
-fn inlines_small_pure_callee() {
- // function double($x) { return $x * 2; }
- // function compute() { return double(7); }
- // After inline, compute() should contain "imul 7, 2" directly.
- let module = build_two_function_module();
- inline_pass(&mut module, InlineConfig::default());
- let compute = &module.functions[1];
- let has_call = compute.instructions.iter().any(|i| matches!(i.op, crate::ir::Op::Call));
- assert!(!has_call);
-}
-```
-
-- [ ] **Step 2: Implement**
-
-```rust
-//! Purpose:
-//! Substitutes small callees into call sites to eliminate call overhead and
-//! expose downstream optimization.
-//!
-//! Called from:
-//! - `crate::pipeline::compile()` once after AST → IR lowering, before the
-//! IR pass pipeline.
-//!
-//! Key details:
-//! - Inlining renumbers ValueIds, BlockIds, slot indices. The transformation
-//! must rebuild internal tables consistently.
-
-use crate::ir::{Function, Module};
-
-pub struct InlineConfig {
- pub size_threshold: usize,
- pub max_inline_depth: usize,
-}
-
-impl Default for InlineConfig {
- fn default() -> Self {
- Self { size_threshold: 24, max_inline_depth: 3 }
- }
-}
-
-pub fn inline_pass(module: &mut Module, cfg: InlineConfig) {
- let call_graph = build_call_graph(module);
- let candidates = select_inline_candidates(module, &call_graph, &cfg);
- for cand in candidates {
- inline_at_callsites(module, cand);
- }
-}
-
-fn build_call_graph(_m: &Module) -> CallGraph { unimplemented!() }
-fn select_inline_candidates(_m: &Module, _cg: &CallGraph, _cfg: &InlineConfig) -> Vec { unimplemented!() }
-fn inline_at_callsites(_m: &mut Module, _c: InlineCandidate) { unimplemented!() }
-
-struct CallGraph;
-struct InlineCandidate;
-```
-
-- [ ] **Step 3 / 4: Test and verify**
-
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "feat(ir_passes): inline small pure functions"
-```
-
----
-
-## Task 6: Integration and benchmark gate
-
-**Files:**
-- Modify: `src/pipeline.rs`
-- Modify: `src/ir_passes/pass_driver.rs`
-
-- [ ] **Step 1: Wire passes into pipeline**
-
-```rust
-// Phase 08 pipeline shape
-let mut module = ir_lower::lower_program(/* ... */);
-ir_passes::inliner::inline_pass(&mut module, InlineConfig::default());
-
-for func in module.functions.iter_mut().chain(module.class_methods.iter_mut()) {
- ir_passes::pass_driver::run_pass_pipeline(func, PassConfig::all_phase_08());
-}
-
-// Phase 06 register allocation runs as before.
-```
-
-`PassConfig::all_phase_08()` enables Phase 07 passes plus CSE and LICM.
-
-- [ ] **Step 2: Full test gate**
-
-```bash
-cargo test
-cargo test -- --include-ignored
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-Expected: all green.
-
-- [ ] **Step 3: Benchmark gate**
-
-```bash
-./scripts/run-benchmarks.sh --backend ir --baseline phase7
-```
-
-Expected gains:
-- **Tight loop benchmarks** (fibonacci, prime sieve, mandelbrot): ≥10% over Phase 07.
-- **Function-call-heavy benchmarks**: ≥15% over Phase 07 (inlining payoff).
-- **I/O-bound or builtin-bound benchmarks**: ≤5% over Phase 07 (CSE/LICM payoff small here).
-
-If gain is below target, prioritize debugging:
-1. Verify each pass is firing — pass_driver instrumentation per-pass change counts.
-2. Look at `--emit-asm` output of a hot benchmark function before and after to ensure inlining and hoisting actually happened.
-3. Check the register allocator isn't getting *worse* (more spills) on inlined bodies. If so, raise the int pool or refine the inline threshold.
-
-- [ ] **Step 4: Commit**
-
-```bash
-git commit -m "feat(ir_passes): integrate phase 08 pipeline (CSE, LICM, inlining)"
-```
-
----
-
-## Task 7: Documentation
-
-**Files:**
-- Modify: `docs/internals/the-ir.md`
-
-- [ ] **Step 1: Document passes**
-
-Add an "Optimization passes" section listing each pass, its purpose, the patterns it catches, and the expected gain. Cross-reference `src/ir_passes/` files.
-
-- [ ] **Step 2: Commit**
-
-```bash
-git commit -m "docs: document IR optimization passes"
-```
-
----
-
-## Exit criteria
-
-- CSE, LICM, inlining integrated
-- All tests green on macOS and Docker Linux gates
-- Benchmark suite shows ≥10% additional improvement on loop-heavy / call-heavy benchmarks vs Phase 07 baseline
-- Cumulative improvement vs Phase 04 baseline: ≥30% on compute benchmarks
-- Documentation updated
-- Zero compiler warnings
diff --git a/.plans/eir-09-legacy-cleanup.md b/.plans/eir-09-legacy-cleanup.md
deleted file mode 100644
index bdae8e4b51..0000000000
--- a/.plans/eir-09-legacy-cleanup.md
+++ /dev/null
@@ -1,357 +0,0 @@
-# Phase 09 — Legacy Cleanup and Documentation Consolidation
-
-> **For agentic workers:** Final phase. Remove the legacy AST → ASM backend, consolidate documentation, audit naming, and freeze the IR contract for the v1.0 release.
-
-**Goal:** Delete `src/codegen/` paths that the IR backend has replaced. Rename `src/codegen_ir/` to `src/codegen/`. Move historical docs aside. Finalize `docs/internals/the-ir.md` as the canonical codegen explanation alongside `the-codegen.md`.
-
-**Architecture:** Pure cleanup. No new code, no functional change. The IR backend is already the default since Phase 05.
-
-**Tech Stack:** Rust, git, project documentation tooling.
-
----
-
-## File Structure
-
-Heavy delete/rename phase. Approximate map:
-
-- Delete: `src/codegen/expr.rs`, `src/codegen/expr/` (replaced by `src/codegen_ir/lower_inst/`)
-- Delete: `src/codegen/stmt.rs`, `src/codegen/stmt/` (replaced by `src/codegen_ir/lower_inst/` + `src/codegen_ir/lower_term.rs`)
-- Delete: `src/codegen/builtins/` *if* every builtin already routes through the IR backend; otherwise keep until parity is full
-- Delete: `src/codegen/class_methods.rs`, `src/codegen/functions/` (replaced by IR function emission)
-- Keep and merge into new home: `src/codegen/abi/`, `src/codegen/runtime/`, `src/codegen/emit.rs`, `src/codegen/data_section.rs`, `src/codegen/platform.rs`, `src/codegen/ffi.rs`
-- Rename: `src/codegen_ir/` → `src/codegen/` (after the legacy delete)
-- Move historical doc: `docs/internals/legacy-codegen.md` (created from the current `the-codegen.md` content describing the AST-walker, preserved for historical reference)
-- Refresh: `docs/internals/the-codegen.md` to describe the IR-based pipeline as the only pipeline
-- Refresh: `docs/internals/the-ir.md` to remove "preview" / "phase" language and present EIR as the canonical IR
-
----
-
-## Task 1: Verify no consumers of legacy paths
-
-**Files:**
-- Inspect: `src/`, `tests/`
-
-- [ ] **Step 1: Find every reference to the legacy backend**
-
-Run:
-```bash
-grep -rn "generate_user_asm\|--ast-backend\|crate::codegen::expr\|crate::codegen::stmt" src/ tests/ | grep -v codegen_ir
-```
-
-Expected: only the deprecation shim from Phase 05, the `--ast-backend` CLI flag (which is about to be removed), and possibly some test helpers.
-
-If any production path still requires the legacy code, document it in `docs/internals/legacy-codegen.md` and defer that path's removal to a follow-up task.
-
-- [ ] **Step 2: Remove `--ast-backend` from CLI**
-
-In `src/cli.rs`, remove the `--ast-backend` flag. The argument parser should error with `unknown flag: --ast-backend; the EIR backend is the only supported backend`.
-
-- [ ] **Step 3: Run full gate**
-
-```bash
-cargo test
-cargo test -- --include-ignored
-```
-
-Expected: green. (Some tests may use `--ast-backend` — update them or remove if redundant.)
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/cli.rs
-git commit -m "feat(cli): remove deprecated --ast-backend flag"
-```
-
----
-
-## Task 2: Delete the AST emitter
-
-**Files:**
-- Delete: `src/codegen/expr/`, `src/codegen/expr.rs`
-- Delete: `src/codegen/stmt/`, `src/codegen/stmt.rs`
-- Delete: `src/codegen/class_methods.rs`
-- Delete: `src/codegen/functions/`
-- Delete: `src/codegen/main_emission.rs`
-- Delete: `src/codegen/prescan.rs`, `src/codegen/program_usage/`, `src/codegen/program_usage.rs`
-- Delete: `src/codegen/function_variants.rs`
-- Delete: `src/codegen/interface_wrappers.rs`
-- Delete: `src/codegen/driver_support.rs`
-- Modify: `src/codegen/mod.rs` to remove deleted modules
-- Modify: anything that referenced deleted symbols
-
-- [ ] **Step 1: Make the deletion**
-
-```bash
-git rm -r src/codegen/expr src/codegen/expr.rs
-git rm -r src/codegen/stmt src/codegen/stmt.rs
-git rm src/codegen/class_methods.rs
-git rm -r src/codegen/functions
-git rm src/codegen/main_emission.rs
-git rm src/codegen/prescan.rs src/codegen/program_usage.rs
-git rm -r src/codegen/program_usage
-git rm src/codegen/function_variants.rs src/codegen/interface_wrappers.rs src/codegen/driver_support.rs
-```
-
-- [ ] **Step 2: Fix `src/codegen/mod.rs`**
-
-Remove `mod` lines for deleted modules. Remove `pub use` re-exports of deleted symbols.
-
-- [ ] **Step 3: Build until clean**
-
-```bash
-cargo build
-```
-
-Resolve every error, one at a time. Each one is a leftover caller of a deleted symbol. The IR backend should replace it (in most cases, the path was already through the IR backend; the leftover is a stale `pub use` or test helper).
-
-- [ ] **Step 4: Run full gate**
-
-```bash
-cargo test
-cargo test -- --include-ignored
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-- [ ] **Step 5: Commit**
-
-```bash
-git commit -m "refactor: delete legacy AST → ASM emitter"
-```
-
-This will be one large commit (likely 10k+ lines deleted, including tests that exercised the AST emitter specifically). That is acceptable for a delete-only change.
-
----
-
-## Task 3: Rename `codegen_ir` to `codegen`
-
-**Files:**
-- Rename: `src/codegen_ir/` → (moved into `src/codegen/`)
-- Modify: every `use crate::codegen_ir::*` → `use crate::codegen::*`
-
-- [ ] **Step 1: Decide the new layout**
-
-The new `src/codegen/` mixes:
-- IR consumer (was `src/codegen_ir/lower_inst/`, `lower_term.rs`, `block_emit.rs`, `frame.rs`, `value_placement.rs`, `context.rs`)
-- Preserved shared infrastructure (`abi/`, `runtime/`, `emit.rs`, `data_section.rs`, `platform.rs`, `ffi.rs`)
-
-New layout:
-
-```
-src/codegen/
-├── mod.rs # entry: generate_user_asm(module, ...)
-├── abi/ # unchanged
-├── runtime/ # unchanged
-├── emit.rs # unchanged
-├── data_section.rs # unchanged
-├── platform.rs # unchanged
-├── ffi.rs # unchanged
-├── frame.rs # was codegen_ir/frame.rs
-├── block_emit.rs # was codegen_ir/block_emit.rs
-├── context.rs # was codegen_ir/context.rs
-├── value_placement.rs # was codegen_ir/value_placement.rs
-├── lower_inst/ # was codegen_ir/lower_inst/
-│ ├── arithmetic.rs
-│ ├── arrays.rs
-│ ├── calls.rs
-│ ├── ...
-└── lower_term.rs # was codegen_ir/lower_term.rs
-```
-
-- [ ] **Step 2: Execute the moves**
-
-```bash
-git mv src/codegen_ir/frame.rs src/codegen/frame.rs
-git mv src/codegen_ir/block_emit.rs src/codegen/block_emit.rs
-git mv src/codegen_ir/context.rs src/codegen/context.rs
-git mv src/codegen_ir/value_placement.rs src/codegen/value_placement.rs
-git mv src/codegen_ir/lower_inst src/codegen/lower_inst
-git mv src/codegen_ir/lower_term.rs src/codegen/lower_term.rs
-git rm src/codegen_ir/mod.rs
-rmdir src/codegen_ir
-```
-
-- [ ] **Step 3: Fix imports**
-
-```bash
-grep -rln "crate::codegen_ir" src/ tests/ | xargs sed -i '' 's/crate::codegen_ir/crate::codegen/g' # macOS
-# or on Linux:
-# grep -rln "crate::codegen_ir" src/ tests/ | xargs sed -i 's/crate::codegen_ir/crate::codegen/g'
-```
-
-Edit `src/codegen/mod.rs` to expose the renamed submodules.
-
-Edit `src/lib.rs` to remove `pub mod codegen_ir`.
-
-- [ ] **Step 4: Build**
-
-```bash
-cargo build
-```
-
-Fix the remaining stragglers.
-
-- [ ] **Step 5: Full test gate**
-
-```bash
-cargo test
-cargo test -- --include-ignored
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-```
-
-- [ ] **Step 6: Commit**
-
-```bash
-git commit -m "refactor: rename codegen_ir to codegen"
-```
-
----
-
-## Task 4: Documentation consolidation
-
-**Files:**
-- Modify: `docs/internals/the-codegen.md`
-- Move: `docs/internals/legacy-codegen.md` (from old `the-codegen.md` contents)
-- Modify: `docs/internals/the-ir.md`
-- Modify: `docs/internals/architecture.md`
-- Modify: `docs/internals/how-elephc-works.md`
-- Modify: `docs/README.md`
-
-- [ ] **Step 1: Preserve the historical doc**
-
-```bash
-cp docs/internals/the-codegen.md docs/internals/legacy-codegen.md
-```
-
-Open `legacy-codegen.md`. Edit the frontmatter:
-- title: "Legacy AST → ASM emitter (historical)"
-- description: "Description of the AST-walking emitter that was the default through v0.23. Retained for historical reference."
-- sidebar.order: 100 (last)
-
-Add a top-line note:
-
-```markdown
-> **Status:** Removed in v0.26.0. The current codegen pipeline is documented in
-> [the-codegen.md](the-codegen.md) and [the-ir.md](the-ir.md). This page is
-> retained for historical context only.
-```
-
-- [ ] **Step 2: Rewrite `the-codegen.md`**
-
-The new `the-codegen.md` describes the IR-based pipeline:
-1. EIR functions arrive from `crate::ir_lower`.
-2. IR-level optimization passes run via the pass pipeline.
-3. Linear-scan register allocation produces an `Allocation`.
-4. Block-by-block emission lowers each instruction to ASM using the same ABI helpers and runtime calls as before.
-
-- [ ] **Step 3: Cross-link**
-
-In `docs/internals/the-ir.md`, add a "See also" footer linking to `the-codegen.md`. In `the-codegen.md`, add the same back-link.
-
-- [ ] **Step 4: Update `architecture.md`**
-
-The pipeline diagram now includes "AST → EIR" and "EIR → ASM" boxes instead of one monolithic "AST → ASM" box.
-
-- [ ] **Step 5: Update `docs/README.md`**
-
-Add `the-ir.md` to the internals index. Add `legacy-codegen.md` under a "Historical" subsection.
-
-- [ ] **Step 6: Commit**
-
-```bash
-git add docs/
-git commit -m "docs: consolidate codegen documentation around the IR pipeline"
-```
-
----
-
-## Task 5: Final verification
-
-**Files:** the whole repo
-
-- [ ] **Step 1: Run every gate**
-
-```bash
-cargo build
-cargo build --release
-cargo test
-cargo test -- --include-ignored
-./scripts/test-linux-x86_64.sh
-./scripts/test-linux-arm64.sh
-./scripts/run-benchmarks.sh
-```
-
-- [ ] **Step 2: Audit zero-warning policy**
-
-`cargo build` must produce no warnings. Fix or `#[allow]` any that surface.
-
-- [ ] **Step 3: Audit assembly-comment policy**
-
-`CLAUDE.md` requires every `emitter.instruction(...)` to have a `//` comment at column 81. Run the audit script from `CLAUDE.md`:
-
-```bash
-python3 -c "
-import os
-for root, _, files in os.walk('src'):
- for fn in files:
- if not fn.endswith('.rs'): continue
- path = os.path.join(root, fn)
- with open(path) as f:
- for i, line in enumerate(f, 1):
- if 'emitter.instruction' in line and '//' in line:
- pos = line.rstrip().index('//')
- if pos != 80 and len(line[:pos].rstrip()) < 80:
- print(f'{path}:{i}: // at col {pos+1}')
-"
-```
-
-Fix any drifted lines.
-
-- [ ] **Step 4: Audit file-size policy**
-
-```bash
-find src -name '*.rs' -exec wc -l {} \; | sort -nr | head -20
-```
-
-Any file >500 LOC that mixes responsibilities should be split. Cohesive single-feature leaves above the threshold are allowed (per `CLAUDE.md` policy).
-
-- [ ] **Step 5: Audit Rust module preamble policy**
-
-Every `*.rs` file in `src/` must start with a `//!` preamble. Run:
-
-```bash
-for f in $(find src -name '*.rs'); do
- head -1 "$f" | grep -q '^//!' || echo "missing preamble: $f"
-done
-```
-
-Fix any misses.
-
-- [ ] **Step 6: Bench result archive**
-
-Take a clean benchmark run on macos-aarch64 and linux-x86_64. Save the JSON outputs as `benchmarks/results/v0.26.0-final.json`. Commit.
-
-- [ ] **Step 7: Update ROADMAP**
-
-Mark all v0.24, v0.25, v0.26 items complete. The next milestone is the v1.0 freeze.
-
-- [ ] **Step 8: Final release commit**
-
-```bash
-git commit -m "chore: finalize v0.26.0 (IR backend default, legacy removed)"
-```
-
-Tag and release per project convention.
-
----
-
-## Exit criteria
-
-- Legacy AST emitter completely removed
-- `src/codegen_ir/` renamed to `src/codegen/`
-- Documentation reflects the IR-based pipeline as the only pipeline
-- All audit checks pass: zero warnings, assembly-comment alignment, module preambles, file-size policy
-- Benchmark results archived
-- ROADMAP up to date
-- The project is ready for the v1.0 freeze pass
diff --git a/.plans/hot-path-data-type.md b/.plans/hot-path-data-type.md
deleted file mode 100644
index 383ef3daef..0000000000
--- a/.plans/hot-path-data-type.md
+++ /dev/null
@@ -1,177 +0,0 @@
----
-name: hot-path data type
-overview: Definire una nuova primitive/compiler feature esplicita per dati hot-path, privilegiando prestazioni e prevedibilita' rispetto alla compatibilita' PHP totale. Il piano riusa puntatori tipizzati, layout statici ed emissione diretta di load/store per evitare hash lookup, boxing e overhead del runtime array PHP.
-todos:
- - id: design-syntax
- content: Definire la sintassi minima del nuovo tipo hot-path (`buffer` e/o `packed class`) e le semantiche POD consentite
- status: pending
- - id: type-layout
- content: Progettare i nuovi metadata statici in `src/types/` per stride, offset e validazione dei tipi packed
- status: pending
- - id: codegen-runtime
- content: Pianificare emissione diretta load/store e un runtime buffer separato dal path array/hash PHP
- status: pending
- - id: tests-benchmarks
- content: Definire test e microbenchmark per dimostrare il vantaggio su array e assoc array PHP
- status: pending
-isProject: true
----
-
-# Piano per un tipo hot-path esplicito
-
-## Obiettivo
-
-Introdurre in elephc una nuova astrazione dati per inner loop di giochi e renderer, separata dagli array PHP gestiti. Il target e' ottenere accesso contiguo, layout prevedibile e zero hash lookup, mantenendo la logica di alto livello in PHP.
-
-## Scelta di design consigliata
-
-La strada meno rischiosa e piu' performante e' **aggiungere un tipo compiler-specifico packed/SoA**, non cercare di far diventare gli `array` PHP il contenitore giusto per i path caldi.
-
-Perche':
-
-- Gli assoc array passano da `__rt_hash_get` e quindi ogni accesso e' una lookup runtime, non un load diretto: `src/codegen/expr/arrays.rs`, `src/codegen/runtime/arrays/hash_get.rs`.
-- Gli array indicizzati vivono comunque nel runtime heap/COW: `src/codegen/runtime/arrays/array_new.rs`.
-- Esistono gia' i ganci giusti per un percorso piu' basso livello: `PhpType::Pointer`, `ExternClassInfo`, field offsets e property access su `ptr`: `src/types/model.rs`, `src/codegen/expr/objects/access.rs`.
-
-## Forma iniziale proposta
-
-Partire con una feature esplicita e minimale, ad esempio una di queste due forme:
-
-- `packed class` / `extern-like class` allocabile da elephc, con layout POD fisso e accesso a campi a offset statici.
-- `buffer` o `packed_array` come contenitore contiguo di POD (`int`, `float`, `bool`, `ptr`, oppure `packed class`).
-
-Raccomandazione: iniziare da **`buffer` + `packed class`**.
-
-Perche':
-
-- `packed class` da' un record denso e compilabile in offset statici.
-- `buffer` permette sia AoS (`buffer`) sia SoA (`buffer $enemyX`, `buffer $enemyState`).
-- Entrambi possono appoggiarsi a `malloc`/heap interno senza entrare nella semantica array/hash/COW.
-
-## Vincoli semantici da fissare subito
-
-Per avere prestazioni massime, il nuovo tipo deve essere volutamente piu' ristretto dei normali tipi PHP:
-
-- Nessun `mixed` dentro i buffer hot-path.
-- Solo tipi POD inizialmente: `int`, `float`, `bool`, `ptr`, e `packed class` composte solo da POD.
-- Niente stringhe/array/object refcounted nella v1.
-- Indice numerico soltanto, bounds check opzionale o esplicito.
-- Nessuna semantica COW/refcount per elemento.
-- Layout e stride completamente statici al compile time.
-
-## Architettura proposta
-
-```mermaid
-flowchart LR
- PhpSource[PHPSourceWithPackedTypes] --> Parser
- Parser --> TypedAst
- TypedAst --> TypeChecker
- TypeChecker --> PackedLayoutInfo
- PackedLayoutInfo --> Codegen
- Codegen --> DirectLoadsStores
- DirectLoadsStores --> NativeBinary
-
- PackedLayoutInfo --> BufferRuntime
- BufferRuntime --> NativeBinary
-```
-
-## Moduli da toccare
-
-### Parsing e AST
-
-Estendere parser e AST per rappresentare il nuovo tipo e le operazioni base:
-
-- Nuovi token/keyword o nuova sintassi tipo-generica in `src/lexer/token.rs` e `src/parser/expr/`.
-- Nuovi nodi in `src/parser/ast/stmt.rs`, `src/parser/ast/expr.rs`, e `src/parser/ast/types.rs`:
- - dichiarazione `packed class` oppure equivalente
- - tipo `Buffer` / `PackedArray`
- - accesso indicizzato hot-path
- - eventuali builtins tipo `buffer_new($len)`
-
-### Type checker
-
-- Estendere `src/types/model.rs` con tipi nuovi, ad esempio:
- - `PhpType::Packed(String)`
- - `PhpType::Buffer(Box)`
-- Creare metadata statici analoghi a `ExternClassInfo`, ma per tipi packed nativi.
-- Validare che gli elementi siano POD e calcolare `stride` e offset.
-- Integrare ownership: i buffer devono stare fuori da `Mixed/Array/AssocArray/Object` e dal normale refcount per elemento.
-
-### Codegen
-
-- Nuovo path di emissione in `src/codegen/expr.rs` e sotto-moduli dedicati, evitando `__rt_hash_get` e le semantiche COW.
-- Riutilizzare il modello gia' presente per accesso a campi a offset statici in `src/codegen/expr/objects/access.rs`.
-- Generare indirizzo base + `index * stride` + `field_offset`, poi `ldr/str` diretti.
-
-### Runtime
-
-- Aggiungere un runtime minimo per allocazione buffer, idealmente separato dagli array PHP:
- - `__rt_buffer_new`
- - `__rt_buffer_free` o integrazione controllata col heap attuale
- - opzionale `__rt_buffer_bounds_fail`
-- Evitare il coinvolgimento del path hash/array runtime, che oggi introduce il costo che vogliamo bypassare: `src/codegen/runtime/arrays/hash_get.rs`, `src/codegen/runtime/arrays/array_new.rs`.
-
-## Strategia di rollout
-
-### Fase 1: MVP strettissimo
-
-- `buffer`
-- `buffer`
-- `buffer`
-- `buffer`
-- allocazione, lettura, scrittura, `count/len`
-- nessuna crescita dinamica
-- nessuna interoperabilita' automatica con array PHP
-
-Questo basta gia' per:
-
-- z-buffer
-- colonne raycaster
-- stati compatti
-- arrays paralleli SoA per enemy/player/projectiles
-
-### Fase 2: `packed class`
-
-- `packed class EnemyPod { public float $x; public float $y; public int $state; public int $hp; }`
-- supporto `buffer`
-- accesso `$enemies[$i]->x` compilato come load diretto a offset statico
-
-### Fase 3: ergonomia extra
-
-- helper per slice/view
-- eventuale `unsafe` mode per togliere bounds checks
-- memset/memcpy builtins mirati
-- conversioni esplicite da/verso array PHP per tooling/debug
-
-## Criteri di successo per il design
-
-Il nuovo tipo e' corretto se:
-
-- l'accesso a elemento/campo non passa da hash lookup o tag dispatch
-- il layout e' statico e ispezionabile
-- non eredita COW/refcount per elemento dagli array PHP
-- si integra bene con SDL/FFI e i pointer helpers gia' esistenti
-- consente esempi di gameplay/render loop scritti in PHP con dati hot-path fuori dagli assoc array
-
-## Test da prevedere
-
-Seguire il pattern del progetto con test lexer/parser/typechecker/codegen:
-
-- parser del nuovo tipo e della nuova sintassi
-- type errors su tipi non POD
-- codegen tests per lettura/scrittura in loop
-- test FFI/interoperabilita' con `ptr_cast` e layout packed
-- benchmark micro per confrontare:
- - assoc array
- - array indicizzato PHP
- - nuovo `buffer`
-
-## Rischi principali
-
-- Se si cerca di rendere `array` PHP stesso “veloce”, si entra in conflitto con hash/COW/refcount e si allarga troppo il blast radius.
-- Se `packed class` accetta tipi heap-managed troppo presto, si perde il vantaggio del modello hot-path.
-- Se la sintassi e' troppo ambiziosa nella v1, il costo parser/typechecker cresce senza dare subito valore al renderer.
-
-## Raccomandazione finale
-
-Puntare a un MVP con **`buffer` per POD + poi `packed class`**, costruito sopra i meccanismi gia' presenti di layout statico e accesso a offset. E' la strada che massimizza le prestazioni e minimizza il rischio architetturale, lasciando gli array PHP al ruolo di strutture di alto livello e i nuovi buffer ai path caldi.
diff --git a/.plans/null-sentinel-collision.md b/.plans/null-sentinel-collision.md
deleted file mode 100644
index 8072ee849d..0000000000
--- a/.plans/null-sentinel-collision.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Null-sentinel collision — Implementation Plan
-
-**Goal:** Make every valid PHP integer representable as a non-null scalar — eliminating the collision where the integer `9223372036854775806` (= `PHP_INT_MAX - 1` = the in-band null sentinel `0x7fff_ffff_ffff_fffe`) is misread as `null` — without heap-boxing every plain `int`.
-
-**Architecture:** elephc stores PHP `null` in an unboxed scalar slot using the magic i64 `0x7fff_ffff_ffff_fffe`. Because every i64 bit pattern is a valid PHP int, this in-band sentinel collides with the real integer. The fix introduces an inline 2-word `{tag, payload}` tagged scalar for scalar slots that can be null, reusing the existing runtime tag scheme, with no heap allocation. Designed to converge with the int-overflow→float work (same `{tag, payload}` shape, tag ∈ {int, float, null}).
-
-## Phases
-
-- **Phase 0** — Lock repros (`tests/codegen/null_sentinel/repros.rs`), decide representation (see DESIGN-NOTES), add microbench harness.
-- **Phase 1** — Unify the 7 file-local `NULL_SENTINEL` consts + raw literals into one canonical constant; document the incompatibility in `docs/php/types.md`. Zero behavior change.
-- **Phase 2** — Tagged representation behind `NullRepr::{Sentinel, Tagged}` flag (default `Sentinel`); audit every producer/consumer; repros pass under `Tagged` on all 3 targets.
-- **Phase 3** — Narrowing: non-nullable `int` slots stay plain i64 (asm-inspection proof); only genuinely-nullable slots widen.
-- **Phase 4** — Flip default to `Tagged`, update docs, converge with the overflow plan.
- **DONE** except overflow convergence (tracked in the overflow plan): default flipped,
- `--null-repr=sentinel` kept as documented opt-out, §0 repros un-ignored, docs updated.
- Evidence: full default-mode suite green pre-flip (3979), full Tagged-forced suite green
- modulo two since-fixed failures plus green focused reruns, Linux x86_64/ARM64 28/28.
-
-## Sentinel inventory
-
-**Producers (write the null sentinel):** `expr/scalars.rs` (`emit_null_literal`), `expr/objects/nullsafe.rs` (`?->`), `expr/objects/dispatch/enums.rs` (enum `tryFrom`), `expr/objects.rs` (`emit_boxed_null`), `expr/arrays/access/indexed.rs` (array miss), `abi/symbols.rs` (globals), `stmt/assignments/properties/storage.rs` (Void property), `abi/values.rs` (Void/Never local store), `runtime/arrays/array_shift.rs`, boxed-null low-word reuses in `magic_set.rs`, `dynamic_props.rs`.
-
-**Consumers (detect null via sentinel):** `builtins/types/is_null.rs`, `stmt/io.rs` (echo), `builtins/io/var_dump.rs`, `functions/generator/emit/stmts.rs`, `expr/compare/null_coalesce.rs` (`??`), `stmt/null_coalesce_assign.rs` + dup in `stmt/assignments/locals.rs` (`??=`), `builtins/arrays/isset.rs`, `expr/coerce.rs` (`coerce_null_to_zero`), `builtins/io/stream_get_contents.rs`.
-
----
-
-## DESIGN-NOTES (Phase 0)
-
-### Empirical findings (macOS ARM64, elephc 0.23.8, PHP 8.4.20 oracle)
-
-```
-echo 9223372036854775806; elephc: (empty) PHP: 9223372036854775806
-$a=[...806]; echo $a[0]; var_dump($a[0]); elephc: (empty)/NULL PHP: ...806 / int(...806)
-function f():?int{return ...806;} f() elephc: NULL PHP: int(...806)
-$x=...806; var_dump(is_null($x)); elephc: bool(true) PHP: bool(false)
-$x=...806; echo $x ?? 0; elephc: 0 PHP: ...806
-$a=[...806]; isset($a[0]) elephc: true (correct)
-```
-
-Key discovery from asm inspection (`--emit-asm`): a `?int` return is **already boxed as a Mixed cell with the correct int tag** (`__rt_mixed_from_value(tag=0, payload=...806)`); the NULL output comes from the **shared int formatter** (`emit_var_dump_int`) re-checking the payload against the sentinel after unboxing. The collision therefore lives in (a) consumers that sentinel-check every `Int`-typed payload because the type `Int` is overloaded ("definitely int" vs "int-or-null"), and (b) producers that write the sentinel into `Int`-typed slots (array miss, etc.). The boxed Mixed path itself is sound.
-
-Unrelated pre-existing quirk found while locking repros: `var_dump(isset(...))` prints `int(1)` instead of `bool(true)` (isset result is typed int). Repro tests avoid it via ternary echo.
-
-### Decision: option (b) — inline 2-word tagged scalar, new internal `PhpType::TaggedScalar` variant
-
-1. **Carrier.** New variant `PhpType::TaggedScalar` (internal-only, like `Mixed`; the checker never produces it). It is **constructed only in codegen funnels and only when `NullRepr::Tagged` is active**. Under `NullRepr::Sentinel` (default) it is never constructed, so generated code is byte-identical and no flag plumbing is needed in `src/types/model.rs` — `stack_size`/`register_count`/`is_refcounted` for the variant are unconditional.
-
-2. **Layout.**
- - Stack slot: 16 bytes — payload word at `offset`, tag word at `offset - 8` (mirrors `Str`: ptr at `offset`, len at `offset - 8`).
- - `stack_size() = 16`, `register_count() = 2`, `is_refcounted() = false`, `is_float_reg() = false`.
- - Expression result: payload in `int_result_reg` (ARM64 `x0`, x86_64 `rax`), tag in the second-word register (ARM64 `x1`, x86_64 `rdx` — same convention as `Str`'s second word). Payload-in-result-reg makes "narrow to plain int" free once non-null is proven.
- - Arguments: 2 consecutive integer argument registers (payload, tag), reusing the existing 2-register argument machinery built for `Str`; stack spills follow the existing 2-word logic.
-
-3. **Tag domain.** Reuse `runtime_value_tag` values: `0 = int`, `2 = float` (reserved for the int-overflow plan), `3 = bool` (reserved), `8 = null`. `is_null` ⇔ `tag == 8`. The `{tag, payload}` pair is word-compatible with a Mixed cell's words 0/8, so TaggedScalar→Mixed boxing is a direct `__rt_mixed_from_value(tag, payload, 0)` and Mixed→TaggedScalar unboxing is two loads. This is the convergence point with the overflow plan.
-
-4. **Construction funnels (under `Tagged`).**
- - Declared-type lowering (`codegen_static_type`/`codegen_declared_type` + the `Union → Mixed` collapse): `Union([Int, Void])` → `TaggedScalar` for params, returns, locals, properties.
- - `array` element reads (miss-capable): hit → `{tag=int, payload}`, miss → `{tag=null}`.
- - `null` literal flowing into an int context; `?->` over int members; `array_shift` empty result; `stream_get_contents` failure path.
- - **Scope:** only `int|null` widens initially. `?float`/`?bool` keep today's Mixed boxing (tag values reserved; flipping them is follow-up work). `?string` stays Mixed permanently (a string is already 2 words; `{tag, ptr, len}` would need 3). Nullable objects keep their existing pointer-sentinel scheme (separate surface, out of scope).
-
-5. **Consumers (under `Tagged`).** `is_null`, echo suppression, `var_dump`, `??`, `??=`, `isset`, `coerce_null_to_zero` dispatch on the tag word when the operand is `TaggedScalar`, and emit **no sentinel check at all** for plain `Int` operands (plain `Int` becomes "definitely int" again — this alone fixes `echo 9223372036854775806`).
-
-6. **Flag.** `NullRepr::{Sentinel, Tagged}` lives in `CliConfig` → pipeline → codegen `Context`. Default `Sentinel` until Phase 4. CLI/env opt-in: `ELEPHC_NULL_REPR=tagged`. Tests force `Tagged` per-compile through an explicit parameter on the test-support compile helpers (no process-global mutation — tests run in parallel threads).
-
-7. **Scope refinement (Phase 2, validated empirically).** elephc's checker is flow-sensitive
- for locals, and `?int` params/returns/properties already lower to boxed Mixed (null-safe).
- The §0 bugs therefore need TaggedScalar **producers** only for: int-element array reads
- (indexed + assoc, miss-capable), and later `array_pop`/`array_shift`/`?->`-int/globals.
- Params, returns, and properties keep their Mixed boxing — for them only the **consumers**
- needed fixing (the shared int formatter / sentinel checks). Null-then-int locals are
- handled by flow typing today and stay as they are.
-
-8. **Null payload canonicalization.** A tagged null carries `NULL_SENTINEL` as its payload
- word (not zero), so boxing it into a Mixed cell produces exactly the legacy
- `{tag 8, sentinel}` words — `__rt_mixed_strict_eq` compares payload words even for the
- null tag, and un-audited consumers that ignore the tag see precisely the legacy encoding
- (graceful degradation instead of new corruption).
-
-9. **Rejected alternatives.**
- - *(a) rarer sentinel value:* still collides — every i64 is a valid int.
- - *(c) box null-capable scalars as Mixed:* already today's repr for `?int` returns yet still broken (consumer-side sentinel re-check), and boxing every `array` read would heap-allocate per access.
- - *Re-repr `Union([Int,Void])` without a new variant:* every existing `Union(_) => /* boxed pointer */` match arm becomes a silent hazard under the flag; a new variant turns missed paths into compile errors (exhaustive matches) or loud crashes instead of silent miscompilation.
- - *Uninit-property sentinel (`0x...fffd`):* not a value collision (separate metadata word); in scope only for the Phase 1 constant unification.
-
-### Phase 2 status & follow-ups
-
-Done under `NullRepr::Tagged` (default `Sentinel` unchanged, suite green): TaggedScalar
-variant + ABI (slots, args, returns, spills mirror `Str`), `NullRepr` flag
-(`--null-repr=` / `ELEPHC_NULL_REPR`), tag-aware consumers (echo, var_dump + generator dup,
-is_null, `??`, `??=` both copies, isset, empty, gettype, casts, string coercion, truthiness,
-numeric binops, strict `===` via the Mixed boxing path), and the int-array read producers
-(indexed + assoc, hit→tag int / miss→tag null). All §0 repros pass under `Tagged` and are
-covered by `tests/codegen/null_sentinel/tagged.rs` (macOS ARM64 + Linux x86_64 + Linux ARM64).
-
-Discovered during Phase 2 — pre-existing null-read bugs (broken in BOTH modes; Tagged fixes
-most): under the legacy sentinel repr, `$a[miss] + 1`, `$a[miss] . "s"`, `"{$a[miss]}"`,
-`$a[miss] * 3` all leak the sentinel digits; Tagged narrows them correctly.
-
-Phase 2.3 (full suite forced to Tagged) initially failed 9/3978; all four systematic causes
-fixed: local inference now mirrors emission (TaggedScalar for miss-capable int reads, so
-array literals box and === peeks stay runtime), untyped params widen to int|null on null
-call sites (flag installed before checking), array element stores/pushes narrow the payload
-word, and local assignments reinterpret into one-word slots instead of clobbering the
-neighboring slot with the tag word. `array_pop`/`array_shift` empty results, unary minus,
-and `abs()` are tagged/narrowed. Benches (noisy, same-load comparison): plain-int parity,
-array-read loop ~45% faster under Tagged (sentinel materialization disappears), nullable
-roundtrip +9%.
-
-Remaining follow-ups (not blockers; behavior matches the legacy mode in both representations):
-- storing a null-valued tagged read into an int array keeps the in-band payload (reads
- back as the sentinel integer, like the legacy mode); a runtime tag-branch into the boxed
- Mixed conversion would make it PHP-exact.
-- int-builtin argument narrowing beyond abs() (e.g. intdiv on a null read) follows legacy
- semantics; a shared builtin-arg coercion gate would make them PHP-exact.
-- undefined globals read as int 0 in both modes (PHP: null + warning) — pre-existing.
-- `?->` over int members already prints correctly via the Mixed path; enum tryFrom keeps
- its object-pointer sentinel scheme (no int collision).
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 120000
index ef495c00be..0000000000
--- a/AGENTS.md
+++ /dev/null
@@ -1 +0,0 @@
-./CLAUDE.md
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000000..6245315fa5
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,551 @@
+# elephc — Developer Guide
+
+## What is this
+
+A PHP-to-native compiler written in Rust. Compiles a static subset of PHP to native assembly for the supported target matrix, producing standalone binaries. No interpreter, no VM, no runtime dependencies.
+
+## Read CONTRIBUTING.md first
+
+Before contributing, read `CONTRIBUTING.md` in full. It holds the complete step-by-step recipes — adding an operator, a statement type, a built-in function, an EIR optimization pass, and a crate-backed `--with-` feature — plus the assembly comment policy, the coding style, and the Pull Request workflow. This guide covers the architecture, invariants, and context that go alongside those recipes; it deliberately does not repeat them.
+
+## Supported target policy
+
+All supported targets are first-class targets. The supported target matrix is currently `macos-aarch64`, `linux-aarch64`, and `linux-x86_64`.
+
+Do not design or land codegen/runtime features as ARM64-first with x86_64 treated as a later port. New features, builtins, runtime helpers, optimizer assumptions that affect emitted code, ABI behavior, and ownership/GC paths must either support every supported target in the same change or clearly isolate an intentionally unsupported path with diagnostics, tests, and documentation. A feature is not considered done while any supported target has a missing runtime symbol, reduced semantics, stale documentation, or an untested target-specific lowering path.
+
+When examples or internals docs use ARM64 snippets for readability, treat them as examples only. Implementation work must keep the target-aware ABI/runtime boundaries authoritative.
+
+## Build & run
+
+```bash
+cargo build # dev build
+cargo build --release # optimized build
+cargo run -- file.php # compile a PHP file
+```
+
+The compiler outputs a native binary next to the source file (e.g., `file.php` → `file`).
+
+## Test policy
+
+**Every feature must have tests before it's considered done.** The test suite is the primary quality gate.
+
+Local agent runs should stay focused. By default, run only the build checks and tests needed to validate the implementation or regression you touched, then rely on CI for the complete matrix. Do **not** run the full local suite (`cargo test`, `cargo test -- --include-ignored`, or full Linux Docker scripts) unless the user explicitly asks for it, you are doing a release/pre-release verification, CI is unavailable for the needed signal, or a broad/high-risk change cannot be validated responsibly with focused tests.
+
+### Running tests
+
+```bash
+cargo test test_fizzbuzz # run a focused test by name
+cargo test --test codegen_tests test_name # run a focused end-to-end codegen test
+cargo nextest run --profile ci test_name # run a focused nextest test with per-test timeout
+cargo test # full local suite; only when explicitly requested/necessary
+cargo test -- --include-ignored # ignored tests; only when relevant or requested
+```
+
+Linux target-specific regressions can be checked through the Docker scripts in
+`scripts/`. Prefer filters during implementation; the unfiltered commands run
+full Linux suites and should be reserved for explicit requests or genuinely
+necessary local target validation.
+
+```bash
+./scripts/test-linux-x86_64.sh iterable # run tests matching a filter
+./scripts/test-linux-arm64.sh test_my_feature # run tests matching a filter
+./scripts/test-linux-x86_64.sh # full Linux x86_64 suite; only when necessary/requested
+./scripts/test-linux-arm64.sh # full Linux ARM64 suite; only when necessary/requested
+./scripts/test-linux-x86_64.sh --rebuild # rebuild the Docker image first
+./scripts/test-linux-arm64.sh --rebuild # rebuild the Docker image first
+```
+
+Some tests are marked `#[ignore]` because they require external libraries (e.g., SDL2) not available in CI. Run ignored tests only when the change touches that surface, during explicit release verification, or when the user asks for them.
+
+### Test strategy during development
+
+The full test suite is slow because each codegen test spawns `as` + `ld` + runs the binary. CI runs the complete supported matrix (`macos-aarch64`, `linux-x86_64`, and `linux-aarch64`) with sharded codegen tests, so local implementation work should optimize for fast, relevant signal:
+
+1. **While developing a feature**: run only the tests for that feature or regression (`cargo test test_my_feature`).
+2. **Scope to a single test binary**: prefer `cargo test --test codegen_tests ` over a bare `cargo test `. A bare filter rebuilds and links all six test binaries every cycle (~2.5s of wasted link time); `--test ` links only the one you need. Most codegen work lives in `codegen_tests`.
+3. **For target-sensitive changes**: run the smallest relevant macOS/Linux focused tests locally when they materially reduce risk; otherwise let CI provide complete target coverage.
+4. **For broad infrastructure changes**: validate the edited workflow/config directly (YAML parse, `cargo metadata`, `nextest` config checks, etc.) instead of running unrelated Rust suites.
+5. **PHP cross-check**: opt in narrowly with `ELEPHC_PHP_CHECK=1 cargo test ` when PHP equivalence is the question.
+
+### Pre-commit verification
+
+Before committing code changes, run the smallest useful focused tests and hygiene checks that cover the implementation. Do not run full local suites by default; CI is responsible for the complete sharded matrix after push/PR.
+
+```bash
+cargo build # for code changes that should compile warning-free
+cargo test # or the narrower --test form
+git diff --check
+```
+
+For docs-only, workflow-only, or configuration-only changes, replace Rust test runs with the relevant syntax/metadata checks. For codegen changes, also verify assembly-comment coverage/alignment for any files you touched. If the change can affect generated assembly, runtime helpers, ABI behavior, linking, ownership/GC, or target-specific libraries, run focused tests for affected supported targets when local evidence is needed; otherwise rely on CI for full Linux x86_64/Linux ARM64/macOS ARM64 coverage.
+
+### Test structure
+
+| File | What it tests | How |
+|---|---|---|
+| `tests/lexer_tests.rs` | Tokenization | Asserts token sequences from source strings |
+| `tests/parser_tests.rs` | AST construction | Asserts AST node structure and operator precedence |
+| `tests/codegen_tests.rs`, `tests/codegen/` | Full pipeline (end-to-end) | Compiles PHP → binary, runs it, asserts stdout |
+| `tests/error_tests.rs`, `tests/error_tests/` | Error reporting | Asserts that invalid programs produce the right error messages |
+
+### Test coverage requirements
+
+- **New language construct** (keyword, operator, statement): needs lexer, parser, codegen, AND error tests
+- **New operator**: needs a Pratt parser binding power test verifying precedence relative to adjacent operators
+- **New statement type**: needs at least one codegen test showing correct output, one test for edge cases (empty body, nested), and one error test for malformed syntax
+- **New built-in function**: needs codegen tests for normal use and error test for wrong argument count/types
+- **Bug fix**: must include a regression test that would have caught the bug
+- **Every feature also needs an example** in `examples/`. If an existing example can showcase the new feature naturally, update it. Otherwise, create a new `examples//main.php` with its own `.gitignore` (containing `*.s`, `*.o`, `main`). Examples should be small, readable programs that demonstrate real use cases — not just test cases.
+
+### Writing codegen tests
+
+Codegen tests compile inline PHP source and assert stdout:
+
+```rust
+#[test]
+fn test_my_feature() {
+ let out = compile_and_run("` |
+| `src/parser/` | `parse()` | Tokens → `Program` (Vec of Stmt). Pratt parser for expressions |
+| `src/magic_constants/`, `src/magic_constants.rs` | `substitute_file_and_scope_constants()` | Lowers PHP magic constants before resolver/name-resolver/optimizer passes |
+| `src/conditional.rs` | `apply()` | Applies compiler `ifdef` conditional branches |
+| `src/resolver/` | `resolve()` | Resolves `include`/`require`, discovers declarations, and tracks include-loaded function variants. Runs before namespace/name canonicalization |
+| `src/name_resolver/` | `resolve()` | Applies namespace/use rules, rewrites references to canonical fully-qualified names, handles PHP-style builtin fallback, and flattens namespace-only AST nodes before type checking |
+| `src/types/` | `check()` | Type checking, returns `CheckResult` with `TypeEnv`, function/class/interface/enum/FFI metadata, warnings, required libraries, and the internal `Mixed` type for heterogeneous assoc-array values |
+| `src/optimize/`, `src/optimize.rs` | `fold_constants()`, `propagate_constants()`, `eliminate_dead_code()` | AST-level constant folding/propagation, control-flow pruning/normalization, DCE, and effect modeling |
+| `src/ir/` | IR types / builders / validator | EIR program, function, block, instruction, terminator, value, local, effect, ownership, and textual-format definitions |
+| `src/ir_lower/` | `lower_program()` | Active AST → EIR lowering, including local slot creation, hidden temporaries, ownership annotations, and PHP call semantics |
+| `src/ir_passes/` | `optimize_module()` / `allocate_registers()` | EIR analyses and transformations over lowered functions: fixed-point optimization pass driver (identity folding, …; gated by `--ir-opt`) and linear-scan register allocation, before codegen |
+| `src/codegen/` | `generate()` | Active EIR → target assembly backend |
+| `src/codegen_support/abi/` | ABI helpers | Target-specific argument materialization, frame layout, registers, stack slots, symbols, and call helpers |
+| `src/codegen_support/runtime/` | Runtime emitters | Shared `__rt_*` helpers and runtime data used by generated programs |
+| `src/codegen/program_usage/` | Program scans | Collects codegen metadata such as required classes and variables before emission |
+| `src/runtime_cache.rs` | `prepare_runtime_object()` | Builds/reuses the target runtime object before final linking |
+| `src/errors/` | `report()` | Error formatting with line:col |
+| `src/span.rs` | `Span` | Source position (line, col) attached to all AST nodes |
+
+### Bridge crates and `--with-` flags
+
+Optional, heavyweight functionality that is naturally expressed with Rust
+libraries lives in a workspace crate under `crates/elephc-/`, compiled as a
+`staticlib` and linked into the user program on demand. Every such bridge is
+described by one entry in the `BRIDGES` table in `src/linker.rs`; `link()` and the
+discovery/auto-build helpers are fully table-driven, so adding a bridge is a
+single table entry rather than new linker logic.
+
+A bridge is linked when its `lib_name` appears in `extra_link_libs`. That set is
+populated automatically by feature detection: the type checker records a needed
+library via `require_builtin_library(...)` (e.g. hash builtins → `elephc_crypto`)
+or through an injected `extern "elephc_" { ... }` prelude block (e.g. the
+PDO/tz/image preludes), so programs that do not use a feature never link its
+bridge.
+
+`--with-` force-enables a bridge regardless of detection. It force-links
+the staticlib (whole-archived so it is not dead-stripped) and, for crates whose
+PHP surface comes from a prelude (`pdo`, `tz`, `image`), force-injects that
+prelude so the API is declared even when usage was not detected. The flag name is
+the bridge's `flag_name` (`crate_name` minus the `elephc-` prefix): `--with-pdo`,
+`--with-tls`, `--with-crypto`, `--with-phar`, `--with-tz`, `--with-image`.
+`--with-web` is an alias for `--web` (the full server mode, which owns the program
+entry point). An unknown `--with-` is a hard CLI error listing the valid
+crates. The end-to-end wiring is CLI (`src/cli.rs`, `with_crates`) → pipeline
+(`src/pipeline.rs`: force-link + prelude forcing) → linker
+(`src/linker.rs`: `forced_whole_archive`).
+
+### Codegen layout
+
+- `src/ir_lower/` is the active high-level lowering layer. Add PHP-visible semantics there.
+- `src/codegen/lower_inst/` and `src/codegen/lower_term.rs` are the active EIR instruction/terminator assembly lowerers.
+- `src/codegen/context.rs`, `src/codegen/frame.rs`, and `src/codegen/value_placement.rs` carry active backend state, frame layout, and value placement.
+- `src/codegen_support/runtime/mod.rs` emits shared runtime code (`__rt_*` routines)
+- `src/codegen_support/runtime/data/` emits shared runtime `.data` / `.bss` symbols and metadata tables
+- `src/codegen_support/abi/` centralizes target-specific register, stack, frame, symbol, and call mechanics. Prefer these helpers over hardcoding ARM64 or x86_64 details in feature emitters.
+
+### Adding a new operator
+
+Key invariants:
+
+- A new operator touches the whole pipeline: token (`src/lexer/`), Pratt binding power (`infix_bp()` in `src/parser/expr/pratt.rs`), `BinOp` variant (`src/parser/ast.rs`), type inference (`src/types/checker/`, usually `inference/ops.rs`), optimizer folding/effects (`src/optimize/`), and EIR lowering (`src/ir_lower/expr/` + `src/codegen/lower_inst/`).
+- Precedence and associativity must match PHP; keep folds PHP-equivalent and cross-check edge cases with `php -r`.
+- Needs a Pratt binding-power test asserting precedence relative to adjacent operators, plus tests in all 4 test files (lexer, parser, codegen, error).
+
+### Adding a new statement type
+
+Key invariants:
+
+- A new statement touches parser (`StmtKind` in `src/parser/ast.rs` + `src/parser/stmt.rs`), resolver/name-resolver (if it holds names, declarations, includes, function variants, or expressions), type checker (`src/types/checker/`), optimizer/effects/warnings (`src/optimize/`), and EIR lowering (`src/ir_lower/stmt/` + `src/codegen/`).
+- If it introduces variables or hidden temporaries, update EIR local/temp declaration in `src/ir_lower/context.rs` and frame-layout allocation before frame sizing.
+- Also audit every AST-walking pass (see "Adding or changing an AST node") — a missed pass usually causes silent miscompilation rather than a compile error.
+
+### Adding or changing an AST node
+
+When adding a new `ExprKind` or `StmtKind`, check every AST-walking pass. The compiler has many passes that deliberately recurse by variant, and missing one usually creates silent miscompilation rather than a compile error.
+
+Common places to audit:
+
+- Parser construction and lowering in `src/parser/`
+- Resolver/include discovery and function-variant handling in `src/resolver/`
+- Namespace/use/FQN rewriting in `src/name_resolver/`
+- Type checking, inference, return analysis, warnings, and type compatibility in `src/types/`
+- Optimizer folding, propagation, DCE, control-flow normalization, and effect modeling in `src/optimize/`
+- Program usage scans in `src/codegen/program_usage/`
+- Local/hidden-slot declaration in `src/ir_lower/context.rs` and frame placement in `src/codegen/`
+- Ownership metadata in `src/ir_lower/ownership.rs`, EIR ownership lowering in `src/codegen/lower_inst/ownership.rs`, and related runtime/GC paths
+- EIR lowering in `src/ir_lower/` plus EIR backend lowering in `src/codegen/`
+- Lexer/parser/codegen/error/regression tests, depending on the surface area
+
+### Adding a new built-in function
+
+PHP builtins are declared **once** in the single-source registry: one home file per
+builtin at `src/builtins/ /.rs`, declared with the `builtin!` macro and
+collected via `inventory`. From that single declaration the compiler derives the
+catalog name-set (`function_exists`, case-insensitive lookup, namespace fallback), the
+`FunctionSig` (named args, defaults, ref params, variadic, arity), the type-check
+entry, the EIR lowering dispatch (`spec.lower`), and the generated docs. Do **not**
+re-add builtin names to hand-maintained tables (`catalog.rs`, `signatures.rs`, per-area
+`check_builtin` arms) — they are superseded by the registry.
+
+Key invariants:
+
+- **One builtin per home file.** The `lower` hook is a thin wrapper over the real
+ emitter in `src/codegen/lower_inst/builtins/ /`; leaf emitter files hold
+ exactly one emitter function, and runtime data emission stays in
+ `src/codegen/runtime/data.rs`.
+- **`returns:`/`check` are checker-only.** The EIR backend derives return types
+ separately in `call_return_type` (`src/ir_lower/expr/mod.rs`); a `returns: Mixed` +
+ precise-`check` builtin also needs a matching EIR return-type arm, or the checker and
+ EIR disagree on the value's type.
+- **Separate surfaces still need hand-wiring** when relevant: the EIR emitter/runtime
+ routine, optimizer effects (`src/optimize/effects/builtins.rs`), and the
+ runtime-callable wrapper exclusion (`src/codegen/callable_dispatch.rs`).
+- `isset`/`unset`/`empty`/`exit`/`die`/`buffer_*` are language constructs that stay
+ checker-resident (`numeric`/`arrays` `check_builtin`), not in the registry.
+- Add codegen + error tests (include a case-insensitive or namespaced call for
+ PHP-visible builtins); keep the parity gates in `src/builtins/parity_tests.rs` green.
+
+### Adding a new EIR optimization pass
+
+IR-level transformations run after EIR lowering/validation through a fixed-point driver (`src/ir_passes/`), not in the AST optimizer.
+
+Key invariants:
+
+- Implement the `IrPass` trait in `src/ir_passes/.rs` and register it in `default_passes()` (`src/ir_passes/driver.rs`); the driver re-runs the whole set per function to a fixed point, capped by `MAX_PASS_ITERATIONS`.
+- Reuse `src/ir_passes/rewrite.rs` (RAUW via `replace_all_uses` + shared fold helpers) instead of re-walking operands/terminators. Keep rewrites dominance-safe and PHP-equivalent; cross-check edge cases (division by zero, signed-zero/`NaN` floats) with `php -r`.
+- Debug/test builds re-validate each function after every pass and panic (naming the pass) on malformed IR or non-convergence; both guards compile out of `--release`.
+- Behavior must be identical with `--ir-opt` on or off except for performance (gate: `--ir-opt=on|off` / `--no-ir-opt`, env `ELEPHC_IR_OPT`, default on). Add unit tests (`src/ir_passes/tests/`) and e2e tests (`tests/codegen/optimizer/`) using runtime-unknown values (e.g. `$argc`) so the construct survives AST folding.
+
+### Call argument semantics
+
+All function-like call surfaces must share the same argument rules instead of normalizing locally in individual emitters:
+
+- Shared named/positional/spread semantics live in `src/types/call_args/` whenever they are not codegen-specific.
+- `src/types/call_args/` owns the semantic planner (`CallArgPlan` / `plan_call_args`). The checker and EIR lowering should consume that plan; they should not rebuild named-argument matching, duplicate detection, static associative-spread expansion, spread bounds, or the regular/variadic split locally.
+- If a codegen surface uses an internal signature with hidden parameters, such as closure captures, pass the caller-visible regular parameter count through `plan_call_args_with_regular_param_count()` instead of letting the planner infer it from the full internal signature.
+- Type-checker validation and diagnostic mapping lives in `src/types/checker/functions/call_validation.rs`; it maps planner errors to `CompileError` diagnostics instead of owning the semantic rules.
+- `src/ir_lower/expr/` owns active EIR call-argument lowering: planner consumption, source-order named/spread lowering, spread checks, and hidden-temp creation. `src/codegen/` then materializes the lowered call through target-aware ABI helpers.
+- User-defined calls, builtins, and extern calls must use the same named/spread normalization rules before any callee-specific lowering runs.
+- PHP call unpacking with static string keys maps to named arguments (`f(...["a" => 1])` behaves like `f(a: 1)`). Static numeric keys remain positional, and duplicate static string keys inside one unpack use PHP's last-wins behavior before planning.
+- When adding or extending a builtin, verify `first_class_callable_builtin_sig()` as well as the direct builtin signature so first-class callable syntax and callable aliases stay coherent.
+- PHP source evaluation order is distinct from ABI parameter order. Preserve side effects in source order, then materialize arguments in parameter/ABI order; extern calls follow the same rule before C ABI register loading.
+- Spread arguments before named arguments must be evaluated once, length/overwrite checks must happen at the PHP-observable point, and later named-argument side effects must not be skipped by early codegen checks. Too-short spreads for required parameters must fail instead of reading past the array payload.
+- A positional spread into a variadic callee fills visible regular parameters first; only the remaining tail becomes the variadic array.
+- User-defined variadics accept unknown named arguments as string-keyed variadic entries; internal/builtin variadics reject unknown named arguments like PHP internals.
+- Ref-like parameters, including mutating builtin parameters, must avoid value-temp preevaluation so the original storage is passed/mutated.
+- If hidden named-argument temporaries are introduced, update `src/ir_lower/context.rs` and EIR frame placement so slots are allocated before frame-size calculation.
+
+### Optimizer and effects
+
+The optimizer assumes side effects are modeled conservatively. When changing calls, operators, expressions, statements, or builtins:
+
+- Update `src/optimize/effects/` if purity, variable reads/writes, call effects, filesystem/runtime state, exceptions/fatals, or by-ref mutation behavior changes.
+- Do not mark a call as pure if it can read or write globals, files, environment, runtime heap state, object properties, array contents, argument storage, or can emit visible output.
+- Keep constant folding in `src/optimize/fold/` limited to PHP-equivalent results. If PHP behavior is edge-case sensitive, cross-check with `php -r`.
+- Add optimizer regression tests under `tests/codegen/optimizer/` when DCE, constant propagation, control-flow pruning, or folding can observe the change.
+- Magic constants must be lowered before optimizer passes. Do not introduce optimizer paths that expect raw `ExprKind::MagicConstant`.
+- `src/optimize/` is the AST optimizer only. IR-level (EIR) transformations live in `src/ir_passes/` behind the fixed-point pass driver; see "Adding a new EIR optimization pass". Folds that need value identity, basic blocks, or dominance belong there, not in `src/optimize/`.
+
+### Runtime ownership, GC, and COW
+
+Refcounted runtime values are not plain scalars. When changing arrays, strings, objects, `Mixed`, `Iterable`, call returns, or temporaries:
+
+- Preserve the boxed `Mixed` cell contract: the runtime tag and payload shape must stay consistent across codegen and runtime helpers.
+- Respect copy-on-write before mutating arrays or hashes. Use the existing ensure-unique helpers instead of mutating shared storage directly.
+- Track whether a value is owned, borrowed, persistent, or a temporary result. Release only values this code path owns.
+- Keep cleanup paths balanced across normal returns, early exits, throws/fatals, and control-flow merges.
+- Add focused tests in `tests/codegen/runtime_gc/` for ownership, aliasing, cycles, heap debug, stack args, and COW changes.
+
+### File size policy
+
+As a general rule, aim to keep source files under **500 lines of code**. This is a maintainability guideline, not a blind numeric rule.
+
+The real goal is to avoid files that become hard to reason about because they mix multiple responsibilities. In practice:
+
+- **Dispatcher/orchestration files** (`mod.rs`, top-level drivers, large checker/codegen coordinators) should stay slim. If they grow large, split them aggressively.
+- **Multi-responsibility files** should be split once they start accumulating unrelated concerns, even if the line count is not yet extreme.
+- **Leaf files that implement one cohesive feature** are allowed to exceed 500 lines when splitting them would create artificial fragmentation.
+
+Examples of files that may reasonably stay above the soft limit:
+
+- a single runtime emitter implementing one substantial builtin or runtime routine
+- a single compiler pass file that is still clearly about one feature and one code path
+- a self-contained parser/lowering/runtime leaf where splitting would only spread one mental model across several tiny files
+
+Examples of files that should usually be split:
+
+- a file that mixes dispatch, validation, data collection, and post-processing
+- a file that contains several unrelated builtins or runtime helpers
+- a file that acts as a “miscellaneous bucket” for code that did not get a home
+
+So the policy is:
+
+- treat **500 LOC as a warning sign**
+- treat **mixed responsibilities** as the real trigger for refactoring
+- do **not** split a file that owns one coherent feature just to satisfy the number
+
+In short: prefer **cohesion over mechanical line-count compliance**. A 650-line mono-feature leaf is acceptable; a 350-line multi-purpose orchestrator is already a refactor candidate.
+
+### Rust module preamble policy
+
+Every repo-owned Rust source file (`*.rs`) must start with a module-level Rustdoc preamble before any `use`, `mod`, item, or test helper code. Use `//!` comments so the explanation is attached to the module in rustdoc.
+
+The preamble is mandatory for all new Rust files and must be added or preserved when touching existing Rust files. Release verification should report any Rust file that is missing it.
+
+Standard format:
+
+```rust
+//! Purpose:
+//! Explain what this file owns in 2-4 lines.
+//!
+//! Called from:
+//! - `crate::path::caller()` or the relevant test/module entry point.
+//!
+//! Key details:
+//! - Important invariants, ordering constraints, ownership/ABI/runtime rules, or coupling.
+```
+
+For test files, use the same structure but describe the test surface instead of production callers:
+
+```rust
+//! Purpose:
+//! Integration or regression tests for the relevant feature area.
+//!
+//! Called from:
+//! - `cargo test` through Rust's test harness.
+//!
+//! Key details:
+//! - Fixture layout, platform assumptions, ignored-test requirements, or why edge cases exist.
+```
+
+Keep preambles concise and factual. Do not include refactor history, stale line numbers, or broad architecture prose that belongs in `docs/internals/`.
+
+### Rust function docblock policy
+
+Every explicit Rust function in repo-owned Rust source files must have a concise docblock explaining what that function does. This applies equally to public functions, restricted-visibility functions (`pub(crate)`, `pub(super)`, etc.), private helper functions, impl methods, trait methods, and test functions.
+
+Use `///` Rustdoc comments immediately before the function item or its item attributes. Keep the docblock specific to the function's actual responsibility, inputs, outputs, side effects, ownership/ABI/runtime constraints, and failure behavior when those details matter. Do not use vague filler such as "handles logic" or "processes data".
+
+When documenting test functions, describe the behavior or regression being verified and any important fixture/platform assumptions. For explanatory comments inside a function body, use normal `//` comments, not `///`; Rustdoc comments inside function bodies produce warnings or errors because they do not document an item.
+
+Adding or updating function docblocks must not change code behavior. Do not alter function signatures, visibility, attributes, derives, module declarations, control-flow braces, strings, assembly instructions, or instruction-comment alignment while adding documentation. If a doc-only change causes `cargo check`, `cargo check --tests`, or `git diff --check` to fail, fix the documentation placement or comment style rather than changing code to fit the comment.
+
+### Codegen conventions (target-aware)
+
+- Prefer helpers from `src/codegen_support/abi/` for registers, stack slots, frame layout, argument materialization, symbol addresses, and calls.
+- New feature emitters belong in `src/codegen/`.
+- New feature emitters must support every supported target through `emitter.target` or clearly isolate target-specific code behind existing target helpers with explicit tests and diagnostics.
+- Avoid hardcoding ARM64 register names, x86_64 register names, syscall numbers, object formats, or stack alignment rules in shared lowering code.
+- Do not add an ARM64-only runtime helper, builtin emitter, ABI path, or ownership cleanup path unless the feature is intentionally target-gated and documented as unsupported elsewhere.
+- Target-sensitive changes need coverage for every supported target they can affect. During local implementation, run focused target checks only when they are needed for confidence; rely on CI for the complete supported-target matrix unless the user requests local Docker runs or CI cannot provide the needed signal.
+
+### ARM64 quick reference
+
+- **Integers**: result in `x0`
+- **Floats**: result in `d0`
+- **Strings**: pointer in `x1`, length in `x2`
+- **Function args**: `x0`-`x7` (int = 1 reg, string = 2 regs), `d0`-`d7` (floats)
+- **Return value**: same as expression result (`x0`, `d0`, or `x1`/`x2`)
+- **Stack frame**: `x29` = frame pointer, `x30` = link register, locals at negative offsets from `x29`
+- **ABI helpers**: `src/codegen_support/abi/` centralizes load/store/write per type
+- **Labels**: use `ctx.next_label("prefix")` — global counter prevents collisions across functions
+- **Mixed values**: `PhpType::Mixed` is an internal boxed runtime shape used for heterogeneous associative-array values; codegen/runtime must preserve the boxed cell contract instead of treating it like a plain scalar
+
+### Assembly comment policy
+
+Every `emitter.instruction(...)` call must have an inline `//` comment aligned to
+column 81, with `// -- description --` block comments before related groups.
+
+## Examples
+
+Each example lives in `examples//main.php` with its own `.gitignore`. To run:
+
+```bash
+cargo run -- examples/fizzbuzz/main.php
+./examples/fizzbuzz/main
+```
+
+## PHP compatibility
+
+**PHP-derived syntax must be 100% compatible with PHP.** When elephc implements a PHP construct (variables, operators, keywords, built-ins), it must behave identically to PHP. This means:
+
+- Variable names, keywords, operators, and built-in function names must match PHP exactly
+- Superglobals (`$argc`, `$argv`) must use PHP's syntax (e.g., `$argv[0]`, not `argv(0)`)
+- Operator precedence and associativity must match PHP
+- String escape sequences must match PHP behavior
+- Built-in function signatures must match PHP (argument count, order, types)
+
+When in doubt, test with `php -r '...'` to verify behavior.
+
+**elephc also provides compiler-specific extensions** beyond standard PHP (e.g., `ptr`, `extern`, `buffer`, `packed class`). These features have no PHP equivalent and are not expected to run under the PHP interpreter. They are clearly distinguishable from PHP syntax and exist to enable use cases (FFI, game development, low-level memory access) that PHP cannot address.
+
+## Documentation
+
+The `docs/` directory is the project's complete documentation, organized into the following sections:
+
+```
+docs/
+├── README.md # Main index
+├── getting-started/ # Installation and first program
+│ ├── installation.md
+│ └── your-first-program.md
+├── compiling/ # The compiler CLI: flags and the full compilation process
+│ ├── overview.md
+│ ├── compilation-pipeline.md
+│ ├── cli-reference.md
+│ ├── targets.md
+│ ├── optimization.md
+│ ├── output-and-diagnostics.md
+│ └── linking-and-conditional-compilation.md
+├── php/ # PHP syntax (standard PHP features)
+│ ├── types.md
+│ ├── operators.md
+│ ├── control-structures.md
+│ ├── functions.md
+│ ├── strings.md
+│ ├── arrays.md
+│ ├── math.md
+│ ├── classes.md
+│ ├── namespaces.md
+│ ├── magic-constants.md
+│ └── system-and-io.md
+├── beyond-php/ # Compiler extensions (not valid PHP)
+│ ├── pointers.md
+│ ├── buffers.md
+│ ├── packed-classes.md
+│ ├── extern.md
+│ └── ifdef.md
+└── internals/ # Compiler internals
+ ├── what-is-a-compiler.md
+ ├── how-elephc-works.md
+ ├── the-lexer.md
+ ├── the-parser.md
+ ├── the-type-checker.md
+ ├── the-optimizer.md
+ ├── the-codegen.md
+ ├── the-runtime.md
+ ├── memory-model.md
+ ├── architecture.md
+ ├── arm64-assembly.md
+ └── arm64-instructions.md
+```
+
+### Astro compatibility
+
+All docs files are Markdown with YAML frontmatter compatible with Astro content collections. Every `.md` file **must** have this frontmatter format:
+
+```yaml
+---
+title: "Page Title"
+description: "One-line description of the page."
+sidebar:
+ order: N
+---
+```
+
+- `title` replaces the `# Heading` — do **not** add a top-level `# Title` in the body (Astro renders it from frontmatter)
+- `sidebar.order` controls page ordering within its section
+- No navigation links (`[← Back]`, `Next:`, etc.) — Astro handles navigation
+- Use standard Markdown (CommonMark). No custom shortcodes or Astro components inside docs
+
+### Keeping docs up to date
+
+**Documentation must be kept up to date.** When adding a new feature:
+
+1. **PHP syntax feature** (operator, built-in, statement, etc.) → update the relevant page in `docs/php/`. Add the function signature, parameters, return type, and a short example.
+2. **Compiler extension** (pointer, buffer, extern, ifdef) → update the relevant page in `docs/beyond-php/`.
+3. **Compiler internals change** (pipeline, type checker, optimizer, codegen, runtime, ABI, memory model) → update the relevant page in `docs/internals/`.
+4. **Compilation flow or CLI change** (new/changed flag, env var, pipeline phase, target, output mode) → update the relevant page in `docs/compiling/`, keeping `docs/compiling/cli-reference.md` authoritative and in sync with `src/cli.rs`. Mirror user-facing flag examples in `README.md`.
+5. If a feature was previously listed as "not supported", remove that note.
+6. If there are known incompatibilities with PHP, document them in `docs/php/types.md` (incompatibilities section).
+7. Update `docs/README.md` index if adding a new page.
+
+## Roadmap management
+
+`ROADMAP.md` tracks all planned and completed work, organized by version.
+
+- **Never remove completed items** from a version section. Mark them as `[x]` and leave them under the version they belong to. This preserves the history of what was delivered in each release.
+- New work items go under the appropriate future version.
+- When all items in a version are completed, the version is considered done — do not move items elsewhere.
+
+## Changelog management
+
+`CHANGELOG.md` records every released version, newest first, in *Keep a Changelog* style.
+
+When cutting a release:
+
+- Add a new section at the top (under the header), above the previous version:
+
+ ```
+ ## [X.Y.Z] - YYYY-MM-DD
+ - One terse, user-facing bullet per notable change.
+ ```
+
+ Keep entries concise (usually one or two bullets), describe what shipped — not the implementation — and use the absolute release date.
+- Add a matching compare link at the **bottom** of the file, also newest first, immediately above the previous version's link:
+
+ ```
+ [X.Y.Z]: https://github.com/illegalstudio/elephc/compare/v...vX.Y.Z
+ ```
+
+ The first-ever release uses the `releases/tag/v0.1.0` form instead of a compare range. Every version section must have its link; do not leave the link out.
+- Never change elephc's Cargo package version in `Cargo.toml` or `Cargo.lock`. Release automation in CI owns Cargo version bumps; agent changes should leave those files' version numbers untouched unless the user explicitly overrides this policy.
+
+## Conventions
+
+- No `Co-Authored-By` lines in commits
+- Use commit message prefixes such as `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, or `test:`
+- Keep commit messages concise
+- Run the focused pre-commit verification above before committing code changes. Do not knowingly commit with relevant focused tests failing; the full suite must pass in CI.
+- Zero compiler warnings policy (`cargo build` must be clean)
+- Never run `cargo fmt` in this repo. Use targeted manual edits only; global formatting creates noisy churn here.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c42753a1cb..60c45752db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,15 +4,55 @@ All notable changes to elephc, a PHP-to-native compiler written in Rust.
Releases are listed newest first.
## [Unreleased]
+- Fixed name resolution inside named-argument values (issue #495): an imported alias or namespace-relative name nested in a named argument's value — such as `Url` in `new self(url: new Url('/'))` — is now rewritten to its canonical fully-qualified form like positional arguments, instead of failing with "Undefined class: Url". The name resolver's expression walk previously had no `NamedArg` arm, so the value expression escaped rewriting entirely.
+- Removed the legacy direct AST → ASM backend completely. EIR is now the only
+ codegen implementation path.
+- `in_array()` now honors its optional third `$strict` argument: omitted/false
+ uses PHP loose membership for supported scalar/string paths, including
+ numeric-string coercion, string loose equality, and bool/int truthiness, while
+ `true` uses strict type-identical membership.
+- Add an experimental Windows x86_64 (PE32+) cross-compilation target (`--target windows-x86_64`, alias `x86_64-pc-windows-gnu`): produces a GNU/MinGW-ABI `.exe` (`.dll` for `--emit cdylib`) via `x86_64-w64-mingw32-gcc`, and requires the MinGW-w64 cross toolchain on the host doing the build. Runtime shim coverage and end-to-end execution testing are still catching up to macOS/Linux, so treat it as cross-compilation support rather than a fully verified target.
+- Add the `random_bytes(int $length): string` builtin: a cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or a length below 1.
+- Int-backed enum `from()` / `tryFrom()` now accept a dynamically-typed (`mixed`) argument (issue #449): a `foreach` value over a heterogeneous array, an untyped parameter, etc. are coerced on their runtime type before the enum lookup — integer/numeric-string resolve (or throw `ValueError`), float truncates, bool/null coerce, and array/object/resource/closure throw `TypeError` naming the given type. Previously any `mixed` argument was rejected at compile time. Target-aware on every supported backend.
+- Int-backed enum `from()` / `tryFrom()` now accept a numeric string (issue #349): `Level::from("1")` coerces the string to the integer backing value (as a distinct EIR coercion lowered before the enum call) and returns the matching case, instead of being rejected at compile time. A numeric string with no matching case throws `ValueError`; a non-numeric string (e.g. `"x"`) throws `TypeError` with PHP's exact argument-type message — matching PHP's coercive typing on every supported target, including PHP-rejected libc `strtod` extensions such as hexadecimal `"0x1"`, `"INF"`, and `"NAN"`.
+- Fixed an enum `from()` / `tryFrom()` refcount bug (surfaced while fixing #349): the returned case singleton was under-retained, so storing the result into a reassigned variable inside a loop drove the persistent singleton's refcount to zero and freed it — producing garbage reads or a heap crash after a few iterations. `from()`/`tryFrom()` now retain the matched singleton, keeping it alive like direct case access. Affected both backed-enum backings.
+- Fixed integer-arithmetic overflow parity (issue #369): runtime `int + int`, `int - int`, and `int * int` now promote to `float` on 64-bit overflow instead of wrapping, clamping, or staying statically `int`, while non-overflowing runtime arithmetic remains `integer`. The EIR backend now lowers checked integer arithmetic through target-aware runtime helpers, folds constant checked operations back to scalar `int`/`float` values under `--ir-opt`, and preserves PHP behavior through chained arithmetic plus prefix/postfix increment overflow. The ownership cleanup around widened `Mixed` results, statics, ordinary globals, returned locals, and `--web` request resets was tightened so the extra boxed values introduced by checked arithmetic are released exactly once.
+- Fixed a constant-propagation miscompile with by-reference calls in a `match` subject (issue #384): `echo match(bump($i)) { ... } . "|" . $i` kept the pre-call constant for `$i` and printed the stale value, because a call's unknown write set was treated as "no writes" instead of forcing conservative invalidation. A read sequenced after any by-reference-mutating call in the same expression now observes the post-call value, matching PHP; pure calls (`gettype`, `strlen`, …) keep their operands foldable.
+- Fixed PHP parser/codegen parity for parenthesis-free object instantiation (issue #371): `new Foo`, `new self`, `new static`, `new parent`, and `new $class` now compile with empty constructor arguments, while immediate postfix forms such as `new Foo->bar`, `new Foo::bar()`, `new Foo?->bar`, and `new Foo[0]` are rejected instead of being misparsed.
+- Fixed three PHP parity regressions in the EIR backend: indexed array elements such as `$a[0]` can now be passed to by-reference parameters with copy-on-write storage split before mutation (issue #360); `IteratorAggregate::getIterator()` may declare the marker `Traversable` return type while `foreach` still dispatches through the returned object's concrete `Iterator` methods (issue #385); and catchable private/protected method access plus readonly-property write errors now preserve PHP's receiver/RHS evaluation order before throwing `Error` (issue #383). The merge also removes a duplicate `_spl_error_class_id` data symbol that could make post-merge user assembly fail to assemble.
+- Fixed EIR parity for two PHP runtime edge cases: `foreach` by reference now observes elements appended during iteration instead of using the by-value snapshot length, while by-value indexed and mixed-array iteration still stops at the original array length; and reading an uninitialized typed instance or static property now emits PHP's specific fatal message when uncaught while still throwing a catchable `Error` when an exception handler is active.
+- Fixed missing-key reads on indexed arrays: null coalescing now suppresses undefined-key warnings for missing integer, string, mixed, and `null` keys while direct reads still emit PHP-compatible `Warning: Undefined array key ...` diagnostics and return the correct null fallback. The EIR/runtime paths now handle mixed-key indexed reads consistently across macOS ARM64, Linux x86_64, and Linux ARM64, including string-key warnings and mixed integer-key misses.
+- Fixed static-member and integer-division parity (issues #336, #372, #356): `static::CONST` now late-binds to the runtime class and falls back to the declaring-class value when not overridden; prefix/postfix `++` and `--` now work on static properties through `ClassName::$x`, `self::$x`, `static::$x`, and `parent::$x`; and `intdiv(PHP_INT_MIN, -1)` now throws catchable `ArithmeticError` with PHP's message instead of wrapping or trapping on every supported backend target.
+- Fixed mixed float loose equality and `switch` comparisons (issue #397): `Mixed` float operands now compare numerically instead of truncating through integers, and numeric loose equality dispatches through the boxed runtime tag so numeric strings, booleans, non-scalars, and NaN/unordered float comparisons follow PHP semantics on every supported backend target.
+- Fixed undefined-variable compound assignments (issue #370): `$x += 1`, `$x -= 1`, `$x *= 5`, and `$y .= "..."` now treat the missing target as PHP `null`/`0`/`""` with a single warning instead of failing during type checking or reading uninitialized stack storage. Null-coalescing assignment (`??=`) remains warning-free and now also works correctly when used as an expression, such as `echo ($z ??= 42)`.
+- Fixed enum type resolution in class member positions: enum names can now be used as declared property types and constructor-promoted property types without failing early with "Unknown type" during the class schema pass.
+- Fixed the Linux x86_64 `strtotime()` weekday-modifier scanner: `next Mon`, `last Fri`, and similar modifier + weekday forms now pass the remaining input length capped at 16 bytes to the keyword matcher, matching the ARM64 path and avoiding a fragile fixed-width scan into the zero-padded lowercase buffer tail.
+
+## [0.26.0]
+- Runtime dead stripping: compiled executables now link only the runtime helpers the program actually reaches and drop the rest, shrinking binaries without changing behavior. Works on every supported target — Linux via per-symbol sections and `--gc-sections`, macOS via `.subsections_via_symbols` atoms and `-dead_strip`. Shared libraries (`--emit cdylib`) keep the full runtime.
- Closures can be rebound to a new receiver: `Closure::bind()`, `bindTo()`, and `Closure::call()` are supported, and a top-level closure that captures `$this` now binds it correctly instead of losing the receiver. A by-reference `Closure::bind` stored in a variable and called later is tracked as a static callable, so the call carries the bound cell directly rather than going through the generic descriptor invoker.
- New magic methods `__callStatic`, `__isset`, and `__unset`: a static call to an undeclared method dispatches to `__callStatic`, `isset()`/`empty()` on an undeclared property route through `__isset` (and only read `__get` when `__isset` is truthy, so an unset virtual property is empty without ever being read), and `unset($obj->prop)` on a virtual property calls `__unset`.
- Reflection over functions: `ReflectionFunction` (name and parameter counts), `getParameters()`, `ReflectionParameter`, `ReflectionParameter::getType()`, and `ReflectionNamedType`. Attribute arguments are now exposed in reflection metadata, including float, positional-array, named-argument and associative-array values, references to global and class constants, and enum-case references.
- References to object properties: `$x = &$obj->prop` aliases the property with write-through in both directions, and a by-reference function/method return can be captured with `$x = &f()`. By-reference returns also work for `string`- and `float`-typed properties. Reassigning an array reference to a non-empty literal of a different type boxes the literal's elements to match the property's element type.
- `unset()` on array elements: `unset($hash[$key])` removes an associative entry and `unset($arr[$key])` removes a packed indexed element with sparse semantics. `array_map()` now works over heterogeneous (mixed-element) arrays.
+- Added 15 array builtins on the EIR backend: `array_is_list()`, `array_key_first()`/`array_key_last()`, `array_replace()`/`array_replace_recursive()`, `array_diff_assoc()`/`array_intersect_assoc()`, `array_merge_recursive()`, `array_walk_recursive()`, `array_find()`/`array_any()`/`array_all()` (PHP 8.4), `array_udiff()`/`array_uintersect()`, and `array_multisort()`. The hash-based set operations accept associative arrays and scalar-element indexed arrays (converted to integer-keyed hashes, with result keys/values widening to `mixed` for heterogeneous inputs); the predicate/comparator builtins accept string, function, and non-capturing closure callbacks. All are target-aware (macOS ARM64, Linux x86_64, Linux ARM64) and documented in `docs/php/arrays.md`.
- Added the enum case `->name` property (issue #330): every enum case, pure or backed, now exposes the read-only `name` string holding the case identifier (`E::A->name` is `"A"`), matching PHP's `UnitEnum::$name`. Previously this property access was rejected at compile time with "Undefined property". Backed cases keep `->value`, and `$this->name` is now readable inside enum methods. Access works through direct case access, an aliasing variable, `cases()`, and string interpolation.
- Generators now run on stackful coroutines (issue #329): a generator body is compiled by the normal backend and runs on its own coroutine stack, so `Generator::throw()` raises the exception at the suspended `yield` and a `try`/`catch` *inside* the generator body handles it and resumes — instead of always terminating the generator and propagating to the caller. In-generator method calls, arbitrary control flow, and `try`/`finally` around `yield` work like ordinary functions; `yield from` (over generators and arrays), `send()`, and `getReturn()` are preserved. Generator parameters passed on the caller stack (e.g. a 7th integer parameter under the x86_64 SysV ABI) are now forwarded correctly instead of arriving as zero, and a generator declaring more than 7 parameters (counting closure captures) is rejected with a clear diagnostic rather than corrupting coroutine state.
- Fixed associative-array method parameters (issue #406): an `array`-typed parameter of an instance or static method now preserves the associative shape known at the call site, exactly like a free-function `array` parameter. String-key access (`$d['a']`) type-checks instead of failing with "Array index must be integer", and `json_encode()` of the parameter emits a JSON object instead of a JSON list with garbage values. A declared generic `array` parameter is sharpened from the call-site argument type during method-call inference, and the sharpened shape is used when checking the method body.
- Fixed associative-array property defaults (issue #407): a typed `array` (or untyped) property initialized with an associative literal such as `['a' => 1]` is now stored as associative (hash) storage, so string-key reads and writes (`$this->data['a']`, `$this->data[$key]`) type-check and run instead of failing with "Array index must be integer". The EIR backend also lowers string-keyed associative literal defaults for instance and static properties instead of rejecting them as an unsupported `object_new` feature.
+- Fixed heterogeneous associative-array property defaults (issue #413): a typed `array` or untyped property initialized with an associative literal whose values have different types — such as `public array $data = ['n' => 1, 's' => 'hi'];` — now compiles, inferring a boxed `mixed` value slot instead of being rejected with an `unsupported EIR backend feature: prop_set` error. Previously the value type was over-widened to a single scalar (`int` + `string` → `string`), which diverged from the array's actual heterogeneous shape.
+- `serialize()` / `unserialize()` builtins covering scalars, nested arrays, and objects — including the `__serialize`/`__unserialize`/`__sleep`/`__wakeup` magic methods and `r:`/`R:` object back-references (repeated objects rebuild as one shared instance) — byte-for-byte compatible with PHP's wire format.
+- `Phar` / `PharData` global metadata and stub now persist into the archive (native PHAR, tar, and zip) and round-trip across objects and processes via `setMetadata()`/`getMetadata()`/`hasMetadata()`/`delMetadata()` and `setStub()`/`getStub()`.
+- `PharFileInfo` per-file metadata (`setMetadata()`/`getMetadata()`/`hasMetadata()`/`delMetadata()` on `$phar["entry"]`) now persists per-entry into the archive for native PHAR, tar, and zip, round-tripping across objects and the PHP interpreter.
+- `PharData::compress(Phar::GZ|Phar::BZ2)` / `decompress()` now perform whole-archive tar compression, writing a sibling `.tar.gz` / `.tar.bz2` (or plain `.tar`); compressed archives are read transparently and are interchangeable with the PHP interpreter.
+- `Phar::setSignatureAlgorithm()` / `getSignature()` now sign native PHAR, tar, and zip phars, including `Phar::OPENSSL` RSA-SHA1 signing with a PEM private key (verifiable by the PHP interpreter) alongside the MD5/SHA1/SHA256/SHA512 hash algorithms. Tar/zip signatures use a `.phar/signature.bin` control entry.
+- `phar://` zip readers now accept entries written with a streaming data descriptor (general-purpose flag bit 3), reading the authoritative central-directory sizes instead of rejecting the archive.
+- ZIP64 phar archives (over 65535 entries, or sizes/offsets over 4 GiB) are now read and written, interchangeable with the PHP interpreter and other ZIP64 tools.
+- `Phar`/`PharData::setZipPassword()` (a compiler extension) reads and writes traditional-PKWARE (ZipCrypto) encrypted ZIP entries: with a password set, zip entries (the stub included) are encrypted on write and decrypted on read, while the `.phar/signature.bin` entry stays in the clear. The cipher is cryptographically weak and kept only for compatibility with legacy archives.
+- EIR backend correctness: method calls on `mixed` values and on object-iterator `foreach` values (e.g. `DirectoryIterator`, `FilesystemIterator`) now dispatch synthetic SPL methods instead of crashing on an unemitted vtable slot, and `fopen("compress.zlib://…")` / `fopen("compress.bzip2://…")` read wrappers now decompress on the EIR backend.
+- Type-checker correctness: a method call on a `mixed` receiver now infers the union of the declared return types across the classes that declare the method (mirroring the runtime class-id dispatch) instead of falling back to `int`. An un-annotated function such as `function f($x) { return $x->name(); }` now returns the method's actual value — previously the inferred `int` return type coerced a returned string to `0`.
+- Fixed `foreach`-key array rebuilds: `foreach ($src as $k => $v) $dst[$k] = $v` now preserves every entry instead of collapsing all string keys onto index `0` (which dropped all but the last). Sparse and negative integer keys are kept with PHP semantics — the destination promotes to hash storage instead of zero-filling gaps or silently dropping negative-index writes — while contiguous integer-key rebuilds stay on indexed storage so `implode()` and other indexed consumers are unaffected. A `foreach` key variable no longer leaks its key classification into other functions that reuse the same name, and reassigning the key variable to a string inside the loop routes the write as a string key.
+- Resource scope-cleanup: an `fopen()` stream, `popen()` pipe, `opendir()` directory handle, or `hash_init()` hashing context that leaves scope without an explicit `fclose()`/`pclose()`/`closedir()`/`hash_final()` is now released automatically at scope exit (closing the fd, reaping the `popen` child, freeing the context) instead of leaking until the process exits. Aliasing (`$b = $a`) is reference-counted so the handle is freed exactly once; finalizing a hash context and then dropping it is a single safe free; and an explicit close is never doubled even if the descriptor number is later reused.
## [0.25.2] - 2026-06-26
- `--web`: compile a PHP program into a standalone prefork HTTP server binary with per-request top-level execution, `echo`/`print` response bodies, `$_SERVER`/`$_GET`/`$_POST` and `php://input` request input, PHP-compatible `http_response_code()`/`header()` handling, configurable listen address/workers/body limit, clean signal shutdown, worker respawn, bounded keep-alive handling, fixed-heap request cleanup, and full sharded CI coverage across macOS ARM64, Linux x86_64, and Linux ARM64.
@@ -413,7 +453,8 @@ Releases are listed newest first.
## [0.1.0] - 2026-03-22
- Initial compiler: echo, variables, integers, arithmetic and string concatenation, comparison operators, control flow (`if`/`while`/`for`/`break`/`continue`), functions, logical/assignment/increment operators.
-[Unreleased]: https://github.com/illegalstudio/elephc/compare/v0.25.2...HEAD
+[Unreleased]: https://github.com/illegalstudio/elephc/compare/v0.26.0...HEAD
+[0.26.0]: https://github.com/illegalstudio/elephc/compare/v0.25.2...v0.26.0
[0.25.2]: https://github.com/illegalstudio/elephc/compare/v0.25.1...v0.25.2
[0.25.1]: https://github.com/illegalstudio/elephc/compare/v0.25.0...v0.25.1
[0.25.0]: https://github.com/illegalstudio/elephc/compare/v0.24.3...v0.25.0
diff --git a/CLAUDE.md b/CLAUDE.md
index 08d0d5b38a..d8247be8b4 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,559 +1 @@
-# elephc — Developer Guide
-
-## What is this
-
-A PHP-to-native compiler written in Rust. Compiles a static subset of PHP to native assembly for the supported target matrix, producing standalone binaries. No interpreter, no VM, no runtime dependencies.
-
-## Supported target policy
-
-All supported targets are first-class targets. The supported target matrix is currently `macos-aarch64`, `linux-aarch64`, and `linux-x86_64`.
-
-Do not design or land codegen/runtime features as ARM64-first with x86_64 treated as a later port. New features, builtins, runtime helpers, optimizer assumptions that affect emitted code, ABI behavior, and ownership/GC paths must either support every supported target in the same change or clearly isolate an intentionally unsupported path with diagnostics, tests, and documentation. A feature is not considered done while any supported target has a missing runtime symbol, reduced semantics, stale documentation, or an untested target-specific lowering path.
-
-When examples or internals docs use ARM64 snippets for readability, treat them as examples only. Implementation work must keep the target-aware ABI/runtime boundaries authoritative.
-
-## Build & run
-
-```bash
-cargo build # dev build
-cargo build --release # optimized build
-cargo run -- file.php # compile a PHP file
-```
-
-The compiler outputs a native binary next to the source file (e.g., `file.php` → `file`).
-
-## Test policy
-
-**Every feature must have tests before it's considered done.** The test suite is the primary quality gate.
-
-Local agent runs should stay focused. By default, run only the build checks and tests needed to validate the implementation or regression you touched, then rely on CI for the complete matrix. Do **not** run the full local suite (`cargo test`, `cargo test -- --include-ignored`, or full Linux Docker scripts) unless the user explicitly asks for it, you are doing a release/pre-release verification, CI is unavailable for the needed signal, or a broad/high-risk change cannot be validated responsibly with focused tests.
-
-### Running tests
-
-```bash
-cargo test test_fizzbuzz # run a focused test by name
-cargo test --test codegen_tests test_name # run a focused end-to-end codegen test
-cargo nextest run --profile ci test_name # run a focused nextest test with per-test timeout
-cargo test # full local suite; only when explicitly requested/necessary
-cargo test -- --include-ignored # ignored tests; only when relevant or requested
-```
-
-Linux target-specific regressions can be checked through the Docker scripts in
-`scripts/`. Prefer filters during implementation; the unfiltered commands run
-full Linux suites and should be reserved for explicit requests or genuinely
-necessary local target validation.
-
-```bash
-./scripts/test-linux-x86_64.sh iterable # run tests matching a filter
-./scripts/test-linux-arm64.sh test_my_feature # run tests matching a filter
-./scripts/test-linux-x86_64.sh # full Linux x86_64 suite; only when necessary/requested
-./scripts/test-linux-arm64.sh # full Linux ARM64 suite; only when necessary/requested
-./scripts/test-linux-x86_64.sh --rebuild # rebuild the Docker image first
-./scripts/test-linux-arm64.sh --rebuild # rebuild the Docker image first
-```
-
-Some tests are marked `#[ignore]` because they require external libraries (e.g., SDL2) not available in CI. Run ignored tests only when the change touches that surface, during explicit release verification, or when the user asks for them.
-
-### Test strategy during development
-
-The full test suite is slow because each codegen test spawns `as` + `ld` + runs the binary. CI runs the complete supported matrix (`macos-aarch64`, `linux-x86_64`, and `linux-aarch64`) with sharded codegen tests, so local implementation work should optimize for fast, relevant signal:
-
-1. **While developing a feature**: run only the tests for that feature or regression (`cargo test test_my_feature`).
-2. **Scope to a single test binary**: prefer `cargo test --test codegen_tests ` over a bare `cargo test `. A bare filter rebuilds and links all six test binaries every cycle (~2.5s of wasted link time); `--test ` links only the one you need. Most codegen work lives in `codegen_tests`.
-3. **For target-sensitive changes**: run the smallest relevant macOS/Linux focused tests locally when they materially reduce risk; otherwise let CI provide complete target coverage.
-4. **For broad infrastructure changes**: validate the edited workflow/config directly (YAML parse, `cargo metadata`, `nextest` config checks, etc.) instead of running unrelated Rust suites.
-5. **PHP cross-check**: opt in narrowly with `ELEPHC_PHP_CHECK=1 cargo test ` when PHP equivalence is the question.
-
-### Pre-commit verification
-
-Before committing code changes, run the smallest useful focused tests and hygiene checks that cover the implementation. Do not run full local suites by default; CI is responsible for the complete sharded matrix after push/PR.
-
-```bash
-cargo build # for code changes that should compile warning-free
-cargo test # or the narrower --test form
-git diff --check
-```
-
-For docs-only, workflow-only, or configuration-only changes, replace Rust test runs with the relevant syntax/metadata checks. For codegen changes, also verify assembly-comment coverage/alignment for any files you touched. If the change can affect generated assembly, runtime helpers, ABI behavior, linking, ownership/GC, or target-specific libraries, run focused tests for affected supported targets when local evidence is needed; otherwise rely on CI for full Linux x86_64/Linux ARM64/macOS ARM64 coverage.
-
-### Test structure
-
-| File | What it tests | How |
-|---|---|---|
-| `tests/lexer_tests.rs` | Tokenization | Asserts token sequences from source strings |
-| `tests/parser_tests.rs` | AST construction | Asserts AST node structure and operator precedence |
-| `tests/codegen_tests.rs`, `tests/codegen/` | Full pipeline (end-to-end) | Compiles PHP → binary, runs it, asserts stdout |
-| `tests/error_tests.rs`, `tests/error_tests/` | Error reporting | Asserts that invalid programs produce the right error messages |
-
-### Test coverage requirements
-
-- **New language construct** (keyword, operator, statement): needs lexer, parser, codegen, AND error tests
-- **New operator**: needs a Pratt parser binding power test verifying precedence relative to adjacent operators
-- **New statement type**: needs at least one codegen test showing correct output, one test for edge cases (empty body, nested), and one error test for malformed syntax
-- **New built-in function**: needs codegen tests for normal use and error test for wrong argument count/types
-- **Bug fix**: must include a regression test that would have caught the bug
-- **Every feature also needs an example** in `examples/`. If an existing example can showcase the new feature naturally, update it. Otherwise, create a new `examples//main.php` with its own `.gitignore` (containing `*.s`, `*.o`, `main`). Examples should be small, readable programs that demonstrate real use cases — not just test cases.
-
-### Writing codegen tests
-
-Codegen tests compile inline PHP source and assert stdout:
-
-```rust
-#[test]
-fn test_my_feature() {
- let out = compile_and_run("` |
-| `src/parser/` | `parse()` | Tokens → `Program` (Vec of Stmt). Pratt parser for expressions |
-| `src/magic_constants/`, `src/magic_constants.rs` | `substitute_file_and_scope_constants()` | Lowers PHP magic constants before resolver/name-resolver/optimizer passes |
-| `src/conditional.rs` | `apply()` | Applies compiler `ifdef` conditional branches |
-| `src/resolver/` | `resolve()` | Resolves `include`/`require`, discovers declarations, and tracks include-loaded function variants. Runs before namespace/name canonicalization |
-| `src/name_resolver/` | `resolve()` | Applies namespace/use rules, rewrites references to canonical fully-qualified names, handles PHP-style builtin fallback, and flattens namespace-only AST nodes before type checking |
-| `src/types/` | `check()` | Type checking, returns `CheckResult` with `TypeEnv`, function/class/interface/enum/FFI metadata, warnings, required libraries, and the internal `Mixed` type for heterogeneous assoc-array values |
-| `src/optimize/`, `src/optimize.rs` | `fold_constants()`, `propagate_constants()`, `eliminate_dead_code()` | AST-level constant folding/propagation, control-flow pruning/normalization, DCE, and effect modeling |
-| `src/ir/` | IR types / builders / validator | EIR program, function, block, instruction, terminator, value, local, effect, ownership, and textual-format definitions |
-| `src/ir_lower/` | `lower_program()` | Active AST → EIR lowering, including local slot creation, hidden temporaries, ownership annotations, and PHP call semantics |
-| `src/ir_passes/` | `optimize_module()` / `allocate_registers()` | EIR analyses and transformations over lowered functions: fixed-point optimization pass driver (identity folding, …; gated by `--ir-opt`) and linear-scan register allocation, before codegen |
-| `src/codegen_ir/` | `generate()` | Active EIR → target assembly backend |
-| `src/codegen/` | `generate()` / shared emitters | Frozen legacy direct AST backend plus shared ABI/runtime/target helpers still consumed by EIR |
-| `src/codegen/abi/` | ABI helpers | Target-specific argument materialization, frame layout, registers, stack slots, symbols, and call helpers |
-| `src/codegen/program_usage/` | Program scans | Collects codegen metadata such as required classes and variables before emission |
-| `src/runtime_cache.rs` | `prepare_runtime_object()` | Builds/reuses the target runtime object before final linking |
-| `src/errors/` | `report()` | Error formatting with line:col |
-| `src/span.rs` | `Span` | Source position (line, col) attached to all AST nodes |
-
-### Codegen layout
-
-- `src/ir_lower/` is the active high-level lowering layer. Add PHP-visible semantics there, not in legacy direct AST emitters.
-- `src/codegen_ir/lower_inst/` and `src/codegen_ir/lower_term.rs` are the active EIR instruction/terminator assembly lowerers.
-- `src/codegen_ir/context.rs`, `src/codegen_ir/frame.rs`, and `src/codegen_ir/value_placement.rs` carry active backend state, frame layout, and value placement.
-- `src/codegen/expr.rs`, `src/codegen/stmt.rs`, and their focused legacy helper modules are frozen direct AST backend dispatchers. Do not extend them for new features.
-- `src/codegen/runtime/mod.rs` emits shared runtime code (`__rt_*` routines)
-- `src/codegen/runtime/data.rs` emits shared runtime `.data` / `.bss` symbols and metadata tables
-- `src/codegen/abi/` centralizes target-specific register, stack, frame, symbol, and call mechanics. Prefer these helpers over hardcoding ARM64 or x86_64 details in feature emitters.
-
-### Adding a new operator
-
-1. Add token to `src/lexer/token.rs`
-2. Add scanning logic to `src/lexer/scan.rs`
-3. Add `BinOp` variant to `src/parser/ast.rs`
-4. Add one line to `infix_bp()` in `src/parser/expr/pratt.rs` (the Pratt parser binding power table)
-5. Add type checking/inference in the relevant `src/types/checker/` file, usually under `inference/ops.rs` or expression inference
-6. Add optimizer/effect handling when the operator can be folded, propagated, pruned, or has side effects
-7. Add EIR lowering in the relevant `src/ir_lower/expr/` path and target-aware EIR codegen under `src/codegen_ir/lower_inst/` when the operator needs a new IR instruction or lowering path. Do not extend the frozen legacy direct AST emitter.
-8. Add tests in all 4 test files
-
-### Adding a new statement type
-
-1. Add `StmtKind` variant to `src/parser/ast.rs`
-2. Add parser logic in `src/parser/stmt.rs`
-3. Add resolver/name-resolver handling if the statement can contain names, declarations, includes, function variants, or expressions
-4. Add type checking in the relevant `src/types/checker/` module
-5. Add optimizer/effects/warnings handling if the statement can be folded, pruned, read variables, write variables, or alter control flow
-6. Add EIR lowering in `src/ir_lower/stmt/` and target-aware EIR codegen under `src/codegen_ir/` when the statement needs new instruction or terminator support. Do not extend the frozen legacy direct AST emitter.
-7. If it introduces variables or hidden temporaries, update EIR local/temp declaration in `src/ir_lower/context.rs` and any frame-layout allocation needed before frame sizing
-8. Add tests
-
-### Adding or changing an AST node
-
-When adding a new `ExprKind` or `StmtKind`, check every AST-walking pass. The compiler has many passes that deliberately recurse by variant, and missing one usually creates silent miscompilation rather than a compile error.
-
-Common places to audit:
-
-- Parser construction and lowering in `src/parser/`
-- Resolver/include discovery and function-variant handling in `src/resolver/`
-- Namespace/use/FQN rewriting in `src/name_resolver/`
-- Type checking, inference, return analysis, warnings, and type compatibility in `src/types/`
-- Optimizer folding, propagation, DCE, control-flow normalization, and effect modeling in `src/optimize/`
-- Program usage scans in `src/codegen/program_usage/`
-- Local/hidden-slot declaration in `src/ir_lower/context.rs` and frame placement in `src/codegen_ir/`
-- Ownership metadata in `src/ir_lower/ownership.rs`, EIR ownership lowering in `src/codegen_ir/lower_inst/ownership.rs`, and related runtime/GC paths
-- EIR lowering in `src/ir_lower/` plus EIR backend lowering in `src/codegen_ir/`
-- Lexer/parser/codegen/error/regression tests, depending on the surface area
-
-### Adding a new built-in function
-
-1. Add the function to `src/types/checker/builtins/catalog.rs`. This is mandatory: it drives PHP-style case-insensitive builtin lookup, namespace fallback, redeclaration checks, and `name_resolver` behavior.
-2. Confirm `function_exists("...")` recognizes the function. The implementation delegates to the canonical catalog; do not add a second builtin-name table without keeping it in lockstep.
-3. Add or update the call signature in `src/types/signatures.rs`. This is the contract for named arguments: parameter names, default values, variadic name, by-ref/ref-like params, and arity must match PHP. Mark mutating parameters in `ref_params`; named-argument lowering and hidden-temp allocation depend on it.
-4. Add type signature handling in the appropriate `src/types/checker/builtins/.rs` file (argument count, value types, return type, warnings, required Linux libraries).
-5. Add first-class callable support in `first_class_callable_builtin_sig()` if the builtin should work through first-class callable syntax or callable aliases.
-6. Add optimizer effect modeling in `src/optimize/effects/builtins.rs` when purity, reads, writes, or thrown/fatal behavior matters for DCE/constant propagation.
-7. Add or update EIR call/lowering support in `src/ir_lower/expr/` when argument materialization, hidden temporaries, or ownership differ from ordinary calls.
-8. Add target-aware EIR backend support in `src/codegen_ir/lower_inst/builtins/.rs` or the closest focused EIR lowering module.
-9. If the function needs a runtime routine, create it under `src/codegen/runtime//`.
-10. Add module/re-export wiring in the relevant `runtime//mod.rs`, then call it from the runtime emitter orchestration.
-11. Leave the frozen legacy `src/codegen/builtins/` path untouched unless a narrow shared-API build fix is required.
-12. Update docs and add codegen/error tests. New PHP-visible builtins should include at least one case-insensitive or namespaced call test when relevant.
-
-Do not stop after wiring only the checker and EIR backend dispatcher. A builtin is not complete until the catalog, `function_exists()`, case-insensitive lookup, and namespace fallback all see it. New builtins should include at least one case-insensitive or namespaced call test when the feature is PHP-visible.
-
-Leaf builtin/runtime files contain exactly **one emitter function**. Keep dispatcher/re-export files (`mod.rs`) as orchestration-only files, and keep runtime data emission in `src/codegen/runtime/data.rs`.
-
-Do not list every builtin in this guide. `src/types/checker/builtins/catalog.rs` and `src/types/signatures.rs` are the canonical sources; update those instead of maintaining parallel lists.
-
-### Adding a new EIR optimization pass
-
-IR-level transformations run after EIR lowering/validation through a fixed-point driver, not in the AST optimizer.
-
-1. Implement the `IrPass` trait (`name()`, `run(&mut Function, &mut DataPool) -> bool`) in a new `src/ir_passes/.rs`; `run` mutates the function in place and returns whether it changed anything. The `DataPool` is the module's shared literal pool for passes that intern new constants (e.g. peephole string-literal concat folding); ignore it (`_data`) otherwise.
-2. Register the pass in `default_passes()` in `src/ir_passes/driver.rs`. Order matters: the driver re-runs the whole set per function until none reports a change, capped by `MAX_PASS_ITERATIONS`.
-3. Reuse `src/ir_passes/rewrite.rs` for value redirection (`replace_all_uses` for RAUW) and the shared fold helpers (`resolve_chains`, `neutralize_to_nop`, `defining_instruction`, `count_value_uses`) instead of re-walking operands/terminators. Keep rewrites dominance-safe and PHP-equivalent; cross-check edge cases (division by zero, signed-zero/`NaN` floats) with `php -r`.
-4. The driver re-validates each function with `validate_function` after every pass in debug/test builds and panics (naming the pass) on malformed IR or non-convergence; both guards compile out of `--release`. Rely on this during development.
-5. Add unit tests under `src/ir_passes/tests/` (hand-built EIR via `crate::ir::Builder`) and end-to-end tests under `tests/codegen/optimizer/`. In e2e fixtures, use runtime-unknown values (e.g. `$argc`) so the targeted IR construct survives AST-level folding and actually reaches EIR.
-6. Passes are gated by `--ir-opt=on|off` / `--no-ir-opt` (env `ELEPHC_IR_OPT`), default on. Behavior must be identical with the flag on or off except for performance; verify with `--emit-ir` and `--emit-ir --no-ir-opt`.
-
-### Call argument semantics
-
-All function-like call surfaces must share the same argument rules instead of normalizing locally in individual emitters:
-
-- Shared named/positional/spread semantics live in `src/types/call_args/` whenever they are not codegen-specific.
-- `src/types/call_args/` owns the semantic planner (`CallArgPlan` / `plan_call_args`). The checker and EIR lowering should consume that plan; they should not rebuild named-argument matching, duplicate detection, static associative-spread expansion, spread bounds, or the regular/variadic split locally.
-- If a codegen surface uses an internal signature with hidden parameters, such as closure captures, pass the caller-visible regular parameter count through `plan_call_args_with_regular_param_count()` instead of letting the planner infer it from the full internal signature.
-- Type-checker validation and diagnostic mapping lives in `src/types/checker/functions/call_validation.rs`; it maps planner errors to `CompileError` diagnostics instead of owning the semantic rules.
-- `src/ir_lower/expr/` owns active EIR call-argument lowering: planner consumption, source-order named/spread lowering, spread checks, and hidden-temp creation. `src/codegen_ir/` then materializes the lowered call through target-aware ABI helpers. The legacy `src/codegen/expr/calls/` path is frozen.
-- User-defined calls, builtins, and extern calls must use the same named/spread normalization rules before any callee-specific lowering runs.
-- PHP call unpacking with static string keys maps to named arguments (`f(...["a" => 1])` behaves like `f(a: 1)`). Static numeric keys remain positional, and duplicate static string keys inside one unpack use PHP's last-wins behavior before planning.
-- When adding or extending a builtin, verify `first_class_callable_builtin_sig()` as well as the direct builtin signature so first-class callable syntax and callable aliases stay coherent.
-- PHP source evaluation order is distinct from ABI parameter order. Preserve side effects in source order, then materialize arguments in parameter/ABI order; extern calls follow the same rule before C ABI register loading.
-- Spread arguments before named arguments must be evaluated once, length/overwrite checks must happen at the PHP-observable point, and later named-argument side effects must not be skipped by early codegen checks. Too-short spreads for required parameters must fail instead of reading past the array payload.
-- A positional spread into a variadic callee fills visible regular parameters first; only the remaining tail becomes the variadic array.
-- User-defined variadics accept unknown named arguments as string-keyed variadic entries; internal/builtin variadics reject unknown named arguments like PHP internals.
-- Ref-like parameters, including mutating builtin parameters, must avoid value-temp preevaluation so the original storage is passed/mutated.
-- If hidden named-argument temporaries are introduced, update `src/ir_lower/context.rs` and EIR frame placement so slots are allocated before frame-size calculation.
-
-### Optimizer and effects
-
-The optimizer assumes side effects are modeled conservatively. When changing calls, operators, expressions, statements, or builtins:
-
-- Update `src/optimize/effects/` if purity, variable reads/writes, call effects, filesystem/runtime state, exceptions/fatals, or by-ref mutation behavior changes.
-- Do not mark a call as pure if it can read or write globals, files, environment, runtime heap state, object properties, array contents, argument storage, or can emit visible output.
-- Keep constant folding in `src/optimize/fold/` limited to PHP-equivalent results. If PHP behavior is edge-case sensitive, cross-check with `php -r`.
-- Add optimizer regression tests under `tests/codegen/optimizer/` when DCE, constant propagation, control-flow pruning, or folding can observe the change.
-- Magic constants must be lowered before optimizer passes. Do not introduce optimizer paths that expect raw `ExprKind::MagicConstant`.
-- `src/optimize/` is the AST optimizer only. IR-level (EIR) transformations live in `src/ir_passes/` behind the fixed-point pass driver; see "Adding a new EIR optimization pass". Folds that need value identity, basic blocks, or dominance belong there, not in `src/optimize/`.
-
-### Runtime ownership, GC, and COW
-
-Refcounted runtime values are not plain scalars. When changing arrays, strings, objects, `Mixed`, `Iterable`, call returns, or temporaries:
-
-- Preserve the boxed `Mixed` cell contract: the runtime tag and payload shape must stay consistent across codegen and runtime helpers.
-- Respect copy-on-write before mutating arrays or hashes. Use the existing ensure-unique helpers instead of mutating shared storage directly.
-- Track whether a value is owned, borrowed, persistent, or a temporary result. Release only values this code path owns.
-- Keep cleanup paths balanced across normal returns, early exits, throws/fatals, and control-flow merges.
-- Add focused tests in `tests/codegen/runtime_gc/` for ownership, aliasing, cycles, heap debug, stack args, and COW changes.
-
-### File size policy
-
-As a general rule, aim to keep source files under **500 lines of code**. This is a maintainability guideline, not a blind numeric rule.
-
-The real goal is to avoid files that become hard to reason about because they mix multiple responsibilities. In practice:
-
-- **Dispatcher/orchestration files** (`mod.rs`, top-level drivers, large checker/codegen coordinators) should stay slim. If they grow large, split them aggressively.
-- **Multi-responsibility files** should be split once they start accumulating unrelated concerns, even if the line count is not yet extreme.
-- **Leaf files that implement one cohesive feature** are allowed to exceed 500 lines when splitting them would create artificial fragmentation.
-
-Examples of files that may reasonably stay above the soft limit:
-
-- a single runtime emitter implementing one substantial builtin or runtime routine
-- a single compiler pass file that is still clearly about one feature and one code path
-- a self-contained parser/lowering/runtime leaf where splitting would only spread one mental model across several tiny files
-
-Examples of files that should usually be split:
-
-- a file that mixes dispatch, validation, data collection, and post-processing
-- a file that contains several unrelated builtins or runtime helpers
-- a file that acts as a “miscellaneous bucket” for code that did not get a home
-
-So the policy is:
-
-- treat **500 LOC as a warning sign**
-- treat **mixed responsibilities** as the real trigger for refactoring
-- do **not** split a file that owns one coherent feature just to satisfy the number
-
-In short: prefer **cohesion over mechanical line-count compliance**. A 650-line mono-feature leaf is acceptable; a 350-line multi-purpose orchestrator is already a refactor candidate.
-
-### Rust module preamble policy
-
-Every repo-owned Rust source file (`*.rs`) must start with a module-level Rustdoc preamble before any `use`, `mod`, item, or test helper code. Use `//!` comments so the explanation is attached to the module in rustdoc.
-
-The preamble is mandatory for all new Rust files and must be added or preserved when touching existing Rust files. Release verification should report any Rust file that is missing it.
-
-Standard format:
-
-```rust
-//! Purpose:
-//! Explain what this file owns in 2-4 lines.
-//!
-//! Called from:
-//! - `crate::path::caller()` or the relevant test/module entry point.
-//!
-//! Key details:
-//! - Important invariants, ordering constraints, ownership/ABI/runtime rules, or coupling.
-```
-
-For test files, use the same structure but describe the test surface instead of production callers:
-
-```rust
-//! Purpose:
-//! Integration or regression tests for the relevant feature area.
-//!
-//! Called from:
-//! - `cargo test` through Rust's test harness.
-//!
-//! Key details:
-//! - Fixture layout, platform assumptions, ignored-test requirements, or why edge cases exist.
-```
-
-Keep preambles concise and factual. Do not include refactor history, stale line numbers, or broad architecture prose that belongs in `docs/internals/`.
-
-### Rust function docblock policy
-
-Every explicit Rust function in repo-owned Rust source files must have a concise docblock explaining what that function does. This applies equally to public functions, restricted-visibility functions (`pub(crate)`, `pub(super)`, etc.), private helper functions, impl methods, trait methods, and test functions.
-
-Use `///` Rustdoc comments immediately before the function item or its item attributes. Keep the docblock specific to the function's actual responsibility, inputs, outputs, side effects, ownership/ABI/runtime constraints, and failure behavior when those details matter. Do not use vague filler such as "handles logic" or "processes data".
-
-When documenting test functions, describe the behavior or regression being verified and any important fixture/platform assumptions. For explanatory comments inside a function body, use normal `//` comments, not `///`; Rustdoc comments inside function bodies produce warnings or errors because they do not document an item.
-
-Adding or updating function docblocks must not change code behavior. Do not alter function signatures, visibility, attributes, derives, module declarations, control-flow braces, strings, assembly instructions, or instruction-comment alignment while adding documentation. If a doc-only change causes `cargo check`, `cargo check --tests`, or `git diff --check` to fail, fix the documentation placement or comment style rather than changing code to fit the comment.
-
-### Codegen conventions (target-aware)
-
-- Prefer helpers from `src/codegen/abi/` for registers, stack slots, frame layout, argument materialization, symbol addresses, and calls.
-- New feature emitters belong in `src/codegen_ir/`; the legacy direct AST emitters under `src/codegen/expr/`, `src/codegen/stmt/`, and `src/codegen/builtins/` are frozen.
-- New feature emitters must support every supported target through `emitter.target` or clearly isolate target-specific code behind existing target helpers with explicit tests and diagnostics.
-- Avoid hardcoding ARM64 register names, x86_64 register names, syscall numbers, object formats, or stack alignment rules in shared lowering code.
-- Do not add an ARM64-only runtime helper, builtin emitter, ABI path, or ownership cleanup path unless the feature is intentionally target-gated and documented as unsupported elsewhere.
-- Target-sensitive changes need coverage for every supported target they can affect. During local implementation, run focused target checks only when they are needed for confidence; rely on CI for the complete supported-target matrix unless the user requests local Docker runs or CI cannot provide the needed signal.
-
-### ARM64 quick reference
-
-- **Integers**: result in `x0`
-- **Floats**: result in `d0`
-- **Strings**: pointer in `x1`, length in `x2`
-- **Function args**: `x0`-`x7` (int = 1 reg, string = 2 regs), `d0`-`d7` (floats)
-- **Return value**: same as expression result (`x0`, `d0`, or `x1`/`x2`)
-- **Stack frame**: `x29` = frame pointer, `x30` = link register, locals at negative offsets from `x29`
-- **ABI helpers**: `src/codegen/abi/` centralizes load/store/write per type
-- **Labels**: use `ctx.next_label("prefix")` — global counter prevents collisions across functions
-- **Mixed values**: `PhpType::Mixed` is an internal boxed runtime shape used for heterogeneous associative-array values; codegen/runtime must preserve the boxed cell contract instead of treating it like a plain scalar
-
-### Assembly comment policy
-
-**Every `emitter.instruction(...)` call MUST have an inline `//` comment** explaining what the assembly instruction does. This is mandatory — the generated assembly is meant to be read, and every assembly line must be understandable by someone learning how compilers work.
-
-Rules:
-
-1. **Every instruction line gets a comment.** No exceptions. If you add a new `emitter.instruction(...)`, it must have a `// comment`.
-2. **Alignment: `//` starts at column 81.** Pad with spaces so the `//` is at the 81st character position (1-indexed). If the code itself is >= 80 characters, add exactly one space before `//`.
-3. **Block comments before related groups.** Use `// -- description --` on a standalone line before a block of related instructions (e.g., `// -- set up stack frame --`, `// -- copy bytes from source --`).
-4. **Comments explain intent, not mnemonics.** Write "store argc from OS" not "store x0 to memory". The reader can see the instruction — explain *why* it's there.
-
-Example of correct formatting:
-
-```rust
- // -- set up stack frame --
- emitter.instruction("sub sp, sp, #32"); // allocate 32 bytes on the stack
- emitter.instruction("stp x29, x30, [sp, #16]"); // save frame pointer and return address
- emitter.instruction("add x29, sp, #16"); // set new frame pointer
-
- // -- convert integer to string and write to stdout --
- emitter.instruction("bl __rt_itoa"); // convert x0 to decimal string → x1=ptr, x2=len
- emitter.instruction("mov x0, #1"); // fd = stdout
- emitter.instruction("mov x16, #4"); // syscall 4 = sys_write
- emitter.instruction("svc #0x80"); // invoke macOS kernel
-```
-
-To verify alignment, run:
-```bash
-python3 -c "
-with open('path/to/file.rs') as f:
- for i, line in enumerate(f, 1):
- if 'emitter.instruction' in line and '//' in line:
- pos = line.rstrip().index('//')
- if pos != 80 and len(line[:pos].rstrip()) < 80:
- print(f'Line {i}: // at col {pos+1}')
-"
-```
-
-## Examples
-
-Each example lives in `examples//main.php` with its own `.gitignore`. To run:
-
-```bash
-cargo run -- examples/fizzbuzz/main.php
-./examples/fizzbuzz/main
-```
-
-## PHP compatibility
-
-**PHP-derived syntax must be 100% compatible with PHP.** When elephc implements a PHP construct (variables, operators, keywords, built-ins), it must behave identically to PHP. This means:
-
-- Variable names, keywords, operators, and built-in function names must match PHP exactly
-- Superglobals (`$argc`, `$argv`) must use PHP's syntax (e.g., `$argv[0]`, not `argv(0)`)
-- Operator precedence and associativity must match PHP
-- String escape sequences must match PHP behavior
-- Built-in function signatures must match PHP (argument count, order, types)
-
-When in doubt, test with `php -r '...'` to verify behavior.
-
-**elephc also provides compiler-specific extensions** beyond standard PHP (e.g., `ptr`, `extern`, `buffer`, `packed class`). These features have no PHP equivalent and are not expected to run under the PHP interpreter. They are clearly distinguishable from PHP syntax and exist to enable use cases (FFI, game development, low-level memory access) that PHP cannot address.
-
-## Documentation
-
-The `docs/` directory is the project's complete documentation, organized into the following sections:
-
-```
-docs/
-├── README.md # Main index
-├── getting-started/ # Installation and first program
-│ ├── installation.md
-│ └── your-first-program.md
-├── compiling/ # The compiler CLI: flags and the full compilation process
-│ ├── overview.md
-│ ├── compilation-pipeline.md
-│ ├── cli-reference.md
-│ ├── targets.md
-│ ├── optimization.md
-│ ├── output-and-diagnostics.md
-│ └── linking-and-conditional-compilation.md
-├── php/ # PHP syntax (standard PHP features)
-│ ├── types.md
-│ ├── operators.md
-│ ├── control-structures.md
-│ ├── functions.md
-│ ├── strings.md
-│ ├── arrays.md
-│ ├── math.md
-│ ├── classes.md
-│ ├── namespaces.md
-│ ├── magic-constants.md
-│ └── system-and-io.md
-├── beyond-php/ # Compiler extensions (not valid PHP)
-│ ├── pointers.md
-│ ├── buffers.md
-│ ├── packed-classes.md
-│ ├── extern.md
-│ └── ifdef.md
-└── internals/ # Compiler internals
- ├── what-is-a-compiler.md
- ├── how-elephc-works.md
- ├── the-lexer.md
- ├── the-parser.md
- ├── the-type-checker.md
- ├── the-optimizer.md
- ├── the-codegen.md
- ├── the-runtime.md
- ├── memory-model.md
- ├── architecture.md
- ├── arm64-assembly.md
- └── arm64-instructions.md
-```
-
-### Astro compatibility
-
-All docs files are Markdown with YAML frontmatter compatible with Astro content collections. Every `.md` file **must** have this frontmatter format:
-
-```yaml
----
-title: "Page Title"
-description: "One-line description of the page."
-sidebar:
- order: N
----
-```
-
-- `title` replaces the `# Heading` — do **not** add a top-level `# Title` in the body (Astro renders it from frontmatter)
-- `sidebar.order` controls page ordering within its section
-- No navigation links (`[← Back]`, `Next:`, etc.) — Astro handles navigation
-- Use standard Markdown (CommonMark). No custom shortcodes or Astro components inside docs
-
-### Keeping docs up to date
-
-**Documentation must be kept up to date.** When adding a new feature:
-
-1. **PHP syntax feature** (operator, built-in, statement, etc.) → update the relevant page in `docs/php/`. Add the function signature, parameters, return type, and a short example.
-2. **Compiler extension** (pointer, buffer, extern, ifdef) → update the relevant page in `docs/beyond-php/`.
-3. **Compiler internals change** (pipeline, type checker, optimizer, codegen, runtime, ABI, memory model) → update the relevant page in `docs/internals/`.
-4. **Compilation flow or CLI change** (new/changed flag, env var, pipeline phase, target, output mode) → update the relevant page in `docs/compiling/`, keeping `docs/compiling/cli-reference.md` authoritative and in sync with `src/cli.rs`. Mirror user-facing flag examples in `README.md`.
-5. If a feature was previously listed as "not supported", remove that note.
-6. If there are known incompatibilities with PHP, document them in `docs/php/types.md` (incompatibilities section).
-7. Update `docs/README.md` index if adding a new page.
-
-## Roadmap management
-
-`ROADMAP.md` tracks all planned and completed work, organized by version.
-
-- **Never remove completed items** from a version section. Mark them as `[x]` and leave them under the version they belong to. This preserves the history of what was delivered in each release.
-- New work items go under the appropriate future version.
-- When all items in a version are completed, the version is considered done — do not move items elsewhere.
-
-## Changelog management
-
-`CHANGELOG.md` records every released version, newest first, in *Keep a Changelog* style.
-
-When cutting a release:
-
-- Add a new section at the top (under the header), above the previous version:
-
- ```
- ## [X.Y.Z] - YYYY-MM-DD
- - One terse, user-facing bullet per notable change.
- ```
-
- Keep entries concise (usually one or two bullets), describe what shipped — not the implementation — and use the absolute release date.
-- Add a matching compare link at the **bottom** of the file, also newest first, immediately above the previous version's link:
-
- ```
- [X.Y.Z]: https://github.com/illegalstudio/elephc/compare/v...vX.Y.Z
- ```
-
- The first-ever release uses the `releases/tag/v0.1.0` form instead of a compare range. Every version section must have its link; do not leave the link out.
-- Never change elephc's Cargo package version in `Cargo.toml` or `Cargo.lock`. Release automation in CI owns Cargo version bumps; agent changes should leave those files' version numbers untouched unless the user explicitly overrides this policy.
-
-## Conventions
-
-- No `Co-Authored-By` lines in commits
-- Use commit message prefixes such as `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, or `test:`
-- Keep commit messages concise
-- Run the focused pre-commit verification above before committing code changes. Do not knowingly commit with relevant focused tests failing; the full suite must pass in CI.
-- Zero compiler warnings policy (`cargo build` must be clean)
-- Never run `cargo fmt` in this repo. Use targeted manual edits only; global formatting creates noisy churn here.
+Refer to @AGENTS.md.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..fd338d3f72
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,560 @@
+# Contributing to Elephc
+
+First of all, thank you for considering contributing to Elephc! ❤️
+
+Every contribution matters, whether it's fixing a typo, improving documentation, reporting a bug, or implementing a new feature.
+
+## Before You Start
+
+If you're planning to work on a significant feature or architectural change, please open an issue first so we can discuss the design before implementation.
+
+This helps avoid duplicated work and ensures that the proposed solution aligns with the project's long-term direction.
+
+## AI-Assisted Contributions
+
+Contributions created with the help of AI tools are welcome. What matters to us is the quality and correctness of the result, not how it was produced — so the usual expectations still apply: the code must build, be covered by tests, follow the surrounding style, and come with a clear description. Please review anything an AI helps you write as carefully as you would your own work, since you remain responsible for whatever you submit.
+
+## Planning Larger Work
+
+If you're working toward something bigger than a single, self-contained change, we recommend writing a plan before you dive into the code. Plans live in the `.plans` directory of the repository.
+
+Start each plan with a checklist of the tasks it involves, then follow it with the detailed implementation notes for each of them. Keeping the task list up front makes the plan's progress easy to verify at a glance — whether it's complete is simply a matter of checking which tasks are marked done.
+
+Leave your plan in the repository until the work it describes is entirely finished. You don't have to land everything in one Pull Request: a plan may be split across several PRs, as long as the test suite stays green at every step. Once a plan is complete, the maintainers will clear it out during periodic cleanups, so there's no need to remove it yourself.
+
+## Reporting Bugs
+
+When reporting a bug, please include as much information as possible:
+
+* Operating system
+* CPU architecture
+* Elephc version
+* Steps to reproduce
+* Expected behavior
+* Actual behavior
+* Relevant code snippets or logs
+
+A minimal reproducible example is always appreciated.
+
+## Working locally
+
+Do your work on a dedicated branch — one branch per feature or fix — and prefer a
+separate **git worktree** per branch so your changes stay isolated from `main` and
+from any other work in progress. Worktrees let you keep several branches checked
+out at once without stashing or switching back and forth in a single checkout.
+
+Name the branch with a short, descriptive slug behind a type prefix that mirrors
+the commit-message prefixes used in this project:
+
+- `feat/` — a new feature
+- `fix/` — a bug fix
+- `docs/` — documentation only
+- `refactor/` — internal restructuring with no behavior change
+- `chore/` — tooling, CI, or housekeeping
+- `test/` — tests only
+
+When the work tracks a GitHub issue, include the issue number, e.g.
+`fix/369-tier2-range-analysis`.
+
+Worktrees can be managed by hand (`git worktree add`), but a small helper makes it
+painless. We recommend [`ggw`](https://github.com/illegalstudio/ggw), which
+creates, navigates, and pushes worktree-backed branches for you:
+
+```bash
+ggw create feat/my-feature # create the branch and its worktree
+ggw cd feat/my-feature # switch into the worktree
+# ... implement your change ...
+git commit -m "feat: add my feature"
+ggw push # push the branch and set its upstream
+```
+
+`ggw push` is equivalent to `git push -u feat/my-feature` — use whichever
+you prefer. Once the branch is pushed, open your Pull Request as described below.
+
+## Pull Requests
+
+Before opening a Pull Request, please ensure that:
+
+* Your code builds successfully.
+* Existing tests continue to pass.
+* New functionality includes tests whenever possible.
+* Documentation is updated when appropriate.
+* Commits are reasonably organized and have meaningful commit messages.
+* When the Pull Request addresses an existing issue, reference it in the description.
+
+Please keep Pull Requests focused and self-contained. A Pull Request that solves a single, well-defined problem is far easier to review than a large one that bundles several unrelated changes together.
+
+### Draft until it's ready
+
+Open your Pull Request as a **draft** while you are still iterating, and keep it
+in draft until you are confident the implementation is complete and correct.
+
+Once you switch it to **ready for review**, please leave the Pull Request
+untouched — do not push further changes, and do not rebase or merge `main` into it
+to keep it aligned. From that point on the maintainers take over the Pull Request
+and will handle reviewing, updating, and integrating it.
+
+## Coding Style
+
+Try to follow the style already used throughout the codebase.
+
+Consistency is generally more important than personal preference.
+
+### Assembly comment alignment
+
+The assembly elephc emits is meant to be read and understood by someone learning
+how compilers work, so **every `emitter.instruction(...)` call must carry an inline
+`//` comment** explaining what the instruction does — and those comments are aligned
+to a fixed column. A few rules keep them consistent:
+
+1. **Every instruction line gets a comment.** No exceptions: if you add an
+ `emitter.instruction(...)`, it gets a `// comment`.
+2. **The `//` starts at column 81.** Pad the line with spaces so the `//` sits at the
+ 81st character (1-indexed). If the code itself already reaches 80 characters or
+ more, put exactly one space before the `//`.
+3. **Group related instructions under a block comment.** Put a standalone
+ `// -- description --` line before a block of related instructions (e.g.
+ `// -- set up stack frame --`).
+4. **Explain intent, not the mnemonic.** Write "store argc from OS", not "store x0 to
+ memory" — the reader can already see the instruction; the comment should say *why*
+ it's there.
+
+For example:
+
+```rust
+ // -- set up stack frame --
+ emitter.instruction("sub sp, sp, #32"); // allocate 32 bytes on the stack
+ emitter.instruction("stp x29, x30, [sp, #16]"); // save frame pointer and return address
+ emitter.instruction("add x29, sp, #16"); // set new frame pointer
+```
+
+To verify alignment, run `./scripts/check_asm_comments.py` against any codegen file you
+touch before opening a Pull Request. It flags every `emitter.instruction(...)` whose
+`//` comment is misaligned and exits non-zero if it finds any, so it also works in a
+pre-commit hook or CI:
+
+```bash
+./scripts/check_asm_comments.py src/codegen/lower_inst/your_file.rs
+```
+
+It accepts multiple files at once, e.g. `./scripts/check_asm_comments.py src/codegen/lower_inst/*.rs`.
+
+## Adding a new operator
+
+elephc parses expressions with a Pratt parser, so a new binary operator flows
+through the whole pipeline — lexer, parser, type checker, optimizer, EIR
+lowering, and target-aware codegen. Implement it end-to-end:
+
+1. Add the token to `src/lexer/token.rs`.
+2. Add scanning logic to `src/lexer/scan.rs`.
+3. Add the `BinOp` variant to `src/parser/ast.rs`.
+4. Add one line to `infix_bp()` in `src/parser/expr/pratt.rs` (the Pratt parser
+ binding-power table) so precedence and associativity match PHP.
+5. Add type checking/inference in the relevant `src/types/checker/` file, usually
+ under `inference/ops.rs` or expression inference.
+6. Add optimizer/effect handling when the operator can be folded, propagated,
+ pruned, or has side effects (`src/optimize/`). Keep folds PHP-equivalent —
+ cross-check edge cases with `php -r`.
+7. Add EIR lowering in the relevant `src/ir_lower/expr/` path and target-aware EIR
+ codegen under `src/codegen/lower_inst/` when the operator needs a new IR
+ instruction or lowering path.
+8. Add tests in all four test files (lexer, parser, codegen, error), including a
+ Pratt binding-power test that asserts precedence relative to adjacent operators.
+
+## Adding a new statement type
+
+A new statement kind must be threaded through parsing, the frontend passes, the
+type checker, the optimizer, and EIR lowering. Missing a pass usually produces
+silent miscompilation rather than a compile error, so also audit every
+AST-walking pass (see "Adding or changing an AST node" in `CLAUDE.md`):
+
+1. Add the `StmtKind` variant to `src/parser/ast.rs`.
+2. Add parser logic in `src/parser/stmt.rs`.
+3. Add resolver/name-resolver handling if the statement can contain names,
+ declarations, includes, function variants, or expressions.
+4. Add type checking in the relevant `src/types/checker/` module.
+5. Add optimizer/effects/warnings handling if the statement can be folded, pruned,
+ read variables, write variables, or alter control flow (`src/optimize/`).
+6. Add EIR lowering in `src/ir_lower/stmt/` and target-aware EIR codegen under
+ `src/codegen/` when the statement needs new instruction or terminator
+ support.
+7. If it introduces variables or hidden temporaries, update EIR local/temp
+ declaration in `src/ir_lower/context.rs` and any frame-layout allocation needed
+ before frame sizing.
+8. Add tests: at least one codegen test showing correct output, one for edge cases
+ (empty body, nested), and one error test for malformed syntax.
+
+## Adding a new EIR optimization pass
+
+IR-level (EIR) transformations run after EIR lowering/validation through a
+fixed-point driver, not in the AST optimizer (`src/optimize/`). Folds that need
+value identity, basic blocks, or dominance belong here.
+
+1. Implement the `IrPass` trait (`name()`, `run(&mut Function, &mut DataPool) ->
+ bool`) in a new `src/ir_passes/.rs`; `run` mutates the function in place
+ and returns whether it changed anything. The `DataPool` is the module's shared
+ literal pool for passes that intern new constants (e.g. peephole string-literal
+ concat folding); ignore it (`_data`) otherwise.
+2. Register the pass in `default_passes()` in `src/ir_passes/driver.rs`. Order
+ matters: the driver re-runs the whole set per function until none reports a
+ change, capped by `MAX_PASS_ITERATIONS`.
+3. Reuse `src/ir_passes/rewrite.rs` for value redirection (`replace_all_uses` for
+ RAUW) and the shared fold helpers (`resolve_chains`, `neutralize_to_nop`,
+ `defining_instruction`, `count_value_uses`) instead of re-walking
+ operands/terminators. Keep rewrites dominance-safe and PHP-equivalent;
+ cross-check edge cases (division by zero, signed-zero/`NaN` floats) with `php -r`.
+4. The driver re-validates each function with `validate_function` after every pass
+ in debug/test builds and panics (naming the pass) on malformed IR or
+ non-convergence; both guards compile out of `--release`. Rely on this during
+ development.
+5. Add unit tests under `src/ir_passes/tests/` (hand-built EIR via
+ `crate::ir::Builder`) and end-to-end tests under `tests/codegen/optimizer/`. In
+ e2e fixtures, use runtime-unknown values (e.g. `$argc`) so the targeted IR
+ construct survives AST-level folding and actually reaches EIR.
+6. Passes are gated by `--ir-opt=on|off` / `--no-ir-opt` (env `ELEPHC_IR_OPT`),
+ default on. Behavior must be identical with the flag on or off except for
+ performance; verify with `--emit-ir` and `--emit-ir --no-ir-opt`.
+
+## Adding a built-in function
+
+elephc's PHP built-in functions are declared **once** in a single-source registry.
+Each builtin has one *home file* at `src/builtins/ /.rs` that declares it
+with the `builtin!` macro; all declarations are collected at link time through the
+`inventory` crate. From that single declaration the compiler derives the catalog
+name-set (case-insensitive lookup, `function_exists`, namespace fallback,
+redeclaration checks), the call signature (named arguments, defaults, by-ref params,
+variadic, arity), the type-check entry, the EIR lowering dispatch, and the generated
+documentation.
+
+Do **not** re-add builtin names to the old hand-maintained tables (`catalog.rs`,
+`signatures.rs`, per-area `check_builtin` arms). They are superseded by the registry;
+a builtin is fully wired the moment its home file compiles.
+
+### 1. Create the home file
+
+Add `src/builtins/ /.rs` and register it in `src/builtins/ /mod.rs`
+with `pub mod ;` (keep the list alphabetical). Areas are `string`, `array`,
+`math`, `io`, `system`, `types`, `callables`, `spl`, `pointers` (plus `internal` for
+compiler-internal builtins). One builtin per home file; the file owns its declaration
+plus its `check`/`lower` hooks. Start with the mandatory `//!` module preamble.
+
+### 2. Declare it with `builtin!`
+
+```rust
+builtin! {
+ name: "strlen",
+ area: String,
+ params: [string: Str],
+ returns: Int,
+ check: check,
+ lazy_check: true,
+ lower: lower,
+ summary: "Returns the length of a string.",
+ php_manual: "function.strlen",
+}
+```
+
+Fields must appear in this canonical order; optional fields (marked `?`) may be
+omitted:
+
+`name`, `area`, `params`, `variadic?`, `min_args?`, `max_args?`, `arity_error?`,
+`returns`, `by_ref_return?`, `check?`, `lazy_check?`, `lower`, `summary`, `examples?`,
+`php_manual?`, `deprecation?`, `internal?`.
+
+- **`params`** — `[name: TypeSpec, name: TypeSpec = DefaultSpec::Variant, ...]`. A
+ parameter with `= DefaultSpec::…` is optional; without it, required. Prefix a
+ parameter with `ref` to pass it by reference (mutating builtins):
+ `params: [ref array: Mixed, offset: Int]`. Parameter names become PHP's
+ named-argument keys and must match PHP exactly (Rust keywords work as names via raw
+ identifiers, e.g. `r#type`).
+- **`returns` and param `TypeSpec`** — written as a bare scalar type ident: `Int`,
+ `Float`, `Str`, `Bool`, `Mixed`, `Null`, `Void`. Non-scalar shapes (arrays, unions,
+ resources) are declared as `Mixed`; supply the precise type from a `check` hook when
+ it matters (see the note in step 3).
+- **`DefaultSpec`** — full path form: `DefaultSpec::Null`, `DefaultSpec::Int(0)`,
+ `DefaultSpec::Bool(false)`, `DefaultSpec::Float(1.5)`, `DefaultSpec::Str("…")`,
+ `DefaultSpec::IntMax`, `DefaultSpec::IntMin`, `DefaultSpec::EmptyArray`.
+- **`variadic`** — the PHP name of the trailing variadic parameter, e.g.
+ `variadic: "values"`.
+- **`min_args` / `max_args` / `arity_error`** — override only the arity check (not the
+ derived signature or the parity gate). Use when a builtin's PHP arity is
+ tighter/looser than its declared parameter list, or needs a verbatim error message.
+- **`summary` / `examples` / `php_manual` / `deprecation`** — documentation metadata
+ surfaced by the `gen_builtins` exporter.
+- **`internal: true`** — a compiler-internal builtin that is not PHP-visible and is
+ excluded from catalogs and docs.
+
+A builtin whose return type does not depend on its arguments and needs no extra
+validation can omit `check` entirely — `returns:` is then authoritative for the
+checker.
+
+### 3. The `check` hook (type checking)
+
+Add a `check` hook when the return type depends on argument types/values, or when the
+call needs validation beyond arity and the parameter list:
+
+```rust
+fn check(cx: &mut BuiltinCheckCtx) -> Result {
+ let ty = cx.checker.infer_type(&cx.args[0], cx.env)?;
+ if !matches!(ty, PhpType::Str | PhpType::Mixed | PhpType::Union(_)) {
+ return Err(CompileError::new(cx.span, "strlen() argument must be string"));
+ }
+ Ok(PhpType::Int)
+}
+```
+
+The hook receives `BuiltinCheckCtx { checker, name, args, span, env }` and returns the
+call's `PhpType` (or a diagnostic). Its returned type overrides `returns:` for the
+checker.
+
+For a normal builtin the registry already checks arity and infers every argument once
+(for side effects such as variable narrowing and undefined-variable diagnostics)
+before calling the hook. Set **`lazy_check: true`** when the hook must control
+inference order — most importantly when it injects element/parameter type hints into
+an unannotated closure argument *before* that closure is inferred (e.g. `usort`,
+`array_map` with a callback). With `lazy_check: true` the hook is responsible for
+inferring each argument itself.
+
+> **Return typing is a checker-only contract.** The `returns:` field and the `check`
+> hook drive the **type checker** only. The EIR backend derives call return types
+> independently in `call_return_type` (`src/ir_lower/expr/mod.rs`). If you declare
+> `returns: Mixed` + a precise `check` hook (the standard pattern for non-scalar
+> returns), you must also add a matching arm to the EIR return-type derivation, or the
+> checker and EIR will disagree on the value's type. This caveat is documented on the
+> `returns`/`check` fields in `src/builtins/spec.rs`.
+
+### 4. The `lower` hook (EIR codegen)
+
+`lower` is mandatory — it is the builtin's EIR lowering entry point. Keep it a thin
+wrapper that dispatches to the actual emitter:
+
+```rust
+fn lower(ctx: &mut FunctionContext, inst: &Instruction) -> Result<(), CodegenIrError> {
+ crate::codegen::lower_inst::builtins::lower_strlen(ctx, inst)
+}
+```
+
+Write the emitter itself under `src/codegen/lower_inst/builtins/ /`, following
+the target-aware codegen conventions in `CLAUDE.md` (support every target through
+`emitter.target`, one emitter per leaf file, an inline `//` comment on every
+`emitter.instruction(...)`). If the builtin needs a runtime routine, add it under
+`src/codegen_support/runtime//`. The registry dispatches `spec.lower` first, so no
+match arm needs editing.
+
+### 5. What derives automatically
+
+Once the home file compiles, all of the following see the builtin with no further
+edits: `function_exists()` and case-insensitive/namespaced lookup, the named-argument
+`FunctionSig`, first-class-callable syntax (`strlen(...)`), the arity check and its
+error message, and the `gen_builtins` JSON docs export.
+
+### 6. Surfaces you still wire by hand
+
+The registry single-sources the declaration, signature, checker entry, lowering
+*dispatch*, and docs. These related surfaces are **not** derived and must be updated
+when relevant:
+
+- **The EIR emitter** the `lower` hook calls (and any runtime routine it needs).
+- **EIR return typing** — see the note in step 3.
+- **Optimizer effects** in `src/optimize/effects/builtins.rs` when purity, reads/writes,
+ or thrown/fatal behavior matter for DCE and constant propagation. Never mark a call
+ pure if it can read/write globals, files, the environment, heap state, or emit output.
+- **Runtime-callable wrapper exclusion** — if the builtin cannot be dispatched through
+ the dynamic string-callable wrapper, add it to `runtime_builtin_wrapper_excluded()`
+ in `src/codegen/callable_dispatch.rs`.
+
+### 7. Tests, examples, and docs
+
+- Add codegen tests for normal use (plus at least one case-insensitive or namespaced
+ call for a PHP-visible builtin), and error tests for wrong argument count/types.
+- Add or update an example under `examples/` when the builtin is a notable user-facing
+ feature.
+- Document the PHP surface (signature, parameters, return type, a short example) on the
+ relevant `docs/php/` page.
+- The signature/arity parity gates in `src/builtins/parity_tests.rs` must stay green.
+
+### 8. Not every "builtin" is a function
+
+A small set of PHP language constructs — `isset`, `unset`, `empty`, `exit`, `die`, plus
+the `buffer_*` intrinsics — are l-value/lazy constructs with dedicated EIR paths and are
+intentionally kept in the checker (`numeric`/`arrays` `check_builtin`), not in the
+registry. Do not migrate those into `builtin!`.
+
+## Adding functionality via a Rust crate (bridge crates)
+
+elephc compiles a static subset of PHP straight to native code, so most features
+are implemented directly in the compiler (lexer → parser → type checker → EIR →
+codegen). But some functionality is heavy, well-served by an existing Rust
+library, or simply not worth re-implementing by hand — TLS, PDO database drivers,
+image codecs, hashing, timezone tables, Phar archives, the `--web` server.
+
+**If the functionality you want to add can be realized through Rust libraries,
+implement it as a bridge crate and register a `--with-` flag** instead of
+hand-writing it in the runtime. A *bridge crate* is a `staticlib` under
+`crates/elephc-/` that elephc links into compiled PHP programs on demand.
+The whole linking model is table-driven from the `BRIDGES` table in
+`src/linker.rs`, so wiring a new bridge is a single table entry plus the PHP-facing
+surface.
+
+Follow these technical specifications in full.
+
+### 1. Decide it belongs in a crate
+
+Use a bridge crate when the feature (a) maps cleanly onto a maintained Rust crate,
+(b) is optional (programs that do not use it must not pay for it), and (c) exposes
+a small, stable C ABI surface. Do **not** use a crate for core language semantics,
+ownership/GC, or anything that must be understood line-by-line in the generated
+assembly — that belongs in the compiler proper (`AGENTS.md`/`CLAUDE.md`).
+
+### 2. Create the crate
+
+Create `crates/elephc-/` as a workspace member:
+
+```toml
+# crates/elephc-/Cargo.toml
+[package]
+name = "elephc-"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+publish = false
+
+# staticlib: linked into compiled PHP programs. rlib: lets the bridge be
+# unit-tested via `cargo test -p elephc-`.
+[lib]
+crate-type = ["staticlib", "rlib"]
+
+[dependencies]
+# Prefer pure-Rust, musl-friendly crates so the Linux Docker test images link.
+```
+
+Add the crate to **both** `members` and `default-members` in the root
+`Cargo.toml`, and an entry under `[workspace.dependencies]`. Being a default
+member is what makes a plain `cargo build` materialize `target//libelephc_.a`.
+
+Expose a **stable C ABI**: every entry point is `#[no_mangle] pub extern "C"` and
+must be panic-free across the boundary (catch/encode errors, return error codes or
+null; never unwind into generated code). Keep the surface small and explicit —
+pass pointers + lengths for strings/buffers, return primitive status values. Name
+exports `elephc__*` so they are easy to find and namespace-clean.
+
+Every supported target must build and link the crate: `macos-aarch64`,
+`linux-aarch64`, `linux-x86_64`. A bridge that only works on one target is not
+acceptable (see the supported-target policy in `CLAUDE.md`).
+
+### 3. Register the bridge in `BRIDGES` (`src/linker.rs`)
+
+Add one `BridgeStaticlib` entry. This is the only linker change required —
+discovery, on-demand build, search paths, whole-archiving, and macOS frameworks
+are all driven from the table:
+
+```rust
+BridgeStaticlib {
+ lib_name: "elephc_", // `-l` name → links lib.a
+ env_var: "ELEPHC__LIB_DIR", // dir override for prebuilt staticlibs
+ crate_name: "elephc-", // cargo package (auto-build + workspace)
+ flag_name: "", // user-facing `--with-` flag
+ whole_archive: false, // true if link-time side effects / owns entry
+ macos_frameworks: &[], // transitive native deps' frameworks
+ needs_libdl: true, // Rust runtime/unwinder needs -ldl on Linux
+},
+```
+
+Set `whole_archive: true` only when the staticlib has link-time side effects that
+must survive (e.g. a provider registration) or owns the program entry point (like
+`elephc_web`). Otherwise leave it `false`; `--with-` force-loads it anyway.
+
+### 4. Expose the PHP-visible surface
+
+Pick one of two paths so PHP code can actually call into the crate. Both make the
+type checker record `elephc_` as a required library, which is what links the
+bridge automatically when the feature is used.
+
+- **Core builtins** — when the feature is a set of PHP built-in functions
+ (`md5()`, `hash()`, …). Follow "Adding a built-in function" above (declare each
+ builtin in `src/builtins/ /` with its `check`/`lower` hooks), and call
+ `Checker::require_builtin_library("elephc_")` from the `check` hook when a
+ builtin that needs the crate is used. The PHP names are always available, so no
+ prelude is needed.
+
+- **A prelude** — when the feature is a set of classes/functions written in
+ elephc-PHP that wrap the crate (PDO, timezone introspection, image). Add
+ `src/_prelude.rs`:
+ - a static elephc-PHP source string declaring `extern "elephc_" { ... }`
+ plus the wrapper classes/functions;
+ - `pub fn inject_if_used(program: Program, force: bool) -> Program` that returns
+ `program` unchanged when `!force && !detect::program_uses_(&program)`,
+ and otherwise tokenizes/parses the prelude and prepends it (declarations are
+ hoisted, so prepending does not change execution order);
+ - a `detect` submodule that scans the AST for the feature's symbols.
+ Wire the call into `src/pipeline.rs` after include resolution, mirroring the PDO
+ block. The injected `extern "elephc_"` block is what adds the bridge to
+ `required_libraries`, so usage auto-links it.
+
+### 5. Register the `--with-` flag
+
+Registering the bridge in `BRIDGES` with a `flag_name` is what enables the flag —
+`src/cli.rs` parses `--with-` generically against the table (an unknown
+crate is rejected, listing the valid ones), and `src/pipeline.rs` force-links the
+bridge (whole-archived via `forced_bridge_libs`) for every crate named in
+`with_crates`.
+
+If your crate uses a **prelude**, also thread the force flag into its
+`inject_if_used` call in `src/pipeline.rs`, mirroring pdo/tz/image:
+
+```rust
+let ast = _prelude::inject_if_used(ast, with_crates.contains(""));
+```
+
+so `--with-` declares the PHP surface even when auto-detection would not.
+Core-builtin crates need nothing extra here — force-linking the staticlib is
+enough because their PHP names are always available.
+
+`--with-` semantics: it guarantees the crate is compiled in (whole-archived,
+not dead-stripped) and, for prelude crates, that its API is declared — useful when
+detection cannot see indirect usage. It is additive and never disables
+auto-detection. Note that it increases binary size by force-including the whole
+crate.
+
+### 6. Tests
+
+- Unit-test the `BRIDGES`/flag mapping in `src/linker.rs` and the CLI parsing in
+ `src/cli.rs` (both are fast, no assembler/linker).
+- Add codegen/end-to-end tests that exercise the feature, and error tests for
+ argument-count/usage diagnostics, per the test-coverage rules in `CLAUDE.md`.
+- The crate itself should have `cargo test -p elephc-` unit tests (that is
+ what the `rlib` crate-type is for).
+- Run focused tests locally; CI runs the full supported-target matrix.
+
+### 7. Documentation
+
+- `docs/compiling/cli-reference.md` — add the `--with-` flag.
+- `docs/compiling/linking-and-conditional-compilation.md` — describe the bridge
+ and its auto-link trigger.
+- The relevant `docs/php/` or `docs/beyond-php/` page — document the PHP surface.
+- Update `CLAUDE.md` only if you changed the bridge/flag mechanism itself.
+
+## Contributor Certification
+
+By submitting a contribution to this repository, you represent and warrant that:
+
+* you have the legal right to submit the contribution;
+* the contribution is your original work, or you have sufficient rights to submit it;
+* to the best of your knowledge, the contribution does not knowingly infringe the intellectual property rights of any third party;
+* you agree that your contribution will be distributed under the same license as the Elephc project.
+
+You retain the copyright to your contributions.
+
+## Code of Conduct
+
+Please be respectful and constructive.
+
+Healthy technical discussions are encouraged. Personal attacks, harassment, or disrespectful behavior will not be tolerated.
+
+## Questions
+
+If you're unsure about anything, feel free to open an issue or start a discussion.
+
+Contributions of all sizes are welcome.
+
+Happy hacking! 🐘
diff --git a/Cargo.lock b/Cargo.lock
index f120da1c41..6718c4a830 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,6 +96,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
[[package]]
name = "bindgen"
version = "0.72.1"
@@ -355,6 +361,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
[[package]]
name = "const-oid"
version = "0.10.2"
@@ -493,6 +505,17 @@ dependencies = [
"cmov",
]
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid 0.9.6",
+ "pem-rfc7468",
+ "zeroize",
+]
+
[[package]]
name = "derive_utils"
version = "0.15.1"
@@ -511,6 +534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
+ "const-oid 0.9.6",
"crypto-common 0.1.7",
]
@@ -521,7 +545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
- "const-oid",
+ "const-oid 0.10.2",
"crypto-common 0.2.2",
"ctutils",
]
@@ -545,7 +569,7 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
[[package]]
name = "elephc"
-version = "0.25.2"
+version = "0.26.0"
dependencies = [
"bitflags 2.13.0",
"bzip2-rs",
@@ -557,6 +581,7 @@ dependencies = [
"elephc-tz",
"elephc-web",
"flate2",
+ "inventory",
"rustls",
"rustls-pemfile",
"serde_json",
@@ -610,7 +635,10 @@ dependencies = [
"bzip2",
"bzip2-rs",
"flate2",
+ "md-5 0.10.6",
+ "rsa",
"sha1",
+ "sha2 0.10.9",
]
[[package]]
@@ -1111,6 +1139,15 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "inventory"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
+dependencies = [
+ "rustversion",
+]
+
[[package]]
name = "io-enum"
version = "1.2.1"
@@ -1179,6 +1216,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
[[package]]
name = "leb128fmt"
@@ -1208,6 +1248,12 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
[[package]]
name = "libredox"
version = "0.1.17"
@@ -1431,6 +1477,22 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
+dependencies = [
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.6",
+ "smallvec",
+ "zeroize",
+]
+
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -1440,6 +1502,17 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -1447,6 +1520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
+ "libm",
]
[[package]]
@@ -1506,6 +1580,15 @@ dependencies = [
"serde_core",
]
+[[package]]
+name = "pem-rfc7468"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -1537,6 +1620,27 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+[[package]]
+name = "pkcs1"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
+dependencies = [
+ "der",
+ "pkcs8",
+ "spki",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.33"
@@ -1867,6 +1971,28 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "rsa"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
+dependencies = [
+ "const-oid 0.9.6",
+ "digest 0.10.7",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "sha1",
+ "sha2 0.10.9",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rust_decimal"
version = "1.42.0"
@@ -2062,6 +2188,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest 0.10.7",
+ "rand_core 0.6.4",
+]
+
[[package]]
name = "simd-adler32"
version = "0.3.9"
@@ -2112,6 +2248,22 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
diff --git a/Cargo.toml b/Cargo.toml
index 217bdfc475..95c054243f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,8 @@
[package]
name = "elephc"
-version = "0.25.2"
+version = "0.26.0"
edition = "2021"
+default-run = "elephc"
description = "A PHP-to-native AOT compiler"
license = "MIT"
repository = "https://github.com/illegalstudio/elephc"
@@ -27,7 +28,12 @@ resolver = "2"
name = "elephc"
path = "src/main.rs"
+[[bin]]
+name = "gen_builtins"
+path = "src/bin/gen_builtins.rs"
+
[dependencies]
+inventory = "0.3"
bitflags = "2"
serde_json = "1"
# flate2 (default miniz_oxide backend = pure Rust, no system zlib) is used by the
diff --git a/README.md b/README.md
index 9b7ed97e88..f297ccf0b5 100644
--- a/README.md
+++ b/README.md
@@ -21,11 +21,11 @@
- 3 native targets · no Zend Engine · zero runtime dependencies · single standalone binary
+ 4 native targets · no Zend Engine · zero runtime dependencies · single standalone binary
- A PHP-to-native compiler that takes a subset of PHP and compiles it directly to native assembly, producing standalone binaries for macOS ARM64 , Linux ARM64 , and Linux x86_64 . No opcode fallback, just real machine code.
+ A PHP-to-native compiler that takes a subset of PHP and compiles it directly to native assembly, producing standalone binaries for macOS ARM64 , Linux ARM64 , and Linux x86_64 , plus an experimental Windows x86_64 cross-compilation target. No opcode fallback, just real machine code.
@@ -42,6 +42,18 @@ elephc is built and maintained independently. You can support the project by eit
- ⭐ **[Starring the repo](https://github.com/illegalstudio/elephc/stargazers)** — it helps others discover it and keeps the project going.
- 💜 **[Sponsoring on GitHub](https://github.com/sponsors/nahime0)** — every contribution, big or small, makes a real difference.
+## Core Contributors
+
+
+
+ Vincenzo Petrucci
+
+
+
+
+ Guillaume Loulier
+
+
## An async HTTP server in PHP
An asynchronous HTTP/1.1 server — a non-blocking `poll()` event loop, one Fiber per connection, raw TCP sockets through `extern` FFI, plus an HTTP parser and a router — written entirely in PHP and compiled to a single native binary. No interpreter, no PHP-FPM, no Nginx.
@@ -101,7 +113,7 @@ elephc takes a narrower but cleaner route: it is a from-scratch compiler for a s
That tradeoff is intentional:
-- **Less legacy compatibility** than a VM-backed PHP implementation.
+- **Less long-tail compatibility** than a VM-backed PHP implementation.
- **More mechanical transparency**: readable assembly output, source maps, line-by-line commented codegen, and a documented memory model.
- **No hidden runtime dependency**: the generated binary does not need PHP, the Zend Engine, a loader extension, or an embedded interpreter.
- **Native-oriented extensions**: `extern`, `ptr`, `buffer`, and `packed class` let PHP-shaped code cross into systems, FFI, game, and performance-sensitive workloads.
@@ -145,11 +157,8 @@ xattr -cr elephc
## Usage
-> **Important:** Starting with v0.23.10, elephc uses the new EIR backend by default.
-> The legacy AST backend is frozen: it will not receive new language or runtime
-> features, and it is scheduled for complete removal in v0.26.0. If you need to
-> compare behavior with the old backend during the transition, compile with
-> `--ast-backend`.
+> **Important:** elephc lowers every build through the EIR pipeline and the
+> target-aware assembly emitter.
```bash
# Compile a PHP file to a native binary
@@ -186,10 +195,14 @@ elephc --no-ir-opt hot.php
# Link extra native libraries or frameworks for FFI
elephc app.php -l sqlite3 -L /opt/homebrew/lib --framework Cocoa
+# Force-enable a bridge crate (pdo, tls, crypto, phar, tz, image) regardless of auto-detection
+elephc app.php --with-pdo --with-crypto
+
# Explicit target selection
-# Supported targets today: macos-aarch64, linux-aarch64, linux-x86_64
+# Supported targets today: macos-aarch64, linux-aarch64, linux-x86_64, windows-x86_64 (experimental)
elephc --target linux-aarch64 hello.php
elephc --target linux-x86_64 hello.php
+elephc --target windows-x86_64 hello.php # experimental cross-compilation, requires MinGW-w64
# Compile a standalone prefork HTTP server binary
elephc --web app.php
@@ -320,26 +333,26 @@ The full list of supported constructs, operators, and control structures is in t
-### Built-in functions (380+)
+### Built-in functions (420+)
-Over 380 PHP built-ins are implemented natively, grouped here by category — strings, arrays, math, I/O, streams/sockets, system, and more.
+Over 420 PHP built-ins are implemented natively, grouped here by category — strings, arrays, math, I/O, streams/sockets, system, and more.
Show all built-in functions by category
**Strings:** `strlen`, `substr`, `strpos`, `strrpos`, `strstr`, `str_replace`, `str_ireplace`, `substr_replace`, `strtolower`, `strtoupper`, `ucfirst`, `lcfirst`, `ucwords`, `trim`, `ltrim`, `rtrim`, `str_repeat`, `str_pad`, `strrev`, `chop`, `grapheme_strrev`, `str_split`, `strcmp`, `strcasecmp`, `str_contains`, `str_starts_with`, `str_ends_with`, `ord`, `chr`, `explode`, `implode`, `sprintf`, `printf`, `vprintf`, `vsprintf`, `sscanf`, `md5`, `sha1`, `hash`, `hash_algos`, `hash_equals`, `hash_hmac`, `hash_init`, `hash_update`, `hash_final`, `hash_copy`, `crc32`, `number_format`, `addslashes`, `stripslashes`, `nl2br`, `wordwrap`, `bin2hex`, `hex2bin`, `htmlspecialchars`, `htmlentities`, `html_entity_decode`, `urlencode`, `urldecode`, `rawurlencode`, `rawurldecode`, `base64_encode`, `base64_decode`, `gzcompress`, `gzdeflate`, `gzinflate`, `gzuncompress`, `ip2long`, `long2ip`, `inet_ntop`, `inet_pton`, `ctype_alpha`, `ctype_digit`, `ctype_alnum`, `ctype_space`
-**Arrays:** `count`, `array_push`, `array_pop`, `in_array`, `array_keys`, `array_values`, `sort`, `rsort`, `isset`, `array_key_exists`, `array_search`, `array_merge`, `array_slice`, `array_splice`, `array_combine`, `array_flip`, `array_reverse`, `array_unique`, `array_sum`, `array_product`, `array_chunk`, `array_pad`, `array_fill`, `array_fill_keys`, `array_diff`, `array_intersect`, `array_diff_key`, `array_intersect_key`, `array_unshift`, `array_shift`, `asort`, `arsort`, `ksort`, `krsort`, `natsort`, `natcasesort`, `shuffle`, `array_rand`, `array_column`, `range`, `array_map`, `array_filter`, `array_reduce`, `array_walk`, `usort`, `uksort`, `uasort`, `call_user_func`, `call_user_func_array`, `function_exists`
+**Arrays:** `count`, `array_push`, `array_pop`, `in_array`, `array_keys`, `array_values`, `sort`, `rsort`, `isset`, `array_key_exists`, `array_search`, `array_merge`, `array_slice`, `array_splice`, `array_combine`, `array_flip`, `array_reverse`, `array_unique`, `array_sum`, `array_product`, `array_chunk`, `array_pad`, `array_fill`, `array_fill_keys`, `array_diff`, `array_intersect`, `array_diff_key`, `array_intersect_key`, `array_unshift`, `array_shift`, `asort`, `arsort`, `ksort`, `krsort`, `natsort`, `natcasesort`, `shuffle`, `array_rand`, `array_column`, `range`, `array_map`, `array_filter`, `array_reduce`, `array_walk`, `array_walk_recursive`, `array_is_list`, `array_key_first`, `array_key_last`, `array_replace`, `array_replace_recursive`, `array_merge_recursive`, `array_diff_assoc`, `array_intersect_assoc`, `array_udiff`, `array_uintersect`, `array_find`, `array_any`, `array_all`, `array_multisort`, `usort`, `uksort`, `uasort`, `call_user_func`, `call_user_func_array`, `function_exists`
**Math:** `abs`, `floor`, `ceil`, `round`, `sqrt`, `pow`, `min`, `max`, `clamp`, `intdiv`, `fmod`, `fdiv`, `rand`, `mt_rand`, `random_int`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `log`, `log2`, `log10`, `exp`, `hypot`, `deg2rad`, `rad2deg`, `pi`
-**Types and class introspection:** `gettype`, `settype`, `empty`, `unset`, `is_int`, `is_float`, `is_string`, `is_bool`, `is_null`, `is_numeric`, `is_nan`, `is_finite`, `is_infinite`, `is_iterable`, `is_callable`, `is_resource`, `boolval`, `floatval`, `intval`, `get_resource_type`, `get_resource_id`, `class_exists`, `interface_exists`, `trait_exists`, `enum_exists`, `class_alias`, `get_class`, `get_parent_class`, `get_declared_classes`, `get_declared_interfaces`, `get_declared_traits`, `is_a`, `is_subclass_of`, `class_implements`, `class_parents`, `class_uses`
+**Types and class introspection:** `gettype`, `settype`, `empty`, `unset`, `is_int`, `is_float`, `is_string`, `is_bool`, `is_null`, `is_numeric`, `is_nan`, `is_finite`, `is_infinite`, `is_iterable`, `is_callable`, `is_resource`, `is_array`, `is_object`, `is_scalar`, `boolval`, `floatval`, `intval`, `get_resource_type`, `get_resource_id`, `class_exists`, `interface_exists`, `trait_exists`, `enum_exists`, `class_alias`, `get_class`, `get_parent_class`, `get_declared_classes`, `get_declared_interfaces`, `get_declared_traits`, `is_a`, `is_subclass_of`, `class_implements`, `class_parents`, `class_uses`
-**I/O:** `fopen`, `fclose`, `fread`, `fwrite`, `fprintf`, `vfprintf`, `fscanf`, `fgets`, `fgetc`, `fpassthru`, `flock`, `tmpfile`, `readfile`, `feof`, `readline`, `fseek`, `ftell`, `rewind`, `file_get_contents`, `file_put_contents`, `file`, `hash_file`, `fgetcsv`, `fputcsv`, `file_exists`, `is_file`, `is_dir`, `is_readable`, `is_writable`, `is_writeable`, `is_executable`, `is_link`, `symlink`, `link`, `readlink`, `linkinfo`, `filesize`, `filemtime`, `fileatime`, `filectime`, `fileperms`, `fileowner`, `filegroup`, `fileinode`, `filetype`, `stat`, `lstat`, `fstat`, `clearstatcache`, `disk_free_space`, `disk_total_space`, `basename`, `dirname`, `pathinfo`, `realpath`, `fnmatch`, `touch`, `chmod`, `chown`, `chgrp`, `lchown`, `lchgrp`, `umask`, `ftruncate`, `fflush`, `fsync`, `fdatasync`, `copy`, `rename`, `unlink`, `mkdir`, `rmdir`, `opendir`, `readdir`, `rewinddir`, `closedir`, `scandir`, `glob`, `getcwd`, `chdir`, `tempnam`, `sys_get_temp_dir`, `var_dump`, `print_r`
+**I/O:** `fopen`, `fclose`, `fread`, `fwrite`, `fprintf`, `vfprintf`, `fscanf`, `fgets`, `fgetc`, `fpassthru`, `flock`, `tmpfile`, `readfile`, `feof`, `readline`, `fseek`, `ftell`, `rewind`, `file_get_contents`, `file_put_contents`, `file`, `hash_file`, `fgetcsv`, `fputcsv`, `file_exists`, `is_file`, `is_dir`, `is_readable`, `is_writable`, `is_writeable`, `is_executable`, `is_link`, `symlink`, `link`, `readlink`, `linkinfo`, `filesize`, `filemtime`, `fileatime`, `filectime`, `fileperms`, `fileowner`, `filegroup`, `fileinode`, `filetype`, `stat`, `lstat`, `fstat`, `clearstatcache`, `disk_free_space`, `disk_total_space`, `basename`, `dirname`, `pathinfo`, `realpath`, `realpath_cache_get`, `realpath_cache_size`, `fnmatch`, `touch`, `chmod`, `chown`, `chgrp`, `lchown`, `lchgrp`, `umask`, `ftruncate`, `fflush`, `fsync`, `fdatasync`, `copy`, `rename`, `unlink`, `mkdir`, `rmdir`, `opendir`, `readdir`, `rewinddir`, `closedir`, `scandir`, `glob`, `getcwd`, `chdir`, `tempnam`, `sys_get_temp_dir`, `var_dump`, `print_r`
**Streams and sockets:** `stream_isatty`, `stream_is_local`, `stream_supports_lock`, `stream_get_wrappers`, `stream_get_transports`, `stream_get_filters`, `stream_context_create`, `stream_context_get_default`, `stream_context_set_default`, `stream_context_set_option`, `stream_context_set_params`, `stream_context_get_options`, `stream_context_get_params`, `stream_resolve_include_path`, `stream_get_contents`, `stream_copy_to_stream`, `stream_get_line`, `stream_get_meta_data`, `stream_set_chunk_size`, `stream_set_read_buffer`, `stream_set_write_buffer`, `stream_set_blocking`, `stream_set_timeout`, `stream_select`, `stream_filter_register`, `stream_filter_append`, `stream_filter_prepend`, `stream_filter_remove`, `stream_bucket_new`, `stream_bucket_make_writeable`, `stream_bucket_append`, `stream_bucket_prepend`, `stream_wrapper_register`, `stream_wrapper_unregister`, `stream_wrapper_restore`, `stream_socket_server`, `stream_socket_client`, `stream_socket_accept`, `stream_socket_enable_crypto`, `stream_socket_shutdown`, `stream_socket_sendto`, `stream_socket_recvfrom`, `stream_socket_get_name`, `stream_socket_pair`, `fsockopen`, `pfsockopen`, `popen`, `pclose`, `gethostname`, `gethostbyname`, `gethostbyaddr`, `getprotobyname`, `getprotobynumber`, `getservbyname`, `getservbyport`
-**System:** `exit`, `die`, `time`, `microtime`, `hrtime`, `date`, `gmdate`, `mktime`, `gmmktime`, `checkdate`, `getdate`, `localtime`, `strtotime`, `date_default_timezone_get`, `date_default_timezone_set`, `sleep`, `usleep`, `getenv`, `putenv`, `php_uname`, `phpversion`, `exec`, `shell_exec`, `system`, `passthru`, `json_encode`, `json_decode`, `json_last_error`, `json_last_error_msg`, `json_validate`, `preg_match`, `preg_match_all`, `preg_replace_callback`, `preg_replace`, `preg_split`, `define`, `defined`, `class_attribute_names`, `class_attribute_args`, `class_get_attributes`
+**System:** `exit`, `die`, `time`, `microtime`, `hrtime`, `date`, `gmdate`, `mktime`, `gmmktime`, `checkdate`, `getdate`, `localtime`, `strtotime`, `date_default_timezone_get`, `date_default_timezone_set`, `sleep`, `usleep`, `getenv`, `putenv`, `php_uname`, `phpversion`, `exec`, `shell_exec`, `system`, `passthru`, `json_encode`, `json_decode`, `json_last_error`, `json_last_error_msg`, `json_validate`, `preg_match`, `preg_match_all`, `preg_replace_callback`, `preg_replace`, `preg_split`, `define`, `defined`, `class_attribute_names`, `class_attribute_args`, `class_get_attributes`, `serialize`, `unserialize`, `header`, `http_response_code`
**SPL/autoload:** `spl_autoload_register`, `spl_autoload_unregister`, `spl_autoload_functions`, `spl_autoload_extensions`, `spl_autoload_call`, `spl_autoload`, `spl_classes`, `spl_object_id`, `spl_object_hash`, `iterator_to_array`, `iterator_count`, `iterator_apply`
@@ -509,21 +522,18 @@ src/
│
├── ir/ # EIR data model, builder, validator, and printer
├── ir_lower/ # Active AST → EIR lowering
-├── codegen_ir/ # Active EIR → target assembly backend
-├── codegen/ # Frozen legacy AST backend plus shared ABI/runtime/target helpers
-│ ├── mod.rs # Pipeline entry, main/global codegen orchestration
-│ ├── driver_support.rs # Pipeline glue and orchestration helpers
-│ ├── prescan.rs # Pre-pass collecting program-wide codegen metadata
-│ ├── program_usage.rs # Usage analysis feeding metadata emission
+├── codegen/ # Active EIR → target assembly backend
+├── codegen_support/ # Shared ABI/runtime/target helpers used by codegen
+│ ├── mod.rs # Shared metadata registries and support re-exports
+│ ├── driver_support.rs # Runtime object, deferred callable, boxing, and hash-key helpers
+│ ├── prescan.rs # Constant pre-scan feeding EIR lowering
+│ ├── program_usage.rs # Required-class usage analysis feeding metadata emission
│ ├── expr.rs # Expression codegen dispatcher
│ ├── expr/ # Focused expression helpers (arrays, calls, objects, binops, ...)
│ ├── stmt.rs # Statement codegen dispatcher
│ ├── stmt/ # Focused statement helpers (arrays, control_flow, io, storage, ...)
│ ├── abi/ # Target-aware calling-convention, frame, and value helpers
-│ ├── functions/ # User function emission, wrappers, and epilogue cleanup
-│ ├── main_emission.rs # Top-level program emission
-│ ├── class_methods.rs # Class/static method emission orchestration
-│ ├── function_variants.rs # Include-loaded function dispatchers
+│ ├── functions/ # Closure/FCC wrapper emission and epilogue cleanup
│ ├── interface_wrappers.rs # Interface dispatch return-shape adapters
│ ├── callables.rs # Top-level callable metadata and indirect-call helpers
│ ├── ffi.rs # Extern function/global/class codegen
@@ -579,7 +589,7 @@ ELEPHC_PHP_CHECK=1 cargo test # cross-check output with PHP interpreter
The **[docs/](docs/)** directory is a complete wiki covering every aspect of the compiler. Inside you'll find:
-- **PHP syntax reference** — types, operators, control structures, functions, classes, namespaces, and all 380+ built-in functions with signatures and examples
+- **PHP syntax reference** — types, operators, control structures, functions, classes, namespaces, and all 420+ built-in functions with signatures and examples
- **Compiler extensions** — pointers, `buffer`, `packed class`, FFI with `extern`, and conditional compilation with `ifdef` — the features that take PHP beyond the web
- **Compiler internals** — a step-by-step walkthrough of the full pipeline, from lexing to Pratt parsing to type checking to code generation and runtime structure
- **ARM64 primer** — an introduction to ARM64 assembly for people who've never seen it, plus a quick reference of the ARM64 instruction set used by elephc's AArch64 backend
@@ -598,3 +608,13 @@ MIT
[](https://www.youtube.com/watch?v=x06307Ui3uY)
**[Nuno Maduro: PHP Is Getting a Compiler?](https://www.youtube.com/watch?v=x06307Ui3uY)**
+
+## Star History
+
+
+
+
+
+
+
+
diff --git a/ROADMAP.md b/ROADMAP.md
index 3951e81cf4..54637b0721 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -575,7 +575,7 @@ PHP-visible surface, not the internal commits.
- [x] Phase 47 — runtime-built `phar://` write streams for `fopen()`: non-literal `fopen($path, $mode)` now publishes the PHAR URL writer bridge alongside the dynamic reader bridge. `__rt_fopen_maybe_phar` still routes `r*` modes to `__rt_phar_read_entry`, but `w`/`a`/`c`/`x` modes now tail-call `__rt_phar_write_open_url`, which persists the full runtime URL with `__rt_str_persist` so `fclose()` can finalize through `elephc_phar_put_url`. Dynamic stream writes therefore preserve sibling entries in native PHAR archives just like literal streams and dynamic `file_put_contents()` writes. Test: `test_fopen_dynamic_phar_write_preserves_existing_entries`. Follow-up phases cover compressed-entry controls and concurrent PHAR write streams; private-key signing remains deferred. ARM64 + x86_64 (both Docker-verified)
- [x] Phase 48 — tar/zip `phar://` writes: the `elephc-phar` bridge now preserves the archive family for existing native PHAR, tar, and ZIP containers, and missing `.tar` / `.zip` archive paths are created in that family instead of native PHAR. Literal write splitting recognizes `.phar/`, `.tar/`, and `.zip/` boundaries; runtime-built URLs use the same suffix-aware split. ZIP output preserves stored/deflated entries, tar output is POSIX ustar, and native PHAR gzip/bzip2 entries keep their compression when replaced. Tests: `writes_tar_entries`, `writes_zip_entries`, `writes_preserve_gzip_native_phar_entries`, `writes_preserve_bzip2_native_phar_entries`, `test_file_put_contents_phar_tar_archive_runtime_readback`, `test_file_put_contents_phar_zip_archive_runtime_readback`. Follow-up phases cover compression controls and concurrent PHAR write streams; private-key signing remains deferred. ARM64 + x86_64 (both Docker-verified)
- [x] Phase 49 — concurrent `phar://` write streams: write-mode `fopen()` now publishes buffered `elephc-phar` stream entrypoints, so literal and runtime-built PHAR URLs receive real synthetic descriptors in the `0x50000000..0x50000020` range instead of sharing one global `0x50000000` stream. `fwrite()` and `fclose()` dispatch that whole range, and the bridge owns per-descriptor payload/target state until finalization; the old assembly single-stream writer remains as an unlinked-bridge fallback. Tests: `concurrent_phar_write_streams_preserve_distinct_entries`, `test_fopen_concurrent_phar_write_streams_preserve_entries`. Deferred: private-key signing. ARM64 + x86_64 (both Docker-verified)
-- [x] Phase 50 — `Phar` / `PharData` OOP baseline: the checker now injects builtin `Phar`, `PharData`, and `PharFileInfo` classes with PHP-facing format/compression/signature constants, constructors that store the archive path, object-local mixed metadata/string stub state, archive-scanned plus object-local entry iteration state, `addFromString()`, `delete()`, `compressFiles()`, `decompressFiles()`, path helpers, entry `getContent()`, and ArrayAccess methods (`offsetGet`, `offsetSet`, `offsetExists`, `offsetUnset`) lowered as synthetic PHP bodies over the existing `phar://` `file_get_contents()` / `file_put_contents()` / `unlink()` runtime paths plus the elephc-phar compression/listing bridge. This gives `$phar->addFromString("entry", "data")`, `$phar->delete("entry")`, `setMetadata()` / `getMetadata()` / `hasMetadata()` / `delMetadata()` for strings, arrays, ints, and null, `setStub()` / `getStub()`, native-PHAR `Phar::GZ` / `Phar::BZ2` / `Phar::NONE` compression control, ZIP `Phar::GZ` / `Phar::NONE` compression control, `$phar["entry"]->getContent()` reads through `PharFileInfo`, `foreach ($phar as $name => $info)` for entries scanned from existing native PHAR/tar/ZIP archives and entries written through that object, `isset($phar["entry"])`, and `unset($phar["entry"])` coverage for native PHAR plus tar/ZIP containers without new target-specific assembly. Tests: `test_phar_oop_array_access_read_write`, `test_phar_oop_add_from_string_writes_entries`, `test_phar_oop_metadata_stub_and_path_helpers`, `test_phar_oop_iteration_tracks_written_entries`, `test_phar_oop_iteration_scans_existing_archives`, `test_phar_oop_array_access_unset_deletes_entry`, `test_phar_oop_delete_method_removes_entries`, `test_phar_oop_compress_and_decompress_files`. Deferred: persisted metadata/stub serialization, tar compression controls, and private-key signing.
+- [x] Phase 50 — `Phar` / `PharData` OOP baseline: the checker now injects builtin `Phar`, `PharData`, and `PharFileInfo` classes with PHP-facing format/compression/signature constants, constructors that store the archive path, object-local mixed metadata/string stub state, archive-scanned plus object-local entry iteration state, `addFromString()`, `delete()`, `compressFiles()`, `decompressFiles()`, path helpers, entry `getContent()`, and ArrayAccess methods (`offsetGet`, `offsetSet`, `offsetExists`, `offsetUnset`) lowered as synthetic PHP bodies over the existing `phar://` `file_get_contents()` / `file_put_contents()` / `unlink()` runtime paths plus the elephc-phar compression/listing bridge. This gives `$phar->addFromString("entry", "data")`, `$phar->delete("entry")`, `setMetadata()` / `getMetadata()` / `hasMetadata()` / `delMetadata()` for strings, arrays, ints, and null, `setStub()` / `getStub()`, native-PHAR `Phar::GZ` / `Phar::BZ2` / `Phar::NONE` compression control, ZIP `Phar::GZ` / `Phar::NONE` compression control, `$phar["entry"]->getContent()` reads through `PharFileInfo`, `foreach ($phar as $name => $info)` for entries scanned from existing native PHAR/tar/ZIP archives and entries written through that object, `isset($phar["entry"])`, and `unset($phar["entry"])` coverage for native PHAR plus tar/ZIP containers without new target-specific assembly. Tests: `test_phar_oop_array_access_read_write`, `test_phar_oop_add_from_string_writes_entries`, `test_phar_oop_metadata_stub_and_path_helpers`, `test_phar_oop_iteration_tracks_written_entries`, `test_phar_oop_iteration_scans_existing_archives`, `test_phar_oop_array_access_unset_deletes_entry`, `test_phar_oop_delete_method_removes_entries`, `test_phar_oop_compress_and_decompress_files`. Persisted metadata/stub serialization landed in a later change; tar archive-wide compression controls and private-key signing remain deferred.
### Streams — remaining work (subsystem considered *partially complete*; merged as-is)
@@ -610,18 +610,61 @@ none are needed for typical stream usage.
rewrites and OpenSSL/private-key signing.
- [x] **`phar://` tar/zip variants** — native PHAR, tar-based PHAR, and
zip-based PHAR containers are readable and writable through literal and
- runtime PHAR URLs. ZIP64, encrypted ZIP entries, and ZIP data descriptors
- remain deferred.
+ runtime PHAR URLs. ZIP entries written with a streaming data descriptor
+ (general-purpose flag bit 3) are read via the authoritative
+ central-directory sizes. ZIP64 archives (over 65535 entries, or sizes/offsets
+ over 4 GiB) are read and written — verified interchangeable with PHP and
+ Python. Traditional-PKWARE (ZipCrypto) encrypted ZIP entries are read and
+ written with a password set via the `Phar`/`PharData::setZipPassword()` compiler
+ extension (when set, zip entries — the stub included — are encrypted on write and
+ decrypted on read; the `.phar/signature.bin` entry stays in the clear). ZipCrypto
+ is cryptographically weak and kept only for compatibility with legacy archives.
- [x] **`Phar` / `PharData` OOP API** — a baseline constructor/constants,
`addFromString()`, `delete()`, native-PHAR `compressFiles()` /
`decompressFiles()` (native PHAR plus ZIP `Phar::GZ` / `Phar::NONE`),
- object-local mixed metadata/string stub accessors, path helpers, `PharFileInfo`
+ path helpers, `PharFileInfo`
`getContent()`, archive-scanned and object-local entry iteration, and
ArrayAccess read/write/isset surface is implemented (Phase 50). `offsetUnset()`
- deletes archive entries through the PHAR-aware `unlink()` path. The closed
- scope is the baseline OOP archive surface; persisted metadata/stub
- serialization, tar compression controls, and OpenSSL/private-key signing remain
- future work.
+ deletes archive entries through the PHAR-aware `unlink()` path.
+- [x] **`Phar` persisted global metadata and stub** — `setMetadata()` /
+ `getMetadata()` / `hasMetadata()` / `delMetadata()` and `setStub()` / `getStub()`
+ persist into the archive file and round-trip across fresh objects and processes
+ (and the PHP interpreter) for native PHAR (manifest field + byte-prefix stub),
+ tar (`.phar/.metadata.bin` + `.phar/stub.php`), and zip (EOCD comment +
+ `.phar/stub.php`); reserved `.phar/*` control entries are hidden from listings.
+ Backed by new public `serialize()` / `unserialize()` builtins (scalar + nested
+ array subset, byte-for-byte PHP-compatible).
+- [x] **`serialize()` / `unserialize()` objects + references** — objects serialize as
+ `O::""::{...}` with PHP's exact public/protected/private key
+ mangling, honour the `__serialize`/`__unserialize`/`__sleep`/`__wakeup` magic methods,
+ and emit `r:;` back-references for repeated objects (PHP's global value counter);
+ `unserialize()` rebuilds shared objects as one instance (`===` identity preserved).
+ Byte-for-byte PHP-compatible; 3-target verified. Deferred: cyclic references inside an
+ object's own properties resolve to `null` on read, and the deprecated `Serializable`
+ (`C:`) interface is unsupported.
+- [x] **`PharData` whole-archive (tar) compression** — `compress(Phar::GZ)` /
+ `compress(Phar::BZ2)` write a sibling `.tar.gz` / `.tar.bz2` and return a fresh
+ `PharData`; `decompress()` writes the plain `.tar` back. Compressed archives are read
+ transparently (the bridge detects the gzip/bzip2 wrapper) and are interchangeable with
+ the PHP interpreter. Per-entry native/zip compression stays on `compressFiles()`.
+- [x] **`Phar` signatures incl. OpenSSL (RSA)** — `setSignatureAlgorithm()` /
+ `getSignature()` across native PHAR, tar, and zip phars. Hash algorithms
+ (MD5/SHA1/SHA256/SHA512) and `setSignatureAlgorithm(Phar::OPENSSL, $privateKey)`
+ (RSA-SHA1, PEM PKCS#1/PKCS#8 key via the pure-Rust `rsa` crate). Native PHARs use the
+ `digest/sig ++ flag ++ "GBMB"` trailer; tar and zip phars use a `.phar/signature.bin`
+ control entry (`LE32(flag) ++ LE32(len) ++ signature`) appended last, with the
+ signature computed over the data records (tar) or local entries + central directory +
+ comment (zip), matching php-src. `getSignature()` returns `['hash' => ,
+ 'hash_type' => ...]`. The PHP interpreter verifies elephc-written signatures across all
+ three families (OpenSSL against the matching `.pubkey`).
+- [x] **`PharFileInfo` persisted per-file metadata** — `setMetadata()` /
+ `getMetadata()` / `hasMetadata()` / `delMetadata()` on the `PharFileInfo` objects
+ returned by ArrayAccess persist per-entry metadata into the archive file and
+ round-trip across fresh objects and the PHP interpreter for native PHAR (per-entry
+ manifest field), tar (`.phar/.metadata//.metadata.bin` side entry), and zip
+ (per-entry central-directory file comment). Same `serialize()` scalar + array
+ subset as global metadata; verified byte-compatible by reading elephc-written
+ archives back with the PHP interpreter (including nested entry paths).
- [x] **TLS `ciphers` / `security_level`** — accepted without error but *not
honored*: rustls has no OpenSSL-cipher-string equivalent and selects TLS
1.2/1.3 automatically. Honest no-op by design (upstream limitation), not a
@@ -713,9 +756,9 @@ imposed. See `docs/internals/the-ir.md`.
- [x] Register-pressure mitigations: caller-saved reuse for non-call-crossing intervals; better spill heuristic. The linear-scan allocator now classifies each live interval as call-free (never crosses a clobber point — an instruction/terminator whose lowering emits a call or touches a caller-saved register, per the safe-by-default allowlist in `src/ir_passes/clobber.rs`) and assigns call-free intervals from caller-saved pools that need no prologue save/restore (`x12`–`x15`/`d16`–`d23` on aarch64, `rsi`/`rdi`/`r8`/`r9`/`xmm2`–`xmm7` on x86_64), falling back to callee-saved (`x21`–`x28`/`d8`–`d14`/`rbx`) for cross-call values. This notably unlocks register allocation for x86_64 floats (no callee-saved XMM) and integers (callee pool is only `rbx`). The spill heuristic is now use-weighted: under pressure the rarely-used, furthest-reaching interval is evicted first, keeping hot values in registers
Expected outcome: EIR is the default and only active implementation backend in
-v0.24.x. The legacy AST backend is frozen behind `--ast-backend` for diagnostics
-and removal work only, and ≥15% performance improvement on compute benchmarks
-after Phase 06 by end of v0.24.x.
+v0.24.x. The legacy AST backend is frozen for diagnostics and removal work only,
+and ≥15% performance improvement on compute benchmarks after Phase 06 by end of
+v0.24.x.
## v0.25.x — EIR optimization passes and Image support
@@ -868,32 +911,57 @@ tested, diagnostic-emitting gaps.
for the procedural PHP layer, and `program_uses_image` now detects the `cairo_`
prefix so pure-procedural programs pull in the prelude
+### Array builtin parity (key/list helpers, associative set-ops, recursive merge/walk)
+
+Well-bounded PHP-visible array builtins implemented on the EIR backend. All
+target-aware (ARM64 + x86_64), with codegen and error tests; the shared `__rt_*`
+runtime helpers are reused and driven through EIR lowering.
+
+- [x] `array_key_first()` / `array_key_last()` (PHP 7.3) — first/last key in insertion order, boxed as `Mixed`, `null` for empty arrays
+- [x] `array_is_list()` (PHP 8.1) — sequential `0..n-1` key check (indexed arrays are lists by construction; associative arrays walk the insertion-order chain)
+- [x] `array_replace()` / `array_replace_recursive()` — right-wins key merge over associative arrays (recursive variant merges when both values at a key are associative arrays)
+- [x] `array_diff_assoc()` / `array_intersect_assoc()` — key + string-cast-value comparison via the unified `__rt_assoc_diff_intersect` helper
+- [x] `array_merge_recursive()` — integer-key renumbering, string-key collisions recurse (both arrays) or combine into a list (scalars)
+- [x] `array_walk_recursive()` — invokes the callback on each non-array leaf, recursing through nested indexed/associative arrays
+- [x] `array_find()` / `array_any()` / `array_all()` (PHP 8.4) — predicate callbacks; find returns the first match or `null`, any/all return booleans
+- [x] `array_udiff()` / `array_uintersect()` — difference/intersection with a user comparator (`$cmp($a, $b) === 0`)
+- [x] `array_multisort()` — sort the first indexed array ascending (stable) and reorder a second array in tandem, both by reference (two scalar-element arrays; flags/descending/multi-key are follow-ups)
+- [x] Scalar indexed-array inputs for the hash-based functions converted to integer-keyed hashes via `__rt_array_to_hash`; result key/value widen to `Mixed` for heterogeneous inputs so `foreach` dispatches keys correctly. Callback/comparator builtins reuse the EIR descriptor-callback machinery (string, function, and non-capturing closure callbacks). Hash-based functions accept associative arrays and scalar-element indexed arrays; string/heap-element indexed inputs and the callback/sort element-type limits are documented in `docs/php/arrays.md`
+
## v0.26.x — Performance closure, legacy cleanup, and 0.x stabilization
Optimization work should now be driven by benchmarks, generated assembly size,
and 0.x validation rather than by speculative pass work.
-- [x] Generators reimplemented on stackful coroutines (issue #329) — a generator body is compiled by the normal EIR backend and runs on its own coroutine stack (reusing the Fiber runtime), replacing the v1 state-machine lowering on the EIR path. `Generator::throw()` now raises the exception at the suspended `yield`, so a `try`/`catch` inside the generator body handles it and resumes instead of always terminating the generator and propagating to the caller; in-generator method calls, arbitrary control flow, and `try`/`finally` around `yield` work like ordinary functions. `yield from` over generators delegates through `__rt_gen_delegate` (forwarding sent values and returning the inner `getReturn()`) and over arrays desugars into an iterator loop; `send()`/`getReturn()`/closure captures preserved; Generator GC frees the coroutine stack and boxed key/value/return cells. The frozen legacy AST backend keeps its own `GeneratorFrame` state machine.
+- [x] Generators reimplemented on stackful coroutines (issue #329) — a generator body is compiled by the normal EIR backend and runs on its own coroutine stack (reusing the Fiber runtime), replacing the v1 state-machine lowering on the EIR path. `Generator::throw()` now raises the exception at the suspended `yield`, so a `try`/`catch` inside the generator body handles it and resumes instead of always terminating the generator and propagating to the caller; in-generator method calls, arbitrary control flow, and `try`/`finally` around `yield` work like ordinary functions. `yield from` over generators delegates through `__rt_gen_delegate` (forwarding sent values and returning the inner `getReturn()`) and over arrays desugars into an iterator loop; `send()`/`getReturn()`/closure captures preserved; Generator GC frees the coroutine stack and boxed key/value/return cells.
+- [x] Closure rebinding — `Closure::bind()`, `bindTo()`, and `Closure::call()` rebind a closure to a new receiver; a top-level closure that captures `$this` now binds it correctly instead of losing the receiver, and a by-reference `Closure::bind` stored in a variable and called later is tracked as a static callable so the call carries the bound cell directly (`__rt_closure_bind`) rather than going through the generic descriptor invoker
+- [x] New magic methods `__callStatic`, `__isset`, and `__unset` — a static call to an undeclared method dispatches to `__callStatic`; `isset()`/`empty()` on an undeclared property route through `__isset` (and only read `__get` when `__isset` is truthy, so an unset virtual property is empty without ever being read); and `unset($obj->prop)` on a virtual property calls `__unset`
+- [x] Reflection over functions — `ReflectionFunction` (name and parameter counts), `getParameters()`, `ReflectionParameter`, `ReflectionParameter::getType()`, and `ReflectionNamedType`; attribute arguments are exposed in reflection metadata, including float, positional-array, named-argument and associative-array values, references to global and class constants, and enum-case references
+- [x] References to object properties — `$x = &$obj->prop` aliases the property with write-through in both directions, and a by-reference function/method return can be captured with `$x = &f()` (including `string`- and `float`-typed properties); lowered via the `LoadPropRefCell` / `BindRefCellPtr` IR ops. Reassigning an array reference to a non-empty literal of a different type boxes the literal's elements to match the property's element type
+- [x] Enum case `->name` property (issue #330) — every enum case, pure or backed, exposes the read-only `name` string holding the case identifier (`E::A->name` is `"A"`), matching PHP's `UnitEnum::$name`; backed cases keep `->value`, `$this->name` is readable inside enum methods, and access works through direct case access, an aliasing variable, `cases()`, and string interpolation
- [ ] Source maps v2 — richer mappings for functions / expressions / labels and a more stable machine-readable schema for external tooling
- [ ] Memory-model-aware propagation for heap-backed locals and targeted runtime invalidations beyond `unset($var)` and the currently modeled local writes
-- [ ] Resource scope-cleanup — auto-free tag-9 resource handles that leave scope without their explicit close (today an unclosed `fopen()` leaks its fd and an unfinalized `hash_init()` context leaks its heap state until process exit; `functions/cleanup.rs` skips `Resource`s by design). Prerequisites: a resource-kind subtype in the Mixed cell so the cleanup pass can pick the right destructor (fd → `close()`, HashContext → `elephc_crypto_free`, …), and aliasing safety (resources have no refcount; `$b = $a` would double-free under naive scope-free). Includes wiring the currently-uncalled `elephc_crypto_free` (`_elephc_crypto_free_fn` slot + publish entry + a `__rt_hash_ctx_free` helper) and nulling the Mixed payload in `hash_final` so finalized contexts are skipped — which also defuses the double-final UB documented in `src/codegen/runtime/strings/hash_context.rs`
+- [x] Resource scope-cleanup — auto-free tag-9 resource handles that leave scope without their explicit close (today an unclosed `fopen()` leaks its fd and an unfinalized `hash_init()` context leaks its heap state until process exit; `functions/cleanup.rs` skips `Resource`s by design). Prerequisites: a resource-kind subtype in the Mixed cell so the cleanup pass can pick the right destructor (fd → `close()`, HashContext → `elephc_crypto_free`, …), and aliasing safety (resources have no refcount; `$b = $a` would double-free under naive scope-free). Includes wiring the currently-uncalled `elephc_crypto_free` (`_elephc_crypto_free_fn` slot + publish entry + a `__rt_hash_ctx_free` helper) as the single HashContext destructor and making `hash_final` finalize a *clone* (leaving the original owned by its Mixed box) so a finalized context that later leaves scope is freed exactly once — closing the double-final/use-after-free hole documented in `src/codegen/runtime/strings/hash_context.rs`. `popen` pipes (kind 3 → `__rt_pclose`) and `opendir` streams (kind 4 → `__rt_closedir`) are released the same way, and an explicit `fclose`/`pclose`/`closedir` stamps a `-1` sentinel into the box so a descriptor (whose fd number may be reused) is never closed twice
- [ ] Purity / may-throw v2 for dynamic instance dispatch, richer property/array reads, and less pessimistic builtin modeling (feeds the EIR effects table)
- [ ] Guard reasoning v2 for dead-code elimination — broader range reasoning and multi-variable facts beyond current strict-scalar, boolean, loose-comparison, and safe relational-complement guards
- [ ] Exception-aware DCE v2 — exact thrown-type / handler reachability, nested try rethrow modeling, and less conservative finally-path invalidation
- [ ] Control-flow normalization v2 — broader canonicalization of nested block/control shells before CFG-aware optimization passes
- [ ] Composite conditional include function variants — extend include-graph exclusivity from one direct `if` / `elseif` / `else` chain to nested/composed conditional paths where declarations are pairwise exclusive only after combining multiple branch decisions
- [ ] Switch-aware conditional include function variants — extend include-graph exclusivity beyond `if` / `elseif` / `else` to `switch` cases once fall-through, `break`, and terminating case bodies are modeled precisely; revisit `match` only if include-like statement lowering ever appears inside match arms
-- [ ] Runtime routine dead stripping — include or link only runtime helpers reachable from the generated program instead of carrying the whole target runtime slice
+- [x] Runtime routine dead stripping — include or link only runtime helpers reachable from the generated program instead of carrying the whole target runtime slice
+- [x] Windows x86_64 (PE32+) cross-compilation target (experimental, newly added) — `--target windows-x86_64` (alias `x86_64-pc-windows-gnu`) cross-compiles to a GNU/MinGW-ABI binary via `x86_64-w64-mingw32-gcc` (`msvcrt`), producing `.exe` (`.dll` for `--emit cdylib`); requires the MinGW-w64 cross toolchain (`x86_64-w64-mingw32-as`, `x86_64-w64-mingw32-gcc`) on the host doing the build. CI cross-compiles and validates PE32+ structure (assemble + link) with MinGW-w64, and additionally executes the cross-compiled binaries under Wine (`wine64`/`wine`) to assert stdout for echo, arithmetic, string concatenation, loops, and function calls — closing the prior compile-only testing gap; broader runtime shim coverage (files, sockets, process control, …) is still not at parity with macOS/Linux.
+- [x] `random_bytes(int $length): string` — cryptographically secure random byte string on every supported target (arc4random_buf / getrandom / BCryptGenRandom), fatal on entropy failure or length below 1
+- [x] Statically-known catchable `Error` conditions (issue #383) — private/protected method access from an inaccessible scope and readonly property writes outside the declaring constructor raise a catchable `Error` at runtime instead of being rejected at compile time, matching PHP
- [ ] Tail-call optimization — direct tail self- and mutual-recursion lowering on top of EIR (`Br` to function entry with parameter rebinding)
- [ ] Performance within 2x of C -O0 on compute benchmarks
-- [ ] DOOM showcase performance gate after EIR optimizations — build and run a reproducible SDL benchmark for `showcases/doom`, track EIR FPS / generated assembly size / runtime helper counts, optionally compare against the last known legacy baseline when available, and require no large real-world regression before deleting the frozen legacy backend
+- [ ] DOOM showcase performance gate after EIR optimizations — build and run a reproducible SDL benchmark for `showcases/doom`, track EIR FPS / generated assembly size / runtime helper counts, optionally compare against the last known legacy baseline when available, and require no large real-world regression before release
- [ ] Real-world CLI tools compiled as validation
-- [ ] Audit remaining references to `--ast-backend` and legacy AST emitters so docs, help text, and release notes present them as frozen diagnostic-only fallback before removal
-- [ ] Remove the deprecated `--ast-backend` CLI flag once diagnostic fallback is no longer needed; report it as unsupported
-- [ ] Delete frozen legacy AST → ASM emitter modules after shared ABI/runtime dependencies are disentangled
-- [ ] Rename `src/codegen_ir/` to `src/codegen/`
-- [ ] Move historical codegen doc to `docs/internals/legacy-codegen.md`; refresh `docs/internals/the-codegen.md` to describe the IR pipeline
-- [ ] Refresh `docs/internals/the-ir.md` as the canonical, non-preview IR contract for v1.0
+- [x] Audit remaining references to `--ast-backend` and legacy AST emitters so docs, help text, and release notes no longer present a selectable fallback
+- [x] Remove the deprecated `--ast-backend` CLI flag once diagnostic fallback is no longer needed; report it as unsupported
+- [x] Delete frozen legacy AST → ASM emitter modules after shared ABI/runtime dependencies are disentangled
+- [x] Rename `src/codegen_ir/` to `src/codegen/`
+- [x] Move historical codegen doc to `docs/internals/legacy-codegen.md`; refresh `docs/internals/the-codegen.md` to describe the IR pipeline
+- [x] Refresh `docs/internals/the-ir.md` as the canonical, non-preview IR contract for v1.0
- [ ] Apple notarization for direct downloads (codesign + notarytool)
- [ ] Installation / packaging documentation for the supported host platforms
diff --git a/crates/elephc-crypto/src/lib.rs b/crates/elephc-crypto/src/lib.rs
index 328bdcc259..c18cc48dc6 100644
--- a/crates/elephc-crypto/src/lib.rs
+++ b/crates/elephc-crypto/src/lib.rs
@@ -11,7 +11,11 @@
//! Key details:
//! - All ABI functions are `#[no_mangle] pub extern "C"`; raw digests are written
//! into a caller-provided 64-byte buffer (max digest size across supported algos).
-//! - `ctx` handles are thin pointers to a boxed `HashCtx`; `final`/`free` own them.
+//! - `ctx` handles are thin pointers to a boxed `HashCtx`. The boxed `Mixed`
+//! resource cell owns the handle for its whole lifetime: `free` is the sole
+//! destructor (driven by compiler scope-cleanup), while `final` finalizes a
+//! *clone* and leaves the original handle live. This keeps a context that is
+//! finalized and then dropped at scope exit a single, safe free.
mod algos;
mod hmac;
@@ -191,18 +195,26 @@ pub unsafe extern "C" fn elephc_crypto_update(
ctx.update(slice(data_ptr, data_len));
}
-/// Finalizes the context into `out`, consuming and freeing it. Returns the
-/// digest length, or -1 for a null handle.
+/// Finalizes a *clone* of the context into `out`, leaving the original handle
+/// live and owned by its boxed `Mixed` resource cell. Returns the digest length,
+/// or -1 for a null handle.
+///
+/// The handle is intentionally NOT freed here: ownership stays with the boxed
+/// resource cell, whose scope-cleanup destructor calls `elephc_crypto_free`
+/// exactly once when the box is released. Finalizing a clone keeps the handle
+/// valid afterwards, so a redundant `hash_final()`/`hash_update()` on the same
+/// handle (which PHP rejects) stays memory-safe instead of being a use-after-free
+/// or a double-free against scope cleanup.
///
/// # Safety
-/// `ctx` must be a live handle (invalid after this call); `out` must hold 64 bytes.
+/// `ctx` must be a live handle; `out` must hold 64 bytes.
#[no_mangle]
pub unsafe extern "C" fn elephc_crypto_final(ctx: *mut c_void, out_ptr: *mut u8) -> isize {
if ctx.is_null() {
return -1;
}
- let ctx = Box::from_raw(ctx as *mut HashCtx);
- let digest = ctx.finalize();
+ let ctx = &*(ctx as *mut HashCtx);
+ let digest = ctx.clone_box().finalize();
std::ptr::copy_nonoverlapping(digest.as_ptr(), out_ptr, digest.len());
digest.len() as isize
}
@@ -220,12 +232,14 @@ pub unsafe extern "C" fn elephc_crypto_clone(ctx: *mut c_void) -> *mut c_void {
Box::into_raw(Box::new(ctx.clone_box())) as *mut c_void
}
-/// Frees a context without finalizing (scope-exit / error cleanup).
+/// Frees a context (scope-exit / error cleanup) without finalizing.
///
-/// Currently UNWIRED on the compiler side by design: elephc has no Resource
-/// scope-cleanup yet, so nothing calls this and an unfinalized context leaks
-/// until process exit (like an unclosed `fopen()` stream). Kept in the ABI for
-/// the planned cleanup pass — see ROADMAP.md (v0.26.x, "Resource scope-cleanup").
+/// This is the single owner that frees a `HashContext`: the compiler's Resource
+/// scope-cleanup boxes the handle as a `Mixed` resource cell (tag 9, kind 2) and
+/// releases it here through `__rt_mixed_free_deep` → `__rt_hash_ctx_free` when the
+/// owning variable leaves scope. Because `elephc_crypto_final` no longer frees,
+/// finalizing a context and then dropping it at scope exit is a single free with
+/// no double-free.
///
/// # Safety
/// `ctx` must be a live handle and must not be used afterwards.
diff --git a/crates/elephc-phar/Cargo.toml b/crates/elephc-phar/Cargo.toml
index 197021c4d4..e415fbed71 100644
--- a/crates/elephc-phar/Cargo.toml
+++ b/crates/elephc-phar/Cargo.toml
@@ -14,3 +14,6 @@ flate2 = "1"
bzip2 = "0.6"
bzip2-rs = "0.1"
sha1 = "0.10"
+sha2 = "0.10"
+md-5 = "0.10"
+rsa = { version = "0.9", features = ["sha1", "sha2", "pem"] }
diff --git a/crates/elephc-phar/src/lib.rs b/crates/elephc-phar/src/lib.rs
index fbcc51ed15..a2f2256024 100644
--- a/crates/elephc-phar/src/lib.rs
+++ b/crates/elephc-phar/src/lib.rs
@@ -15,26 +15,49 @@
//! until the next `elephc_phar_extract_url` or `elephc_phar_list_entries` call.
//! - Writes preserve the archive family for existing native PHAR, tar, and ZIP
//! archives. Native PHAR gzip/bzip2 entries and ZIP deflate entries keep their
-//! compression when replaced. ZIP64, encrypted ZIP entries, ZIP data
-//! descriptors, tar archive compression, and private-key signing are
-//! intentionally unsupported.
+//! compression when replaced. ZIP64 archives are read and written (entry counts
+//! over 65535 or sizes/offsets over 4 GiB). Traditional-PKWARE (ZipCrypto)
+//! encrypted entries are read and written, using a password set via
+//! `elephc_phar_set_zip_password` (ZipCrypto is cryptographically weak — kept for
+//! compatibility, not as a real confidentiality mechanism).
use std::io::{Read, Write};
+use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock};
+use std::time::{SystemTime, UNIX_EPOCH};
const PHAR_FLAG_GZIP: u32 = 0x0000_1000;
const PHAR_FLAG_BZIP2: u32 = 0x0000_2000;
const PHAR_HDR_SIGNATURE: u32 = 0x0001_0000;
const PHAR_FILE_MODE_0644: u32 = 0x0000_01a4;
const PHAR_SHA1_SIGNATURE_TYPE: u32 = 0x0000_0002;
+const PHAR_OPENSSL_SIGNATURE_TYPE: u32 = 0x0000_0010;
const ZIP_METHOD_STORE: u16 = 0;
const ZIP_METHOD_DEFLATE: u16 = 8;
+/// ZIP general-purpose flag bit 0: the entry is encrypted (traditional ZipCrypto).
+const ZIP_FLAG_ENCRYPTED: u16 = 0x0001;
+/// ZIP general-purpose flag bit 3: sizes/CRC are in a trailing data descriptor.
const ZIP_FLAG_DATA_DESCRIPTOR: u16 = 0x0008;
+/// ZIP64 extended-information extra-field tag.
+const ZIP64_EXTRA_TAG: u16 = 0x0001;
+/// 32-bit field value meaning "real value is in the ZIP64 extra field / EOCD64".
+const ZIP32_SENTINEL: u32 = 0xFFFF_FFFF;
+/// 16-bit entry-count field value meaning "real count is in the EOCD64".
+const ZIP16_SENTINEL: u16 = 0xFFFF;
const PHAR_WRITE_FD_BASE: usize = 0x5000_0000;
const PHAR_WRITE_STREAM_LIMIT: usize = 32;
static EXTRACT_BUFFER: OnceLock>> = OnceLock::new();
static WRITE_STREAMS: OnceLock>>> = OnceLock::new();
+thread_local! {
+ /// Password used to read and write traditional-PKWARE (ZipCrypto) encrypted ZIP
+ /// entries, set through [`elephc_phar_set_zip_password`]; `None` until provided.
+ /// When set, zip phars are written with their entries encrypted. Thread-local:
+ /// it is set and consumed on the same (single) runtime thread, which also keeps
+ /// parallel unit tests from clobbering each other's password state.
+ static ZIP_PASSWORD: std::cell::RefCell>> =
+ const { std::cell::RefCell::new(None) };
+}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PharCompression {
@@ -48,6 +71,11 @@ struct ArchiveEntry {
name: Vec,
payload: Vec,
compression: PharCompression,
+ /// PHP-`serialize()`d per-file metadata blob (empty when the entry has none).
+ /// Stored in the native manifest's per-entry metadata field, a tar
+ /// `.phar/.metadata//.metadata.bin` side entry, or a zip central-directory
+ /// file comment, depending on the archive family.
+ metadata: Vec,
}
#[derive(Clone, Copy)]
@@ -57,6 +85,41 @@ enum ArchiveFormat {
Zip,
}
+/// Internal entry name holding a tar/zip-based phar's executable stub.
+const PHAR_STUB_ENTRY: &[u8] = b".phar/stub.php";
+/// Internal entry name holding a tar/zip-based phar's serialized global metadata.
+const PHAR_METADATA_ENTRY: &[u8] = b".phar/.metadata.bin";
+/// Internal entry name holding a tar/zip-based phar's signature. Its payload is
+/// `LE32(sig_flag) ++ LE32(sig_len) ++ signature`, and it is always the archive's
+/// last entry so the signed range is everything that precedes it.
+const PHAR_SIGNATURE_ENTRY: &[u8] = b".phar/signature.bin";
+/// Prefix of the tar side entry holding one file's serialized metadata. The full
+/// name is `.phar/.metadata//.metadata.bin` (matching php-src).
+const PHAR_FILE_METADATA_PREFIX: &[u8] = b".phar/.metadata/";
+/// Suffix of the tar per-file metadata side entry (see [`PHAR_FILE_METADATA_PREFIX`]).
+const PHAR_FILE_METADATA_SUFFIX: &[u8] = b"/.metadata.bin";
+/// Default native-PHAR stub emitted when no custom stub has been set.
+const PHAR_DEFAULT_STUB: &[u8] = b"\r\n";
+
+/// A parsed archive plus its archive-level global metadata and stub.
+///
+/// `metadata` holds the PHP-`serialize()`d global metadata blob (empty when none);
+/// `stub` holds the executable stub bytes (empty when none/default). Both are
+/// preserved across read-modify-write cycles and re-emitted by [`build_archive`].
+#[derive(Clone)]
+struct Archive {
+ entries: Vec,
+ format: ArchiveFormat,
+ metadata: Vec,
+ stub: Vec,
+}
+
+/// Returns true for the reserved `.phar/*` control entries that phars hide from
+/// their public entry listing (stub, metadata, alias, signature, per-file metadata).
+fn is_phar_control_entry(name: &[u8]) -> bool {
+ name.starts_with(b".phar/")
+}
+
enum WriteStreamTarget {
Entry { archive: Vec, entry: Vec },
Url(Vec),
@@ -87,6 +150,13 @@ pub fn extract_url_bytes(url: &[u8]) -> Option> {
/// arbitrary stubs before the payload. Plain ZIP and TAR containers are then
/// tried by signature/layout.
pub fn extract_entry_bytes(archive: &[u8], entry: &[u8]) -> Option> {
+ // Whole-archive gzip/bzip2 wrappers are decoded transparently before extraction.
+ if archive.starts_with(b"\x1f\x8b") {
+ return extract_entry_bytes(&decompress_gzip_stream(archive)?, entry);
+ }
+ if archive.starts_with(b"BZh") {
+ return extract_entry_bytes(&decompress_bzip2_stream(archive)?, entry);
+ }
parse_native_phar_entry(archive, entry)
.or_else(|| parse_zip_entry(archive, entry))
.or_else(|| parse_tar_entry(archive, entry))
@@ -125,15 +195,20 @@ pub fn put_entry_bytes(
}
let archive_path = std::str::from_utf8(archive_path).ok()?;
let path = std::path::Path::new(archive_path);
- let (mut entries, format) = if path.exists() {
- let archive = std::fs::read(path).ok()?;
- parse_archive_entries(&archive)?
+ let mut archive = if path.exists() {
+ let bytes = std::fs::read(path).ok()?;
+ parse_archive(&bytes)?
} else {
- (Vec::new(), format_for_new_archive_path(path))
+ Archive {
+ entries: Vec::new(),
+ format: format_for_new_archive_path(path),
+ metadata: Vec::new(),
+ stub: Vec::new(),
+ }
};
- upsert_entry(&mut entries, entry_name, payload);
- let archive = build_archive(&entries, format)?;
- std::fs::write(path, archive).ok()?;
+ upsert_entry(&mut archive.entries, entry_name, payload);
+ let out = build_archive_value(&archive)?;
+ std::fs::write(path, out).ok()?;
Some(payload.len())
}
@@ -159,11 +234,11 @@ pub fn delete_entry_bytes(archive_path: &[u8], entry_name: &[u8]) -> Option<()>
}
let archive_path = std::str::from_utf8(archive_path).ok()?;
let path = std::path::Path::new(archive_path);
- let archive = std::fs::read(path).ok()?;
- let (mut entries, format) = parse_archive_entries(&archive)?;
- remove_entry(&mut entries, entry_name)?;
- let archive = build_archive(&entries, format)?;
- std::fs::write(path, archive).ok()?;
+ let bytes = std::fs::read(path).ok()?;
+ let mut archive = parse_archive(&bytes)?;
+ remove_entry(&mut archive.entries, entry_name)?;
+ let out = build_archive_value(&archive)?;
+ std::fs::write(path, out).ok()?;
Some(())
}
@@ -184,22 +259,80 @@ pub fn set_archive_compression(archive_path: &[u8], compression_code: usize) ->
let compression = compression_from_php_constant(compression_code)?;
let archive_path = std::str::from_utf8(archive_path).ok()?;
let path = std::path::Path::new(archive_path);
- let archive = std::fs::read(path).ok()?;
- let (mut entries, format) = parse_archive_entries(&archive)?;
- if matches!(format, ArchiveFormat::Tar) {
+ let bytes = std::fs::read(path).ok()?;
+ let mut archive = parse_archive(&bytes)?;
+ if matches!(archive.format, ArchiveFormat::Tar) {
return None;
}
- if matches!(format, ArchiveFormat::Zip) && matches!(compression, PharCompression::Bzip2) {
+ if matches!(archive.format, ArchiveFormat::Zip)
+ && matches!(compression, PharCompression::Bzip2)
+ {
return None;
}
- for entry in &mut entries {
+ for entry in &mut archive.entries {
entry.compression = compression;
}
- let archive = build_archive(&entries, format)?;
- std::fs::write(path, archive).ok()?;
+ let out = build_archive_value(&archive)?;
+ std::fs::write(path, out).ok()?;
+ Some(())
+}
+
+/// Reads an archive's serialized global metadata blob (empty when unset).
+fn get_metadata_bytes(archive_path: &[u8]) -> Option> {
+ let path = std::str::from_utf8(archive_path).ok()?;
+ let bytes = std::fs::read(path).ok()?;
+ Some(parse_archive(&bytes)?.metadata)
+}
+
+/// Reads an archive's stub bytes (empty when unset / default).
+fn get_stub_bytes(archive_path: &[u8]) -> Option> {
+ let path = std::str::from_utf8(archive_path).ok()?;
+ let bytes = std::fs::read(path).ok()?;
+ Some(parse_archive(&bytes)?.stub)
+}
+
+/// Sets an archive's global metadata, preserving all entries and the stub.
+///
+/// Creates the archive (format chosen by extension) when it does not yet exist.
+fn set_metadata_bytes(archive_path: &[u8], metadata: &[u8]) -> Option<()> {
+ let path_str = std::str::from_utf8(archive_path).ok()?;
+ let path = std::path::Path::new(path_str);
+ let mut archive = read_or_new_archive(path)?;
+ archive.metadata = metadata.to_vec();
+ std::fs::write(path, build_archive_value(&archive)?).ok()?;
+ Some(())
+}
+
+/// Sets an archive's stub, preserving all entries and global metadata.
+///
+/// The stub must contain `__HALT_COMPILER();` (matching PHP); creates the archive
+/// (format chosen by extension) when it does not yet exist.
+fn set_stub_bytes(archive_path: &[u8], stub: &[u8]) -> Option<()> {
+ if find_subslice(stub, b"__HALT_COMPILER();").is_none() {
+ return None;
+ }
+ let path_str = std::str::from_utf8(archive_path).ok()?;
+ let path = std::path::Path::new(path_str);
+ let mut archive = read_or_new_archive(path)?;
+ archive.stub = stub.to_vec();
+ std::fs::write(path, build_archive_value(&archive)?).ok()?;
Some(())
}
+/// Parses an existing archive, or builds an empty one whose format follows the path.
+fn read_or_new_archive(path: &std::path::Path) -> Option {
+ if path.exists() {
+ parse_archive(&std::fs::read(path).ok()?)
+ } else {
+ Some(Archive {
+ entries: Vec::new(),
+ format: format_for_new_archive_path(path),
+ metadata: Vec::new(),
+ stub: Vec::new(),
+ })
+ }
+}
+
/// C ABI wrapper around [`extract_url_bytes`].
///
/// Returns a pointer to a stable process-global buffer and writes the byte
@@ -319,6 +452,22 @@ pub unsafe extern "C" fn elephc_phar_delete_url(
}
}
+/// C ABI wrapper around [`set_zip_password`].
+///
+/// Sets the password used to read and write traditional-PKWARE (ZipCrypto)
+/// encrypted ZIP entries; an empty password clears it. Always returns `1`.
+///
+/// # Safety
+/// `password_ptr` must be valid for `password_len` bytes unless `password_len` is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_set_zip_password(
+ password_ptr: *const u8,
+ password_len: usize,
+) -> usize {
+ let _ = std::panic::catch_unwind(|| set_zip_password(slice(password_ptr, password_len)));
+ 1
+}
+
/// C ABI wrapper around [`set_archive_compression`].
///
/// Returns `1` when the native PHAR archive was rewritten, or `0` for invalid
@@ -341,6 +490,297 @@ pub unsafe extern "C" fn elephc_phar_set_compression(
}
}
+/// C ABI wrapper around [`get_metadata_bytes`].
+///
+/// Returns a pointer to the serialized global metadata buffer and writes its byte
+/// length into `out_len`. Returns null and writes zero when there is no metadata or
+/// the archive cannot be read.
+///
+/// # Safety
+/// `path_ptr` must be valid for `path_len` bytes unless `path_len` is zero.
+/// `out_len` may be null; when non-null it must be writable.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_get_metadata(
+ path_ptr: *const u8,
+ path_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ match std::panic::catch_unwind(|| get_metadata_bytes(slice(path_ptr, path_len))) {
+ Ok(Some(bytes)) if !bytes.is_empty() => publish_result(bytes, out_len),
+ _ => {
+ write_len(out_len, 0);
+ std::ptr::null()
+ }
+ }
+}
+
+/// C ABI wrapper around [`get_stub_bytes`].
+///
+/// Returns a pointer to the stub buffer and writes its byte length into `out_len`.
+/// Returns null and writes zero when there is no stub or the archive cannot be read.
+///
+/// # Safety
+/// `path_ptr` must be valid for `path_len` bytes unless `path_len` is zero.
+/// `out_len` may be null; when non-null it must be writable.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_get_stub(
+ path_ptr: *const u8,
+ path_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ match std::panic::catch_unwind(|| get_stub_bytes(slice(path_ptr, path_len))) {
+ Ok(Some(bytes)) if !bytes.is_empty() => publish_result(bytes, out_len),
+ _ => {
+ write_len(out_len, 0);
+ std::ptr::null()
+ }
+ }
+}
+
+/// C ABI wrapper around [`set_metadata_bytes`].
+///
+/// Returns `1` when the archive was rewritten with the new global metadata, or `0`
+/// on any failure.
+///
+/// # Safety
+/// Each pointer must be valid for its paired byte length unless that length is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_set_metadata(
+ path_ptr: *const u8,
+ path_len: usize,
+ data_ptr: *const u8,
+ data_len: usize,
+) -> usize {
+ let result = std::panic::catch_unwind(|| {
+ set_metadata_bytes(slice(path_ptr, path_len), slice(data_ptr, data_len))
+ });
+ match result {
+ Ok(Some(())) => 1,
+ _ => 0,
+ }
+}
+
+/// C ABI wrapper around [`set_stub_bytes`].
+///
+/// Returns `1` when the archive was rewritten with the new stub, or `0` on any
+/// failure (including a stub missing the `__HALT_COMPILER();` marker).
+///
+/// # Safety
+/// Each pointer must be valid for its paired byte length unless that length is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_set_stub(
+ path_ptr: *const u8,
+ path_len: usize,
+ data_ptr: *const u8,
+ data_len: usize,
+) -> usize {
+ let result = std::panic::catch_unwind(|| {
+ set_stub_bytes(slice(path_ptr, path_len), slice(data_ptr, data_len))
+ });
+ match result {
+ Ok(Some(())) => 1,
+ _ => 0,
+ }
+}
+
+/// C ABI wrapper around [`get_file_metadata_url`].
+///
+/// Takes a `phar://archive/entry` URL and returns a pointer to that entry's
+/// serialized metadata, writing its byte length into `out_len`. Returns null and
+/// writes zero when the entry has no metadata, the entry is absent, or the archive
+/// cannot be read.
+///
+/// # Safety
+/// Each pointer must be valid for its paired byte length unless that length is zero.
+/// `out_len` may be null; when non-null it must be writable.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_get_file_metadata(
+ url_ptr: *const u8,
+ url_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ let result = std::panic::catch_unwind(|| get_file_metadata_url(slice(url_ptr, url_len)));
+ match result {
+ Ok(Some(bytes)) if !bytes.is_empty() => publish_result(bytes, out_len),
+ _ => {
+ write_len(out_len, 0);
+ std::ptr::null()
+ }
+ }
+}
+
+/// C ABI wrapper around [`set_file_metadata_url`].
+///
+/// Takes a `phar://archive/entry` URL and serialized metadata, rewriting the archive
+/// so the entry carries it (an empty `data` clears it). Returns `1` on success, or
+/// `0` on any failure including a missing entry.
+///
+/// # Safety
+/// Each pointer must be valid for its paired byte length unless that length is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_set_file_metadata(
+ url_ptr: *const u8,
+ url_len: usize,
+ data_ptr: *const u8,
+ data_len: usize,
+) -> usize {
+ let result = std::panic::catch_unwind(|| {
+ set_file_metadata_url(slice(url_ptr, url_len), slice(data_ptr, data_len))
+ });
+ match result {
+ Ok(Some(())) => 1,
+ _ => 0,
+ }
+}
+
+/// C ABI wrapper around [`gzip_archive`] — whole-archive gzip compression.
+///
+/// Returns a pointer to the written destination path and writes its length into
+/// `out_len`; returns null and writes zero on failure.
+///
+/// # Safety
+/// `src` must be valid for `src_len` unless zero; `out_len` must be writable when non-null.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_gzip_archive(
+ src_ptr: *const u8,
+ src_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ publish_archive_path_result(
+ std::panic::catch_unwind(|| gzip_archive(slice(src_ptr, src_len))),
+ out_len,
+ )
+}
+
+/// C ABI wrapper around [`bzip2_archive`] — whole-archive bzip2 compression.
+///
+/// Returns a pointer to the written destination path and writes its length into
+/// `out_len`; returns null and writes zero on failure.
+///
+/// # Safety
+/// `src` must be valid for `src_len` unless zero; `out_len` must be writable when non-null.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_bzip2_archive(
+ src_ptr: *const u8,
+ src_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ publish_archive_path_result(
+ std::panic::catch_unwind(|| bzip2_archive(slice(src_ptr, src_len))),
+ out_len,
+ )
+}
+
+/// C ABI wrapper around [`decompress_archive`] — whole-archive decompression.
+///
+/// Returns a pointer to the written destination path and writes its length into
+/// `out_len`; returns null and writes zero on failure (including an uncompressed src).
+///
+/// # Safety
+/// `src` must be valid for `src_len` unless zero; `out_len` must be writable when non-null.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_decompress_archive(
+ src_ptr: *const u8,
+ src_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ publish_archive_path_result(
+ std::panic::catch_unwind(|| decompress_archive(slice(src_ptr, src_len))),
+ out_len,
+ )
+}
+
+/// Shared result handling for the archive (de)compression bridges: publishes a
+/// non-empty destination path, or returns null + zero length on failure.
+fn publish_archive_path_result(
+ result: std::thread::Result>>,
+ out_len: *mut usize,
+) -> *const u8 {
+ match result {
+ Ok(Some(path)) if !path.is_empty() => publish_result(path, out_len),
+ _ => {
+ write_len(out_len, 0);
+ std::ptr::null()
+ }
+ }
+}
+
+/// C ABI wrapper around [`sign_archive_openssl`] — RSA-SHA1 (OpenSSL) PHAR signing.
+///
+/// Returns `1` when the archive was re-signed, `0` on any failure (bad key, unreadable
+/// archive).
+///
+/// # Safety
+/// Each pointer must be valid for its paired byte length unless that length is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_sign_openssl(
+ path_ptr: *const u8,
+ path_len: usize,
+ key_ptr: *const u8,
+ key_len: usize,
+) -> usize {
+ let result = std::panic::catch_unwind(|| {
+ sign_archive_openssl(slice(path_ptr, path_len), slice(key_ptr, key_len))
+ });
+ usize::from(matches!(result, Ok(Some(_))))
+}
+
+/// C ABI wrapper around [`sign_archive_hash`] — MD5/SHA1/SHA256/SHA512 PHAR signing.
+///
+/// Returns `1` when the archive was re-signed, `0` on any failure or unknown `algo`.
+///
+/// # Safety
+/// `path` must be valid for `path_len` unless that length is zero.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_sign_hash(
+ path_ptr: *const u8,
+ path_len: usize,
+ algo: usize,
+) -> usize {
+ let result = std::panic::catch_unwind(|| {
+ sign_archive_hash(slice(path_ptr, path_len), algo as u32)
+ });
+ usize::from(matches!(result, Ok(Some(()))))
+}
+
+/// C ABI wrapper around [`signature_hash_hex`] — `Phar::getSignature()['hash']`.
+///
+/// Returns the uppercase-hex signature/digest pointer and writes its length into
+/// `out_len`; returns null + zero on failure.
+///
+/// # Safety
+/// `path` must be valid for `path_len`; `out_len` must be writable when non-null.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_get_signature_hash(
+ path_ptr: *const u8,
+ path_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ publish_archive_path_result(
+ std::panic::catch_unwind(|| signature_hash_hex(slice(path_ptr, path_len))),
+ out_len,
+ )
+}
+
+/// C ABI wrapper around [`signature_type_name`] — `Phar::getSignature()['hash_type']`.
+///
+/// Returns the type-name pointer and writes its length into `out_len`; returns null +
+/// zero on failure.
+///
+/// # Safety
+/// `path` must be valid for `path_len`; `out_len` must be writable when non-null.
+#[no_mangle]
+pub unsafe extern "C" fn elephc_phar_get_signature_type(
+ path_ptr: *const u8,
+ path_len: usize,
+ out_len: *mut usize,
+) -> *const u8 {
+ publish_archive_path_result(
+ std::panic::catch_unwind(|| signature_type_name(slice(path_ptr, path_len))),
+ out_len,
+ )
+}
+
/// C ABI wrapper that opens a buffered write stream for a literal PHAR entry.
///
/// Returns a synthetic descriptor in the `0x50000000..0x50000020` range, or
@@ -557,12 +997,120 @@ fn split_write_url_entry(rest: &[u8]) -> Option<(&[u8], &[u8])> {
Some((rest.get(..idx)?, rest.get(idx + 1..)?))
}
+/// Parses archive bytes into a full [`Archive`] (entries plus global metadata/stub).
+///
+/// Dispatch is by container signature rather than try-each-and-fallback: tar/zip-based
+/// phars embed a `.phar/stub.php` containing `__HALT_COMPILER();`, so a native-first
+/// scan would mistake them for native PHARs. ZIP starts with `PK\x03\x04` (or
+/// `PK\x05\x06` when empty); TAR carries the ustar magic at offset 257; everything
+/// else (a ` Option {
+ // A whole-archive gzip/bzip2 wrapper (e.g. `.tar.gz` / `.tar.bz2`) is decoded
+ // transparently, then the inner archive is parsed normally.
+ if data.starts_with(b"\x1f\x8b") {
+ return parse_archive(&decompress_gzip_stream(data)?);
+ }
+ if data.starts_with(b"BZh") {
+ return parse_archive(&decompress_bzip2_stream(data)?);
+ }
+ if data.starts_with(b"PK\x03\x04") || data.starts_with(b"PK\x05\x06") {
+ parse_zip_archive(data)
+ } else if data.get(257..262) == Some(b"ustar") {
+ parse_tar_archive(data)
+ } else {
+ parse_native_phar_archive(data)
+ }
+}
+
+/// Decompresses a whole gzip (`.gz`) stream into its plain bytes.
+fn decompress_gzip_stream(data: &[u8]) -> Option> {
+ let mut out = Vec::new();
+ let mut decoder = flate2::read::GzDecoder::new(data);
+ std::io::Read::read_to_end(&mut decoder, &mut out).ok()?;
+ Some(out)
+}
+
+/// Decompresses a whole bzip2 (`.bz2`) stream into its plain bytes.
+fn decompress_bzip2_stream(data: &[u8]) -> Option> {
+ let mut out = Vec::new();
+ let mut decoder = bzip2_rs::DecoderReader::new(data);
+ std::io::Read::read_to_end(&mut decoder, &mut out).ok()?;
+ Some(out)
+}
+
+/// Returns the plain (uncompressed) archive bytes, stripping a whole-archive gzip or
+/// bzip2 wrapper when present so a recompress operates on the canonical archive.
+fn uncompressed_archive_bytes(raw: &[u8]) -> Option> {
+ if raw.starts_with(b"\x1f\x8b") {
+ decompress_gzip_stream(raw)
+ } else if raw.starts_with(b"BZh") {
+ decompress_bzip2_stream(raw)
+ } else {
+ Some(raw.to_vec())
+ }
+}
+
+/// Returns the destination path for compressing `src`: any existing `.gz`/`.bz2`
+/// suffix is stripped, then `.` is appended (e.g. `foo.tar` → `foo.tar.gz`).
+fn compression_dest_path(src: &[u8], new_ext: &str) -> Option> {
+ let s = std::str::from_utf8(src).ok()?;
+ let base = s
+ .strip_suffix(".gz")
+ .or_else(|| s.strip_suffix(".bz2"))
+ .unwrap_or(s);
+ Some(format!("{base}.{new_ext}").into_bytes())
+}
+
+/// Reads `src`, gzip-wraps its plain archive bytes, writes them to ` .gz`, and
+/// returns that destination path (PHP `PharData::compress(Phar::GZ)`).
+fn gzip_archive(src: &[u8]) -> Option> {
+ let plain = uncompressed_archive_bytes(&read_path(src)?)?;
+ let mut encoder =
+ flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
+ std::io::Write::write_all(&mut encoder, &plain).ok()?;
+ let dest = compression_dest_path(src, "gz")?;
+ write_path(&dest, &encoder.finish().ok()?)?;
+ Some(dest)
+}
+
+/// Reads `src`, bzip2-wraps its plain archive bytes, writes them to ` .bz2`, and
+/// returns that destination path (PHP `PharData::compress(Phar::BZ2)`).
+fn bzip2_archive(src: &[u8]) -> Option> {
+ let plain = uncompressed_archive_bytes(&read_path(src)?)?;
+ let mut encoder = bzip2::write::BzEncoder::new(Vec::new(), bzip2::Compression::default());
+ std::io::Write::write_all(&mut encoder, &plain).ok()?;
+ let dest = compression_dest_path(src, "bz2")?;
+ write_path(&dest, &encoder.finish().ok()?)?;
+ Some(dest)
+}
+
+/// Reads a whole-archive-compressed `src` (a `.gz`/`.bz2` path), writes its plain
+/// bytes to the path with that suffix removed, and returns that destination path
+/// (PHP `PharData::decompress()`). Fails when `src` carries no compression suffix.
+fn decompress_archive(src: &[u8]) -> Option> {
+ let s = std::str::from_utf8(src).ok()?;
+ let dest = s
+ .strip_suffix(".gz")
+ .or_else(|| s.strip_suffix(".bz2"))?
+ .as_bytes()
+ .to_vec();
+ write_path(&dest, &uncompressed_archive_bytes(&read_path(src)?)?)?;
+ Some(dest)
+}
+
+/// Reads a filesystem path given as UTF-8 bytes.
+fn read_path(path: &[u8]) -> Option> {
+ std::fs::read(std::path::Path::new(std::str::from_utf8(path).ok()?)).ok()
+}
+
+/// Writes `bytes` to a filesystem path given as UTF-8 bytes.
+fn write_path(path: &[u8], bytes: &[u8]) -> Option<()> {
+ std::fs::write(std::path::Path::new(std::str::from_utf8(path).ok()?), bytes).ok()
+}
+
/// Parses archive bytes into decoded entries and reports the archive family.
fn parse_archive_entries(data: &[u8]) -> Option<(Vec, ArchiveFormat)> {
- parse_native_phar_entries(data)
- .map(|entries| (entries, ArchiveFormat::NativePhar))
- .or_else(|| parse_zip_entries(data).map(|entries| (entries, ArchiveFormat::Zip)))
- .or_else(|| parse_tar_entries(data).map(|entries| (entries, ArchiveFormat::Tar)))
+ parse_archive(data).map(|archive| (archive.entries, archive.format))
}
/// Selects the archive family for a missing output path.
@@ -575,24 +1123,43 @@ fn format_for_new_archive_path(path: &std::path::Path) -> ArchiveFormat {
}
/// Builds an archive in the selected output family.
-fn build_archive(entries: &[ArchiveEntry], format: ArchiveFormat) -> Option> {
+fn build_archive(
+ entries: &[ArchiveEntry],
+ format: ArchiveFormat,
+ metadata: &[u8],
+ stub: &[u8],
+) -> Option> {
match format {
- ArchiveFormat::NativePhar => build_native_phar_archive(entries),
- ArchiveFormat::Tar => build_tar_archive(entries),
- ArchiveFormat::Zip => build_zip_archive(entries),
+ ArchiveFormat::NativePhar => build_native_phar_archive(entries, metadata, stub),
+ ArchiveFormat::Tar => build_tar_archive(entries, metadata, stub),
+ ArchiveFormat::Zip => build_zip_archive(entries, metadata, stub),
}
}
+/// Rebuilds an [`Archive`] into serialized bytes, preserving its metadata and stub.
+fn build_archive_value(archive: &Archive) -> Option> {
+ build_archive(
+ &archive.entries,
+ archive.format,
+ &archive.metadata,
+ &archive.stub,
+ )
+}
+
/// Parses a native PHAR archive and returns a decoded entry payload.
fn parse_native_phar_entry(data: &[u8], entry: &[u8]) -> Option> {
- parse_native_phar_entries(data)?
+ parse_native_phar_archive(data)?
+ .entries
.into_iter()
.find(|candidate| candidate.name == entry)
.map(|candidate| candidate.payload)
}
-/// Parses a native PHAR archive and returns every decoded entry payload.
-fn parse_native_phar_entries(data: &[u8]) -> Option> {
+/// Parses a native PHAR archive into entries plus its global metadata and stub.
+///
+/// The stub is the byte prefix up to and including the `__HALT_COMPILER();` marker
+/// (and any trailing ` ?>\r\n`); the global metadata is the manifest's metadata field.
+fn parse_native_phar_archive(data: &[u8]) -> Option {
let halt = b"__HALT_COMPILER();";
let halt_idx = find_subslice(data, halt)?;
let mut p = halt_idx + halt.len();
@@ -603,6 +1170,7 @@ fn parse_native_phar_entries(data: &[u8]) -> Option> {
}
let manifest_start = p;
+ let stub = data.get(..manifest_start)?.to_vec();
let manifest_len = le32(data, manifest_start)? as usize;
let data_section = manifest_start.checked_add(4)?.checked_add(manifest_len)?;
let num_files = le32(data, manifest_start + 4)?;
@@ -610,7 +1178,9 @@ fn parse_native_phar_entries(data: &[u8]) -> Option> {
let alias_len = le32(data, q)? as usize;
q = q.checked_add(4)?.checked_add(alias_len)?;
let meta_len = le32(data, q)? as usize;
- q = q.checked_add(4)?.checked_add(meta_len)?;
+ q = q.checked_add(4)?;
+ let metadata = data.get(q..q.checked_add(meta_len)?)?.to_vec();
+ q = q.checked_add(meta_len)?;
let mut data_offset = 0usize;
let mut entries = Vec::with_capacity(num_files as usize);
@@ -628,7 +1198,9 @@ fn parse_native_phar_entries(data: &[u8]) -> Option> {
let flags = le32(data, q)?;
q = q.checked_add(4)?;
let entry_meta_len = le32(data, q)? as usize;
- q = q.checked_add(4)?.checked_add(entry_meta_len)?;
+ q = q.checked_add(4)?;
+ let entry_metadata = data.get(q..q.checked_add(entry_meta_len)?)?.to_vec();
+ q = q.checked_add(entry_meta_len)?;
let start = data_section.checked_add(data_offset)?;
let stored = data.get(start..start.checked_add(compressed)?)?;
@@ -637,10 +1209,16 @@ fn parse_native_phar_entries(data: &[u8]) -> Option> {
name: name.to_vec(),
payload,
compression: phar_compression_from_flags(flags),
+ metadata: entry_metadata,
});
data_offset = data_offset.checked_add(compressed)?;
}
- Some(entries)
+ Some(Archive {
+ entries,
+ format: ArchiveFormat::NativePhar,
+ metadata,
+ stub,
+ })
}
/// Extracts the PHAR compression mode from per-entry flags.
@@ -681,26 +1259,74 @@ fn upsert_entry(entries: &mut Vec, entry_name: &[u8], payload: &[u
name: entry_name.to_vec(),
payload: payload.to_vec(),
compression: PharCompression::None,
+ metadata: Vec::new(),
});
}
}
-/// Removes an archive entry and reports failure when no matching entry exists.
-fn remove_entry(entries: &mut Vec, entry_name: &[u8]) -> Option<()> {
- let index = entries.iter().position(|entry| entry.name == entry_name)?;
- entries.remove(index);
- Some(())
+/// Returns the serialized per-file metadata for `entry_name`, or `None` if the
+/// archive cannot be read or has no such entry.
+fn get_file_metadata_bytes(archive_path: &[u8], entry_name: &[u8]) -> Option> {
+ let path = std::path::Path::new(std::str::from_utf8(archive_path).ok()?);
+ let archive = parse_archive(&std::fs::read(path).ok()?)?;
+ let entry = archive.entries.iter().find(|e| e.name == entry_name)?;
+ Some(entry.metadata.clone())
}
-/// Builds a SHA1-signed native PHAR archive from decoded entries.
-fn build_native_phar_archive(entries: &[ArchiveEntry]) -> Option> {
- let mut manifest = Vec::new();
+/// Sets (or clears, when `metadata` is empty) the per-file metadata for
+/// `entry_name` and rewrites the archive. Fails if the entry does not exist.
+fn set_file_metadata_bytes(
+ archive_path: &[u8],
+ entry_name: &[u8],
+ metadata: &[u8],
+) -> Option<()> {
+ let path = std::path::Path::new(std::str::from_utf8(archive_path).ok()?);
+ let mut archive = parse_archive(&std::fs::read(path).ok()?)?;
+ let entry = archive.entries.iter_mut().find(|e| e.name == entry_name)?;
+ entry.metadata.clear();
+ entry.metadata.extend_from_slice(metadata);
+ let rebuilt = build_archive_value(&archive)?;
+ std::fs::write(path, rebuilt).ok()
+}
+
+/// Reads per-file metadata addressed by a `phar://archive/entry` URL, splitting it
+/// into archive path and entry name before delegating to [`get_file_metadata_bytes`].
+fn get_file_metadata_url(url: &[u8]) -> Option> {
+ let rest = url.strip_prefix(b"phar://")?;
+ let (archive_path, entry) = split_archive_entry(rest)?;
+ get_file_metadata_bytes(archive_path, entry)
+}
+
+/// Writes per-file metadata addressed by a `phar://archive/entry` URL, splitting it
+/// into archive path and entry name before delegating to [`set_file_metadata_bytes`].
+fn set_file_metadata_url(url: &[u8], metadata: &[u8]) -> Option<()> {
+ let rest = url.strip_prefix(b"phar://")?;
+ let (archive_path, entry) = split_archive_entry(rest)?;
+ set_file_metadata_bytes(archive_path, entry, metadata)
+}
+
+/// Removes an archive entry and reports failure when no matching entry exists.
+fn remove_entry(entries: &mut Vec, entry_name: &[u8]) -> Option<()> {
+ let index = entries.iter().position(|entry| entry.name == entry_name)?;
+ entries.remove(index);
+ Some(())
+}
+
+/// Builds a SHA1-signed native PHAR archive from decoded entries.
+fn build_native_phar_archive(
+ entries: &[ArchiveEntry],
+ metadata: &[u8],
+ stub: &[u8],
+) -> Option> {
+ let mut manifest = Vec::new();
let mut stored_entries = Vec::with_capacity(entries.len());
manifest.extend_from_slice(&u32::try_from(entries.len()).ok()?.to_le_bytes());
manifest.extend_from_slice(&[0x11, 0x00]);
manifest.extend_from_slice(&PHAR_HDR_SIGNATURE.to_le_bytes());
manifest.extend_from_slice(&0u32.to_le_bytes());
- manifest.extend_from_slice(&0u32.to_le_bytes());
+ // Global metadata field: length-prefixed serialized blob (empty when unset).
+ manifest.extend_from_slice(&u32::try_from(metadata.len()).ok()?.to_le_bytes());
+ manifest.extend_from_slice(metadata);
for entry in entries {
let name_len = u32::try_from(entry.name.len()).ok()?;
let payload_len = u32::try_from(entry.payload.len()).ok()?;
@@ -715,12 +1341,18 @@ fn build_native_phar_archive(entries: &[ArchiveEntry]) -> Option> {
manifest.extend_from_slice(
&(PHAR_FILE_MODE_0644 | phar_compression_flag(entry.compression)).to_le_bytes(),
);
- manifest.extend_from_slice(&0u32.to_le_bytes());
+ // Per-entry metadata field: length-prefixed serialized blob (empty when unset).
+ manifest.extend_from_slice(&u32::try_from(entry.metadata.len()).ok()?.to_le_bytes());
+ manifest.extend_from_slice(&entry.metadata);
stored_entries.push(stored);
}
let mut out = Vec::new();
- out.extend_from_slice(b"\r\n");
+ if stub.is_empty() {
+ out.extend_from_slice(PHAR_DEFAULT_STUB);
+ } else {
+ out.extend_from_slice(stub);
+ }
out.extend_from_slice(&u32::try_from(manifest.len()).ok()?.to_le_bytes());
out.extend_from_slice(&manifest);
for stored in stored_entries {
@@ -769,44 +1401,98 @@ fn compression_from_php_constant(value: usize) -> Option {
}
/// Builds a POSIX ustar archive with stored regular-file entries.
-fn build_tar_archive(entries: &[ArchiveEntry]) -> Option> {
+fn build_tar_archive(entries: &[ArchiveEntry], metadata: &[u8], stub: &[u8]) -> Option> {
let mut out = Vec::new();
+ write_tar_body(&mut out, entries, metadata, stub)?;
+ out.extend_from_slice(&[0u8; 1024]);
+ Some(out)
+}
+
+/// Writes a tar phar's data records (stub, global metadata, entries, and per-file
+/// metadata side entries) into `out`, without the trailing zero blocks. The bytes
+/// it produces are exactly the range a tar phar signature is computed over.
+fn write_tar_body(
+ out: &mut Vec,
+ entries: &[ArchiveEntry],
+ metadata: &[u8],
+ stub: &[u8],
+) -> Option<()> {
+ // Tar-based phars store the stub and global metadata as reserved `.phar/*` files.
+ if !stub.is_empty() {
+ write_tar_entry(out, PHAR_STUB_ENTRY, stub)?;
+ }
+ if !metadata.is_empty() {
+ write_tar_entry(out, PHAR_METADATA_ENTRY, metadata)?;
+ }
for entry in entries {
- let (name, prefix) = split_tar_name(&entry.name)?;
- let mut header = [0u8; 512];
- header[..name.len()].copy_from_slice(name);
- if let Some(prefix) = prefix {
- header[345..345 + prefix.len()].copy_from_slice(prefix);
- }
- let mode = b"0000644\0";
- header[100..100 + mode.len()].copy_from_slice(mode);
- let uid = b"0000000\0";
- header[108..108 + uid.len()].copy_from_slice(uid);
- header[116..116 + uid.len()].copy_from_slice(uid);
- let size = format!("{:011o}\0", entry.payload.len());
- header[124..124 + size.len()].copy_from_slice(size.as_bytes());
- let mtime = b"00000000000\0";
- header[136..136 + mtime.len()].copy_from_slice(mtime);
- header[156] = b'0';
- header[257..263].copy_from_slice(b"ustar\0");
- header[263..265].copy_from_slice(b"00");
- for byte in &mut header[148..156] {
- *byte = b' ';
+ write_tar_entry(out, &entry.name, &entry.payload)?;
+ }
+ // Per-file metadata rides in `.phar/.metadata//.metadata.bin` side entries.
+ for entry in entries {
+ if !entry.metadata.is_empty() {
+ write_tar_entry(out, &tar_file_metadata_name(&entry.name), &entry.metadata)?;
}
- let checksum: u32 = header.iter().map(|&byte| byte as u32).sum();
- let checksum = format!("{:06o}\0 ", checksum);
- header[148..156].copy_from_slice(checksum.as_bytes());
- out.extend_from_slice(&header);
- out.extend_from_slice(&entry.payload);
- out.resize(
- out.len() + round_up_to_512(entry.payload.len())? - entry.payload.len(),
- 0,
- );
}
+ Some(())
+}
+
+/// Rebuilds a tar phar with a PHP-compatible `.phar/signature.bin` trailer entry.
+///
+/// The signature is computed over the data records (everything before the
+/// signature entry's header), then the signature entry is appended as the last
+/// record before the trailing zero blocks, matching php-src `phar_tar_flush`.
+fn sign_tar_archive(archive: &Archive, flag: u32, key: Option<&[u8]>) -> Option> {
+ let mut out = Vec::new();
+ write_tar_body(&mut out, &archive.entries, &archive.metadata, &archive.stub)?;
+ let sig = compute_signature(flag, key, &out)?;
+ write_tar_entry(&mut out, PHAR_SIGNATURE_ENTRY, &signature_bin_payload(flag, &sig)?)?;
out.extend_from_slice(&[0u8; 1024]);
Some(out)
}
+/// Builds the tar side-entry path that holds one file's serialized metadata.
+fn tar_file_metadata_name(entry_name: &[u8]) -> Vec {
+ let mut name = Vec::with_capacity(
+ PHAR_FILE_METADATA_PREFIX.len() + entry_name.len() + PHAR_FILE_METADATA_SUFFIX.len(),
+ );
+ name.extend_from_slice(PHAR_FILE_METADATA_PREFIX);
+ name.extend_from_slice(entry_name);
+ name.extend_from_slice(PHAR_FILE_METADATA_SUFFIX);
+ name
+}
+
+/// Writes one uncompressed POSIX ustar entry (512-byte header + padded payload).
+fn write_tar_entry(out: &mut Vec, entry_name: &[u8], payload: &[u8]) -> Option<()> {
+ let (name, prefix) = split_tar_name(entry_name)?;
+ let mut header = [0u8; 512];
+ header[..name.len()].copy_from_slice(name);
+ if let Some(prefix) = prefix {
+ header[345..345 + prefix.len()].copy_from_slice(prefix);
+ }
+ let mode = b"0000644\0";
+ header[100..100 + mode.len()].copy_from_slice(mode);
+ let uid = b"0000000\0";
+ header[108..108 + uid.len()].copy_from_slice(uid);
+ header[116..116 + uid.len()].copy_from_slice(uid);
+ let size = format!("{:011o}\0", payload.len());
+ header[124..124 + size.len()].copy_from_slice(size.as_bytes());
+ let mtime = b"00000000000\0";
+ header[136..136 + mtime.len()].copy_from_slice(mtime);
+ header[156] = b'0';
+ header[257..263].copy_from_slice(b"ustar\0");
+ header[263..265].copy_from_slice(b"00");
+ for byte in &mut header[148..156] {
+ *byte = b' ';
+ }
+ let checksum: u32 = header.iter().map(|&byte| byte as u32).sum();
+ let checksum = format!("{:06o}\0 ", checksum);
+ header[148..156].copy_from_slice(checksum.as_bytes());
+ out.extend_from_slice(&header);
+ out.extend_from_slice(payload);
+ out.resize(out.len() + round_up_to_512(payload.len())? - payload.len(), 0);
+ Some(())
+}
+
/// Splits a tar entry path into ustar `name` and optional `prefix` fields.
fn split_tar_name(name: &[u8]) -> Option<(&[u8], Option<&[u8]>)> {
if name.len() <= 100 {
@@ -826,65 +1512,259 @@ fn split_tar_name(name: &[u8]) -> Option<(&[u8], Option<&[u8]>)> {
}
/// Builds a ZIP archive with stored or deflated entries and central-directory records.
-fn build_zip_archive(entries: &[ArchiveEntry]) -> Option> {
+fn build_zip_archive(entries: &[ArchiveEntry], metadata: &[u8], stub: &[u8]) -> Option> {
let mut out = Vec::new();
let mut central = Vec::new();
- for entry in entries {
- let name_len = u16::try_from(entry.name.len()).ok()?;
- let payload_len = u32::try_from(entry.payload.len()).ok()?;
- let (method, stored) = encode_zip_payload(&entry.payload, entry.compression)?;
- let stored_len = u32::try_from(stored.len()).ok()?;
- let local_offset = u32::try_from(out.len()).ok()?;
- let crc = crc32(&entry.payload);
-
- out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
- out.extend_from_slice(&20u16.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&method.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&crc.to_le_bytes());
- out.extend_from_slice(&stored_len.to_le_bytes());
- out.extend_from_slice(&payload_len.to_le_bytes());
- out.extend_from_slice(&name_len.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&entry.name);
- out.extend_from_slice(&stored);
+ let count = write_zip_body(&mut out, &mut central, entries, stub)?;
+ finalize_zip(&mut out, ¢ral, count, metadata)?;
+ Some(out)
+}
- central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
- central.extend_from_slice(&20u16.to_le_bytes());
- central.extend_from_slice(&20u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&method.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&crc.to_le_bytes());
- central.extend_from_slice(&stored_len.to_le_bytes());
- central.extend_from_slice(&payload_len.to_le_bytes());
- central.extend_from_slice(&name_len.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u32.to_le_bytes());
- central.extend_from_slice(&local_offset.to_le_bytes());
- central.extend_from_slice(&entry.name);
+/// Writes a zip phar's local file entries (stub plus regular entries) into `out`
+/// and their central-directory records into `central`, returning the entry count.
+/// When a zip password is set, every file entry — the stub included — is
+/// ZipCrypto-encrypted; only the separately-written `.phar/signature.bin` stays
+/// in the clear.
+fn write_zip_body(
+ out: &mut Vec,
+ central: &mut Vec,
+ entries: &[ArchiveEntry],
+ stub: &[u8],
+) -> Option {
+ let mut count = 0usize;
+ // Zip-based phars store the stub as the reserved `.phar/stub.php` entry.
+ if !stub.is_empty() {
+ write_zip_entry(out, central, PHAR_STUB_ENTRY, stub, PharCompression::None, &[], true)?;
+ count += 1;
}
- let central_offset = u32::try_from(out.len()).ok()?;
- let central_len = u32::try_from(central.len()).ok()?;
- let entry_count = u16::try_from(entries.len()).ok()?;
- out.extend_from_slice(¢ral);
+ for entry in entries {
+ write_zip_entry(
+ out,
+ central,
+ &entry.name,
+ &entry.payload,
+ entry.compression,
+ &entry.metadata,
+ true,
+ )?;
+ count += 1;
+ }
+ Some(count)
+}
+
+/// Appends the central directory and the end-of-central-directory record (with the
+/// global metadata carried as the ZIP archive comment) to a zip phar under build.
+fn finalize_zip(out: &mut Vec, central: &[u8], count: usize, metadata: &[u8]) -> Option<()> {
+ let central_offset = out.len();
+ let central_len = central.len();
+ // Zip-based phars store global metadata in the EOCD archive comment.
+ let comment_len = u16::try_from(metadata.len()).ok()?;
+ out.extend_from_slice(central);
+
+ // Emit the ZIP64 EOCD record + locator when the entry count, central-directory
+ // size, or offset overflows the regular EOCD's 16-/32-bit fields.
+ let sentinel = ZIP32_SENTINEL as usize;
+ let needs_zip64 =
+ count >= ZIP16_SENTINEL as usize || central_offset > sentinel || central_len > sentinel;
+ if needs_zip64 {
+ let eocd64_offset = out.len() as u64;
+ // -- ZIP64 end-of-central-directory record --
+ out.extend_from_slice(&0x0606_4b50u32.to_le_bytes());
+ out.extend_from_slice(&44u64.to_le_bytes()); // size of the rest of this record
+ out.extend_from_slice(&45u16.to_le_bytes()); // version made by
+ out.extend_from_slice(&45u16.to_le_bytes()); // version needed to extract
+ out.extend_from_slice(&0u32.to_le_bytes()); // number of this disk
+ out.extend_from_slice(&0u32.to_le_bytes()); // disk with central directory
+ out.extend_from_slice(&(count as u64).to_le_bytes()); // entries on this disk
+ out.extend_from_slice(&(count as u64).to_le_bytes()); // total entries
+ out.extend_from_slice(&(central_len as u64).to_le_bytes());
+ out.extend_from_slice(&(central_offset as u64).to_le_bytes());
+ // -- ZIP64 end-of-central-directory locator --
+ out.extend_from_slice(&0x0706_4b50u32.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes()); // disk with the ZIP64 EOCD
+ out.extend_from_slice(&eocd64_offset.to_le_bytes());
+ out.extend_from_slice(&1u32.to_le_bytes()); // total number of disks
+ }
+
+ // Regular EOCD, using the 0xFFFF / 0xFFFFFFFF sentinels for overflowed fields.
+ let entry_count = u16::try_from(count).unwrap_or(ZIP16_SENTINEL);
+ let cd_len = u32::try_from(central_len).unwrap_or(ZIP32_SENTINEL);
+ let cd_offset = u32::try_from(central_offset).unwrap_or(ZIP32_SENTINEL);
out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&entry_count.to_le_bytes());
out.extend_from_slice(&entry_count.to_le_bytes());
- out.extend_from_slice(¢ral_len.to_le_bytes());
- out.extend_from_slice(¢ral_offset.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&cd_len.to_le_bytes());
+ out.extend_from_slice(&cd_offset.to_le_bytes());
+ out.extend_from_slice(&comment_len.to_le_bytes());
+ out.extend_from_slice(metadata);
+ Some(())
+}
+
+/// Rebuilds a zip phar with a PHP-compatible `.phar/signature.bin` entry.
+///
+/// php-src `phar_zip_applysignature` hashes the local file entries, the central
+/// directory, and the archive comment — but not the EOCD — and then appends the
+/// signature as the archive's last local entry and last central record.
+fn sign_zip_archive(archive: &Archive, flag: u32, key: Option<&[u8]>) -> Option> {
+ let mut out = Vec::new();
+ let mut central = Vec::new();
+ let mut count = write_zip_body(&mut out, &mut central, &archive.entries, &archive.stub)?;
+ // Signed range: local entries ++ central records ++ comment, signature excluded.
+ let mut signed = out.clone();
+ signed.extend_from_slice(¢ral);
+ signed.extend_from_slice(&archive.metadata);
+ let sig = compute_signature(flag, key, &signed)?;
+ // The signature entry stays in the clear — a verifier must read it without the
+ // password (and the signature already covers the encrypted local-entry bytes).
+ write_zip_entry(
+ &mut out,
+ &mut central,
+ PHAR_SIGNATURE_ENTRY,
+ &signature_bin_payload(flag, &sig)?,
+ PharCompression::None,
+ &[],
+ false,
+ )?;
+ count += 1;
+ finalize_zip(&mut out, ¢ral, count, &archive.metadata)?;
Some(out)
}
+/// Writes one ZIP entry: its local file header + stored payload into `out`, and the
+/// matching central-directory record into `central`. When `encrypt` is set and a zip
+/// password is configured, the stored payload is ZipCrypto-encrypted and the
+/// general-purpose "encrypted" flag is set in both headers.
+fn write_zip_entry(
+ out: &mut Vec,
+ central: &mut Vec,
+ name: &[u8],
+ payload: &[u8],
+ compression: PharCompression,
+ metadata: &[u8],
+ encrypt: bool,
+) -> Option<()> {
+ let name_len = u16::try_from(name.len()).ok()?;
+ let comment_len = u16::try_from(metadata.len()).ok()?;
+ let payload_len = payload.len();
+ let (method, stored) = encode_zip_payload(payload, compression)?;
+ let local_offset = out.len();
+ let crc = crc32(payload);
+
+ // Encrypt the stored payload (traditional ZipCrypto) when requested and a zip
+ // password is set. The 12-byte header's check byte is the CRC's high byte, since
+ // no data descriptor is written — matching the read-side `zip_entry_crypto`
+ // branch. Encryption grows the stored size by 12 bytes and sets flag bit 0.
+ let password = if encrypt { current_zip_password() } else { None };
+ let (stored, flags) = match password {
+ Some(pw) => (
+ zipcrypto_encrypt(&pw, &stored, (crc >> 24) as u8),
+ ZIP_FLAG_ENCRYPTED,
+ ),
+ None => (stored, 0u16),
+ };
+ let stored_len = stored.len();
+
+ // ZIP64 is needed when a size or the local-header offset overflows 32 bits.
+ let sentinel = ZIP32_SENTINEL as usize;
+ let zip64_sizes = stored_len > sentinel || payload_len > sentinel;
+ let zip64_offset = local_offset > sentinel;
+ let version: u16 = if zip64_sizes || zip64_offset { 45 } else { 20 };
+
+ // Local header: defers both sizes to a ZIP64 extra field once either overflows.
+ let local_csz = if zip64_sizes { ZIP32_SENTINEL } else { stored_len as u32 };
+ let local_usz = if zip64_sizes { ZIP32_SENTINEL } else { payload_len as u32 };
+ let local_extra = if zip64_sizes {
+ zip64_local_extra(payload_len as u64, stored_len as u64)
+ } else {
+ Vec::new()
+ };
+ let local_extra_len = u16::try_from(local_extra.len()).ok()?;
+
+ out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
+ out.extend_from_slice(&version.to_le_bytes());
+ out.extend_from_slice(&flags.to_le_bytes());
+ out.extend_from_slice(&method.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&crc.to_le_bytes());
+ out.extend_from_slice(&local_csz.to_le_bytes());
+ out.extend_from_slice(&local_usz.to_le_bytes());
+ out.extend_from_slice(&name_len.to_le_bytes());
+ out.extend_from_slice(&local_extra_len.to_le_bytes());
+ out.extend_from_slice(name);
+ out.extend_from_slice(&local_extra);
+ out.extend_from_slice(&stored);
+
+ // Central record: each overflowed field becomes a sentinel + a ZIP64 extra entry.
+ let cen_csz = if stored_len > sentinel { ZIP32_SENTINEL } else { stored_len as u32 };
+ let cen_usz = if payload_len > sentinel { ZIP32_SENTINEL } else { payload_len as u32 };
+ let cen_off = if zip64_offset { ZIP32_SENTINEL } else { local_offset as u32 };
+ let cen_extra = if zip64_sizes || zip64_offset {
+ zip64_central_extra(
+ (payload_len > sentinel).then_some(payload_len as u64),
+ (stored_len > sentinel).then_some(stored_len as u64),
+ zip64_offset.then_some(local_offset as u64),
+ )
+ } else {
+ Vec::new()
+ };
+ let cen_extra_len = u16::try_from(cen_extra.len()).ok()?;
+
+ central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
+ central.extend_from_slice(&version.to_le_bytes());
+ central.extend_from_slice(&version.to_le_bytes());
+ central.extend_from_slice(&flags.to_le_bytes());
+ central.extend_from_slice(&method.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&crc.to_le_bytes());
+ central.extend_from_slice(&cen_csz.to_le_bytes());
+ central.extend_from_slice(&cen_usz.to_le_bytes());
+ central.extend_from_slice(&name_len.to_le_bytes());
+ central.extend_from_slice(&cen_extra_len.to_le_bytes());
+ // File comment length: carries this entry's serialized per-file metadata.
+ central.extend_from_slice(&comment_len.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&cen_off.to_le_bytes());
+ central.extend_from_slice(name);
+ central.extend_from_slice(&cen_extra);
+ central.extend_from_slice(metadata);
+ Some(())
+}
+
+/// Builds a ZIP64 local-header extra field carrying the 64-bit uncompressed and
+/// compressed sizes (tag 0x0001, both fields always present in local headers).
+fn zip64_local_extra(uncompressed: u64, compressed: u64) -> Vec {
+ let mut out = Vec::with_capacity(20);
+ out.extend_from_slice(&ZIP64_EXTRA_TAG.to_le_bytes());
+ out.extend_from_slice(&16u16.to_le_bytes());
+ out.extend_from_slice(&uncompressed.to_le_bytes());
+ out.extend_from_slice(&compressed.to_le_bytes());
+ out
+}
+
+/// Builds a ZIP64 central-directory extra field holding only the overflowed
+/// fields, in APPNOTE order: uncompressed size, compressed size, header offset.
+fn zip64_central_extra(
+ uncompressed: Option,
+ compressed: Option,
+ offset: Option,
+) -> Vec {
+ let mut body = Vec::new();
+ for field in [uncompressed, compressed, offset].into_iter().flatten() {
+ body.extend_from_slice(&field.to_le_bytes());
+ }
+ let mut out = Vec::with_capacity(4 + body.len());
+ out.extend_from_slice(&ZIP64_EXTRA_TAG.to_le_bytes());
+ out.extend_from_slice(&(body.len() as u16).to_le_bytes());
+ out.extend_from_slice(&body);
+ out
+}
+
/// Encodes a ZIP entry payload and returns its ZIP compression method.
fn encode_zip_payload(payload: &[u8], compression: PharCompression) -> Option<(u16, Vec)> {
match compression {
@@ -909,6 +1789,287 @@ fn append_sha1_signature(archive: &mut Vec) {
archive.extend_from_slice(b"GBMB");
}
+/// Returns the raw digest length for a PHP hash-based PHAR signature flag
+/// (MD5=1, SHA1=2, SHA256=3, SHA512=4); `None` for non-hash flags.
+fn signature_digest_len(flags: u32) -> Option {
+ match flags {
+ 1 => Some(16),
+ 2 => Some(20),
+ 3 => Some(32),
+ 4 => Some(64),
+ _ => None,
+ }
+}
+
+/// Returns the archive bytes with any trailing PHP signature trailer removed
+/// (native PHAR `digest ++ LE32(flag) ++ "GBMB"`, or the OpenSSL variant
+/// `sig ++ LE32(sig_len) ++ LE32(0x10) ++ "GBMB"`). Returns the input unchanged
+/// when no recognized trailer is present.
+fn strip_signature_trailer(archive: &[u8]) -> &[u8] {
+ let n = archive.len();
+ if n < 8 || &archive[n - 4..] != b"GBMB" {
+ return archive;
+ }
+ let flags = u32::from_le_bytes(archive[n - 8..n - 4].try_into().unwrap());
+ if flags == PHAR_OPENSSL_SIGNATURE_TYPE {
+ if n >= 12 {
+ let sig_len = u32::from_le_bytes(archive[n - 12..n - 8].try_into().unwrap()) as usize;
+ if let Some(total) = sig_len.checked_add(12) {
+ if n >= total {
+ return &archive[..n - total];
+ }
+ }
+ }
+ } else if let Some(dlen) = signature_digest_len(flags) {
+ let total = dlen + 8;
+ if n >= total {
+ return &archive[..n - total];
+ }
+ }
+ archive
+}
+
+/// Computes the PKCS#1 v1.5 RSA-SHA1 signature of `data` with a PEM private key
+/// (PKCS#8 or PKCS#1), matching PHP's `openssl_sign(..., OPENSSL_ALGO_SHA1)`.
+fn rsa_sha1_sign(data: &[u8], key_pem: &[u8]) -> Option> {
+ use rsa::pkcs1::DecodeRsaPrivateKey;
+ use rsa::pkcs8::DecodePrivateKey;
+ use rsa::{Pkcs1v15Sign, RsaPrivateKey};
+ use sha1::{Digest, Sha1};
+
+ let pem = std::str::from_utf8(key_pem).ok()?;
+ let key = RsaPrivateKey::from_pkcs8_pem(pem)
+ .ok()
+ .or_else(|| RsaPrivateKey::from_pkcs1_pem(pem).ok())?;
+ let hashed = Sha1::digest(data);
+ key.sign(Pkcs1v15Sign::new::(), &hashed).ok()
+}
+
+/// Computes a PHP-compatible signature over `data` for a signature `flag`: a raw
+/// MD5/SHA1/SHA256/SHA512 digest (flags 1..=4) or an RSA-SHA1 OpenSSL signature
+/// (flag 0x10, requiring the PEM `key`). Returns `None` for an unknown flag or a
+/// missing/invalid key.
+fn compute_signature(flag: u32, key: Option<&[u8]>, data: &[u8]) -> Option> {
+ use md5::Md5;
+ use sha1::{Digest, Sha1};
+ use sha2::{Sha256, Sha512};
+
+ match flag {
+ 1 => Some(Md5::digest(data).to_vec()),
+ 2 => Some(Sha1::digest(data).to_vec()),
+ 3 => Some(Sha256::digest(data).to_vec()),
+ 4 => Some(Sha512::digest(data).to_vec()),
+ PHAR_OPENSSL_SIGNATURE_TYPE => rsa_sha1_sign(data, key?),
+ _ => None,
+ }
+}
+
+/// Builds the `.phar/signature.bin` payload for a tar/zip phar:
+/// `LE32(sig_flag) ++ LE32(sig_len) ++ signature`.
+fn signature_bin_payload(flag: u32, sig: &[u8]) -> Option> {
+ let mut out = Vec::with_capacity(8 + sig.len());
+ out.extend_from_slice(&flag.to_le_bytes());
+ out.extend_from_slice(&u32::try_from(sig.len()).ok()?.to_le_bytes());
+ out.extend_from_slice(sig);
+ Some(out)
+}
+
+/// Detects the archive family of `data` for signature operations: zip (PK magic),
+/// tar (ustar magic at offset 257), or native PHAR (default). Returns `None` for a
+/// gzip/bzip2-wrapped archive, where signature rewriting is not supported.
+fn signing_format(data: &[u8]) -> Option {
+ if data.starts_with(&[0x50, 0x4b, 0x03, 0x04]) || data.starts_with(&[0x50, 0x4b, 0x05, 0x06]) {
+ Some(ArchiveFormat::Zip)
+ } else if data.get(257..262) == Some(b"ustar") {
+ Some(ArchiveFormat::Tar)
+ } else if data.starts_with(&[0x1f, 0x8b]) || data.starts_with(b"BZh") {
+ None
+ } else {
+ Some(ArchiveFormat::NativePhar)
+ }
+}
+
+/// Re-signs the phar at `path` with an OpenSSL (RSA-SHA1) signature. Native PHARs
+/// gain a `sig ++ LE32(sig_len) ++ LE32(0x10) ++ "GBMB"` trailer; tar/zip phars
+/// gain a `.phar/signature.bin` entry. The caller-supplied public key is what
+/// verifiers use; PHP does not auto-write a `.pubkey` here either.
+fn sign_archive_openssl(path: &[u8], key_pem: &[u8]) -> Option<()> {
+ let data = read_path(path)?;
+ match signing_format(&data)? {
+ ArchiveFormat::Zip => {
+ let signed =
+ sign_zip_archive(&parse_zip_archive(&data)?, PHAR_OPENSSL_SIGNATURE_TYPE, Some(key_pem))?;
+ write_path(path, &signed)
+ }
+ ArchiveFormat::Tar => {
+ let signed =
+ sign_tar_archive(&parse_tar_archive(&data)?, PHAR_OPENSSL_SIGNATURE_TYPE, Some(key_pem))?;
+ write_path(path, &signed)
+ }
+ ArchiveFormat::NativePhar => {
+ let mut out = strip_signature_trailer(&data).to_vec();
+ let sig = rsa_sha1_sign(&out, key_pem)?;
+ out.extend_from_slice(&sig);
+ out.extend_from_slice(&u32::try_from(sig.len()).ok()?.to_le_bytes());
+ out.extend_from_slice(&PHAR_OPENSSL_SIGNATURE_TYPE.to_le_bytes());
+ out.extend_from_slice(b"GBMB");
+ write_path(path, &out)
+ }
+ }
+}
+
+/// Re-signs the phar at `path` with a hash-based signature (MD5/SHA1/SHA256/SHA512
+/// per `algo` 1..=4). Native PHARs append `digest ++ LE32(algo) ++ "GBMB"`; tar/zip
+/// phars gain a `.phar/signature.bin` entry.
+fn sign_archive_hash(path: &[u8], algo: u32) -> Option<()> {
+ let data = read_path(path)?;
+ match signing_format(&data)? {
+ ArchiveFormat::Zip => {
+ let signed = sign_zip_archive(&parse_zip_archive(&data)?, algo, None)?;
+ write_path(path, &signed)
+ }
+ ArchiveFormat::Tar => {
+ let signed = sign_tar_archive(&parse_tar_archive(&data)?, algo, None)?;
+ write_path(path, &signed)
+ }
+ ArchiveFormat::NativePhar => {
+ let mut out = strip_signature_trailer(&data).to_vec();
+ let digest = compute_signature(algo, None, &out)?;
+ out.extend_from_slice(&digest);
+ out.extend_from_slice(&algo.to_le_bytes());
+ out.extend_from_slice(b"GBMB");
+ write_path(path, &out)
+ }
+ }
+}
+
+/// Decodes a tar/zip `.phar/signature.bin` payload into its flag and signature
+/// bytes (`LE32(flag) ++ LE32(len) ++ signature`).
+fn parse_signature_bin(payload: &[u8]) -> Option<(u32, Vec)> {
+ let flag = le32(payload, 0)?;
+ let len = le32(payload, 4)? as usize;
+ Some((flag, payload.get(8..8usize.checked_add(len)?)?.to_vec()))
+}
+
+/// Returns the raw `.phar/signature.bin` payload from a tar phar, if present.
+fn read_tar_signature(data: &[u8]) -> Option> {
+ let mut p = 0usize;
+ while p.checked_add(512)? <= data.len() {
+ let header = &data[p..p + 512];
+ if header.iter().all(|&b| b == 0) {
+ break;
+ }
+ let size = parse_tar_octal(&header[124..136])?;
+ let payload_start = p.checked_add(512)?;
+ let typeflag = header[156];
+ if (typeflag == 0 || typeflag == b'0') && tar_entry_name(header)? == PHAR_SIGNATURE_ENTRY {
+ return data
+ .get(payload_start..payload_start.checked_add(size)?)
+ .map(<[u8]>::to_vec);
+ }
+ p = payload_start.checked_add(round_up_to_512(size)?)?;
+ }
+ None
+}
+
+/// Returns the raw `.phar/signature.bin` payload from a zip phar, if present.
+fn read_zip_signature(data: &[u8]) -> Option> {
+ let (entry_count, central_dir_offset) = zip_eocd_info(data)?;
+ let mut p = central_dir_offset;
+ for _ in 0..entry_count {
+ if le32(data, p)? != 0x0201_4b50 {
+ return None;
+ }
+ let method = le16(data, p + 10)?;
+ let mut compressed_size = le32(data, p + 20)? as usize;
+ let mut uncompressed_size = le32(data, p + 24)? as usize;
+ let name_len = le16(data, p + 28)? as usize;
+ let extra_len = le16(data, p + 30)? as usize;
+ let comment_len = le16(data, p + 32)? as usize;
+ let mut local_offset = le32(data, p + 42)? as usize;
+ let name_start = p + 46;
+ let name = data.get(name_start..name_start.checked_add(name_len)?)?;
+ if name == PHAR_SIGNATURE_ENTRY {
+ apply_zip64_central_extra(
+ data,
+ name_start.checked_add(name_len)?,
+ extra_len,
+ &mut uncompressed_size,
+ &mut compressed_size,
+ &mut local_offset,
+ )?;
+ // The reserved signature entry is never encrypted.
+ return decode_zip_local_entry(
+ data,
+ local_offset,
+ method,
+ compressed_size,
+ uncompressed_size,
+ false,
+ 0,
+ );
+ }
+ p = name_start
+ .checked_add(name_len)?
+ .checked_add(extra_len)?
+ .checked_add(comment_len)?;
+ }
+ None
+}
+
+/// Reads the signature of the phar at `path`, returning the flag and the raw
+/// signature/digest bytes. Native PHARs use the `GBMB` trailer; tar/zip phars use
+/// the `.phar/signature.bin` entry.
+fn read_signature_info(path: &[u8]) -> Option<(u32, Vec)> {
+ let data = read_path(path)?;
+ match signing_format(&data)? {
+ ArchiveFormat::Zip => parse_signature_bin(&read_zip_signature(&data)?),
+ ArchiveFormat::Tar => parse_signature_bin(&read_tar_signature(&data)?),
+ ArchiveFormat::NativePhar => {
+ let n = data.len();
+ if n < 8 || &data[n - 4..] != b"GBMB" {
+ return None;
+ }
+ let flags = u32::from_le_bytes(data[n - 8..n - 4].try_into().unwrap());
+ if flags == PHAR_OPENSSL_SIGNATURE_TYPE {
+ let sig_len =
+ u32::from_le_bytes(data.get(n - 12..n - 8)?.try_into().unwrap()) as usize;
+ let start = n.checked_sub(12)?.checked_sub(sig_len)?;
+ Some((flags, data.get(start..n - 12)?.to_vec()))
+ } else {
+ let dlen = signature_digest_len(flags)?;
+ let start = n.checked_sub(8)?.checked_sub(dlen)?;
+ Some((flags, data.get(start..n - 8)?.to_vec()))
+ }
+ }
+ }
+}
+
+/// Returns the uppercase hex of the PHAR's signature/digest bytes (PHP
+/// `Phar::getSignature()['hash']`).
+fn signature_hash_hex(path: &[u8]) -> Option> {
+ let (_, bytes) = read_signature_info(path)?;
+ let mut hex = Vec::with_capacity(bytes.len() * 2);
+ for byte in bytes {
+ hex.extend_from_slice(format!("{byte:02X}").as_bytes());
+ }
+ Some(hex)
+}
+
+/// Returns the PHP signature type name for the PHAR (`getSignature()['hash_type']`).
+fn signature_type_name(path: &[u8]) -> Option> {
+ let (flags, _) = read_signature_info(path)?;
+ let name: &[u8] = match flags {
+ 1 => b"MD5",
+ 2 => b"SHA-1",
+ 3 => b"SHA-256",
+ 4 => b"SHA-512",
+ PHAR_OPENSSL_SIGNATURE_TYPE => b"OpenSSL",
+ _ => return None,
+ };
+ Some(name.to_vec())
+}
+
/// Computes PHP-compatible reflected CRC32 for a PHAR entry payload.
fn crc32(bytes: &[u8]) -> u32 {
let mut crc = 0xffff_ffffu32;
@@ -924,55 +2085,89 @@ fn crc32(bytes: &[u8]) -> u32 {
/// Parses a ZIP archive central directory and returns a store/deflate entry.
fn parse_zip_entry(data: &[u8], entry: &[u8]) -> Option> {
- parse_zip_entries(data)?
+ parse_zip_archive(data)?
+ .entries
.into_iter()
.find(|candidate| candidate.name == entry)
.map(|candidate| candidate.payload)
}
-/// Parses a ZIP archive central directory and returns every supported entry.
-fn parse_zip_entries(data: &[u8]) -> Option> {
+/// Parses a zip-based phar into entries plus its global metadata and stub.
+///
+/// Global metadata is read from the EOCD archive comment; the reserved
+/// `.phar/stub.php` entry becomes the stub and other `.phar/*` control entries are
+/// hidden from the entry listing.
+fn parse_zip_archive(data: &[u8]) -> Option {
let eocd = find_zip_eocd(data)?;
- let entry_count = le16(data, eocd + 10)? as usize;
- let central_dir_offset = le32(data, eocd + 16)? as usize;
- let mut entries = Vec::with_capacity(entry_count);
+ let (entry_count, central_dir_offset) = zip_eocd_info(data)?;
+ let comment_len = le16(data, eocd + 20)? as usize;
+ let comment_start = eocd.checked_add(22)?;
+ let metadata = data
+ .get(comment_start..comment_start.checked_add(comment_len)?)?
+ .to_vec();
+ let mut entries = Vec::with_capacity(entry_count.min(1 << 16));
+ let mut stub = Vec::new();
let mut p = central_dir_offset;
for _ in 0..entry_count {
if le32(data, p)? != 0x0201_4b50 {
return None;
}
- let flags = le16(data, p + 8)?;
- if flags & ZIP_FLAG_DATA_DESCRIPTOR != 0 {
- return None;
- }
+ // A data-descriptor entry (general-purpose flag bit 3) carries zeroed
+ // CRC/sizes in its local header and the real values in the central
+ // directory we are already reading here, so it needs no special handling
+ // beyond trusting these central-directory sizes.
let method = le16(data, p + 10)?;
- let compressed_size = le32(data, p + 20)? as usize;
- let uncompressed_size = le32(data, p + 24)? as usize;
+ let mut compressed_size = le32(data, p + 20)? as usize;
+ let mut uncompressed_size = le32(data, p + 24)? as usize;
let name_len = le16(data, p + 28)? as usize;
let extra_len = le16(data, p + 30)? as usize;
- let comment_len = le16(data, p + 32)? as usize;
- let local_offset = le32(data, p + 42)? as usize;
+ let entry_comment_len = le16(data, p + 32)? as usize;
+ let mut local_offset = le32(data, p + 42)? as usize;
let name_start = p + 46;
let name = data.get(name_start..name_start.checked_add(name_len)?)?;
+ // ZIP64: sentinel size/offset fields defer to the central record's extra.
+ apply_zip64_central_extra(
+ data,
+ name_start.checked_add(name_len)?,
+ extra_len,
+ &mut uncompressed_size,
+ &mut compressed_size,
+ &mut local_offset,
+ )?;
+ let (encrypted, check_byte) = zip_entry_crypto(data, p)?;
let payload = decode_zip_local_entry(
data,
local_offset,
method,
compressed_size,
uncompressed_size,
+ encrypted,
+ check_byte,
)?;
- let compression = zip_compression_from_method(method)?;
- entries.push(ArchiveEntry {
- name: name.to_vec(),
- payload,
- compression,
- });
- p = name_start
- .checked_add(name_len)?
- .checked_add(extra_len)?
- .checked_add(comment_len)?;
+ let comment_start = name_start.checked_add(name_len)?.checked_add(extra_len)?;
+ if name == PHAR_STUB_ENTRY {
+ stub = payload;
+ } else if !is_phar_control_entry(name) {
+ let compression = zip_compression_from_method(method)?;
+ // Per-file metadata rides in the central-directory file comment.
+ let entry_metadata = data
+ .get(comment_start..comment_start.checked_add(entry_comment_len)?)?
+ .to_vec();
+ entries.push(ArchiveEntry {
+ name: name.to_vec(),
+ payload,
+ compression,
+ metadata: entry_metadata,
+ });
+ }
+ p = comment_start.checked_add(entry_comment_len)?;
}
- Some(entries)
+ Some(Archive {
+ entries,
+ format: ArchiveFormat::Zip,
+ metadata,
+ stub,
+ })
}
/// Maps supported ZIP methods to the bridge's compression representation.
@@ -995,13 +2190,111 @@ fn find_zip_eocd(data: &[u8]) -> Option {
.find(|&i| data.get(i..i + 4) == Some(&[0x50, 0x4b, 0x05, 0x06]))
}
+/// Returns a ZIP archive's `(total entry count, central-directory offset)`,
+/// transparently following the ZIP64 EOCD record when the regular EOCD uses
+/// sentinels for an entry count, central-directory size, or offset that overflows
+/// its 32-/16-bit field.
+fn zip_eocd_info(data: &[u8]) -> Option<(usize, usize)> {
+ let eocd = find_zip_eocd(data)?;
+ let mut entry_count = le16(data, eocd + 10)? as usize;
+ let cd_size = le32(data, eocd + 12)?;
+ let mut cd_offset = le32(data, eocd + 16)? as usize;
+ let needs_zip64 = le16(data, eocd + 10)? == ZIP16_SENTINEL
+ || cd_size == ZIP32_SENTINEL
+ || cd_offset as u32 == ZIP32_SENTINEL;
+ if needs_zip64 {
+ if let Some((count, offset)) = read_zip64_eocd(data, eocd) {
+ entry_count = count;
+ cd_offset = offset;
+ }
+ }
+ Some((entry_count, cd_offset))
+}
+
+/// Reads the ZIP64 end-of-central-directory record (located via the 20-byte
+/// locator immediately before the regular EOCD), returning its 64-bit total entry
+/// count and central-directory offset.
+fn read_zip64_eocd(data: &[u8], eocd: usize) -> Option<(usize, usize)> {
+ let locator = eocd.checked_sub(20)?;
+ if le32(data, locator)? != 0x0706_4b50 {
+ return None;
+ }
+ let eocd64 = le64(data, locator + 8)? as usize;
+ if le32(data, eocd64)? != 0x0606_4b50 {
+ return None;
+ }
+ let total_entries = le64(data, eocd64 + 32)? as usize;
+ let cd_offset = le64(data, eocd64 + 48)? as usize;
+ Some((total_entries, cd_offset))
+}
+
+/// Overrides any sentinel (`0xFFFFFFFF`) compressed size, uncompressed size, or
+/// local-header offset of a ZIP central record with the 64-bit value from its
+/// ZIP64 extra field (tag 0x0001). The extra field lists only the overflowed
+/// fields, in the fixed order: original size, compressed size, header offset.
+fn apply_zip64_central_extra(
+ data: &[u8],
+ extra_start: usize,
+ extra_len: usize,
+ uncompressed: &mut usize,
+ compressed: &mut usize,
+ local_offset: &mut usize,
+) -> Option<()> {
+ let end = extra_start.checked_add(extra_len)?;
+ let mut p = extra_start;
+ while p.checked_add(4)? <= end {
+ let tag = le16(data, p)?;
+ let size = le16(data, p + 2)? as usize;
+ let body = p + 4;
+ if tag == ZIP64_EXTRA_TAG {
+ let mut q = body;
+ if *uncompressed as u32 == ZIP32_SENTINEL {
+ *uncompressed = le64(data, q)? as usize;
+ q += 8;
+ }
+ if *compressed as u32 == ZIP32_SENTINEL {
+ *compressed = le64(data, q)? as usize;
+ q += 8;
+ }
+ if *local_offset as u32 == ZIP32_SENTINEL {
+ *local_offset = le64(data, q)? as usize;
+ }
+ return Some(());
+ }
+ p = body.checked_add(size)?;
+ }
+ Some(())
+}
+
+/// Reads a ZIP central record's encryption state: whether the entry is ZipCrypto
+/// encrypted (flag bit 0) and the password check byte (the high byte of the mod
+/// time for data-descriptor entries, otherwise of the CRC).
+fn zip_entry_crypto(data: &[u8], central_off: usize) -> Option<(bool, u8)> {
+ let flags = le16(data, central_off + 8)?;
+ let encrypted = flags & ZIP_FLAG_ENCRYPTED != 0;
+ let check_byte = if flags & ZIP_FLAG_DATA_DESCRIPTOR != 0 {
+ (le16(data, central_off + 12)? >> 8) as u8
+ } else {
+ (le32(data, central_off + 16)? >> 24) as u8
+ };
+ Some((encrypted, check_byte))
+}
+
/// Decodes a ZIP local file payload using sizes from its central directory.
+///
+/// `encrypted` marks a traditional-PKWARE (ZipCrypto) entry; `check_byte` is the
+/// expected last byte of its 12-byte encryption header used to reject a wrong
+/// password. Encrypted entries require a password set via
+/// [`elephc_phar_set_zip_password`]; without one (or with the wrong one) they
+/// return `None`.
fn decode_zip_local_entry(
data: &[u8],
local_offset: usize,
method: u16,
compressed_size: usize,
uncompressed_size: usize,
+ encrypted: bool,
+ check_byte: u8,
) -> Option> {
if le32(data, local_offset)? != 0x0403_4b50 {
return None;
@@ -1013,11 +2306,21 @@ fn decode_zip_local_entry(
.checked_add(local_name_len)?
.checked_add(local_extra_len)?;
let stored = data.get(payload_start..payload_start.checked_add(compressed_size)?)?;
+ // Traditional ZipCrypto entries carry a 12-byte encryption header that the
+ // password-derived keystream removes before the (optionally deflated) payload.
+ let decrypted;
+ let body: &[u8] = if encrypted {
+ let password = current_zip_password()?;
+ decrypted = zipcrypto_decrypt(&password, stored, check_byte)?;
+ &decrypted
+ } else {
+ stored
+ };
match method {
- ZIP_METHOD_STORE => Some(stored.to_vec()),
+ ZIP_METHOD_STORE => Some(body.to_vec()),
ZIP_METHOD_DEFLATE => {
let mut out = Vec::with_capacity(uncompressed_size);
- let mut decoder = flate2::read::DeflateDecoder::new(stored);
+ let mut decoder = flate2::read::DeflateDecoder::new(body);
decoder.read_to_end(&mut out).ok()?;
(out.len() == uncompressed_size).then_some(out)
}
@@ -1025,38 +2328,225 @@ fn decode_zip_local_entry(
}
}
+/// Traditional-PKWARE (ZipCrypto) cipher state: three 32-bit keys advanced per
+/// plaintext byte. Drives both reading and writing of encrypted entries.
+/// Cryptographically weak — kept only for compatibility with legacy ZipCrypto
+/// archives, not as a real confidentiality mechanism.
+struct ZipCryptoKeys {
+ k0: u32,
+ k1: u32,
+ k2: u32,
+}
+
+impl ZipCryptoKeys {
+ /// Seeds the keys from the password (PKWARE's fixed initial constants).
+ fn new(password: &[u8]) -> Self {
+ let mut keys = Self {
+ k0: 0x1234_5678,
+ k1: 0x2345_6789,
+ k2: 0x3456_7890,
+ };
+ for &byte in password {
+ keys.update(byte);
+ }
+ keys
+ }
+
+ /// Advances the three keys with one plaintext byte.
+ fn update(&mut self, byte: u8) {
+ self.k0 = crc32_byte(self.k0, byte);
+ self.k1 = self.k1.wrapping_add(self.k0 & 0xff);
+ self.k1 = self.k1.wrapping_mul(134_775_813).wrapping_add(1);
+ self.k2 = crc32_byte(self.k2, (self.k1 >> 24) as u8);
+ }
+
+ /// Returns the next keystream byte (derived from `k2`).
+ fn keystream(&self) -> u8 {
+ let temp = (self.k2 | 2) & 0xffff;
+ ((temp.wrapping_mul(temp ^ 1)) >> 8) as u8
+ }
+
+ /// Decrypts one ciphertext byte and advances the keys with the plaintext.
+ fn decrypt(&mut self, cipher: u8) -> u8 {
+ let plain = cipher ^ self.keystream();
+ self.update(plain);
+ plain
+ }
+
+ /// Encrypts one plaintext byte and advances the keys with that plaintext.
+ fn encrypt(&mut self, plain: u8) -> u8 {
+ let cipher = plain ^ self.keystream();
+ self.update(plain);
+ cipher
+ }
+}
+
+/// One-byte CRC32 step (poly 0xEDB88320) used by the ZipCrypto key schedule.
+fn crc32_byte(crc: u32, byte: u8) -> u32 {
+ let mut t = (crc ^ byte as u32) & 0xff;
+ for _ in 0..8 {
+ t = if t & 1 != 0 { (t >> 1) ^ 0xedb8_8320 } else { t >> 1 };
+ }
+ (crc >> 8) ^ t
+}
+
+/// Decrypts a ZipCrypto entry payload (12-byte header + ciphertext) with
+/// `password`, returning the post-header plaintext. Returns `None` when the data
+/// is too short or the header's check byte rejects the password.
+fn zipcrypto_decrypt(password: &[u8], data: &[u8], check_byte: u8) -> Option> {
+ if data.len() < 12 {
+ return None;
+ }
+ let mut keys = ZipCryptoKeys::new(password);
+ let mut header_last = 0u8;
+ for &byte in &data[..12] {
+ header_last = keys.decrypt(byte);
+ }
+ if header_last != check_byte {
+ return None;
+ }
+ Some(data[12..].iter().map(|&c| keys.decrypt(c)).collect())
+}
+
+/// Encrypts `data` as a traditional-PKWARE (ZipCrypto) entry payload with
+/// `password`: prepends a 12-byte encryption header (11 pseudo-random filler bytes
+/// plus `check_byte` at index 11) and returns the encrypted `header ++ data`, which
+/// is 12 bytes longer than `data`. `check_byte` must be the byte the reader will
+/// verify (the CRC's high byte when no data descriptor is used). The first 11 header
+/// bytes are never read back, so their randomness affects only resistance to attack,
+/// not round-trip correctness.
+fn zipcrypto_encrypt(password: &[u8], data: &[u8], check_byte: u8) -> Vec {
+ let mut header = [0u8; 12];
+ header[..11].copy_from_slice(&zipcrypto_header_filler());
+ header[11] = check_byte;
+ let mut keys = ZipCryptoKeys::new(password);
+ let mut out = Vec::with_capacity(data.len() + 12);
+ for &plain in header.iter().chain(data) {
+ out.push(keys.encrypt(plain));
+ }
+ out
+}
+
+/// Produces 11 non-constant filler bytes for a ZipCrypto encryption header, mixing
+/// a per-call atomic nonce with the current time through an xorshift64* step.
+/// Dependency-free; only needs to avoid an all-constant header, since the bytes are
+/// discarded on read.
+fn zipcrypto_header_filler() -> [u8; 11] {
+ static NONCE: AtomicU64 = AtomicU64::new(0);
+ let now = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .map(|d| d.as_nanos() as u64)
+ .unwrap_or(0);
+ let mut state = now ^ NONCE.fetch_add(1, Ordering::Relaxed).wrapping_mul(0x9E37_79B9_7F4A_7C15);
+ let mut filler = [0u8; 11];
+ for byte in filler.iter_mut() {
+ // xorshift64* advance, then take a high byte of the scrambled state.
+ state ^= state >> 12;
+ state ^= state << 25;
+ state ^= state >> 27;
+ *byte = (state.wrapping_mul(0x2545_F491_4F6C_DD1D) >> 33) as u8;
+ }
+ filler
+}
+
+/// Returns the password currently set for reading and writing encrypted ZIP
+/// entries, if any.
+fn current_zip_password() -> Option> {
+ ZIP_PASSWORD.with(|slot| slot.borrow().clone())
+}
+
+/// Sets (or, when empty, clears) the password used to read and write encrypted
+/// ZIP entries.
+fn set_zip_password(password: &[u8]) {
+ ZIP_PASSWORD.with(|slot| {
+ *slot.borrow_mut() = if password.is_empty() {
+ None
+ } else {
+ Some(password.to_vec())
+ };
+ });
+}
+
/// Parses a POSIX tar archive and returns a regular-file entry.
fn parse_tar_entry(data: &[u8], entry: &[u8]) -> Option> {
- parse_tar_entries(data)?
+ parse_tar_archive(data)?
+ .entries
.into_iter()
.find(|candidate| candidate.name == entry)
.map(|candidate| candidate.payload)
}
-/// Parses a POSIX tar archive and returns regular-file entries.
-fn parse_tar_entries(data: &[u8]) -> Option> {
+/// Parses a tar-based phar into regular entries plus its global metadata and stub.
+///
+/// The reserved `.phar/stub.php` and `.phar/.metadata.bin` files become the stub and
+/// metadata; any other `.phar/*` control file is hidden from the entry listing.
+fn parse_tar_archive(data: &[u8]) -> Option {
let mut p = 0usize;
let mut entries = Vec::new();
+ let mut metadata = Vec::new();
+ let mut stub = Vec::new();
+ // Per-file metadata side entries may appear after their target entry; collect
+ // them and attach once the full entry list is known.
+ let mut file_metadata: Vec<(Vec, Vec)> = Vec::new();
+ let mut first_header = true;
while p.checked_add(512)? <= data.len() {
let header = &data[p..p + 512];
if header.iter().all(|&b| b == 0) {
- return Some(entries);
+ break;
}
+ // Require the POSIX ustar magic on the first record so non-tar inputs
+ // (e.g. native PHARs whose stub contains `__HALT_COMPILER();`) are rejected
+ // rather than mis-parsed as tar.
+ if first_header && header.get(257..262) != Some(b"ustar") {
+ return None;
+ }
+ first_header = false;
let size = parse_tar_octal(&header[124..136])?;
let payload_start = p.checked_add(512)?;
let payload_end = payload_start.checked_add(size)?;
let payload = data.get(payload_start..payload_end)?;
let typeflag = header[156];
if typeflag == 0 || typeflag == b'0' {
- entries.push(ArchiveEntry {
- name: tar_entry_name(header)?,
- payload: payload.to_vec(),
- compression: PharCompression::None,
- });
+ let name = tar_entry_name(header)?;
+ if name == PHAR_STUB_ENTRY {
+ stub = payload.to_vec();
+ } else if name == PHAR_METADATA_ENTRY {
+ metadata = payload.to_vec();
+ } else if let Some(target) = tar_file_metadata_target(&name) {
+ file_metadata.push((target, payload.to_vec()));
+ } else if !is_phar_control_entry(&name) {
+ entries.push(ArchiveEntry {
+ name,
+ payload: payload.to_vec(),
+ compression: PharCompression::None,
+ metadata: Vec::new(),
+ });
+ }
}
p = payload_start.checked_add(round_up_to_512(size)?)?;
}
- Some(entries)
+ for (target, meta) in file_metadata {
+ if let Some(entry) = entries.iter_mut().find(|e| e.name == target) {
+ entry.metadata = meta;
+ }
+ }
+ Some(Archive {
+ entries,
+ format: ArchiveFormat::Tar,
+ metadata,
+ stub,
+ })
+}
+
+/// If `name` is a `.phar/.metadata//.metadata.bin` side entry, returns the
+/// target entry path ``; otherwise returns `None`.
+fn tar_file_metadata_target(name: &[u8]) -> Option> {
+ let rest = name.strip_prefix(PHAR_FILE_METADATA_PREFIX)?;
+ let inner = rest.strip_suffix(PHAR_FILE_METADATA_SUFFIX)?;
+ if inner.is_empty() {
+ return None;
+ }
+ Some(inner.to_vec())
}
/// Builds the full tar path from the `prefix` and `name` header fields.
@@ -1121,6 +2611,12 @@ fn le32(data: &[u8], off: usize) -> Option {
Some(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
+/// Reads a little-endian `u64` from `data` (used for ZIP64 fields).
+fn le64(data: &[u8], off: usize) -> Option {
+ let b = data.get(off..off + 8)?;
+ Some(u64::from_le_bytes(b.try_into().ok()?))
+}
+
/// Returns the offset of `needle` in `hay`.
fn find_subslice(hay: &[u8], needle: &[u8]) -> Option {
if needle.is_empty() || hay.len() < needle.len() {
@@ -1196,136 +2692,557 @@ mod tests {
.map(|entry| entry.payload.as_slice())
}
- /// Builds the serialized entry-name format returned by `entry_names_bytes`.
- fn serialized_names(names: &[&str]) -> Vec {
- let mut out = Vec::new();
- for name in names {
- out.extend_from_slice(&(name.len() as u64).to_le_bytes());
- out.extend_from_slice(name.as_bytes());
- }
- out
+ /// Builds the serialized entry-name format returned by `entry_names_bytes`.
+ fn serialized_names(names: &[&str]) -> Vec {
+ let mut out = Vec::new();
+ for name in names {
+ out.extend_from_slice(&(name.len() as u64).to_le_bytes());
+ out.extend_from_slice(name.as_bytes());
+ }
+ out
+ }
+
+ /// Builds a small tar archive with regular-file entries.
+ fn build_tar(entries: &[(&str, &[u8])]) -> Vec {
+ let mut out = Vec::new();
+ for (name, content) in entries {
+ let mut header = [0u8; 512];
+ header[..name.len()].copy_from_slice(name.as_bytes());
+ let size = format!("{:011o}\0", content.len());
+ header[124..124 + size.len()].copy_from_slice(size.as_bytes());
+ header[156] = b'0';
+ header[257..263].copy_from_slice(b"ustar\0");
+ header[263..265].copy_from_slice(b"00");
+ for byte in &mut header[148..156] {
+ *byte = b' ';
+ }
+ let checksum: u32 = header.iter().map(|&b| b as u32).sum();
+ let checksum = format!("{:06o}\0 ", checksum);
+ header[148..156].copy_from_slice(checksum.as_bytes());
+ out.extend_from_slice(&header);
+ out.extend_from_slice(content);
+ out.resize(out.len() + round_up_to_512(content.len()).unwrap() - content.len(), 0);
+ }
+ out.extend_from_slice(&[0u8; 1024]);
+ out
+ }
+
+ /// Builds a ZIP archive with central-directory records.
+ fn build_zip(entries: &[(&str, &[u8], bool)]) -> Vec {
+ let mut out = Vec::new();
+ let mut central = Vec::new();
+ for (name, content, deflate) in entries {
+ let local_offset = out.len() as u32;
+ let stored = if *deflate {
+ let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
+ encoder.write_all(content).unwrap();
+ encoder.finish().unwrap()
+ } else {
+ content.to_vec()
+ };
+ let method = if *deflate { ZIP_METHOD_DEFLATE } else { ZIP_METHOD_STORE };
+ out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
+ out.extend_from_slice(&20u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&method.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&(stored.len() as u32).to_le_bytes());
+ out.extend_from_slice(&(content.len() as u32).to_le_bytes());
+ out.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(name.as_bytes());
+ out.extend_from_slice(&stored);
+
+ central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&method.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&(stored.len() as u32).to_le_bytes());
+ central.extend_from_slice(&(content.len() as u32).to_le_bytes());
+ central.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&local_offset.to_le_bytes());
+ central.extend_from_slice(name.as_bytes());
+ }
+ let central_offset = out.len() as u32;
+ out.extend_from_slice(¢ral);
+ out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&(entries.len() as u16).to_le_bytes());
+ out.extend_from_slice(&(entries.len() as u16).to_le_bytes());
+ out.extend_from_slice(&(central.len() as u32).to_le_bytes());
+ out.extend_from_slice(¢ral_offset.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out
+ }
+
+ /// Verifies native PHAR manifest extraction.
+ #[test]
+ fn extracts_native_phar_entry() {
+ let archive = build_native_phar(&[("a.txt", b"alpha"), ("dir/b.txt", b"bravo")]);
+ assert_eq!(
+ extract_entry_bytes(&archive, b"dir/b.txt").as_deref(),
+ Some(&b"bravo"[..])
+ );
+ }
+
+ /// Verifies tar container extraction.
+ #[test]
+ fn extracts_tar_entry() {
+ let archive = build_tar(&[("a.txt", b"alpha"), ("dir/b.txt", b"bravo")]);
+ assert_eq!(
+ extract_entry_bytes(&archive, b"dir/b.txt").as_deref(),
+ Some(&b"bravo"[..])
+ );
+ }
+
+ /// Verifies ZIP store and deflate extraction.
+ #[test]
+ fn extracts_zip_entries() {
+ let archive = build_zip(&[
+ ("plain.txt", b"stored", false),
+ ("deflated.txt", b"deflated payload", true),
+ ]);
+ assert_eq!(
+ extract_entry_bytes(&archive, b"plain.txt").as_deref(),
+ Some(&b"stored"[..])
+ );
+ assert_eq!(
+ extract_entry_bytes(&archive, b"deflated.txt").as_deref(),
+ Some(&b"deflated payload"[..])
+ );
+ }
+
+ /// Builds a single-entry ZIP whose local header uses a streaming data
+ /// descriptor (general-purpose flag bit 3): the local CRC/size fields are
+ /// zero, the real values live in a trailing data descriptor, and the central
+ /// directory carries the authoritative sizes.
+ fn build_zip_with_data_descriptor(name: &str, content: &[u8], deflate: bool) -> Vec {
+ let stored = if deflate {
+ let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
+ encoder.write_all(content).unwrap();
+ encoder.finish().unwrap()
+ } else {
+ content.to_vec()
+ };
+ let method = if deflate { ZIP_METHOD_DEFLATE } else { ZIP_METHOD_STORE };
+ let crc = crc32(content);
+ let comp = stored.len() as u32;
+ let uncomp = content.len() as u32;
+ let mut out = Vec::new();
+ // -- local file header: zeroed sizes, data-descriptor flag set --
+ out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
+ out.extend_from_slice(&20u16.to_le_bytes());
+ out.extend_from_slice(&0x0008u16.to_le_bytes());
+ out.extend_from_slice(&method.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(name.as_bytes());
+ out.extend_from_slice(&stored);
+ // -- trailing data descriptor carrying the real crc/sizes --
+ out.extend_from_slice(&0x0807_4b50u32.to_le_bytes());
+ out.extend_from_slice(&crc.to_le_bytes());
+ out.extend_from_slice(&comp.to_le_bytes());
+ out.extend_from_slice(&uncomp.to_le_bytes());
+ // -- central directory with authoritative sizes --
+ let central_offset = out.len() as u32;
+ let mut central = Vec::new();
+ central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&0x0008u16.to_le_bytes());
+ central.extend_from_slice(&method.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&crc.to_le_bytes());
+ central.extend_from_slice(&comp.to_le_bytes());
+ central.extend_from_slice(&uncomp.to_le_bytes());
+ central.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(name.as_bytes());
+ out.extend_from_slice(¢ral);
+ // -- end of central directory --
+ out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&1u16.to_le_bytes());
+ out.extend_from_slice(&1u16.to_le_bytes());
+ out.extend_from_slice(&(central.len() as u32).to_le_bytes());
+ out.extend_from_slice(¢ral_offset.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out
+ }
+
+ /// Verifies a ZIP entry written with a streaming data descriptor (flag bit 3)
+ /// is read via the authoritative central-directory sizes instead of rejected,
+ /// for both stored and deflated payloads.
+ #[test]
+ fn extracts_zip_entry_with_data_descriptor() {
+ let stored = build_zip_with_data_descriptor("stream.txt", b"streamed payload", false);
+ assert_eq!(
+ extract_entry_bytes(&stored, b"stream.txt").as_deref(),
+ Some(&b"streamed payload"[..])
+ );
+ let deflated =
+ build_zip_with_data_descriptor("stream.txt", b"streamed deflated payload", true);
+ assert_eq!(
+ extract_entry_bytes(&deflated, b"stream.txt").as_deref(),
+ Some(&b"streamed deflated payload"[..])
+ );
+ }
+
+ /// The ZIP64 extra-field builders emit the tag, length, and only the requested
+ /// 64-bit fields in APPNOTE order.
+ #[test]
+ fn builds_zip64_extra_fields() {
+ // Local extra always carries both sizes (16-byte body).
+ let local = zip64_local_extra(0x1_0000_0001, 0x2_0000_0002);
+ assert_eq!(le16(&local, 0), Some(ZIP64_EXTRA_TAG));
+ assert_eq!(le16(&local, 2), Some(16));
+ assert_eq!(le64(&local, 4), Some(0x1_0000_0001));
+ assert_eq!(le64(&local, 12), Some(0x2_0000_0002));
+ // Central extra carries only the overflowed fields, in order.
+ let central = zip64_central_extra(Some(7), None, Some(9));
+ assert_eq!(le16(¢ral, 2), Some(16));
+ assert_eq!(le64(¢ral, 4), Some(7));
+ assert_eq!(le64(¢ral, 12), Some(9));
+ assert!(zip64_central_extra(None, None, None).len() == 4);
+ }
+
+ /// Builds a single-entry ZIP that uses every ZIP64 read path: a central record
+ /// whose sizes and header offset are 0xFFFFFFFF sentinels resolved by a ZIP64
+ /// extra field, plus a ZIP64 EOCD record + locator behind a sentinel EOCD.
+ fn build_zip64_sentinel_fixture(name: &str, content: &[u8]) -> Vec {
+ let mut out = Vec::new();
+ let local_offset = out.len() as u32;
+ let crc = crc32(content);
+ let len = content.len() as u32;
+ // -- local header with real sizes (central drives sizes anyway) --
+ out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
+ out.extend_from_slice(&45u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&ZIP_METHOD_STORE.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&crc.to_le_bytes());
+ out.extend_from_slice(&len.to_le_bytes());
+ out.extend_from_slice(&len.to_le_bytes());
+ out.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(name.as_bytes());
+ out.extend_from_slice(content);
+ // -- central record: all three size/offset fields are sentinels --
+ let central_offset = out.len();
+ let extra = zip64_central_extra(Some(len as u64), Some(len as u64), Some(local_offset as u64));
+ let mut central = Vec::new();
+ central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
+ central.extend_from_slice(&45u16.to_le_bytes());
+ central.extend_from_slice(&45u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&ZIP_METHOD_STORE.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&crc.to_le_bytes());
+ central.extend_from_slice(&ZIP32_SENTINEL.to_le_bytes());
+ central.extend_from_slice(&ZIP32_SENTINEL.to_le_bytes());
+ central.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ central.extend_from_slice(&(extra.len() as u16).to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&ZIP32_SENTINEL.to_le_bytes());
+ central.extend_from_slice(name.as_bytes());
+ central.extend_from_slice(&extra);
+ let central_len = central.len();
+ out.extend_from_slice(¢ral);
+ // -- ZIP64 EOCD record + locator --
+ let eocd64_offset = out.len() as u64;
+ out.extend_from_slice(&0x0606_4b50u32.to_le_bytes());
+ out.extend_from_slice(&44u64.to_le_bytes());
+ out.extend_from_slice(&45u16.to_le_bytes());
+ out.extend_from_slice(&45u16.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&1u64.to_le_bytes());
+ out.extend_from_slice(&1u64.to_le_bytes());
+ out.extend_from_slice(&(central_len as u64).to_le_bytes());
+ out.extend_from_slice(&(central_offset as u64).to_le_bytes());
+ out.extend_from_slice(&0x0706_4b50u32.to_le_bytes());
+ out.extend_from_slice(&0u32.to_le_bytes());
+ out.extend_from_slice(&eocd64_offset.to_le_bytes());
+ out.extend_from_slice(&1u32.to_le_bytes());
+ // -- regular EOCD with count/offset sentinels --
+ out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&ZIP16_SENTINEL.to_le_bytes());
+ out.extend_from_slice(&ZIP16_SENTINEL.to_le_bytes());
+ out.extend_from_slice(&ZIP32_SENTINEL.to_le_bytes());
+ out.extend_from_slice(&ZIP32_SENTINEL.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out
+ }
+
+ /// A ZIP64 archive (sentinel central fields + extra field + EOCD64/locator) is
+ /// read by resolving the 64-bit values, not rejected.
+ #[test]
+ fn reads_zip64_archive_with_sentinels() {
+ let archive = build_zip64_sentinel_fixture("big.txt", b"zip64 payload body");
+ assert_eq!(
+ extract_entry_bytes(&archive, b"big.txt").as_deref(),
+ Some(&b"zip64 payload body"[..])
+ );
}
- /// Builds a small tar archive with regular-file entries.
- fn build_tar(entries: &[(&str, &[u8])]) -> Vec {
- let mut out = Vec::new();
- for (name, content) in entries {
- let mut header = [0u8; 512];
- header[..name.len()].copy_from_slice(name.as_bytes());
- let size = format!("{:011o}\0", content.len());
- header[124..124 + size.len()].copy_from_slice(size.as_bytes());
- header[156] = b'0';
- header[257..263].copy_from_slice(b"ustar\0");
- header[263..265].copy_from_slice(b"00");
- for byte in &mut header[148..156] {
- *byte = b' ';
- }
- let checksum: u32 = header.iter().map(|&b| b as u32).sum();
- let checksum = format!("{:06o}\0 ", checksum);
- header[148..156].copy_from_slice(checksum.as_bytes());
- out.extend_from_slice(&header);
- out.extend_from_slice(content);
- out.resize(out.len() + round_up_to_512(content.len()).unwrap() - content.len(), 0);
+ /// Writing more than 65535 entries triggers ZIP64 output (EOCD64 record +
+ /// locator), and the bridge reads its own ZIP64 archive back. Set
+ /// `ELEPHC_KEEP_ZIP64=` to also dump the archive for an external check.
+ #[test]
+ fn writes_and_reads_zip64_many_entries() {
+ let count = 70_000usize;
+ let entries: Vec = (0..count)
+ .map(|i| ArchiveEntry {
+ name: format!("f{i}.txt").into_bytes(),
+ payload: b"x".to_vec(),
+ compression: PharCompression::None,
+ metadata: Vec::new(),
+ })
+ .collect();
+ let archive = build_zip_archive(&entries, &[], &[]).unwrap();
+ // The ZIP64 EOCD record and locator must be present.
+ assert!(find_subslice(&archive, &0x0606_4b50u32.to_le_bytes()).is_some());
+ assert!(find_subslice(&archive, &0x0706_4b50u32.to_le_bytes()).is_some());
+ // The regular EOCD carries the count sentinel.
+ let eocd = find_zip_eocd(&archive).unwrap();
+ assert_eq!(le16(&archive, eocd + 10), Some(ZIP16_SENTINEL));
+ // Round-trip: the bridge reads back all entries and a sampled payload.
+ let parsed = parse_zip_archive(&archive).unwrap();
+ assert_eq!(parsed.entries.len(), count);
+ assert_eq!(
+ extract_entry_bytes(&archive, b"f69999.txt").as_deref(),
+ Some(&b"x"[..])
+ );
+ if let Some(path) = std::env::var_os("ELEPHC_KEEP_ZIP64") {
+ std::fs::write(path, &archive).unwrap();
}
- out.extend_from_slice(&[0u8; 1024]);
- out
}
- /// Builds a ZIP archive with central-directory records.
- fn build_zip(entries: &[(&str, &[u8], bool)]) -> Vec {
- let mut out = Vec::new();
- let mut central = Vec::new();
- for (name, content, deflate) in entries {
- let local_offset = out.len() as u32;
- let stored = if *deflate {
- let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
- encoder.write_all(content).unwrap();
- encoder.finish().unwrap()
- } else {
- content.to_vec()
- };
- let method = if *deflate { ZIP_METHOD_DEFLATE } else { ZIP_METHOD_STORE };
- out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
- out.extend_from_slice(&20u16.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&method.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&0u32.to_le_bytes());
- out.extend_from_slice(&(stored.len() as u32).to_le_bytes());
- out.extend_from_slice(&(content.len() as u32).to_le_bytes());
- out.extend_from_slice(&(name.len() as u16).to_le_bytes());
- out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(name.as_bytes());
- out.extend_from_slice(&stored);
-
- central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
- central.extend_from_slice(&20u16.to_le_bytes());
- central.extend_from_slice(&20u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&method.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u32.to_le_bytes());
- central.extend_from_slice(&(stored.len() as u32).to_le_bytes());
- central.extend_from_slice(&(content.len() as u32).to_le_bytes());
- central.extend_from_slice(&(name.len() as u16).to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u16.to_le_bytes());
- central.extend_from_slice(&0u32.to_le_bytes());
- central.extend_from_slice(&local_offset.to_le_bytes());
- central.extend_from_slice(name.as_bytes());
+ /// Returns the general-purpose bit-flag field of the first local file header
+ /// whose name matches `name`, found by scanning for the local-header signature.
+ /// Used to assert that the writer set (or cleared) the ZipCrypto "encrypted"
+ /// flag bit on a given entry.
+ fn zip_local_flag(archive: &[u8], name: &[u8]) -> Option {
+ let sig = 0x0403_4b50u32.to_le_bytes();
+ let mut i = 0;
+ while i + 30 <= archive.len() {
+ if archive[i..i + 4] == sig {
+ let flag = u16::from_le_bytes([archive[i + 6], archive[i + 7]]);
+ let name_len = u16::from_le_bytes([archive[i + 26], archive[i + 27]]) as usize;
+ let name_start = i + 30;
+ if archive.get(name_start..name_start + name_len) == Some(name) {
+ return Some(flag);
+ }
+ }
+ i += 1;
}
+ None
+ }
+
+ /// Builds a single-entry ZIP whose stored entry is traditional-PKWARE
+ /// (ZipCrypto) encrypted with `password`: a 12-byte encryption header (last
+ /// byte = the CRC's high byte check) plus the encrypted payload.
+ fn build_zipcrypto_zip(name: &str, content: &[u8], password: &[u8]) -> Vec {
+ let crc = crc32(content);
+ // Reuse the production encryptor so the test fixture and the writer share a
+ // single cipher direction (check byte = the CRC's high byte, no descriptor).
+ let enc = zipcrypto_encrypt(password, content, (crc >> 24) as u8);
+ let csz = enc.len() as u32;
+ let usz = content.len() as u32;
+ let mut out = Vec::new();
+ // -- local header with the encrypted flag set --
+ out.extend_from_slice(&0x0403_4b50u32.to_le_bytes());
+ out.extend_from_slice(&20u16.to_le_bytes());
+ out.extend_from_slice(&ZIP_FLAG_ENCRYPTED.to_le_bytes());
+ out.extend_from_slice(&ZIP_METHOD_STORE.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(&crc.to_le_bytes());
+ out.extend_from_slice(&csz.to_le_bytes());
+ out.extend_from_slice(&usz.to_le_bytes());
+ out.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ out.extend_from_slice(&0u16.to_le_bytes());
+ out.extend_from_slice(name.as_bytes());
+ out.extend_from_slice(&enc);
+ // -- central record --
let central_offset = out.len() as u32;
+ let mut central = Vec::new();
+ central.extend_from_slice(&0x0201_4b50u32.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&20u16.to_le_bytes());
+ central.extend_from_slice(&ZIP_FLAG_ENCRYPTED.to_le_bytes());
+ central.extend_from_slice(&ZIP_METHOD_STORE.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&crc.to_le_bytes());
+ central.extend_from_slice(&csz.to_le_bytes());
+ central.extend_from_slice(&usz.to_le_bytes());
+ central.extend_from_slice(&(name.len() as u16).to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u16.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes());
+ central.extend_from_slice(&0u32.to_le_bytes()); // local header offset
+ central.extend_from_slice(name.as_bytes());
out.extend_from_slice(¢ral);
+ // -- end of central directory --
out.extend_from_slice(&0x0605_4b50u32.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
- out.extend_from_slice(&(entries.len() as u16).to_le_bytes());
- out.extend_from_slice(&(entries.len() as u16).to_le_bytes());
+ out.extend_from_slice(&1u16.to_le_bytes());
+ out.extend_from_slice(&1u16.to_le_bytes());
out.extend_from_slice(&(central.len() as u32).to_le_bytes());
out.extend_from_slice(¢ral_offset.to_le_bytes());
out.extend_from_slice(&0u16.to_le_bytes());
out
}
- /// Verifies native PHAR manifest extraction.
+ /// A ZipCrypto-encrypted ZIP entry decrypts only with the correct password set
+ /// via `set_zip_password`; a missing or wrong password yields no payload.
#[test]
- fn extracts_native_phar_entry() {
- let archive = build_native_phar(&[("a.txt", b"alpha"), ("dir/b.txt", b"bravo")]);
+ fn reads_zipcrypto_encrypted_entry() {
+ let content = b"secret zipcrypto payload\n";
+ let archive = build_zipcrypto_zip("zc.txt", content, b"hunter2");
+ // No password set: the encrypted entry is unreadable.
+ set_zip_password(b"");
+ assert_eq!(extract_entry_bytes(&archive, b"zc.txt"), None);
+ // Wrong password is rejected by the header check byte.
+ set_zip_password(b"wrong-password");
+ assert_eq!(extract_entry_bytes(&archive, b"zc.txt"), None);
+ // Correct password decrypts the entry.
+ set_zip_password(b"hunter2");
assert_eq!(
- extract_entry_bytes(&archive, b"dir/b.txt").as_deref(),
- Some(&b"bravo"[..])
+ extract_entry_bytes(&archive, b"zc.txt").as_deref(),
+ Some(&content[..])
);
+ set_zip_password(b"");
}
- /// Verifies tar container extraction.
+ /// With a zip password set, `build_zip_archive` encrypts every file entry — the
+ /// stub included — so entries read back only with the correct password; a wrong
+ /// or cleared password fails, and an archive built with no password stays plain.
#[test]
- fn extracts_tar_entry() {
- let archive = build_tar(&[("a.txt", b"alpha"), ("dir/b.txt", b"bravo")]);
+ fn writes_then_reads_zipcrypto_entry() {
+ let stored = b"plain stored payload".to_vec();
+ // Repetitive bytes so the deflate path actually compresses.
+ let deflated = b"compress me ".repeat(64);
+ let entries = vec![
+ ArchiveEntry {
+ name: b"a.txt".to_vec(),
+ payload: stored.clone(),
+ compression: PharCompression::None,
+ metadata: Vec::new(),
+ },
+ ArchiveEntry {
+ name: b"b.txt".to_vec(),
+ payload: deflated.clone(),
+ compression: PharCompression::Gzip,
+ metadata: Vec::new(),
+ },
+ ];
+ let stub = PHAR_DEFAULT_STUB.to_vec();
+
+ set_zip_password(b"hunter2");
+ let archive = build_zip_archive(&entries, &[], &stub).unwrap();
+
+ // The correct password decrypts both the stored and the deflated entry.
assert_eq!(
- extract_entry_bytes(&archive, b"dir/b.txt").as_deref(),
- Some(&b"bravo"[..])
+ extract_entry_bytes(&archive, b"a.txt").as_deref(),
+ Some(&stored[..])
);
+ assert_eq!(
+ extract_entry_bytes(&archive, b"b.txt").as_deref(),
+ Some(&deflated[..])
+ );
+
+ // The encrypted flag is set on a regular entry and on the stub (chosen scope).
+ assert_eq!(zip_local_flag(&archive, b"a.txt"), Some(ZIP_FLAG_ENCRYPTED));
+ assert_eq!(zip_local_flag(&archive, PHAR_STUB_ENTRY), Some(ZIP_FLAG_ENCRYPTED));
+
+ // A wrong then cleared password cannot decrypt the entry.
+ set_zip_password(b"nope");
+ assert_eq!(extract_entry_bytes(&archive, b"a.txt"), None);
+ set_zip_password(b"");
+ assert_eq!(extract_entry_bytes(&archive, b"a.txt"), None);
+
+ // Built with no password the archive is plain and reads with none set.
+ let plain = build_zip_archive(&entries, &[], &stub).unwrap();
+ assert_eq!(
+ extract_entry_bytes(&plain, b"a.txt").as_deref(),
+ Some(&stored[..])
+ );
+ assert_eq!(zip_local_flag(&plain, b"a.txt"), Some(0));
}
- /// Verifies ZIP store and deflate extraction.
+ /// Signing a zip phar whose entries are encrypted still produces a readable
+ /// `.phar/signature.bin`: the signed range covers the encrypted bytes, the entry
+ /// decrypts with the password, the signature reports SHA-256, and the signature
+ /// entry itself stays in the clear (no encrypted flag).
#[test]
- fn extracts_zip_entries() {
- let archive = build_zip(&[
- ("plain.txt", b"stored", false),
- ("deflated.txt", b"deflated payload", true),
- ]);
+ fn signed_encrypted_zip_still_verifies() {
+ let path =
+ std::env::temp_dir().join(format!("elephc_phar_encsig_{}.zip", std::process::id()));
+ let pb = path.to_string_lossy();
+
+ set_zip_password(b"hunter2");
+ // Write an encrypted entry, then SHA-256 (algo 3) sign the archive.
assert_eq!(
- extract_entry_bytes(&archive, b"plain.txt").as_deref(),
- Some(&b"stored"[..])
+ put_entry_bytes(pb.as_bytes(), b"doc.txt", b"top secret\n"),
+ Some(11)
);
+ assert_eq!(sign_archive_hash(pb.as_bytes(), 3), Some(()));
+ let data = std::fs::read(&path).unwrap();
+
+ // The entry still decrypts; the signature reports SHA-256 with a 32-byte digest.
assert_eq!(
- extract_entry_bytes(&archive, b"deflated.txt").as_deref(),
- Some(&b"deflated payload"[..])
+ extract_entry_bytes(&data, b"doc.txt").as_deref(),
+ Some(&b"top secret\n"[..])
);
+ assert_eq!(signature_type_name(pb.as_bytes()).as_deref(), Some(&b"SHA-256"[..]));
+ let (flag, digest) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(flag, 3);
+ assert_eq!(digest.len(), 32);
+
+ // The entry is encrypted but the signature entry stays in the clear.
+ assert_eq!(zip_local_flag(&data, b"doc.txt"), Some(ZIP_FLAG_ENCRYPTED));
+ assert_eq!(zip_local_flag(&data, PHAR_SIGNATURE_ENTRY), Some(0));
+
+ // Without the password the encrypted entry is unreadable.
+ set_zip_password(b"");
+ assert_eq!(extract_entry_bytes(&data, b"doc.txt"), None);
+ std::fs::remove_file(&path).ok();
}
/// Verifies entry-name listing across supported archive families.
@@ -1431,7 +3348,7 @@ mod tests {
);
let archive = std::fs::read(&path).unwrap();
std::fs::remove_file(&path).ok();
- let entries = parse_native_phar_entries(&archive).unwrap();
+ let entries = parse_native_phar_archive(&archive).unwrap().entries;
assert_eq!(entries[0].compression, PharCompression::Gzip);
assert_eq!(entries[0].payload, b"gzip updated payload");
}
@@ -1460,7 +3377,7 @@ mod tests {
);
let archive = std::fs::read(&path).unwrap();
std::fs::remove_file(&path).ok();
- let entries = parse_native_phar_entries(&archive).unwrap();
+ let entries = parse_native_phar_archive(&archive).unwrap().entries;
assert_eq!(entries[0].compression, PharCompression::Bzip2);
assert_eq!(entries[0].payload, b"bzip2 updated payload");
}
@@ -1498,7 +3415,7 @@ mod tests {
assert_eq!(elephc_phar_stream_finalize(fd_two), 1);
let archive = std::fs::read(&path).unwrap();
std::fs::remove_file(&path).ok();
- let entries = parse_native_phar_entries(&archive).unwrap();
+ let entries = parse_native_phar_archive(&archive).unwrap().entries;
assert_eq!(entry_payload(&entries, b"one.txt"), Some(b"alpha".as_slice()));
assert_eq!(entry_payload(&entries, b"two.txt"), Some(b"bravo".as_slice()));
}
@@ -1664,7 +3581,7 @@ mod tests {
);
assert_eq!(set_archive_compression(path_bytes.as_bytes(), 4_096), Some(()));
let gzip_archive = std::fs::read(&path).unwrap();
- let gzip_entries = parse_native_phar_entries(&gzip_archive).unwrap();
+ let gzip_entries = parse_native_phar_archive(&gzip_archive).unwrap().entries;
assert!(gzip_entries
.iter()
.all(|entry| entry.compression == PharCompression::Gzip));
@@ -1676,7 +3593,7 @@ mod tests {
assert_eq!(set_archive_compression(path_bytes.as_bytes(), 0), Some(()));
let plain_archive = std::fs::read(&path).unwrap();
std::fs::remove_file(&path).ok();
- let plain_entries = parse_native_phar_entries(&plain_archive).unwrap();
+ let plain_entries = parse_native_phar_archive(&plain_archive).unwrap().entries;
assert!(plain_entries
.iter()
.all(|entry| entry.compression == PharCompression::None));
@@ -1705,7 +3622,7 @@ mod tests {
let path_bytes = path.to_string_lossy();
assert_eq!(set_archive_compression(path_bytes.as_bytes(), 4_096), Some(()));
let deflated_archive = std::fs::read(&path).unwrap();
- let deflated_entries = parse_zip_entries(&deflated_archive).unwrap();
+ let deflated_entries = parse_zip_archive(&deflated_archive).unwrap().entries;
assert!(deflated_entries
.iter()
.all(|entry| entry.compression == PharCompression::Gzip));
@@ -1717,7 +3634,7 @@ mod tests {
assert_eq!(set_archive_compression(path_bytes.as_bytes(), 0), Some(()));
let stored_archive = std::fs::read(&path).unwrap();
std::fs::remove_file(&path).ok();
- let stored_entries = parse_zip_entries(&stored_archive).unwrap();
+ let stored_entries = parse_zip_archive(&stored_archive).unwrap().entries;
assert!(stored_entries
.iter()
.all(|entry| entry.compression == PharCompression::None));
@@ -1787,4 +3704,390 @@ mod tests {
Some(&b"bravo"[..])
);
}
+
+ /// Stub used by the metadata/stub round-trip tests; ends with `?>\r\n` so the
+ /// native-PHAR `__HALT_COMPILER();` boundary scan round-trips it exactly.
+ const ROUND_TRIP_STUB: &[u8] = b"\r\n";
+ const ROUND_TRIP_META: &[u8] = b"a:1:{s:3:\"ver\";s:5:\"1.2.3\";}";
+
+ /// Shared body: set metadata+stub, prove they survive a later entry write, and
+ /// that the reserved `.phar/*` control files stay hidden from the entry listing.
+ fn check_metadata_stub_round_trip(ext: &str, tag: &str) {
+ let path = std::env::temp_dir().join(format!(
+ "elephc_phar_meta_{}_{}.{}",
+ std::process::id(),
+ tag,
+ ext
+ ));
+ let pb = path.to_string_lossy();
+ let pbytes = pb.as_bytes();
+ assert_eq!(put_entry_bytes(pbytes, b"a.txt", b"alpha"), Some(5));
+ assert_eq!(set_metadata_bytes(pbytes, ROUND_TRIP_META), Some(()));
+ assert_eq!(set_stub_bytes(pbytes, ROUND_TRIP_STUB), Some(()));
+ // A later entry write must preserve both metadata and stub.
+ assert_eq!(put_entry_bytes(pbytes, b"b.txt", b"bravo"), Some(5));
+ assert_eq!(get_metadata_bytes(pbytes).as_deref(), Some(ROUND_TRIP_META));
+ assert_eq!(get_stub_bytes(pbytes).as_deref(), Some(ROUND_TRIP_STUB));
+ let archive = std::fs::read(&path).unwrap();
+ std::fs::remove_file(&path).ok();
+ assert_eq!(
+ extract_entry_bytes(&archive, b"a.txt").as_deref(),
+ Some(&b"alpha"[..])
+ );
+ assert_eq!(
+ extract_entry_bytes(&archive, b"b.txt").as_deref(),
+ Some(&b"bravo"[..])
+ );
+ let (entries, _) = parse_archive_entries(&archive).unwrap();
+ assert_eq!(entries.len(), 2, "{} entry count", tag);
+ assert!(
+ entries.iter().all(|e| !e.name.starts_with(b".phar/")),
+ "{} leaked a .phar/ control entry",
+ tag
+ );
+ }
+
+ const ROUND_TRIP_FILE_META: &[u8] = b"a:1:{s:4:\"role\";s:5:\"first\";}";
+
+ /// Drives a per-file metadata round-trip for one archive family: set metadata on
+ /// one entry, confirm it survives a later entry write, and that only the targeted
+ /// entry carries metadata while `.phar/` control entries never leak.
+ fn check_file_metadata_round_trip(ext: &str, tag: &str) {
+ let path = std::env::temp_dir().join(format!(
+ "elephc_phar_filemeta_{}_{}.{}",
+ std::process::id(),
+ tag,
+ ext
+ ));
+ let pb = path.to_string_lossy();
+ let pbytes = pb.as_bytes();
+ assert_eq!(put_entry_bytes(pbytes, b"a.txt", b"alpha"), Some(5));
+ assert_eq!(put_entry_bytes(pbytes, b"b.txt", b"bravo"), Some(5));
+ assert_eq!(
+ set_file_metadata_bytes(pbytes, b"a.txt", ROUND_TRIP_FILE_META),
+ Some(())
+ );
+ // A later entry write must preserve the per-file metadata.
+ assert_eq!(put_entry_bytes(pbytes, b"c.txt", b"charlie"), Some(7));
+ assert_eq!(
+ get_file_metadata_bytes(pbytes, b"a.txt").as_deref(),
+ Some(ROUND_TRIP_FILE_META),
+ "{} a.txt metadata",
+ tag
+ );
+ // Untouched entries carry no metadata.
+ assert_eq!(
+ get_file_metadata_bytes(pbytes, b"b.txt").as_deref(),
+ Some(&b""[..]),
+ "{} b.txt metadata",
+ tag
+ );
+ // Setting metadata on a missing entry fails.
+ assert_eq!(
+ set_file_metadata_bytes(pbytes, b"missing.txt", ROUND_TRIP_FILE_META),
+ None
+ );
+ let archive = std::fs::read(&path).unwrap();
+ std::fs::remove_file(&path).ok();
+ let (entries, _) = parse_archive_entries(&archive).unwrap();
+ assert_eq!(entries.len(), 3, "{} entry count", tag);
+ assert!(
+ entries.iter().all(|e| !e.name.starts_with(b".phar/")),
+ "{} leaked a .phar/ control entry",
+ tag
+ );
+ }
+
+ /// Drives a whole-archive compression round-trip: build a tar, compress it with
+ /// `compressor`, confirm the returned compressed file parses transparently with
+ /// entries intact, then decompress it and confirm the entries survive again.
+ fn check_archive_compression_round_trip(
+ tag: &str,
+ ext: &str,
+ compressor: fn(&[u8]) -> Option>,
+ ) {
+ let dir = std::env::temp_dir();
+ let src = dir.join(format!("elephc_phar_comp_{}_{}.tar", std::process::id(), tag));
+ let sb = src.to_string_lossy();
+ assert_eq!(put_entry_bytes(sb.as_bytes(), b"a.txt", b"alpha"), Some(5));
+ assert_eq!(put_entry_bytes(sb.as_bytes(), b"b.txt", b"bravo"), Some(5));
+ let comp_bytes = compressor(sb.as_bytes()).expect("compress");
+ let comp = String::from_utf8(comp_bytes).unwrap();
+ assert_eq!(comp, format!("{}.{}", sb, ext), "{} dest path", tag);
+ // The compressed file parses transparently with entries intact.
+ let (entries, _) = parse_archive_entries(&std::fs::read(&comp).unwrap()).unwrap();
+ assert_eq!(entries.len(), 2, "{} compressed entry count", tag);
+ assert_eq!(
+ extract_entry_bytes(&std::fs::read(&comp).unwrap(), b"a.txt"),
+ Some(b"alpha".to_vec())
+ );
+ // Decompressing reproduces a plain tar (the `.tar` base) with the same entries.
+ let back_bytes = decompress_archive(comp.as_bytes()).expect("decompress");
+ let back = String::from_utf8(back_bytes).unwrap();
+ assert_eq!(back, sb.to_string(), "{} decompress dest path", tag);
+ let plain = std::fs::read(&back).unwrap();
+ assert_eq!(plain.get(257..262), Some(&b"ustar"[..]), "{} decompressed is tar", tag);
+ assert_eq!(extract_entry_bytes(&plain, b"b.txt"), Some(b"bravo".to_vec()));
+ for p in [src.to_string_lossy().to_string(), comp] {
+ std::fs::remove_file(p).ok();
+ }
+ }
+
+ /// A tar archive round-trips through whole-archive gzip compression.
+ #[test]
+ fn tar_archive_gzip_compress_round_trip() {
+ check_archive_compression_round_trip("gz", "gz", gzip_archive);
+ }
+
+ /// A tar archive round-trips through whole-archive bzip2 compression.
+ #[test]
+ fn tar_archive_bzip2_compress_round_trip() {
+ check_archive_compression_round_trip("bz", "bz2", bzip2_archive);
+ }
+
+ const TEST_RSA_KEY_PEM: &str = "-----BEGIN PRIVATE KEY-----\n\
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOuAP7xZaVfhwn9l\n\
+BaMgxKPU1ODBpuT7Ybu6Fav03TJp1BKc1wUMiXnUPraUUI2R2JxoattDe7R/LcGk\n\
+jVoPiBGGPoxxTaByd5LJZJk6MJAiGBhzQT7bkK3OMDHLQqhziefqDFfnDLt/TN7+\n\
+umuMCPtLmuF6UUXiebMzyH21x7jvAgMBAAECgYBBhL+2rgVxzrxm5vsnhEFQ9zB2\n\
+i0ncYNey+7V1zr0PfoPi3cGwhOlmfJcqAp9ak534/c/kyqSK9esL+bTdvn5zIQqC\n\
+Swt2znffaW9nC6lM/pkZcvGLETt2m0L71n6pZVkMewsGBm9YrBQFA1krC7BV674U\n\
+mlOmmYpM3LPgzmRLwQJBAPm/G7O4Stmzu5xV5qtvYX1dNZ2gydkVyfK/AwCYpfbK\n\
+8ZXntKeWCt1BER1hNBSMPacHKb0LotK3j3LNNteLHCECQQDxZdNsXNLTHylWKA/X\n\
+dyM3SH9mM6ESZP07cU7Ifq6t9zJdTfGdiyxsAjaaXxDmShL+bAjU16iwaTAGcYTB\n\
+NrMPAkEAoUGwVV7Nlbvji5I7mr4UKKoikGDdc/oJp1+GRMBLiQqI6s3ta7gJ08rL\n\
+jjjRM+NJe6u4W4RD4eL8EJhIrOv5gQJAK4Tm+8c0PtmEU0L/sCGLWMEaLquqIy3P\n\
+tXK0+FJWXYiOLOILaBKaHJK9k1EGM+4wxGtnoC+M+tjLzq2SeF7LIwJAPdLUn2Qq\n\
+eGMK12chOVcx41RxYctqsOlEKCIt011yGsV2/Mdm9ljTXeyXvNXCVOVcnHaf1v5w\n\
+rNiobfy8sSb6iw==\n\
+-----END PRIVATE KEY-----\n";
+
+ /// OpenSSL signing replaces the native PHAR's SHA1 trailer with an RSA-SHA1
+ /// signature trailer, the signature is deterministic and verifies against the
+ /// derived public key, and the signature metadata reads back as OpenSSL.
+ #[test]
+ fn native_phar_openssl_signature_round_trip() {
+ use rsa::pkcs8::DecodePrivateKey;
+ use rsa::{Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey};
+ use sha1::{Digest, Sha1};
+
+ let path = std::env::temp_dir().join(format!("elephc_phar_sig_{}.phar", std::process::id()));
+ let pb = path.to_string_lossy();
+ assert_eq!(put_entry_bytes(pb.as_bytes(), b"a.txt", b"alpha"), Some(5));
+ assert_eq!(
+ sign_archive_openssl(pb.as_bytes(), TEST_RSA_KEY_PEM.as_bytes()),
+ Some(())
+ );
+
+ let signed = std::fs::read(&path).unwrap();
+ let n = signed.len();
+ assert_eq!(&signed[n - 4..], b"GBMB");
+ assert_eq!(
+ u32::from_le_bytes(signed[n - 8..n - 4].try_into().unwrap()),
+ PHAR_OPENSSL_SIGNATURE_TYPE
+ );
+
+ // The signature reads back as OpenSSL with a 1024-bit (128-byte) RSA signature.
+ let (flags, sig) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(flags, PHAR_OPENSSL_SIGNATURE_TYPE);
+ assert_eq!(sig.len(), 128, "1024-bit RSA signature is 128 bytes");
+ assert_eq!(
+ u32::from_le_bytes(signed[n - 12..n - 8].try_into().unwrap()) as usize,
+ sig.len()
+ );
+ assert_eq!(signature_type_name(pb.as_bytes()).as_deref(), Some(&b"OpenSSL"[..]));
+
+ // Re-signing is deterministic (PKCS#1 v1.5).
+ assert_eq!(
+ sign_archive_openssl(pb.as_bytes(), TEST_RSA_KEY_PEM.as_bytes()),
+ Some(())
+ );
+ let (_, sig2) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(sig, sig2, "PKCS#1 v1.5 signature is deterministic");
+
+ // The signature verifies against the public key over the signed data.
+ let key = RsaPrivateKey::from_pkcs8_pem(TEST_RSA_KEY_PEM).unwrap();
+ let pubkey = RsaPublicKey::from(&key);
+ let data = strip_signature_trailer(&std::fs::read(&path).unwrap()).to_vec();
+ let hashed = Sha1::digest(&data);
+ pubkey
+ .verify(Pkcs1v15Sign::new::(), &hashed, &sig)
+ .expect("signature verifies");
+ std::fs::remove_file(&path).ok();
+ }
+
+ /// Hash-based signing rewrites the native PHAR trailer with the requested digest
+ /// algorithm, readable back via the signature metadata.
+ #[test]
+ fn native_phar_hash_signature_round_trip() {
+ let path = std::env::temp_dir().join(format!("elephc_phar_hsig_{}.phar", std::process::id()));
+ let pb = path.to_string_lossy();
+ assert_eq!(put_entry_bytes(pb.as_bytes(), b"a.txt", b"alpha"), Some(5));
+ // SHA-256 (algo 3): 32-byte digest, type "SHA-256".
+ assert_eq!(sign_archive_hash(pb.as_bytes(), 3), Some(()));
+ let (flags, digest) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(flags, 3);
+ assert_eq!(digest.len(), 32);
+ assert_eq!(signature_type_name(pb.as_bytes()).as_deref(), Some(&b"SHA-256"[..]));
+ std::fs::remove_file(&path).ok();
+ }
+
+ /// Reconstructs the byte range a tar/zip phar signature is computed over from a
+ /// parsed archive: the tar data records, or the zip locals + central + comment.
+ fn tar_zip_signed_range(arch: &Archive) -> Vec {
+ match arch.format {
+ ArchiveFormat::Tar => {
+ let mut body = Vec::new();
+ write_tar_body(&mut body, &arch.entries, &arch.metadata, &arch.stub).unwrap();
+ body
+ }
+ ArchiveFormat::Zip => {
+ let mut out = Vec::new();
+ let mut central = Vec::new();
+ write_zip_body(&mut out, &mut central, &arch.entries, &arch.stub).unwrap();
+ out.extend_from_slice(¢ral);
+ out.extend_from_slice(&arch.metadata);
+ out
+ }
+ ArchiveFormat::NativePhar => unreachable!("native phars sign with a trailer"),
+ }
+ }
+
+ /// Hash signing a tar/zip phar writes a hidden `.phar/signature.bin` entry
+ /// (`LE32(flag) ++ LE32(len) ++ digest`) computed over the signed range, leaving
+ /// real entries readable and reporting the right algorithm.
+ fn check_tar_zip_hash_signature(ext: &str) {
+ let path =
+ std::env::temp_dir().join(format!("elephc_phar_sig_{}_{ext}.{ext}", std::process::id()));
+ let pb = path.to_string_lossy();
+ assert_eq!(
+ put_entry_bytes(pb.as_bytes(), b"doc.txt", b"bundled document\n"),
+ Some(17)
+ );
+ // SHA-256 (algo 3).
+ assert_eq!(sign_archive_hash(pb.as_bytes(), 3), Some(()));
+ let data = std::fs::read(&path).unwrap();
+ // The signature entry is hidden; the real entry still reads back.
+ assert_eq!(
+ extract_entry_bytes(&data, b"doc.txt").as_deref(),
+ Some(&b"bundled document\n"[..])
+ );
+ let (flag, digest) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(flag, 3);
+ assert_eq!(digest.len(), 32);
+ assert_eq!(signature_type_name(pb.as_bytes()).as_deref(), Some(&b"SHA-256"[..]));
+ // The digest covers exactly the reconstructed signed range.
+ let arch = parse_archive(&data).unwrap();
+ assert_eq!(digest, compute_signature(3, None, &tar_zip_signed_range(&arch)).unwrap());
+ std::fs::remove_file(&path).ok();
+ }
+
+ /// SHA-256 signing a tar phar round-trips through `.phar/signature.bin`.
+ #[test]
+ fn tar_phar_hash_signature_round_trip() {
+ check_tar_zip_hash_signature("tar");
+ }
+
+ /// SHA-256 signing a zip phar round-trips through `.phar/signature.bin`.
+ #[test]
+ fn zip_phar_hash_signature_round_trip() {
+ check_tar_zip_hash_signature("zip");
+ }
+
+ /// OpenSSL signing a tar/zip phar writes an RSA-SHA1 `.phar/signature.bin` that
+ /// verifies against the derived public key over the archive's signed range.
+ fn check_tar_zip_openssl_signature(ext: &str) {
+ use rsa::pkcs8::DecodePrivateKey;
+ use rsa::{Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey};
+ use sha1::{Digest, Sha1};
+
+ let path =
+ std::env::temp_dir().join(format!("elephc_phar_osig_{}_{ext}.{ext}", std::process::id()));
+ let pb = path.to_string_lossy();
+ assert_eq!(
+ put_entry_bytes(pb.as_bytes(), b"doc.txt", b"bundled document\n"),
+ Some(17)
+ );
+ assert_eq!(
+ sign_archive_openssl(pb.as_bytes(), TEST_RSA_KEY_PEM.as_bytes()),
+ Some(())
+ );
+ let data = std::fs::read(&path).unwrap();
+ let (flag, sig) = read_signature_info(pb.as_bytes()).unwrap();
+ assert_eq!(flag, PHAR_OPENSSL_SIGNATURE_TYPE);
+ assert_eq!(sig.len(), 128, "1024-bit RSA signature is 128 bytes");
+ assert_eq!(signature_type_name(pb.as_bytes()).as_deref(), Some(&b"OpenSSL"[..]));
+ // The signature verifies against the public key over the signed range.
+ let arch = parse_archive(&data).unwrap();
+ let key = RsaPrivateKey::from_pkcs8_pem(TEST_RSA_KEY_PEM).unwrap();
+ let pubkey = RsaPublicKey::from(&key);
+ let hashed = Sha1::digest(tar_zip_signed_range(&arch));
+ pubkey
+ .verify(Pkcs1v15Sign::new::(), &hashed, &sig)
+ .expect("tar/zip OpenSSL signature verifies");
+ std::fs::remove_file(&path).ok();
+ }
+
+ /// OpenSSL signing a tar phar verifies against the derived public key.
+ #[test]
+ fn tar_phar_openssl_signature_round_trip() {
+ check_tar_zip_openssl_signature("tar");
+ }
+
+ /// OpenSSL signing a zip phar verifies against the derived public key.
+ #[test]
+ fn zip_phar_openssl_signature_round_trip() {
+ check_tar_zip_openssl_signature("zip");
+ }
+
+ /// Per-file metadata round-trips through the native manifest per-entry field.
+ #[test]
+ fn native_phar_file_metadata_round_trip() {
+ check_file_metadata_round_trip("phar", "native");
+ }
+
+ /// Per-file metadata round-trips through `.phar/.metadata//.metadata.bin`.
+ #[test]
+ fn tar_phar_file_metadata_round_trip() {
+ check_file_metadata_round_trip("tar", "tar");
+ }
+
+ /// Per-file metadata round-trips through the zip central-directory file comment.
+ #[test]
+ fn zip_phar_file_metadata_round_trip() {
+ check_file_metadata_round_trip("zip", "zip");
+ }
+
+ /// Verifies native-PHAR global metadata and stub persist and survive entry writes.
+ #[test]
+ fn native_phar_metadata_and_stub_round_trip() {
+ check_metadata_stub_round_trip("phar", "native");
+ }
+
+ /// Verifies tar-based phar metadata/stub persist via `.phar/.metadata.bin` and
+ /// `.phar/stub.php`, and survive entry writes.
+ #[test]
+ fn tar_phar_metadata_and_stub_round_trip() {
+ check_metadata_stub_round_trip("tar", "tar");
+ }
+
+ /// Verifies zip-based phar metadata persists in the EOCD comment and the stub in
+ /// `.phar/stub.php`, and both survive entry writes.
+ #[test]
+ fn zip_phar_metadata_and_stub_round_trip() {
+ check_metadata_stub_round_trip("zip", "zip");
+ }
+
+ /// Verifies `set_stub_bytes` rejects a stub without the `__HALT_COMPILER();` marker.
+ #[test]
+ fn set_stub_requires_halt_compiler() {
+ let path = std::env::temp_dir().join(format!(
+ "elephc_phar_badstub_{}.phar",
+ std::process::id()
+ ));
+ let pb = path.to_string_lossy();
+ assert_eq!(put_entry_bytes(pb.as_bytes(), b"a.txt", b"alpha"), Some(5));
+ assert_eq!(set_stub_bytes(pb.as_bytes(), b" tz-prelude inject the timezone-introspection prelude when used
-> list-id-prelude inject the DateTimeZone identifier-list prelude when used
-> var-export-prelude inject the var_export prelude when used
+ -> image-prelude inject the image (GD/Exif/Imagick) prelude when used
+ -> web-prelude inject the web runtime prelude with --web
-> name-resolve apply namespace/use rules, canonicalize names
-> autoload-run run autoload insertion
-> opt-fold AST constant folding
@@ -56,8 +58,9 @@ PHP source
passed with [`--define`](linking-and-conditional-compilation.md#conditional-compilation).
- **resolve / prelude injection / name-resolve** — `include`/`require` are
resolved, declarations are discovered, demand-loaded PHP preludes for PDO,
- timezone introspection, `DateTimeZone::listIdentifiers()`, and `var_export()`
- are injected only when referenced, and namespace/`use` rules rewrite
+ timezone introspection, `DateTimeZone::listIdentifiers()`, `var_export()`,
+ and image processing are injected only when referenced, the web runtime
+ prelude is injected with `--web`, and namespace/`use` rules rewrite
references to fully-qualified names. Autoloading is wired in around these
steps.
- **typecheck** — the [Type Checker](../internals/the-type-checker.md) infers and
diff --git a/docs/compiling/linking-and-conditional-compilation.md b/docs/compiling/linking-and-conditional-compilation.md
index b145cd7872..44397f61bf 100644
--- a/docs/compiling/linking-and-conditional-compilation.md
+++ b/docs/compiling/linking-and-conditional-compilation.md
@@ -45,6 +45,36 @@ elephc app.php --framework Cocoa --framework Metal
automatically; the flags above are for libraries not already named in the source.
See [FFI & Extern](../beyond-php/extern.md).
+## Bridge crates and `--with-CRATE`
+
+Some optional features are implemented as Rust *bridge crates* (`staticlib`
+archives) that elephc links into the program: `pdo` (database access), `tls`
+(`https://`/`ftps://` streams), `crypto` (the `hash()`/`md5()`/`sha1()` family),
+`phar` (Phar archives), `tz` (timezone introspection), `image` (GD/Imagick image
+processing), and `web` (the `--web` server).
+
+By default a bridge is linked **only when the program uses it** — using a hash
+function pulls in `crypto`, opening an `https://` stream pulls in `tls`,
+referencing `PDO` pulls in `pdo`, and so on. Programs that do not use a feature
+never link its crate, so binaries stay small.
+
+`--with-CRATE` force-enables a bridge regardless of that auto-detection. It
+force-links the staticlib (whole-archived, so it is retained even if no symbol
+references it) and, for crates whose PHP surface comes from an injected prelude
+(`pdo`, `tz`, `image`), force-injects that prelude so the classes/functions are
+available. This is useful when a program reaches a feature through indirection
+that detection cannot see. The flag is repeatable:
+
+```bash
+elephc app.php --with-pdo
+elephc app.php --with-crypto --with-tls
+```
+
+`--with-web` is an alias for [`--web`](../beyond-php/web.md) (the full server
+mode, which owns the program entry point). An unknown crate name is rejected with
+the list of valid crates. Forcing a crate increases binary size, since the whole
+archive is included.
+
## Heap size
The compiled program uses a fixed-size runtime heap, **8 MB** by default. Programs
@@ -61,6 +91,25 @@ elephc --heap-size=16777216 heavy.php # 16 MB
If a program exhausts its heap it aborts with a fatal "heap memory exhausted"
error; raising `--heap-size` is the fix. See [Memory Model](../internals/memory-model.md).
+## Runtime dead stripping
+
+The compiler ships a single runtime with helpers for every supported builtin, but
+a given program only uses a few of them. When linking an **executable**, the
+linker keeps only the runtime helpers reachable from the program and drops the
+rest, so a small program does not carry the whole runtime. This is automatic —
+there is no flag — and never changes behavior, only binary size.
+
+It works the same on every supported target, using each platform's native
+mechanism:
+
+- **Linux** emits each runtime helper into its own section and links with
+ `--gc-sections`.
+- **macOS** emits the runtime object with `.subsections_via_symbols` so each
+ helper is a separately collectable atom, and links with `-dead_strip`.
+
+Shared libraries (`--emit cdylib`) keep the full runtime, since any exported
+symbol may be reached by a host the linker cannot see.
+
## Conditional compilation
elephc supports compile-time feature branches with `ifdef`. Symbols are defined
diff --git a/docs/compiling/optimization.md b/docs/compiling/optimization.md
index efec17f7d2..0ffc57b7da 100644
--- a/docs/compiling/optimization.md
+++ b/docs/compiling/optimization.md
@@ -292,20 +292,12 @@ on compute-heavy code.
| Value | Meaning |
|---|---|
| `tagged` (default) | Inline two-word `{payload, tag}` scalars. |
-| `sentinel` | In-band `PHP_INT_MAX - 1` sentinel in one-word slots (legacy opt-out). |
+| `sentinel` | In-band `PHP_INT_MAX - 1` sentinel in one-word slots (compatibility opt-out). |
```bash
-elephc --null-repr=sentinel legacy.php
+elephc --null-repr=sentinel sentinel.php
```
`ELEPHC_NULL_REPR` overrides the default for a whole run. Most programs should
-use the default; `sentinel` exists as a legacy opt-out. See
+use the default; `sentinel` exists as a compatibility opt-out. See
[Memory Model](../internals/memory-model.md).
-
-## The frozen legacy backend
-
-`--ast-backend` selects the legacy direct AST→assembly backend. It is
-**deprecated**, frozen (no new language or runtime features), emits a warning,
-and is scheduled for removal in v0.26.0. Use it only to compare behavior with the
-old backend during the transition. The EIR backend is the default and the only
-active implementation target.
diff --git a/docs/compiling/targets.md b/docs/compiling/targets.md
index 6dd480c511..09607e4e9f 100644
--- a/docs/compiling/targets.md
+++ b/docs/compiling/targets.md
@@ -51,3 +51,71 @@ targets from a macOS host.
For the target-aware ABI and runtime details behind each platform, see
[Architecture](../internals/architecture.md) and
[The Code Generator](../internals/the-codegen.md).
+
+## Windows codegen parity gate
+
+`windows-x86_64` is an experimental cross-compilation target, not yet a
+first-class supported target. CI cross-compiles every codegen fixture to
+`windows-x86_64` and runs it under Wine to measure how much of the suite already
+behaves correctly on Windows. To let that parity grow without silently
+regressing, CI enforces a **curated no-regression gate**.
+
+### How the gate works
+
+Two lists live in the repository as the source of truth:
+
+- `tests/codegen/support/windows_codegen_allowlist.txt` — the codegen tests that
+ currently **pass** on `windows-x86_64` under Wine (the known-good set).
+- `tests/codegen/support/windows_codegen_known_failures.txt` — the companion list
+ of tests that currently **fail** on Windows.
+
+Together they partition the `ci`-profile *runnable* codegen tests:
+
+```
+allow_list = (ci-profile runnable codegen tests) - known_failures
+known_failures = (ci-profile runnable codegen tests) - allow_list
+```
+
+The sharded `windows-codegen-parity` CI job runs the full suite under Wine and,
+per shard, computes `regressions = actual_failures ∩ allow_list`. The gate rule
+is exact:
+
+> **The gate fails if and only if a test in the allow-list failed on Windows.**
+
+Tests that are **not** in the allow-list — the known failures **and** any
+brand-new or native-only fixtures — never fail the gate. This protects the
+known-good set while letting Windows-incompatible tests exist freely: parity can
+only improve, never regress. The aggregating `windows-codegen-gate` job is green
+only when all 16 shards are green, and it is the single Windows-codegen
+dependency of the top-level `test` gate. Each shard also prints its
+passed / failed / parity% to the job summary, so the informational parity picture
+stays visible alongside the gate.
+
+### Refreshing the allow-list as parity grows
+
+When Windows fixes land and previously-failing tests start passing, move them
+from the known-failures list into the allow-list by regenerating both files from
+a real parity run:
+
+1. Download the 16 `windows-codegen-junit-` artifacts from a
+ `windows-codegen-parity` CI run (each is that shard's `junit.xml`).
+2. Produce the current runnable set:
+
+ ```bash
+ cargo nextest list --profile ci --test codegen_tests \
+ --message-format json > nextest_list.json
+ ```
+
+3. Regenerate both lists deterministically:
+
+ ```bash
+ python3 scripts/gen_windows_codegen_allowlist.py generate \
+ --list-json nextest_list.json \
+ --junit path/to/windows-codegen-junit-*/junit.xml
+ ```
+
+The script writes both files sorted and locale-independent, so the same inputs
+always reproduce byte-identical lists. It errors if a supplied failing test is
+not in the runnable set (a sign the inputs came from a different revision). Never
+hand-edit the lists — always regenerate. The same script's `gate` subcommand is
+what the CI job runs to perform the intersection check.
diff --git a/docs/internals/architecture.md b/docs/internals/architecture.md
index 19a11d26b1..efa256be47 100644
--- a/docs/internals/architecture.md
+++ b/docs/internals/architecture.md
@@ -137,9 +137,8 @@ PHP source (.php)
│
▼
┌─────────────┐
-│ EIR Codegen │ src/codegen_ir/ + shared src/codegen/abi/
-│ │ Emits target assembly text from EIR. The legacy AST backend
-│ │ remains in src/codegen/ behind --ast-backend.
+│ EIR Codegen │ src/codegen/ + shared src/codegen_support/abi/
+│ │ Emits target assembly text from EIR.
└──────┬──────┘
│
▼
@@ -190,7 +189,8 @@ src/
├── ir/ EIR types, builder, validator, printer, effects, and tests
├── ir_lower/ Active checked-AST to EIR lowering
├── ir_passes/ EIR optimization pass driver, identity folding, peephole patterns, constant folding, common-subexpression elimination, loop-invariant code motion, dead-instruction elimination, dead-store elimination, branch simplification, the cross-function small-function inliner (run to a module-level fixed point), dominance analysis, loop analysis, and linear-scan register allocation
-├── codegen_ir/ Active EIR to target assembly backend
+├── codegen/ Active EIR to target assembly backend
+├── codegen_support/ Shared ABI, runtime, platform, metadata, and callable support
├── runtime_cache.rs Cached shared runtime object preparation
├── source_map.rs Assembly comment markers → JSON sidecar map
├── termination.rs Structured terminal-effect analysis shared by checker and optimizer
@@ -259,19 +259,20 @@ src/
│ ├── yield_validation/ Generator return coercion and yield-scope validation
│ └── ...
│
-├── codegen/
-│ ├── mod.rs Frozen legacy AST-backend orchestration plus shared runtime feature scans
-│ ├── driver_support.rs Pipeline glue and orchestration helpers
-│ ├── main_emission.rs Top-level program, globals, and deferred-body emission
-│ ├── class_methods.rs Instance/static method emission orchestration
-│ ├── function_variants.rs Include-loaded function variant dispatchers
+├── codegen_support/
+│ ├── mod.rs Shared codegen metadata registries and support re-exports
+│ ├── driver_support.rs Runtime object, deferred callable, boxing, and hash-key helpers
+│ ├── arrays.rs Shared array value-type metadata stamping helpers
+│ ├── callable_invoker_args.rs Descriptor-invoker argument cloning and Mixed boxing helpers
+│ ├── value_boxing.rs Shared runtime-value and owned-value boxing into Mixed cells
+│ ├── wrappers/ Shared callback and fiber wrapper emitters
│ ├── interface_wrappers.rs Interface dispatch return-shape adapters
│ ├── callables.rs Top-level callable metadata and indirect-call helpers
│ ├── reflection.rs Shared ReflectionAttribute materialization helpers
-│ ├── prescan.rs Pre-pass that collects program-wide codegen metadata
-│ ├── program_usage.rs Program-usage analysis feeding metadata emission
-│ ├── program_usage/ Required-class and variable usage scanners
-│ ├── functions/generator/ Generator wrapper and resume state-machine lowering
+│ ├── prescan.rs Constant pre-scan feeding EIR lowering
+│ ├── program_usage.rs Required-class analysis feeding metadata emission
+│ ├── program_usage/ Required-class scanners
+│ ├── functions/generator/ Closure generator wrapper and resume state-machine lowering
│ ├── expr.rs Expression codegen dispatcher
│ ├── expr/ Expression submodules
│ │ ├── arrays.rs Array-expression dispatch
@@ -307,10 +308,8 @@ src/
│ │ └── storage/ `locals.rs`, `extern_globals.rs`
│ ├── functions/ User function emission
│ │ ├── mod.rs Function lowering entry point
-│ │ ├── callback_wrapper.rs Captured callback environment wrappers
│ │ ├── cleanup.rs Epilogue / ownership cleanup helpers
│ │ ├── control_flow.rs Return / early-exit lowering
-│ │ ├── fiber_wrapper.rs Fiber callback entry wrappers
│ │ ├── locals.rs Local slot layout
│ │ └── types/ Function-local type inference helpers
│ ├── abi/ Target-aware calling convention helpers
@@ -353,18 +352,18 @@ src/
│ ├── diagnostics.rs Suppressible runtime-warning channel used by `@`
│ ├── emitters.rs `emit_runtime()` orchestration — emits every runtime category in a fixed order
│ ├── strings/ itoa, concat, resource display, ftoa, sprintf, md5, sha1, str_persist, ... (71 files)
-│ ├── arrays/ heap_alloc, heap_free, array_free_deep, array_grow, hash_grow, hash_*, mixed boxing/freeing, mixed instanceof, sort, usort, refcount, gc/decref dispatch, ... (132 files)
-│ ├── callables/ Runtime `is_callable()` fallback for dynamic strings/arrays/hashes/objects/Mixed plus callable descriptor release (3 files)
+│ ├── arrays/ heap_alloc, heap_free, array_free_deep, array_grow, hash_grow, hash_*, mixed boxing/freeing, mixed instanceof, sort, usort, refcount, gc/decref dispatch, ... (145 files)
+│ ├── callables/ Runtime `is_callable()` fallback for dynamic strings/arrays/hashes/objects/Mixed, callable descriptor release, and `Closure::bind` support (4 files)
│ ├── io/ fopen, fgets, fread, stat, streams, sockets, filters, scandir, ... (113 files)
│ ├── buffers/ buffer_new, buffer_len, bounds_fail, use_after_free helpers (5 files incl. mod.rs)
│ ├── exceptions.rs Exception runtime module root / re-exports
│ ├── exceptions/ cleanup_frames, dynamic_instanceof, matches, throw_current, rethrow_current, class_implements helpers (6 files)
-│ ├── system/ build_argv, time, getenv, shell_exec, php_uname, date, gmdate, mktime, strtotime, getdate, localtime, checkdate, microtime, hrtime, date_default_timezone, match_unhandled, json_encode_*, json_decode, preg_*, ... (40 files)
+│ ├── system/ build_argv, time, getenv, shell_exec, php_uname, date, gmdate, mktime, strtotime, getdate, localtime, checkdate, microtime, hrtime, date_default_timezone, match_unhandled, json_encode_*, json_decode, preg_*, ... (42 files)
│ ├── pointers/ ptoa, ptr_check_nonnull, str_to_cstr, cstr_to_str, ptr_read_string, ptr_write_string, ... (7 files)
│ ├── fibers/ stack allocation/free, context switch, entry trampoline (4 files) + `api/` (target-aware public API helpers)
│ ├── objects/ stdClass, Mixed property/index access, JSON stdClass encoding, destructor dispatch, new-by-name helpers (6 files)
│ ├── spl/ SplDoublyLinkedList and SplFixedArray runtime container helpers (3 files)
-│ └── generators/ Generator frame layout and __rt_gen_* helpers (2 files)
+│ └── generators/ Generator frame layout and fiber-backed coroutine __rt_gen_* helpers (3 files)
│
│
└── errors/
@@ -488,7 +487,7 @@ The runtime data emission in `src/codegen/runtime/data/` is split into `emit_run
Offset Size Field
0 8 count (number of occupied entries)
8 8 capacity (number of slots)
- 16 8 value_type (coarse summary: 0=int, 1=str, 2=float, 3=bool, 4=array, 5=assoc, 6=object, 7=mixed)
+ 16 8 value_type (coarse summary: 0=int, 1=str, 2=float, 3=bool, 4=array, 5=assoc, 6=object, 7=mixed, 8=null)
24 8 head (slot index of first inserted entry, or -1)
32 8 tail (slot index of last inserted entry, or -1)
40 ... entries (each entry is 64 bytes)
diff --git a/docs/internals/builtins/_internal/__elephc_gmmktime_raw.md b/docs/internals/builtins/_internal/__elephc_gmmktime_raw.md
index 779f45a215..2a74ed73d8 100644
--- a/docs/internals/builtins/_internal/__elephc_gmmktime_raw.md
+++ b/docs/internals/builtins/_internal/__elephc_gmmktime_raw.md
@@ -2,15 +2,15 @@
title: "__elephc_gmmktime_raw() — internals"
description: "Compiler internals for __elephc_gmmktime_raw(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 254
+ order: 429
---
## `__elephc_gmmktime_raw()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L151) (`lower_gmmktime`)
+- **Signature**: [`src/builtins/system/__elephc_gmmktime_raw.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/__elephc_gmmktime_raw.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L151) (`lower_gmmktime`)
- **Function symbol**: `lower_gmmktime()`
@@ -39,4 +39,3 @@ function __elephc_gmmktime_raw(int $hour, int $minute, int $second, int $month,
## Cross-references
- _No user-facing reference — this is a compiler internal helper._
-
diff --git a/docs/internals/builtins/_internal/__elephc_mktime_raw.md b/docs/internals/builtins/_internal/__elephc_mktime_raw.md
index c6d11a549c..adc4c4f0ef 100644
--- a/docs/internals/builtins/_internal/__elephc_mktime_raw.md
+++ b/docs/internals/builtins/_internal/__elephc_mktime_raw.md
@@ -2,15 +2,15 @@
title: "__elephc_mktime_raw() — internals"
description: "Compiler internals for __elephc_mktime_raw(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 255
+ order: 430
---
## `__elephc_mktime_raw()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:140](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L140) (`lower_mktime`)
+- **Signature**: [`src/builtins/system/__elephc_mktime_raw.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/__elephc_mktime_raw.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:140](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L140) (`lower_mktime`)
- **Function symbol**: `lower_mktime()`
@@ -39,4 +39,3 @@ function __elephc_mktime_raw(int $hour, int $minute, int $second, int $month, in
## Cross-references
- _No user-facing reference — this is a compiler internal helper._
-
diff --git a/docs/internals/builtins/_internal/__elephc_phar_bzip2_archive.md b/docs/internals/builtins/_internal/__elephc_phar_bzip2_archive.md
new file mode 100644
index 0000000000..3433fe71b4
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_bzip2_archive.md
@@ -0,0 +1,38 @@
+---
+title: "__elephc_phar_bzip2_archive() — internals"
+description: "Compiler internals for __elephc_phar_bzip2_archive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 431
+---
+
+## `__elephc_phar_bzip2_archive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_bzip2_archive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_bzip2_archive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4120](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4120) (`lower_elephc_phar_bzip2_archive`)
+- **Function symbol**: `lower_elephc_phar_bzip2_archive()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_bzip2_archive(src)` into the whole-archive bzip2 bridge,
+- returning the written destination path (or an empty string on failure).
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_bzip2_archive(string $src): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_decompress_archive.md b/docs/internals/builtins/_internal/__elephc_phar_decompress_archive.md
new file mode 100644
index 0000000000..8a52d76979
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_decompress_archive.md
@@ -0,0 +1,38 @@
+---
+title: "__elephc_phar_decompress_archive() — internals"
+description: "Compiler internals for __elephc_phar_decompress_archive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 432
+---
+
+## `__elephc_phar_decompress_archive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_decompress_archive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_decompress_archive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4135](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4135) (`lower_elephc_phar_decompress_archive`)
+- **Function symbol**: `lower_elephc_phar_decompress_archive()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_decompress_archive(src)` into the whole-archive decompression
+- bridge, returning the written destination path (or an empty string on failure).
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_decompress_archive(string $src): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_get_file_metadata.md b/docs/internals/builtins/_internal/__elephc_phar_get_file_metadata.md
new file mode 100644
index 0000000000..d636445a97
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_get_file_metadata.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_get_file_metadata() — internals"
+description: "Compiler internals for __elephc_phar_get_file_metadata(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 433
+---
+
+## `__elephc_phar_get_file_metadata()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_get_file_metadata.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_get_file_metadata.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4074](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4074) (`lower_elephc_phar_get_file_metadata`)
+- **Function symbol**: `lower_elephc_phar_get_file_metadata()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_get_file_metadata()` into the per-file metadata-read bridge.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_get_file_metadata(string $url): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_get_metadata.md b/docs/internals/builtins/_internal/__elephc_phar_get_metadata.md
new file mode 100644
index 0000000000..2aabe977cf
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_get_metadata.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_get_metadata() — internals"
+description: "Compiler internals for __elephc_phar_get_metadata(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 434
+---
+
+## `__elephc_phar_get_metadata()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_get_metadata.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_get_metadata.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3859](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3859) (`lower_elephc_phar_get_metadata`)
+- **Function symbol**: `lower_elephc_phar_get_metadata()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_get_metadata()` into the metadata-read bridge call.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_get_metadata(string $filename): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_get_signature_hash.md b/docs/internals/builtins/_internal/__elephc_phar_get_signature_hash.md
new file mode 100644
index 0000000000..fd5cc4bda1
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_get_signature_hash.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_get_signature_hash() — internals"
+description: "Compiler internals for __elephc_phar_get_signature_hash(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 435
+---
+
+## `__elephc_phar_get_signature_hash()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_get_signature_hash.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_get_signature_hash.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4192](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4192) (`lower_elephc_phar_get_signature_hash`)
+- **Function symbol**: `lower_elephc_phar_get_signature_hash()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_get_signature_hash(path)` into the signature-hash read bridge.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_get_signature_hash(string $path): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_get_signature_type.md b/docs/internals/builtins/_internal/__elephc_phar_get_signature_type.md
new file mode 100644
index 0000000000..93136fe1db
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_get_signature_type.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_get_signature_type() — internals"
+description: "Compiler internals for __elephc_phar_get_signature_type(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 436
+---
+
+## `__elephc_phar_get_signature_type()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_get_signature_type.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_get_signature_type.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4206](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4206) (`lower_elephc_phar_get_signature_type`)
+- **Function symbol**: `lower_elephc_phar_get_signature_type()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_get_signature_type(path)` into the signature-type read bridge.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_get_signature_type(string $path): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_get_stub.md b/docs/internals/builtins/_internal/__elephc_phar_get_stub.md
new file mode 100644
index 0000000000..bb944cd456
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_get_stub.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_get_stub() — internals"
+description: "Compiler internals for __elephc_phar_get_stub(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 437
+---
+
+## `__elephc_phar_get_stub()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_get_stub.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_get_stub.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3873](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3873) (`lower_elephc_phar_get_stub`)
+- **Function symbol**: `lower_elephc_phar_get_stub()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_get_stub()` into the stub-read bridge call.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_get_stub(string $filename): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_gzip_archive.md b/docs/internals/builtins/_internal/__elephc_phar_gzip_archive.md
new file mode 100644
index 0000000000..f4bbf8d80c
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_gzip_archive.md
@@ -0,0 +1,38 @@
+---
+title: "__elephc_phar_gzip_archive() — internals"
+description: "Compiler internals for __elephc_phar_gzip_archive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 438
+---
+
+## `__elephc_phar_gzip_archive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_gzip_archive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_gzip_archive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4105](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4105) (`lower_elephc_phar_gzip_archive`)
+- **Function symbol**: `lower_elephc_phar_gzip_archive()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_gzip_archive(src)` into the whole-archive gzip bridge,
+- returning the written destination path (or an empty string on failure).
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_gzip_archive(string $src): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_list_entries.md b/docs/internals/builtins/_internal/__elephc_phar_list_entries.md
index ee4b47e718..bd3a6f3fec 100644
--- a/docs/internals/builtins/_internal/__elephc_phar_list_entries.md
+++ b/docs/internals/builtins/_internal/__elephc_phar_list_entries.md
@@ -2,15 +2,15 @@
title: "__elephc_phar_list_entries() — internals"
description: "Compiler internals for __elephc_phar_list_entries(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 414
+ order: 439
---
## `__elephc_phar_list_entries()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3634](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3634) (`lower_elephc_phar_list_entries`)
+- **Signature**: [`src/builtins/io/__elephc_phar_list_entries.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_list_entries.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4277](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4277) (`lower_elephc_phar_list_entries`)
- **Function symbol**: `lower_elephc_phar_list_entries()`
@@ -26,7 +26,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function __elephc_phar_list_entries(mixed $filename): array
+function __elephc_phar_list_entries(string $filename): array
```
## What the type checker enforces
diff --git a/docs/internals/builtins/_internal/__elephc_phar_set_compression.md b/docs/internals/builtins/_internal/__elephc_phar_set_compression.md
index f28e92584e..abc214a943 100644
--- a/docs/internals/builtins/_internal/__elephc_phar_set_compression.md
+++ b/docs/internals/builtins/_internal/__elephc_phar_set_compression.md
@@ -2,15 +2,15 @@
title: "__elephc_phar_set_compression() — internals"
description: "Compiler internals for __elephc_phar_set_compression(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 415
+ order: 440
---
## `__elephc_phar_set_compression()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3576](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3576) (`lower_elephc_phar_set_compression`)
+- **Signature**: [`src/builtins/io/__elephc_phar_set_compression.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_set_compression.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3801](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3801) (`lower_elephc_phar_set_compression`)
- **Function symbol**: `lower_elephc_phar_set_compression()`
@@ -26,7 +26,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function __elephc_phar_set_compression(mixed $filename, mixed $compression): bool
+function __elephc_phar_set_compression(string $filename, int $compression): bool
```
## What the type checker enforces
diff --git a/docs/internals/builtins/_internal/__elephc_phar_set_file_metadata.md b/docs/internals/builtins/_internal/__elephc_phar_set_file_metadata.md
new file mode 100644
index 0000000000..f087af7661
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_set_file_metadata.md
@@ -0,0 +1,39 @@
+---
+title: "__elephc_phar_set_file_metadata() — internals"
+description: "Compiler internals for __elephc_phar_set_file_metadata(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 441
+---
+
+## `__elephc_phar_set_file_metadata()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_set_file_metadata.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_set_file_metadata.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4090](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4090) (`lower_elephc_phar_set_file_metadata`)
+- **Function symbol**: `lower_elephc_phar_set_file_metadata()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_set_file_metadata()` into the per-file metadata-write bridge.
+- The single `phar://archive/entry` URL argument is split by the bridge, so this
+- reuses the same `(url, data) -> bool` shape as the archive-level metadata writer.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_set_file_metadata(string $url, string $metadata): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_set_metadata.md b/docs/internals/builtins/_internal/__elephc_phar_set_metadata.md
new file mode 100644
index 0000000000..eab3b4b58b
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_set_metadata.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_set_metadata() — internals"
+description: "Compiler internals for __elephc_phar_set_metadata(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 442
+---
+
+## `__elephc_phar_set_metadata()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_set_metadata.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_set_metadata.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3882](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3882) (`lower_elephc_phar_set_metadata`)
+- **Function symbol**: `lower_elephc_phar_set_metadata()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_set_metadata()` into the metadata-write bridge call.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_set_metadata(string $filename, string $metadata): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_set_stub.md b/docs/internals/builtins/_internal/__elephc_phar_set_stub.md
new file mode 100644
index 0000000000..383107ac65
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_set_stub.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_set_stub() — internals"
+description: "Compiler internals for __elephc_phar_set_stub(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 443
+---
+
+## `__elephc_phar_set_stub()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_set_stub.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_set_stub.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3896](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3896) (`lower_elephc_phar_set_stub`)
+- **Function symbol**: `lower_elephc_phar_set_stub()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_set_stub()` into the stub-write bridge call.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_set_stub(string $filename, string $stub): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_set_zip_password.md b/docs/internals/builtins/_internal/__elephc_phar_set_zip_password.md
new file mode 100644
index 0000000000..8daa3e07ab
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_set_zip_password.md
@@ -0,0 +1,38 @@
+---
+title: "__elephc_phar_set_zip_password() — internals"
+description: "Compiler internals for __elephc_phar_set_zip_password(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 444
+---
+
+## `__elephc_phar_set_zip_password()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_set_zip_password.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_set_zip_password.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4178](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4178) (`lower_elephc_phar_set_zip_password`)
+- **Function symbol**: `lower_elephc_phar_set_zip_password()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_set_zip_password(password)` into the ZipCrypto password
+- bridge that lets later reads decrypt encrypted ZIP entries.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_set_zip_password(string $password): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_sign_hash.md b/docs/internals/builtins/_internal/__elephc_phar_sign_hash.md
new file mode 100644
index 0000000000..9cbb110824
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_sign_hash.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_sign_hash() — internals"
+description: "Compiler internals for __elephc_phar_sign_hash(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 445
+---
+
+## `__elephc_phar_sign_hash()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_sign_hash.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_sign_hash.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4163](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4163) (`lower_elephc_phar_sign_hash`)
+- **Function symbol**: `lower_elephc_phar_sign_hash()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_sign_hash(path, algo)` into the hash-based signing bridge.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_sign_hash(string $path, string $algo): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_phar_sign_openssl.md b/docs/internals/builtins/_internal/__elephc_phar_sign_openssl.md
new file mode 100644
index 0000000000..7974e930f4
--- /dev/null
+++ b/docs/internals/builtins/_internal/__elephc_phar_sign_openssl.md
@@ -0,0 +1,37 @@
+---
+title: "__elephc_phar_sign_openssl() — internals"
+description: "Compiler internals for __elephc_phar_sign_openssl(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 446
+---
+
+## `__elephc_phar_sign_openssl()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/io/__elephc_phar_sign_openssl.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/__elephc_phar_sign_openssl.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4149](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4149) (`lower_elephc_phar_sign_openssl`)
+- **Function symbol**: `lower_elephc_phar_sign_openssl()`
+
+
+### Lowering notes
+
+- Lowers `__elephc_phar_sign_openssl(path, keyPem)` into the RSA-SHA1 signing bridge.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function __elephc_phar_sign_openssl(string $path, string $key): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- _No user-facing reference — this is a compiler internal helper._
diff --git a/docs/internals/builtins/_internal/__elephc_strtotime_raw.md b/docs/internals/builtins/_internal/__elephc_strtotime_raw.md
index 9ffae0b268..291ea94039 100644
--- a/docs/internals/builtins/_internal/__elephc_strtotime_raw.md
+++ b/docs/internals/builtins/_internal/__elephc_strtotime_raw.md
@@ -2,15 +2,15 @@
title: "__elephc_strtotime_raw() — internals"
description: "Compiler internals for __elephc_strtotime_raw(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 256
+ order: 447
---
## `__elephc_strtotime_raw()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:543](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L543) (`lower_elephc_strtotime_raw`)
+- **Signature**: [`src/builtins/system/__elephc_strtotime_raw.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/__elephc_strtotime_raw.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:543](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L543) (`lower_elephc_strtotime_raw`)
- **Function symbol**: `lower_elephc_strtotime_raw()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function __elephc_strtotime_raw(string $datetime, int $baseTimestamp): int
+function __elephc_strtotime_raw(string $datetime, int $baseTimestamp = null): int
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function __elephc_strtotime_raw(string $datetime, int $baseTimestamp): int
## Cross-references
- _No user-facing reference — this is a compiler internal helper._
-
diff --git a/docs/internals/builtins/array/array_all.md b/docs/internals/builtins/array/array_all.md
new file mode 100644
index 0000000000..25d33ca7c2
--- /dev/null
+++ b/docs/internals/builtins/array/array_all.md
@@ -0,0 +1,39 @@
+---
+title: "array_all() — internals"
+description: "Compiler internals for array_all(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 1
+---
+
+## `array_all()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_all.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_all.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1578](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1578) (`lower_array_all`)
+- **Function symbol**: `lower_array_all()`
+
+
+### Lowering notes
+
+- Lowers `array_all()`: returns true when every element satisfies the predicate.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_udiff_uintersect`
+- `__rt_array_walk_recursive`
+
+## Signature summary
+
+```php
+function array_all(mixed $array, mixed $callback): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `array_all()`](../../../php/builtins/array/array_all.md)
diff --git a/docs/internals/builtins/array/array_any.md b/docs/internals/builtins/array/array_any.md
new file mode 100644
index 0000000000..3ade04e927
--- /dev/null
+++ b/docs/internals/builtins/array/array_any.md
@@ -0,0 +1,38 @@
+---
+title: "array_any() — internals"
+description: "Compiler internals for array_any(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 2
+---
+
+## `array_any()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_any.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_any.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1573](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1573) (`lower_array_any`)
+- **Function symbol**: `lower_array_any()`
+
+
+### Lowering notes
+
+- Lowers `array_any()`: returns true when some element satisfies the predicate.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_walk_recursive`
+
+## Signature summary
+
+```php
+function array_any(mixed $array, mixed $callback): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `array_any()`](../../../php/builtins/array/array_any.md)
diff --git a/docs/internals/builtins/array/array_chunk.md b/docs/internals/builtins/array/array_chunk.md
index 1b003782b4..46bd53333e 100644
--- a/docs/internals/builtins/array/array_chunk.md
+++ b/docs/internals/builtins/array/array_chunk.md
@@ -2,15 +2,15 @@
title: "array_chunk() — internals"
description: "Compiler internals for array_chunk(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 1
+ order: 3
---
## `array_chunk()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:81](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L81) (`lower_array_chunk`)
+- **Signature**: [`src/builtins/array/array_chunk.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_chunk.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:80](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L80) (`lower_array_chunk`)
- **Function symbol**: `lower_array_chunk()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_chunk(array $array, int $length, bool $preserve_keys): array
+function array_chunk(array $array, int $length): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `array_chunk()`](../../../php/builtins/array/array_chunk.md)
-
diff --git a/docs/internals/builtins/array/array_column.md b/docs/internals/builtins/array/array_column.md
index 043156eb44..ee7a38c63c 100644
--- a/docs/internals/builtins/array/array_column.md
+++ b/docs/internals/builtins/array/array_column.md
@@ -2,15 +2,15 @@
title: "array_column() — internals"
description: "Compiler internals for array_column(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 2
+ order: 4
---
## `array_column()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/column.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/column.rs#L23) (`lower_array_column`)
+- **Signature**: [`src/builtins/array/array_column.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_column.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/column.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/column.rs#L23) (`lower_array_column`)
- **Function symbol**: `lower_array_column()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_column(array $array, string $column_key, string $index_key): array
+function array_column(array $array, string $column_key): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `array_column()`](../../../php/builtins/array/array_column.md)
-
diff --git a/docs/internals/builtins/array/array_combine.md b/docs/internals/builtins/array/array_combine.md
index 4c6c84fbff..3443233226 100644
--- a/docs/internals/builtins/array/array_combine.md
+++ b/docs/internals/builtins/array/array_combine.md
@@ -2,21 +2,21 @@
title: "array_combine() — internals"
description: "Compiler internals for array_combine(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 3
+ order: 5
---
## `array_combine()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:152](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L152) (`lower_array_combine`)
+- **Signature**: [`src/builtins/array/array_combine.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_combine.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:160](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L160) (`lower_array_combine`)
- **Function symbol**: `lower_array_combine()`
### Lowering notes
-- Lowers `array_combine()` through the legacy hash-building runtime helpers.
+- Lowers `array_combine()` through the hash-building runtime helpers.
## Runtime helpers
@@ -35,4 +35,3 @@ function array_combine(array $keys, array $values): array
## Cross-references
- [User reference for `array_combine()`](../../../php/builtins/array/array_combine.md)
-
diff --git a/docs/internals/builtins/array/array_diff.md b/docs/internals/builtins/array/array_diff.md
index 4c0a2152f2..0b5cbcfb05 100644
--- a/docs/internals/builtins/array/array_diff.md
+++ b/docs/internals/builtins/array/array_diff.md
@@ -2,15 +2,15 @@
title: "array_diff() — internals"
description: "Compiler internals for array_diff(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 4
+ order: 6
---
## `array_diff()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:874](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L874) (`lower_array_diff`)
+- **Signature**: [`src/builtins/array/array_diff.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_diff.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:870](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L870) (`lower_array_diff`)
- **Function symbol**: `lower_array_diff()`
@@ -25,7 +25,6 @@ The following runtime helpers are referenced:
- `__rt_array_diff_key`
- `__rt_array_diff_refcounted`
- `__rt_array_intersect`
-- `__rt_array_intersect_key`
- `__rt_array_intersect_refcounted`
## Signature summary
@@ -42,4 +41,3 @@ function array_diff(array $array, ...$arrays): array
## Cross-references
- [User reference for `array_diff()`](../../../php/builtins/array/array_diff.md)
-
diff --git a/docs/internals/builtins/array/array_diff_assoc.md b/docs/internals/builtins/array/array_diff_assoc.md
new file mode 100644
index 0000000000..9d53e45ba5
--- /dev/null
+++ b/docs/internals/builtins/array/array_diff_assoc.md
@@ -0,0 +1,39 @@
+---
+title: "array_diff_assoc() — internals"
+description: "Compiler internals for array_diff_assoc(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 7
+---
+
+## `array_diff_assoc()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_diff_assoc.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_diff_assoc.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1359](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1359) (`lower_array_diff_assoc`)
+- **Function symbol**: `lower_array_diff_assoc()`
+
+
+### Lowering notes
+
+- Lowers `array_diff_assoc()` via the shared associative diff/intersect helper (mode 0 = diff).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_assoc_diff_intersect`
+
+## Signature summary
+
+```php
+function array_diff_assoc(array $array, ...$arrays): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+- **Variadic**: collects excess arguments into `$arrays`.
+
+## Cross-references
+
+- [User reference for `array_diff_assoc()`](../../../php/builtins/array/array_diff_assoc.md)
diff --git a/docs/internals/builtins/array/array_diff_key.md b/docs/internals/builtins/array/array_diff_key.md
index 8ed9625fb1..d3278e96f6 100644
--- a/docs/internals/builtins/array/array_diff_key.md
+++ b/docs/internals/builtins/array/array_diff_key.md
@@ -2,15 +2,15 @@
title: "array_diff_key() — internals"
description: "Compiler internals for array_diff_key(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 5
+ order: 8
---
## `array_diff_key()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:896](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L896) (`lower_array_diff_key`)
+- **Signature**: [`src/builtins/array/array_diff_key.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_diff_key.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:895](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L895) (`lower_array_diff_key`)
- **Function symbol**: `lower_array_diff_key()`
@@ -38,4 +38,3 @@ function array_diff_key(array $array, ...$arrays): array
## Cross-references
- [User reference for `array_diff_key()`](../../../php/builtins/array/array_diff_key.md)
-
diff --git a/docs/internals/builtins/array/array_fill.md b/docs/internals/builtins/array/array_fill.md
index 437345b8fb..507edba2c7 100644
--- a/docs/internals/builtins/array/array_fill.md
+++ b/docs/internals/builtins/array/array_fill.md
@@ -2,15 +2,15 @@
title: "array_fill() — internals"
description: "Compiler internals for array_fill(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 6
+ order: 9
---
## `array_fill()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:115](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L115) (`lower_array_fill`)
+- **Signature**: [`src/builtins/array/array_fill.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_fill.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:116](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L116) (`lower_array_fill`)
- **Function symbol**: `lower_array_fill()`
@@ -35,4 +35,3 @@ function array_fill(int $start_index, int $count, mixed $value): array
## Cross-references
- [User reference for `array_fill()`](../../../php/builtins/array/array_fill.md)
-
diff --git a/docs/internals/builtins/array/array_fill_keys.md b/docs/internals/builtins/array/array_fill_keys.md
index 4443eae331..28c5fa95fb 100644
--- a/docs/internals/builtins/array/array_fill_keys.md
+++ b/docs/internals/builtins/array/array_fill_keys.md
@@ -2,21 +2,21 @@
title: "array_fill_keys() — internals"
description: "Compiler internals for array_fill_keys(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 7
+ order: 10
---
## `array_fill_keys()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:138](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L138) (`lower_array_fill_keys`)
+- **Signature**: [`src/builtins/array/array_fill_keys.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_fill_keys.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L139) (`lower_array_fill_keys`)
- **Function symbol**: `lower_array_fill_keys()`
### Lowering notes
-- Lowers `array_fill_keys()` through the legacy hash-building runtime helpers.
+- Lowers `array_fill_keys()` through the hash-building runtime helpers.
## Runtime helpers
@@ -35,4 +35,3 @@ function array_fill_keys(array $keys, mixed $value): array
## Cross-references
- [User reference for `array_fill_keys()`](../../../php/builtins/array/array_fill_keys.md)
-
diff --git a/docs/internals/builtins/array/array_filter.md b/docs/internals/builtins/array/array_filter.md
index 2a51bd8899..55b3469842 100644
--- a/docs/internals/builtins/array/array_filter.md
+++ b/docs/internals/builtins/array/array_filter.md
@@ -2,15 +2,15 @@
title: "array_filter() — internals"
description: "Compiler internals for array_filter(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 8
+ order: 11
---
## `array_filter()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:211](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L211) (`lower_array_filter`)
+- **Signature**: [`src/builtins/array/array_filter.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_filter.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:221](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L221) (`lower_array_filter`)
- **Function symbol**: `lower_array_filter()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_filter(array $array, callable $callback, int $mode): array
+function array_filter(array $array, callable $callback = null, int $mode = 0): array
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function array_filter(array $array, callable $callback, int $mode): array
## Cross-references
- [User reference for `array_filter()`](../../../php/builtins/array/array_filter.md)
-
diff --git a/docs/internals/builtins/array/array_find.md b/docs/internals/builtins/array/array_find.md
new file mode 100644
index 0000000000..e9df77a4fd
--- /dev/null
+++ b/docs/internals/builtins/array/array_find.md
@@ -0,0 +1,38 @@
+---
+title: "array_find() — internals"
+description: "Compiler internals for array_find(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 12
+---
+
+## `array_find()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_find.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_find.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1568](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1568) (`lower_array_find`)
+- **Function symbol**: `lower_array_find()`
+
+
+### Lowering notes
+
+- Lowers `array_find()`: returns the first element satisfying the predicate, boxed as Mixed (or null).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_walk_recursive`
+
+## Signature summary
+
+```php
+function array_find(mixed $array, mixed $callback): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `array_find()`](../../../php/builtins/array/array_find.md)
diff --git a/docs/internals/builtins/array/array_flip.md b/docs/internals/builtins/array/array_flip.md
index 188b9f246a..d1b8864780 100644
--- a/docs/internals/builtins/array/array_flip.md
+++ b/docs/internals/builtins/array/array_flip.md
@@ -2,21 +2,21 @@
title: "array_flip() — internals"
description: "Compiler internals for array_flip(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 9
+ order: 13
---
## `array_flip()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:171](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L171) (`lower_array_flip`)
+- **Signature**: [`src/builtins/array/array_flip.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_flip.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:179](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L179) (`lower_array_flip`)
- **Function symbol**: `lower_array_flip()`
### Lowering notes
-- Lowers `array_flip()` through the legacy hash-building runtime helpers.
+- Lowers `array_flip()` through the hash-building runtime helpers.
## Runtime helpers
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_flip(array $array): float
+function array_flip(array $array): array
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function array_flip(array $array): float
## Cross-references
- [User reference for `array_flip()`](../../../php/builtins/array/array_flip.md)
-
diff --git a/docs/internals/builtins/array/array_intersect.md b/docs/internals/builtins/array/array_intersect.md
index 3bf45c26ab..e16a8026f6 100644
--- a/docs/internals/builtins/array/array_intersect.md
+++ b/docs/internals/builtins/array/array_intersect.md
@@ -2,15 +2,15 @@
title: "array_intersect() — internals"
description: "Compiler internals for array_intersect(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 10
+ order: 14
---
## `array_intersect()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:885](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L885) (`lower_array_intersect`)
+- **Signature**: [`src/builtins/array/array_intersect.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_intersect.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:881](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L881) (`lower_array_intersect`)
- **Function symbol**: `lower_array_intersect()`
@@ -40,4 +40,3 @@ function array_intersect(array $array, ...$arrays): array
## Cross-references
- [User reference for `array_intersect()`](../../../php/builtins/array/array_intersect.md)
-
diff --git a/docs/internals/builtins/array/array_intersect_assoc.md b/docs/internals/builtins/array/array_intersect_assoc.md
new file mode 100644
index 0000000000..eeea729525
--- /dev/null
+++ b/docs/internals/builtins/array/array_intersect_assoc.md
@@ -0,0 +1,42 @@
+---
+title: "array_intersect_assoc() — internals"
+description: "Compiler internals for array_intersect_assoc(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 15
+---
+
+## `array_intersect_assoc()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_intersect_assoc.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_intersect_assoc.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1373](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1373) (`lower_array_intersect_assoc`)
+- **Function symbol**: `lower_array_intersect_assoc()`
+
+
+### Lowering notes
+
+- Lowers `array_intersect_assoc()` via the shared associative diff/intersect helper (mode 1 = intersect).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_find_any_all`
+- `__rt_array_merge_recursive`
+- `__rt_array_udiff_uintersect`
+- `__rt_assoc_diff_intersect`
+
+## Signature summary
+
+```php
+function array_intersect_assoc(array $array, ...$arrays): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+- **Variadic**: collects excess arguments into `$arrays`.
+
+## Cross-references
+
+- [User reference for `array_intersect_assoc()`](../../../php/builtins/array/array_intersect_assoc.md)
diff --git a/docs/internals/builtins/array/array_intersect_key.md b/docs/internals/builtins/array/array_intersect_key.md
index fae3b8802a..5d15bd7f22 100644
--- a/docs/internals/builtins/array/array_intersect_key.md
+++ b/docs/internals/builtins/array/array_intersect_key.md
@@ -2,15 +2,15 @@
title: "array_intersect_key() — internals"
description: "Compiler internals for array_intersect_key(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 11
+ order: 16
---
## `array_intersect_key()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:901](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L901) (`lower_array_intersect_key`)
+- **Signature**: [`src/builtins/array/array_intersect_key.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_intersect_key.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:903](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L903) (`lower_array_intersect_key`)
- **Function symbol**: `lower_array_intersect_key()`
@@ -37,4 +37,3 @@ function array_intersect_key(array $array, ...$arrays): array
## Cross-references
- [User reference for `array_intersect_key()`](../../../php/builtins/array/array_intersect_key.md)
-
diff --git a/docs/internals/builtins/array/array_is_list.md b/docs/internals/builtins/array/array_is_list.md
new file mode 100644
index 0000000000..382509af6f
--- /dev/null
+++ b/docs/internals/builtins/array/array_is_list.md
@@ -0,0 +1,42 @@
+---
+title: "array_is_list() — internals"
+description: "Compiler internals for array_is_list(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 17
+---
+
+## `array_is_list()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_is_list.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_is_list.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1150](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1150) (`lower_array_is_list`)
+- **Function symbol**: `lower_array_is_list()`
+
+
+### Lowering notes
+
+- Lowers `array_is_list()` to the `__rt_array_is_list` runtime predicate, returning a bool.
+- The runtime helper accepts any array kind (indexed, associative hash, or boxed mixed cell) and
+- reports `1` when the keys are the sequential integers `0..n-1` in insertion order, `0` otherwise.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_edge_key`
+- `__rt_array_is_list`
+- `__rt_mixed_from_value`
+
+## Signature summary
+
+```php
+function array_is_list(mixed $array): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- [User reference for `array_is_list()`](../../../php/builtins/array/array_is_list.md)
diff --git a/docs/internals/builtins/array/array_key_exists.md b/docs/internals/builtins/array/array_key_exists.md
index 9b29e69060..d75a3f886a 100644
--- a/docs/internals/builtins/array/array_key_exists.md
+++ b/docs/internals/builtins/array/array_key_exists.md
@@ -2,15 +2,15 @@
title: "array_key_exists() — internals"
description: "Compiler internals for array_key_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 12
+ order: 18
---
## `array_key_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/key_exists.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/key_exists.rs#L22) (`lower_array_key_exists`)
+- **Signature**: [`src/builtins/array/array_key_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_key_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/key_exists.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/key_exists.rs#L22) (`lower_array_key_exists`)
- **Function symbol**: `lower_array_key_exists()`
@@ -35,4 +35,3 @@ function array_key_exists(string $key, array $array): bool
## Cross-references
- [User reference for `array_key_exists()`](../../../php/builtins/array/array_key_exists.md)
-
diff --git a/docs/internals/builtins/array/array_key_first.md b/docs/internals/builtins/array/array_key_first.md
new file mode 100644
index 0000000000..6e1607d78a
--- /dev/null
+++ b/docs/internals/builtins/array/array_key_first.md
@@ -0,0 +1,39 @@
+---
+title: "array_key_first() — internals"
+description: "Compiler internals for array_key_first(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 19
+---
+
+## `array_key_first()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_key_first.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_key_first.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1161](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1161) (`lower_array_key_first`)
+- **Function symbol**: `lower_array_key_first()`
+
+
+### Lowering notes
+
+- Lowers `array_key_first()` through the shared edge-key helper with selector `0`.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_edge_key`
+- `__rt_mixed_from_value`
+
+## Signature summary
+
+```php
+function array_key_first(array $array): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- [User reference for `array_key_first()`](../../../php/builtins/array/array_key_first.md)
diff --git a/docs/internals/builtins/array/array_key_last.md b/docs/internals/builtins/array/array_key_last.md
new file mode 100644
index 0000000000..dead1f9f6f
--- /dev/null
+++ b/docs/internals/builtins/array/array_key_last.md
@@ -0,0 +1,39 @@
+---
+title: "array_key_last() — internals"
+description: "Compiler internals for array_key_last(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 20
+---
+
+## `array_key_last()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_key_last.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_key_last.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1169](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1169) (`lower_array_key_last`)
+- **Function symbol**: `lower_array_key_last()`
+
+
+### Lowering notes
+
+- Lowers `array_key_last()` through the shared edge-key helper with selector `1`.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_edge_key`
+- `__rt_mixed_from_value`
+
+## Signature summary
+
+```php
+function array_key_last(array $array): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- [User reference for `array_key_last()`](../../../php/builtins/array/array_key_last.md)
diff --git a/docs/internals/builtins/array/array_keys.md b/docs/internals/builtins/array/array_keys.md
index 3bca53274d..4456b39e94 100644
--- a/docs/internals/builtins/array/array_keys.md
+++ b/docs/internals/builtins/array/array_keys.md
@@ -2,15 +2,15 @@
title: "array_keys() — internals"
description: "Compiler internals for array_keys(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 13
+ order: 21
---
## `array_keys()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/keys.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/keys.rs#L23) (`lower_array_keys`)
+- **Signature**: [`src/builtins/array/array_keys.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_keys.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/keys.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/keys.rs#L23) (`lower_array_keys`)
- **Function symbol**: `lower_array_keys()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_keys(array $array, string $filter_value, bool $strict): array
+function array_keys(array $array): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `array_keys()`](../../../php/builtins/array/array_keys.md)
-
diff --git a/docs/internals/builtins/array/array_map.md b/docs/internals/builtins/array/array_map.md
index abd40ca2c4..fab8abb735 100644
--- a/docs/internals/builtins/array/array_map.md
+++ b/docs/internals/builtins/array/array_map.md
@@ -2,15 +2,15 @@
title: "array_map() — internals"
description: "Compiler internals for array_map(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 14
+ order: 22
---
## `array_map()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:312](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L312) (`lower_array_map`)
+- **Signature**: [`src/builtins/array/array_map.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_map.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:326](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L326) (`lower_array_map`)
- **Function symbol**: `lower_array_map()`
@@ -36,4 +36,3 @@ function array_map(callable $callback, array $array, ...$arrays): array
## Cross-references
- [User reference for `array_map()`](../../../php/builtins/array/array_map.md)
-
diff --git a/docs/internals/builtins/array/array_merge.md b/docs/internals/builtins/array/array_merge.md
index 04de54292c..afd57e2ddf 100644
--- a/docs/internals/builtins/array/array_merge.md
+++ b/docs/internals/builtins/array/array_merge.md
@@ -2,15 +2,15 @@
title: "array_merge() — internals"
description: "Compiler internals for array_merge(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 15
+ order: 23
---
## `array_merge()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:850](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L850) (`lower_array_merge`)
+- **Signature**: [`src/builtins/array/array_merge.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_merge.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:846](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L846) (`lower_array_merge`)
- **Function symbol**: `lower_array_merge()`
@@ -38,4 +38,3 @@ function array_merge(...$arrays): array
## Cross-references
- [User reference for `array_merge()`](../../../php/builtins/array/array_merge.md)
-
diff --git a/docs/internals/builtins/array/array_merge_recursive.md b/docs/internals/builtins/array/array_merge_recursive.md
new file mode 100644
index 0000000000..2b9dcd6b1f
--- /dev/null
+++ b/docs/internals/builtins/array/array_merge_recursive.md
@@ -0,0 +1,41 @@
+---
+title: "array_merge_recursive() — internals"
+description: "Compiler internals for array_merge_recursive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 24
+---
+
+## `array_merge_recursive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_merge_recursive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_merge_recursive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1387](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1387) (`lower_array_merge_recursive`)
+- **Function symbol**: `lower_array_merge_recursive()`
+
+
+### Lowering notes
+
+- Lowers `array_merge_recursive()` (recursive merge with scalar collisions combined into lists).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_find_any_all`
+- `__rt_array_merge_recursive`
+- `__rt_array_udiff_uintersect`
+
+## Signature summary
+
+```php
+function array_merge_recursive(...$arrays): array
+```
+
+## What the type checker enforces
+
+- **Arity**: takes no arguments.
+- **Variadic**: collects excess arguments into `$arrays`.
+
+## Cross-references
+
+- [User reference for `array_merge_recursive()`](../../../php/builtins/array/array_merge_recursive.md)
diff --git a/docs/internals/builtins/array/array_multisort.md b/docs/internals/builtins/array/array_multisort.md
new file mode 100644
index 0000000000..cbfe1d8d7b
--- /dev/null
+++ b/docs/internals/builtins/array/array_multisort.md
@@ -0,0 +1,41 @@
+---
+title: "array_multisort() — internals"
+description: "Compiler internals for array_multisort(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 25
+---
+
+## `array_multisort()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_multisort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_multisort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1719](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1719) (`lower_array_multisort`)
+- **Function symbol**: `lower_array_multisort()`
+
+
+### Lowering notes
+
+- Lowers `array_multisort()`: stable-sorts the first indexed array ascending and reorders the second
+- in tandem, both in place. Both arguments are by-reference, so each is copy-on-write split with
+- `ensure_unique_sort_source` and the (possibly relocated) pointer is written back to its local
+- before the runtime mutates the storage. Returns `true`. Supports 8-byte scalar indexed arrays.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function array_multisort(array $array1, int $array2): bool
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+- **By-reference parameters**: `$array1`, `$array2`.
+
+## Cross-references
+
+- [User reference for `array_multisort()`](../../../php/builtins/array/array_multisort.md)
diff --git a/docs/internals/builtins/array/array_pad.md b/docs/internals/builtins/array/array_pad.md
index 50996b40c6..d24cbcc12e 100644
--- a/docs/internals/builtins/array/array_pad.md
+++ b/docs/internals/builtins/array/array_pad.md
@@ -2,15 +2,15 @@
title: "array_pad() — internals"
description: "Compiler internals for array_pad(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 16
+ order: 26
---
## `array_pad()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:99](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L99) (`lower_array_pad`)
+- **Signature**: [`src/builtins/array/array_pad.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_pad.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:99](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L99) (`lower_array_pad`)
- **Function symbol**: `lower_array_pad()`
@@ -35,4 +35,3 @@ function array_pad(array $array, int $length, mixed $value): array
## Cross-references
- [User reference for `array_pad()`](../../../php/builtins/array/array_pad.md)
-
diff --git a/docs/internals/builtins/array/array_pop.md b/docs/internals/builtins/array/array_pop.md
index 57793e3d3c..b524438a57 100644
--- a/docs/internals/builtins/array/array_pop.md
+++ b/docs/internals/builtins/array/array_pop.md
@@ -2,15 +2,15 @@
title: "array_pop() — internals"
description: "Compiler internals for array_pop(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 17
+ order: 27
---
## `array_pop()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1048](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1048) (`lower_array_pop`)
+- **Signature**: [`src/builtins/array/array_pop.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_pop.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1051](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1051) (`lower_array_pop`)
- **Function symbol**: `lower_array_pop()`
@@ -38,4 +38,3 @@ function array_pop(array $array): mixed
## Cross-references
- [User reference for `array_pop()`](../../../php/builtins/array/array_pop.md)
-
diff --git a/docs/internals/builtins/array/array_product.md b/docs/internals/builtins/array/array_product.md
index d05a4b5e37..9823cef4cb 100644
--- a/docs/internals/builtins/array/array_product.md
+++ b/docs/internals/builtins/array/array_product.md
@@ -2,15 +2,15 @@
title: "array_product() — internals"
description: "Compiler internals for array_product(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 18
+ order: 28
---
## `array_product()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:56](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L56) (`lower_array_product`)
+- **Signature**: [`src/builtins/array/array_product.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_product.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:55](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L55) (`lower_array_product`)
- **Function symbol**: `lower_array_product()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_product(array $array): float
+function array_product(array $array): int
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function array_product(array $array): float
## Cross-references
- [User reference for `array_product()`](../../../php/builtins/array/array_product.md)
-
diff --git a/docs/internals/builtins/array/array_push.md b/docs/internals/builtins/array/array_push.md
index 4fa879726d..daf4d4d7b6 100644
--- a/docs/internals/builtins/array/array_push.md
+++ b/docs/internals/builtins/array/array_push.md
@@ -2,15 +2,15 @@
title: "array_push() — internals"
description: "Compiler internals for array_push(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 19
+ order: 29
---
## `array_push()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:61](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L61) (`lower_array_push`)
+- **Signature**: [`src/builtins/array/array_push.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_push.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:60](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L60) (`lower_array_push`)
- **Function symbol**: `lower_array_push()`
@@ -37,4 +37,3 @@ function array_push(array $array, ...$values): void
## Cross-references
- [User reference for `array_push()`](../../../php/builtins/array/array_push.md)
-
diff --git a/docs/internals/builtins/array/array_rand.md b/docs/internals/builtins/array/array_rand.md
index a3e91ba11e..8c035ab48a 100644
--- a/docs/internals/builtins/array/array_rand.md
+++ b/docs/internals/builtins/array/array_rand.md
@@ -2,15 +2,15 @@
title: "array_rand() — internals"
description: "Compiler internals for array_rand(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 20
+ order: 30
---
## `array_rand()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1007](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1007) (`lower_array_rand`)
+- **Signature**: [`src/builtins/array/array_rand.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_rand.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1010](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1010) (`lower_array_rand`)
- **Function symbol**: `lower_array_rand()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_rand(array $array, int $num): int
+function array_rand(array $array): int
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `array_rand()`](../../../php/builtins/array/array_rand.md)
-
diff --git a/docs/internals/builtins/array/array_reduce.md b/docs/internals/builtins/array/array_reduce.md
index 8decf44861..302d47051e 100644
--- a/docs/internals/builtins/array/array_reduce.md
+++ b/docs/internals/builtins/array/array_reduce.md
@@ -2,15 +2,15 @@
title: "array_reduce() — internals"
description: "Compiler internals for array_reduce(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 21
+ order: 31
---
## `array_reduce()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:701](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L701) (`lower_array_reduce`)
+- **Signature**: [`src/builtins/array/array_reduce.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_reduce.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:695](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L695) (`lower_array_reduce`)
- **Function symbol**: `lower_array_reduce()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_reduce(array $array, callable $callback, mixed $initial): int
+function array_reduce(array $array, callable $callback, mixed $initial = null): int
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function array_reduce(array $array, callable $callback, mixed $initial): int
## Cross-references
- [User reference for `array_reduce()`](../../../php/builtins/array/array_reduce.md)
-
diff --git a/docs/internals/builtins/array/array_replace.md b/docs/internals/builtins/array/array_replace.md
new file mode 100644
index 0000000000..2df08cdd42
--- /dev/null
+++ b/docs/internals/builtins/array/array_replace.md
@@ -0,0 +1,40 @@
+---
+title: "array_replace() — internals"
+description: "Compiler internals for array_replace(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 32
+---
+
+## `array_replace()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_replace.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_replace.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1340](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1340) (`lower_array_replace`)
+- **Function symbol**: `lower_array_replace()`
+
+
+### Lowering notes
+
+- Lowers `array_replace()` (right-wins hash merge of two hashes).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_replace`
+- `__rt_array_replace_recursive`
+- `__rt_assoc_diff_intersect`
+
+## Signature summary
+
+```php
+function array_replace(array $array, array $replacements): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `array_replace()`](../../../php/builtins/array/array_replace.md)
diff --git a/docs/internals/builtins/array/array_replace_recursive.md b/docs/internals/builtins/array/array_replace_recursive.md
new file mode 100644
index 0000000000..d85275ea3f
--- /dev/null
+++ b/docs/internals/builtins/array/array_replace_recursive.md
@@ -0,0 +1,39 @@
+---
+title: "array_replace_recursive() — internals"
+description: "Compiler internals for array_replace_recursive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 33
+---
+
+## `array_replace_recursive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_replace_recursive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_replace_recursive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1345](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1345) (`lower_array_replace_recursive`)
+- **Function symbol**: `lower_array_replace_recursive()`
+
+
+### Lowering notes
+
+- Lowers `array_replace_recursive()` (recursive right-wins hash merge).
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_replace_recursive`
+- `__rt_assoc_diff_intersect`
+
+## Signature summary
+
+```php
+function array_replace_recursive(array $array, array $replacements): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `array_replace_recursive()`](../../../php/builtins/array/array_replace_recursive.md)
diff --git a/docs/internals/builtins/array/array_reverse.md b/docs/internals/builtins/array/array_reverse.md
index b8036cb78d..fec27a94d2 100644
--- a/docs/internals/builtins/array/array_reverse.md
+++ b/docs/internals/builtins/array/array_reverse.md
@@ -2,15 +2,15 @@
title: "array_reverse() — internals"
description: "Compiler internals for array_reverse(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 22
+ order: 34
---
## `array_reverse()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:185](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L185) (`lower_array_reverse`)
+- **Signature**: [`src/builtins/array/array_reverse.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_reverse.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:193](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L193) (`lower_array_reverse`)
- **Function symbol**: `lower_array_reverse()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_reverse(array $array, bool $preserve_keys): array
+function array_reverse(array $array): array
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `array_reverse()`](../../../php/builtins/array/array_reverse.md)
-
diff --git a/docs/internals/builtins/array/array_search.md b/docs/internals/builtins/array/array_search.md
index c436b47093..2d0fc7acc5 100644
--- a/docs/internals/builtins/array/array_search.md
+++ b/docs/internals/builtins/array/array_search.md
@@ -2,15 +2,15 @@
title: "array_search() — internals"
description: "Compiler internals for array_search(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 23
+ order: 35
---
## `array_search()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1141](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1141) (`lower_array_search`)
+- **Signature**: [`src/builtins/array/array_search.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_search.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1761](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1761) (`lower_array_search`)
- **Function symbol**: `lower_array_search()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_search(mixed $needle, array $haystack, bool $strict): mixed
+function array_search(mixed $needle, array $haystack, bool $strict = false): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function array_search(mixed $needle, array $haystack, bool $strict): mixed
## Cross-references
- [User reference for `array_search()`](../../../php/builtins/array/array_search.md)
-
diff --git a/docs/internals/builtins/array/array_shift.md b/docs/internals/builtins/array/array_shift.md
index 23c39b9e7d..a724e7335d 100644
--- a/docs/internals/builtins/array/array_shift.md
+++ b/docs/internals/builtins/array/array_shift.md
@@ -2,15 +2,15 @@
title: "array_shift() — internals"
description: "Compiler internals for array_shift(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 24
+ order: 36
---
## `array_shift()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/shift.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/shift.rs#L23) (`lower_array_shift`)
+- **Signature**: [`src/builtins/array/array_shift.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_shift.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/shift.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/shift.rs#L23) (`lower_array_shift`)
- **Function symbol**: `lower_array_shift()`
@@ -36,4 +36,3 @@ function array_shift(array $array): mixed
## Cross-references
- [User reference for `array_shift()`](../../../php/builtins/array/array_shift.md)
-
diff --git a/docs/internals/builtins/array/array_slice.md b/docs/internals/builtins/array/array_slice.md
index 2e713b100a..f7c38d4b7f 100644
--- a/docs/internals/builtins/array/array_slice.md
+++ b/docs/internals/builtins/array/array_slice.md
@@ -2,15 +2,15 @@
title: "array_slice() — internals"
description: "Compiler internals for array_slice(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 25
+ order: 37
---
## `array_slice()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:906](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L906) (`lower_array_slice`)
+- **Signature**: [`src/builtins/array/array_slice.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_slice.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:911](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L911) (`lower_array_slice`)
- **Function symbol**: `lower_array_slice()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_slice(array $array, int $offset, int $length, bool $preserve_keys): array
+function array_slice(array $array, int $offset, int $length = null): array
```
## What the type checker enforces
-- **Arity**: takes 3–4 arguments (1 optional).
+- **Arity**: takes 2–3 arguments (1 optional).
## Cross-references
- [User reference for `array_slice()`](../../../php/builtins/array/array_slice.md)
-
diff --git a/docs/internals/builtins/array/array_splice.md b/docs/internals/builtins/array/array_splice.md
index f1c8731084..9253e05907 100644
--- a/docs/internals/builtins/array/array_splice.md
+++ b/docs/internals/builtins/array/array_splice.md
@@ -2,15 +2,15 @@
title: "array_splice() — internals"
description: "Compiler internals for array_splice(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 26
+ order: 38
---
## `array_splice()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:949](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L949) (`lower_array_splice`)
+- **Signature**: [`src/builtins/array/array_splice.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_splice.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:956](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L956) (`lower_array_splice`)
- **Function symbol**: `lower_array_splice()`
@@ -25,15 +25,14 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function array_splice(array $array, int $offset, int $length, array $replacement): array
+function array_splice(array $array, int $offset, int $length = null): array
```
## What the type checker enforces
-- **Arity**: takes 3–4 arguments (1 optional).
+- **Arity**: takes 2–3 arguments (1 optional).
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `array_splice()`](../../../php/builtins/array/array_splice.md)
-
diff --git a/docs/internals/builtins/array/array_sum.md b/docs/internals/builtins/array/array_sum.md
index b08a04adb2..5157c8d94b 100644
--- a/docs/internals/builtins/array/array_sum.md
+++ b/docs/internals/builtins/array/array_sum.md
@@ -2,15 +2,15 @@
title: "array_sum() — internals"
description: "Compiler internals for array_sum(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 27
+ order: 39
---
## `array_sum()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:51](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L51) (`lower_array_sum`)
+- **Signature**: [`src/builtins/array/array_sum.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_sum.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:50](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L50) (`lower_array_sum`)
- **Function symbol**: `lower_array_sum()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_sum(array $array): float
+function array_sum(array $array): int
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function array_sum(array $array): float
## Cross-references
- [User reference for `array_sum()`](../../../php/builtins/array/array_sum.md)
-
diff --git a/docs/internals/builtins/array/array_udiff.md b/docs/internals/builtins/array/array_udiff.md
new file mode 100644
index 0000000000..532e8c0091
--- /dev/null
+++ b/docs/internals/builtins/array/array_udiff.md
@@ -0,0 +1,37 @@
+---
+title: "array_udiff() — internals"
+description: "Compiler internals for array_udiff(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 40
+---
+
+## `array_udiff()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_udiff.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_udiff.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1703](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1703) (`lower_array_udiff`)
+- **Function symbol**: `lower_array_udiff()`
+
+
+### Lowering notes
+
+- Lowers `array_udiff()`: keeps first-array elements not equal (per comparator) to any second-array element.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function array_udiff(array $array1, array $array2, callable $callback): array
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 3 arguments.
+
+## Cross-references
+
+- [User reference for `array_udiff()`](../../../php/builtins/array/array_udiff.md)
diff --git a/docs/internals/builtins/array/array_uintersect.md b/docs/internals/builtins/array/array_uintersect.md
new file mode 100644
index 0000000000..3e785eacad
--- /dev/null
+++ b/docs/internals/builtins/array/array_uintersect.md
@@ -0,0 +1,37 @@
+---
+title: "array_uintersect() — internals"
+description: "Compiler internals for array_uintersect(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 41
+---
+
+## `array_uintersect()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_uintersect.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_uintersect.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1708](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1708) (`lower_array_uintersect`)
+- **Function symbol**: `lower_array_uintersect()`
+
+
+### Lowering notes
+
+- Lowers `array_uintersect()`: keeps first-array elements equal (per comparator) to some second-array element.
+
+## Runtime helpers
+
+_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+
+## Signature summary
+
+```php
+function array_uintersect(array $array1, array $array2, callable $callback): array
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 3 arguments.
+
+## Cross-references
+
+- [User reference for `array_uintersect()`](../../../php/builtins/array/array_uintersect.md)
diff --git a/docs/internals/builtins/array/array_unique.md b/docs/internals/builtins/array/array_unique.md
index 3dc034bf15..25f753cfee 100644
--- a/docs/internals/builtins/array/array_unique.md
+++ b/docs/internals/builtins/array/array_unique.md
@@ -2,15 +2,15 @@
title: "array_unique() — internals"
description: "Compiler internals for array_unique(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 28
+ order: 42
---
## `array_unique()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:198](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L198) (`lower_array_unique`)
+- **Signature**: [`src/builtins/array/array_unique.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_unique.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:207](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L207) (`lower_array_unique`)
- **Function symbol**: `lower_array_unique()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_unique(array $array, int $flags): array
+function array_unique(array $array): array
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `array_unique()`](../../../php/builtins/array/array_unique.md)
-
diff --git a/docs/internals/builtins/array/array_unshift.md b/docs/internals/builtins/array/array_unshift.md
index f131d0a583..5a84c4c60c 100644
--- a/docs/internals/builtins/array/array_unshift.md
+++ b/docs/internals/builtins/array/array_unshift.md
@@ -2,15 +2,15 @@
title: "array_unshift() — internals"
description: "Compiler internals for array_unshift(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 29
+ order: 43
---
## `array_unshift()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/unshift.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/unshift.rs#L23) (`lower_array_unshift`)
+- **Signature**: [`src/builtins/array/array_unshift.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_unshift.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/unshift.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/unshift.rs#L23) (`lower_array_unshift`)
- **Function symbol**: `lower_array_unshift()`
@@ -37,4 +37,3 @@ function array_unshift(array $array, ...$values): int
## Cross-references
- [User reference for `array_unshift()`](../../../php/builtins/array/array_unshift.md)
-
diff --git a/docs/internals/builtins/array/array_values.md b/docs/internals/builtins/array/array_values.md
index 2cc23a9b85..b5b3b90a05 100644
--- a/docs/internals/builtins/array/array_values.md
+++ b/docs/internals/builtins/array/array_values.md
@@ -2,15 +2,15 @@
title: "array_values() — internals"
description: "Compiler internals for array_values(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 30
+ order: 44
---
## `array_values()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays/values.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays/values.rs#L22) (`lower_array_values`)
+- **Signature**: [`src/builtins/array/array_values.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_values.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays/values.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays/values.rs#L22) (`lower_array_values`)
- **Function symbol**: `lower_array_values()`
@@ -35,4 +35,3 @@ function array_values(array $array): array
## Cross-references
- [User reference for `array_values()`](../../../php/builtins/array/array_values.md)
-
diff --git a/docs/internals/builtins/array/array_walk.md b/docs/internals/builtins/array/array_walk.md
index 916c973000..590629ecd4 100644
--- a/docs/internals/builtins/array/array_walk.md
+++ b/docs/internals/builtins/array/array_walk.md
@@ -2,15 +2,15 @@
title: "array_walk() — internals"
description: "Compiler internals for array_walk(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 31
+ order: 45
---
## `array_walk()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:783](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L783) (`lower_array_walk`)
+- **Signature**: [`src/builtins/array/array_walk.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_walk.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:779](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L779) (`lower_array_walk`)
- **Function symbol**: `lower_array_walk()`
@@ -26,15 +26,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function array_walk(array $array, callable $callback, mixed $arg): void
+function array_walk(array $array, callable $callback): void
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `array_walk()`](../../../php/builtins/array/array_walk.md)
-
diff --git a/docs/internals/builtins/array/array_walk_recursive.md b/docs/internals/builtins/array/array_walk_recursive.md
new file mode 100644
index 0000000000..ae21641238
--- /dev/null
+++ b/docs/internals/builtins/array/array_walk_recursive.md
@@ -0,0 +1,41 @@
+---
+title: "array_walk_recursive() — internals"
+description: "Compiler internals for array_walk_recursive(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 46
+---
+
+## `array_walk_recursive()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/array/array_walk_recursive.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/array_walk_recursive.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1584](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1584) (`lower_array_walk_recursive`)
+- **Function symbol**: `lower_array_walk_recursive()`
+
+
+### Lowering notes
+
+- Lowers `array_walk_recursive()`: invokes the callback on each scalar leaf of a (possibly nested)
+- array, descending into array-valued elements. Returns void; leaves are passed as 8-byte scalars.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_udiff_uintersect`
+- `__rt_array_walk_recursive`
+
+## Signature summary
+
+```php
+function array_walk_recursive(array $array, callable $callback): void
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+- **By-reference parameters**: `$array`.
+
+## Cross-references
+
+- [User reference for `array_walk_recursive()`](../../../php/builtins/array/array_walk_recursive.md)
diff --git a/docs/internals/builtins/array/arsort.md b/docs/internals/builtins/array/arsort.md
index 63909a96c8..b30e0b6ea0 100644
--- a/docs/internals/builtins/array/arsort.md
+++ b/docs/internals/builtins/array/arsort.md
@@ -2,15 +2,15 @@
title: "arsort() — internals"
description: "Compiler internals for arsort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 32
+ order: 47
---
## `arsort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1091](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1091) (`lower_arsort`)
+- **Signature**: [`src/builtins/array/arsort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/arsort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1094](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1094) (`lower_arsort`)
- **Function symbol**: `lower_arsort()`
@@ -30,15 +30,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function arsort(array $array, int $flags): bool
+function arsort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `arsort()`](../../../php/builtins/array/arsort.md)
-
diff --git a/docs/internals/builtins/array/asort.md b/docs/internals/builtins/array/asort.md
index eda817e4a3..d4cff7b4bd 100644
--- a/docs/internals/builtins/array/asort.md
+++ b/docs/internals/builtins/array/asort.md
@@ -2,15 +2,15 @@
title: "asort() — internals"
description: "Compiler internals for asort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 33
+ order: 48
---
## `asort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1086](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1086) (`lower_asort`)
+- **Signature**: [`src/builtins/array/asort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/asort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1089](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1089) (`lower_asort`)
- **Function symbol**: `lower_asort()`
@@ -31,15 +31,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function asort(array $array, int $flags): bool
+function asort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `asort()`](../../../php/builtins/array/asort.md)
-
diff --git a/docs/internals/builtins/array/call_user_func.md b/docs/internals/builtins/array/call_user_func.md
new file mode 100644
index 0000000000..281edbe9f8
--- /dev/null
+++ b/docs/internals/builtins/array/call_user_func.md
@@ -0,0 +1,40 @@
+---
+title: "call_user_func() — internals"
+description: "Compiler internals for call_user_func(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 49
+---
+
+## `call_user_func()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/callables/call_user_func.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/call_user_func.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:37](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L37) (`lower_call_user_func_builtin_escape`)
+- **Function symbol**: `lower_call_user_func_builtin_escape()`
+
+
+### Lowering notes
+
+- Rejects `call_user_func*` calls that escaped the dedicated EIR callback lowering path.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_product`
+- `__rt_array_sum`
+
+## Signature summary
+
+```php
+function call_user_func(callable $callback, ...$args): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+- **Variadic**: collects excess arguments into `$args`.
+
+## Cross-references
+
+- [User reference for `call_user_func()`](../../../php/builtins/array/call_user_func.md)
diff --git a/docs/internals/builtins/array/call_user_func_array.md b/docs/internals/builtins/array/call_user_func_array.md
new file mode 100644
index 0000000000..ac8baf6e82
--- /dev/null
+++ b/docs/internals/builtins/array/call_user_func_array.md
@@ -0,0 +1,39 @@
+---
+title: "call_user_func_array() — internals"
+description: "Compiler internals for call_user_func_array(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 50
+---
+
+## `call_user_func_array()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/callables/call_user_func_array.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/call_user_func_array.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:37](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L37) (`lower_call_user_func_builtin_escape`)
+- **Function symbol**: `lower_call_user_func_builtin_escape()`
+
+
+### Lowering notes
+
+- Rejects `call_user_func*` calls that escaped the dedicated EIR callback lowering path.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_array_product`
+- `__rt_array_sum`
+
+## Signature summary
+
+```php
+function call_user_func_array(callable $callback, array $args): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 2 arguments.
+
+## Cross-references
+
+- [User reference for `call_user_func_array()`](../../../php/builtins/array/call_user_func_array.md)
diff --git a/docs/internals/builtins/array/count.md b/docs/internals/builtins/array/count.md
index c60dbfa3c1..b25bacadc1 100644
--- a/docs/internals/builtins/array/count.md
+++ b/docs/internals/builtins/array/count.md
@@ -2,21 +2,25 @@
title: "count() — internals"
description: "Compiler internals for count(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 34
+ order: 51
---
## `count()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:917](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L917) (`lower_count`)
+- **Signature**: [`src/builtins/array/count.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/count.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:439](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L439) (`lower_count`)
- **Function symbol**: `lower_count()`
### Lowering notes
- Lowers `count(array)` for concrete array values by reading the runtime length header.
+- Called from `crate::builtins::array::count` (the registry home) via a thin wrapper.
+- Handles Array/AssocArray (reads length directly from the runtime header), Mixed/Union
+- (delegates to `__rt_mixed_count`), and Countable Object (calls the object's `count`
+- method via intrinsic or dynamic dispatch).
## Runtime helpers
@@ -26,7 +30,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function count(array $value, int $mode): int
+function count(array $value, int $mode = 0): int
```
## What the type checker enforces
@@ -36,4 +40,3 @@ function count(array $value, int $mode): int
## Cross-references
- [User reference for `count()`](../../../php/builtins/array/count.md)
-
diff --git a/docs/internals/builtins/array/in_array.md b/docs/internals/builtins/array/in_array.md
index 3e649a97a2..eaceecf097 100644
--- a/docs/internals/builtins/array/in_array.md
+++ b/docs/internals/builtins/array/in_array.md
@@ -2,21 +2,21 @@
title: "in_array() — internals"
description: "Compiler internals for in_array(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 35
+ order: 52
---
## `in_array()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1160](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1160) (`lower_in_array`)
+- **Signature**: [`src/builtins/array/in_array.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/in_array.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1786](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1786) (`lower_in_array`)
- **Function symbol**: `lower_in_array()`
### Lowering notes
-- Lowers `in_array()` for indexed arrays with scalar or string payloads.
+- Lowers `in_array()` for indexed and associative arrays with PHP loose or strict membership.
## Runtime helpers
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function in_array(mixed $needle, array $haystack, bool $strict): mixed
+function in_array(mixed $needle, array $haystack, bool $strict = false): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function in_array(mixed $needle, array $haystack, bool $strict): mixed
## Cross-references
- [User reference for `in_array()`](../../../php/builtins/array/in_array.md)
-
diff --git a/docs/internals/builtins/array/krsort.md b/docs/internals/builtins/array/krsort.md
index 1a7ecb6ecb..ccd956e871 100644
--- a/docs/internals/builtins/array/krsort.md
+++ b/docs/internals/builtins/array/krsort.md
@@ -2,21 +2,21 @@
title: "krsort() — internals"
description: "Compiler internals for krsort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 36
+ order: 53
---
## `krsort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1101](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1101) (`lower_krsort`)
+- **Signature**: [`src/builtins/array/krsort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/krsort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1104](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1104) (`lower_krsort`)
- **Function symbol**: `lower_krsort()`
### Lowering notes
-- Lowers `krsort()` through the legacy reverse key-sort helper surface.
+- Lowers `krsort()` through the reverse key-sort helper surface.
## Runtime helpers
@@ -28,15 +28,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function krsort(array $array, int $flags): bool
+function krsort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `krsort()`](../../../php/builtins/array/krsort.md)
-
diff --git a/docs/internals/builtins/array/ksort.md b/docs/internals/builtins/array/ksort.md
index 94d13d80e6..dc4160fc4e 100644
--- a/docs/internals/builtins/array/ksort.md
+++ b/docs/internals/builtins/array/ksort.md
@@ -2,21 +2,21 @@
title: "ksort() — internals"
description: "Compiler internals for ksort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 37
+ order: 54
---
## `ksort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1096](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1096) (`lower_ksort`)
+- **Signature**: [`src/builtins/array/ksort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/ksort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1099](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1099) (`lower_ksort`)
- **Function symbol**: `lower_ksort()`
### Lowering notes
-- Lowers `ksort()` through the legacy key-sort helper surface.
+- Lowers `ksort()` through the key-sort helper surface.
## Runtime helpers
@@ -29,15 +29,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function ksort(array $array, int $flags): bool
+function ksort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `ksort()`](../../../php/builtins/array/ksort.md)
-
diff --git a/docs/internals/builtins/array/natcasesort.md b/docs/internals/builtins/array/natcasesort.md
index 5c0f941b06..ab599f0366 100644
--- a/docs/internals/builtins/array/natcasesort.md
+++ b/docs/internals/builtins/array/natcasesort.md
@@ -2,15 +2,15 @@
title: "natcasesort() — internals"
description: "Compiler internals for natcasesort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 38
+ order: 55
---
## `natcasesort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1111](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1111) (`lower_natcasesort`)
+- **Signature**: [`src/builtins/array/natcasesort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/natcasesort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1114](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1114) (`lower_natcasesort`)
- **Function symbol**: `lower_natcasesort()`
@@ -37,4 +37,3 @@ function natcasesort(array $array): bool
## Cross-references
- [User reference for `natcasesort()`](../../../php/builtins/array/natcasesort.md)
-
diff --git a/docs/internals/builtins/array/natsort.md b/docs/internals/builtins/array/natsort.md
index 2bf01f499f..0321f5afde 100644
--- a/docs/internals/builtins/array/natsort.md
+++ b/docs/internals/builtins/array/natsort.md
@@ -2,15 +2,15 @@
title: "natsort() — internals"
description: "Compiler internals for natsort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 39
+ order: 56
---
## `natsort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1106](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1106) (`lower_natsort`)
+- **Signature**: [`src/builtins/array/natsort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/natsort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1109](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1109) (`lower_natsort`)
- **Function symbol**: `lower_natsort()`
@@ -38,4 +38,3 @@ function natsort(array $array): bool
## Cross-references
- [User reference for `natsort()`](../../../php/builtins/array/natsort.md)
-
diff --git a/docs/internals/builtins/array/range.md b/docs/internals/builtins/array/range.md
index 782b5dbd2b..c213e1f81c 100644
--- a/docs/internals/builtins/array/range.md
+++ b/docs/internals/builtins/array/range.md
@@ -2,15 +2,15 @@
title: "range() — internals"
description: "Compiler internals for range(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 40
+ order: 57
---
## `range()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1020](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1020) (`lower_range`)
+- **Signature**: [`src/builtins/array/range.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/range.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1023](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1023) (`lower_range`)
- **Function symbol**: `lower_range()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function range(mixed $start, mixed $end, int $step): array
+function range(mixed $start, mixed $end): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `range()`](../../../php/builtins/array/range.md)
-
diff --git a/docs/internals/builtins/array/rsort.md b/docs/internals/builtins/array/rsort.md
index 0ee767c829..0f78283380 100644
--- a/docs/internals/builtins/array/rsort.md
+++ b/docs/internals/builtins/array/rsort.md
@@ -2,15 +2,15 @@
title: "rsort() — internals"
description: "Compiler internals for rsort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 41
+ order: 58
---
## `rsort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1081](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1081) (`lower_rsort`)
+- **Signature**: [`src/builtins/array/rsort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/rsort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1084](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1084) (`lower_rsort`)
- **Function symbol**: `lower_rsort()`
@@ -32,15 +32,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function rsort(array $array, int $flags): bool
+function rsort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `rsort()`](../../../php/builtins/array/rsort.md)
-
diff --git a/docs/internals/builtins/array/shuffle.md b/docs/internals/builtins/array/shuffle.md
index ff5f31d2c3..0e34663a33 100644
--- a/docs/internals/builtins/array/shuffle.md
+++ b/docs/internals/builtins/array/shuffle.md
@@ -2,15 +2,15 @@
title: "shuffle() — internals"
description: "Compiler internals for shuffle(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 42
+ order: 59
---
## `shuffle()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1116](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1116) (`lower_shuffle`)
+- **Signature**: [`src/builtins/array/shuffle.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/shuffle.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1119](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1119) (`lower_shuffle`)
- **Function symbol**: `lower_shuffle()`
@@ -20,7 +20,8 @@ sidebar:
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_array_is_list`
## Signature summary
@@ -36,4 +37,3 @@ function shuffle(array $array): bool
## Cross-references
- [User reference for `shuffle()`](../../../php/builtins/array/shuffle.md)
-
diff --git a/docs/internals/builtins/array/sort.md b/docs/internals/builtins/array/sort.md
index 41afa23d20..9a811cf5d8 100644
--- a/docs/internals/builtins/array/sort.md
+++ b/docs/internals/builtins/array/sort.md
@@ -2,15 +2,15 @@
title: "sort() — internals"
description: "Compiler internals for sort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 43
+ order: 60
---
## `sort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1076](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1076) (`lower_sort`)
+- **Signature**: [`src/builtins/array/sort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/sort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1079](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1079) (`lower_sort`)
- **Function symbol**: `lower_sort()`
@@ -33,15 +33,14 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function sort(array $array, int $flags): bool
+function sort(array $array): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
- **By-reference parameters**: `$array`.
## Cross-references
- [User reference for `sort()`](../../../php/builtins/array/sort.md)
-
diff --git a/docs/internals/builtins/array/uasort.md b/docs/internals/builtins/array/uasort.md
index 87f62df3dd..21f67fbc5a 100644
--- a/docs/internals/builtins/array/uasort.md
+++ b/docs/internals/builtins/array/uasort.md
@@ -2,25 +2,26 @@
title: "uasort() — internals"
description: "Compiler internals for uasort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 44
+ order: 61
---
## `uasort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1131](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1131) (`lower_uasort`)
+- **Signature**: [`src/builtins/array/uasort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/uasort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1134](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1134) (`lower_uasort`)
- **Function symbol**: `lower_uasort()`
### Lowering notes
-- Lowers `uasort()` through the legacy user-sort helper for static comparators.
+- Lowers `uasort()` through the user-sort helper for static comparators.
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_array_is_list`
## Signature summary
@@ -36,4 +37,3 @@ function uasort(array $array, callable $callback): bool
## Cross-references
- [User reference for `uasort()`](../../../php/builtins/array/uasort.md)
-
diff --git a/docs/internals/builtins/array/uksort.md b/docs/internals/builtins/array/uksort.md
index a2d02ce284..3357f721f8 100644
--- a/docs/internals/builtins/array/uksort.md
+++ b/docs/internals/builtins/array/uksort.md
@@ -2,25 +2,26 @@
title: "uksort() — internals"
description: "Compiler internals for uksort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 45
+ order: 62
---
## `uksort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1126](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1126) (`lower_uksort`)
+- **Signature**: [`src/builtins/array/uksort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/uksort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1129](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1129) (`lower_uksort`)
- **Function symbol**: `lower_uksort()`
### Lowering notes
-- Lowers `uksort()` through the legacy user-sort helper for static comparators.
+- Lowers `uksort()` through the user-sort helper for static comparators.
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_array_is_list`
## Signature summary
@@ -36,4 +37,3 @@ function uksort(array $array, callable $callback): bool
## Cross-references
- [User reference for `uksort()`](../../../php/builtins/array/uksort.md)
-
diff --git a/docs/internals/builtins/array/usort.md b/docs/internals/builtins/array/usort.md
index f24339639a..66427c380f 100644
--- a/docs/internals/builtins/array/usort.md
+++ b/docs/internals/builtins/array/usort.md
@@ -2,15 +2,15 @@
title: "usort() — internals"
description: "Compiler internals for usort(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 46
+ order: 63
---
## `usort()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/arrays.rs`:1121](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/arrays.rs#L1121) (`lower_usort`)
+- **Signature**: [`src/builtins/array/usort.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/array/usort.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/arrays.rs`:1124](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/arrays.rs#L1124) (`lower_usort`)
- **Function symbol**: `lower_usort()`
@@ -20,7 +20,8 @@ sidebar:
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_array_is_list`
## Signature summary
@@ -36,4 +37,3 @@ function usort(array $array, callable $callback): bool
## Cross-references
- [User reference for `usort()`](../../../php/builtins/array/usort.md)
-
diff --git a/docs/internals/builtins/buffer/buffer_free.md b/docs/internals/builtins/buffer/buffer_free.md
index 55032f2631..a49ffcb874 100644
--- a/docs/internals/builtins/buffer/buffer_free.md
+++ b/docs/internals/builtins/buffer/buffer_free.md
@@ -2,7 +2,7 @@
title: "buffer_free() — internals"
description: "Compiler internals for buffer_free(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 47
+ order: 64
---
## `buffer_free()` — internals
@@ -10,7 +10,7 @@ sidebar:
## Where it lives
- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/buffers.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/buffers.rs#L24) (`lower_buffer_free`)
+- **Lowering**: [`src/codegen/lower_inst/builtins/buffers.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/buffers.rs#L24) (`lower_buffer_free`)
- **Function symbol**: `lower_buffer_free()`
@@ -35,4 +35,3 @@ function buffer_free(buffer $buffer): mixed
## Cross-references
- [User reference for `buffer_free()`](../../../php/builtins/buffer/buffer_free.md)
-
diff --git a/docs/internals/builtins/buffer/buffer_len.md b/docs/internals/builtins/buffer/buffer_len.md
index f149f0367a..ce22f1982f 100644
--- a/docs/internals/builtins/buffer/buffer_len.md
+++ b/docs/internals/builtins/buffer/buffer_len.md
@@ -2,7 +2,7 @@
title: "buffer_len() — internals"
description: "Compiler internals for buffer_len(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 48
+ order: 65
---
## `buffer_len()` — internals
@@ -10,7 +10,7 @@ sidebar:
## Where it lives
- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/buffers.rs`:19](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/buffers.rs#L19) (`lower_buffer_len`)
+- **Lowering**: [`src/codegen/lower_inst/builtins/buffers.rs`:19](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/buffers.rs#L19) (`lower_buffer_len`)
- **Function symbol**: `lower_buffer_len()`
@@ -35,4 +35,3 @@ function buffer_len(buffer $buffer): int
## Cross-references
- [User reference for `buffer_len()`](../../../php/builtins/buffer/buffer_len.md)
-
diff --git a/docs/internals/builtins/class/class_alias.md b/docs/internals/builtins/class/class_alias.md
index 4be5b4d53a..b578f7661e 100644
--- a/docs/internals/builtins/class/class_alias.md
+++ b/docs/internals/builtins/class/class_alias.md
@@ -2,15 +2,15 @@
title: "class_alias() — internals"
description: "Compiler internals for class_alias(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 49
+ order: 66
---
## `class_alias()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:41](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L41) (`lower_class_alias`)
+- **Signature**: [`src/builtins/callables/class_alias.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/class_alias.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:41](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L41) (`lower_class_alias`)
- **Function symbol**: `lower_class_alias()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_alias(string $class, string $alias, bool $autoload): bool
+function class_alias(string $class, string $alias, bool $autoload = true): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function class_alias(string $class, string $alias, bool $autoload): bool
## Cross-references
- [User reference for `class_alias()`](../../../php/builtins/class/class_alias.md)
-
diff --git a/docs/internals/builtins/class/class_attribute_args.md b/docs/internals/builtins/class/class_attribute_args.md
index b96f5fccfb..c94a5df71d 100644
--- a/docs/internals/builtins/class/class_attribute_args.md
+++ b/docs/internals/builtins/class/class_attribute_args.md
@@ -2,15 +2,15 @@
title: "class_attribute_args() — internals"
description: "Compiler internals for class_attribute_args(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 50
+ order: 67
---
## `class_attribute_args()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/attributes.rs`:52](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/attributes.rs#L52) (`lower_class_attribute_args`)
+- **Signature**: [`src/builtins/system/class_attribute_args.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/class_attribute_args.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/attributes.rs`:52](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/attributes.rs#L52) (`lower_class_attribute_args`)
- **Function symbol**: `lower_class_attribute_args()`
@@ -35,4 +35,3 @@ function class_attribute_args(string $class_name, string $attribute_name): array
## Cross-references
- [User reference for `class_attribute_args()`](../../../php/builtins/class/class_attribute_args.md)
-
diff --git a/docs/internals/builtins/class/class_attribute_names.md b/docs/internals/builtins/class/class_attribute_names.md
index 4bddb841f3..cf38873252 100644
--- a/docs/internals/builtins/class/class_attribute_names.md
+++ b/docs/internals/builtins/class/class_attribute_names.md
@@ -2,15 +2,15 @@
title: "class_attribute_names() — internals"
description: "Compiler internals for class_attribute_names(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 51
+ order: 68
---
## `class_attribute_names()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/attributes.rs`:36](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/attributes.rs#L36) (`lower_class_attribute_names`)
+- **Signature**: [`src/builtins/system/class_attribute_names.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/class_attribute_names.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/attributes.rs`:36](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/attributes.rs#L36) (`lower_class_attribute_names`)
- **Function symbol**: `lower_class_attribute_names()`
@@ -35,4 +35,3 @@ function class_attribute_names(string $class_name): array
## Cross-references
- [User reference for `class_attribute_names()`](../../../php/builtins/class/class_attribute_names.md)
-
diff --git a/docs/internals/builtins/class/class_exists.md b/docs/internals/builtins/class/class_exists.md
index 02897c44fb..cc3671b2f7 100644
--- a/docs/internals/builtins/class/class_exists.md
+++ b/docs/internals/builtins/class/class_exists.md
@@ -2,18 +2,22 @@
title: "class_exists() — internals"
description: "Compiler internals for class_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 52
+ order: 69
---
## `class_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/class_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/class_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:293](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L293) (`lower_class_like_exists`)
+- **Function symbol**: `lower_class_like_exists()`
+### Lowering notes
+
+- Lowers AOT class/interface/enum existence checks for literal names.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_exists(string $class, bool $autoload): bool
+function class_exists(string $class, bool $autoload = true): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function class_exists(string $class, bool $autoload): bool
## Cross-references
- [User reference for `class_exists()`](../../../php/builtins/class/class_exists.md)
-
diff --git a/docs/internals/builtins/class/class_get_attributes.md b/docs/internals/builtins/class/class_get_attributes.md
index 013c855cc7..7ae201dc05 100644
--- a/docs/internals/builtins/class/class_get_attributes.md
+++ b/docs/internals/builtins/class/class_get_attributes.md
@@ -2,15 +2,15 @@
title: "class_get_attributes() — internals"
description: "Compiler internals for class_get_attributes(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 53
+ order: 70
---
## `class_get_attributes()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/attributes.rs`:68](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/attributes.rs#L68) (`lower_class_get_attributes`)
+- **Signature**: [`src/builtins/system/class_get_attributes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/class_get_attributes.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/attributes.rs`:68](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/attributes.rs#L68) (`lower_class_get_attributes`)
- **Function symbol**: `lower_class_get_attributes()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_get_attributes(string $class_name): mixed
+function class_get_attributes(string $class_name): array
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function class_get_attributes(string $class_name): mixed
## Cross-references
- [User reference for `class_get_attributes()`](../../../php/builtins/class/class_get_attributes.md)
-
diff --git a/docs/internals/builtins/class/class_implements.md b/docs/internals/builtins/class/class_implements.md
index 3f133cac47..982dcc0290 100644
--- a/docs/internals/builtins/class/class_implements.md
+++ b/docs/internals/builtins/class/class_implements.md
@@ -2,18 +2,22 @@
title: "class_implements() — internals"
description: "Compiler internals for class_implements(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 54
+ order: 71
---
## `class_implements()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/class_implements.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/class_implements.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/class_relations.rs`:32](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/class_relations.rs#L32) (`lower_class_relation`)
+- **Function symbol**: `lower_class_relation()`
+### Lowering notes
+
+- Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_implements(mixed $object_or_class, bool $autoload): mixed
+function class_implements(mixed $object_or_class, bool $autoload = true): mixed
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function class_implements(mixed $object_or_class, bool $autoload): mixed
## Cross-references
- [User reference for `class_implements()`](../../../php/builtins/class/class_implements.md)
-
diff --git a/docs/internals/builtins/class/class_parents.md b/docs/internals/builtins/class/class_parents.md
index 0f6ed57086..95d5f91740 100644
--- a/docs/internals/builtins/class/class_parents.md
+++ b/docs/internals/builtins/class/class_parents.md
@@ -2,18 +2,22 @@
title: "class_parents() — internals"
description: "Compiler internals for class_parents(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 55
+ order: 72
---
## `class_parents()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/class_parents.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/class_parents.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/class_relations.rs`:32](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/class_relations.rs#L32) (`lower_class_relation`)
+- **Function symbol**: `lower_class_relation()`
+### Lowering notes
+
+- Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_parents(mixed $object_or_class, bool $autoload): mixed
+function class_parents(mixed $object_or_class, bool $autoload = true): mixed
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function class_parents(mixed $object_or_class, bool $autoload): mixed
## Cross-references
- [User reference for `class_parents()`](../../../php/builtins/class/class_parents.md)
-
diff --git a/docs/internals/builtins/class/class_uses.md b/docs/internals/builtins/class/class_uses.md
index 3467272d55..ad4517f2e0 100644
--- a/docs/internals/builtins/class/class_uses.md
+++ b/docs/internals/builtins/class/class_uses.md
@@ -2,18 +2,22 @@
title: "class_uses() — internals"
description: "Compiler internals for class_uses(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 56
+ order: 73
---
## `class_uses()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/class_uses.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/class_uses.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/class_relations.rs`:32](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/class_relations.rs#L32) (`lower_class_relation`)
+- **Function symbol**: `lower_class_relation()`
+### Lowering notes
+
+- Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function class_uses(mixed $object_or_class, bool $autoload): mixed
+function class_uses(mixed $object_or_class, bool $autoload = true): mixed
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function class_uses(mixed $object_or_class, bool $autoload): mixed
## Cross-references
- [User reference for `class_uses()`](../../../php/builtins/class/class_uses.md)
-
diff --git a/docs/internals/builtins/class/enum_exists.md b/docs/internals/builtins/class/enum_exists.md
index 268bc3e37c..4d97bdcb0a 100644
--- a/docs/internals/builtins/class/enum_exists.md
+++ b/docs/internals/builtins/class/enum_exists.md
@@ -2,18 +2,22 @@
title: "enum_exists() — internals"
description: "Compiler internals for enum_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 57
+ order: 74
---
## `enum_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/enum_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/enum_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:293](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L293) (`lower_class_like_exists`)
+- **Function symbol**: `lower_class_like_exists()`
+### Lowering notes
+
+- Lowers AOT class/interface/enum existence checks for literal names.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function enum_exists(string $enum, bool $autoload): bool
+function enum_exists(string $enum, bool $autoload = true): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function enum_exists(string $enum, bool $autoload): bool
## Cross-references
- [User reference for `enum_exists()`](../../../php/builtins/class/enum_exists.md)
-
diff --git a/docs/internals/builtins/class/function_exists.md b/docs/internals/builtins/class/function_exists.md
index d77e700cd1..e72e15dfbb 100644
--- a/docs/internals/builtins/class/function_exists.md
+++ b/docs/internals/builtins/class/function_exists.md
@@ -2,15 +2,15 @@
title: "function_exists() — internals"
description: "Compiler internals for function_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 58
+ order: 75
---
## `function_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:759](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L759) (`lower_function_exists`)
+- **Signature**: [`src/builtins/callables/function_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/function_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:276](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L276) (`lower_function_exists`)
- **Function symbol**: `lower_function_exists()`
@@ -40,4 +40,3 @@ function function_exists(string $function): bool
## Cross-references
- [User reference for `function_exists()`](../../../php/builtins/class/function_exists.md)
-
diff --git a/docs/internals/builtins/class/get_class.md b/docs/internals/builtins/class/get_class.md
index 61b565690f..323193ac61 100644
--- a/docs/internals/builtins/class/get_class.md
+++ b/docs/internals/builtins/class/get_class.md
@@ -2,18 +2,22 @@
title: "get_class() — internals"
description: "Compiler internals for get_class(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 59
+ order: 76
---
## `get_class()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/get_class.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/get_class.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:331](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L331) (`lower_class_name_lookup`)
+- **Function symbol**: `lower_class_name_lookup()`
+### Lowering notes
+
+- Lowers `get_class()` and `get_parent_class()` through static or dynamic class metadata.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function get_class(object $object): string
+function get_class(object $object = null): string
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function get_class(object $object): string
## Cross-references
- [User reference for `get_class()`](../../../php/builtins/class/get_class.md)
-
diff --git a/docs/internals/builtins/class/get_declared_classes.md b/docs/internals/builtins/class/get_declared_classes.md
index 0610281618..aae271d112 100644
--- a/docs/internals/builtins/class/get_declared_classes.md
+++ b/docs/internals/builtins/class/get_declared_classes.md
@@ -2,18 +2,22 @@
title: "get_declared_classes() — internals"
description: "Compiler internals for get_declared_classes(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 60
+ order: 77
---
## `get_declared_classes()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/get_declared_classes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/get_declared_classes.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:388](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L388) (`lower_get_declared_names`)
+- **Function symbol**: `lower_get_declared_names()`
+### Lowering notes
+
+- Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function get_declared_classes(): array
## Cross-references
- [User reference for `get_declared_classes()`](../../../php/builtins/class/get_declared_classes.md)
-
diff --git a/docs/internals/builtins/class/get_declared_interfaces.md b/docs/internals/builtins/class/get_declared_interfaces.md
index 9176e5af3b..05df0d4739 100644
--- a/docs/internals/builtins/class/get_declared_interfaces.md
+++ b/docs/internals/builtins/class/get_declared_interfaces.md
@@ -2,18 +2,22 @@
title: "get_declared_interfaces() — internals"
description: "Compiler internals for get_declared_interfaces(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 61
+ order: 78
---
## `get_declared_interfaces()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/get_declared_interfaces.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/get_declared_interfaces.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:388](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L388) (`lower_get_declared_names`)
+- **Function symbol**: `lower_get_declared_names()`
+### Lowering notes
+
+- Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function get_declared_interfaces(): array
## Cross-references
- [User reference for `get_declared_interfaces()`](../../../php/builtins/class/get_declared_interfaces.md)
-
diff --git a/docs/internals/builtins/class/get_declared_traits.md b/docs/internals/builtins/class/get_declared_traits.md
index 26481e0d0a..f9a034bfc1 100644
--- a/docs/internals/builtins/class/get_declared_traits.md
+++ b/docs/internals/builtins/class/get_declared_traits.md
@@ -2,18 +2,22 @@
title: "get_declared_traits() — internals"
description: "Compiler internals for get_declared_traits(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 62
+ order: 79
---
## `get_declared_traits()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/get_declared_traits.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/get_declared_traits.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:388](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L388) (`lower_get_declared_names`)
+- **Function symbol**: `lower_get_declared_names()`
+### Lowering notes
+
+- Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function get_declared_traits(): array
## Cross-references
- [User reference for `get_declared_traits()`](../../../php/builtins/class/get_declared_traits.md)
-
diff --git a/docs/internals/builtins/class/get_parent_class.md b/docs/internals/builtins/class/get_parent_class.md
index 4c93f18248..a574bde029 100644
--- a/docs/internals/builtins/class/get_parent_class.md
+++ b/docs/internals/builtins/class/get_parent_class.md
@@ -2,18 +2,22 @@
title: "get_parent_class() — internals"
description: "Compiler internals for get_parent_class(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 63
+ order: 80
---
## `get_parent_class()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/get_parent_class.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/get_parent_class.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:331](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L331) (`lower_class_name_lookup`)
+- **Function symbol**: `lower_class_name_lookup()`
+### Lowering notes
+
+- Lowers `get_class()` and `get_parent_class()` through static or dynamic class metadata.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function get_parent_class(mixed $object_or_class): string
+function get_parent_class(mixed $object_or_class = null): string
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function get_parent_class(mixed $object_or_class): string
## Cross-references
- [User reference for `get_parent_class()`](../../../php/builtins/class/get_parent_class.md)
-
diff --git a/docs/internals/builtins/class/interface_exists.md b/docs/internals/builtins/class/interface_exists.md
index a14e918848..11a13350ab 100644
--- a/docs/internals/builtins/class/interface_exists.md
+++ b/docs/internals/builtins/class/interface_exists.md
@@ -2,18 +2,22 @@
title: "interface_exists() — internals"
description: "Compiler internals for interface_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 64
+ order: 81
---
## `interface_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/interface_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/interface_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:293](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L293) (`lower_class_like_exists`)
+- **Function symbol**: `lower_class_like_exists()`
+### Lowering notes
+
+- Lowers AOT class/interface/enum existence checks for literal names.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function interface_exists(string $interface, bool $autoload): bool
+function interface_exists(string $interface, bool $autoload = true): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function interface_exists(string $interface, bool $autoload): bool
## Cross-references
- [User reference for `interface_exists()`](../../../php/builtins/class/interface_exists.md)
-
diff --git a/docs/internals/builtins/class/is_a.md b/docs/internals/builtins/class/is_a.md
index 4062f2ff08..3ca72ca135 100644
--- a/docs/internals/builtins/class/is_a.md
+++ b/docs/internals/builtins/class/is_a.md
@@ -2,18 +2,22 @@
title: "is_a() — internals"
description: "Compiler internals for is_a(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 65
+ order: 82
---
## `is_a()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/is_a.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/is_a.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:369](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L369) (`lower_is_a_relation`)
+- **Function symbol**: `lower_is_a_relation()`
+### Lowering notes
+
+- Lowers `is_a()` and `is_subclass_of()` for object operands and literal targets.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function is_a(object $object_or_class, string $class, bool $allow_string): bool
+function is_a(object $object_or_class, string $class, bool $allow_string = false): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function is_a(object $object_or_class, string $class, bool $allow_string): bool
## Cross-references
- [User reference for `is_a()`](../../../php/builtins/class/is_a.md)
-
diff --git a/docs/internals/builtins/class/is_subclass_of.md b/docs/internals/builtins/class/is_subclass_of.md
index b41195c82e..404beddeac 100644
--- a/docs/internals/builtins/class/is_subclass_of.md
+++ b/docs/internals/builtins/class/is_subclass_of.md
@@ -2,18 +2,22 @@
title: "is_subclass_of() — internals"
description: "Compiler internals for is_subclass_of(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 66
+ order: 83
---
## `is_subclass_of()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/is_subclass_of.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/is_subclass_of.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:369](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L369) (`lower_is_a_relation`)
+- **Function symbol**: `lower_is_a_relation()`
+### Lowering notes
+
+- Lowers `is_a()` and `is_subclass_of()` for object operands and literal targets.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function is_subclass_of(mixed $object_or_class, string $class, bool $allow_string): bool
+function is_subclass_of(mixed $object_or_class, string $class, bool $allow_string = true): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function is_subclass_of(mixed $object_or_class, string $class, bool $allow_strin
## Cross-references
- [User reference for `is_subclass_of()`](../../../php/builtins/class/is_subclass_of.md)
-
diff --git a/docs/internals/builtins/class/trait_exists.md b/docs/internals/builtins/class/trait_exists.md
index 560cfaa628..93e606d30c 100644
--- a/docs/internals/builtins/class/trait_exists.md
+++ b/docs/internals/builtins/class/trait_exists.md
@@ -2,18 +2,22 @@
title: "trait_exists() — internals"
description: "Compiler internals for trait_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 67
+ order: 84
---
## `trait_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/callables/trait_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/trait_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:293](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L293) (`lower_class_like_exists`)
+- **Function symbol**: `lower_class_like_exists()`
+### Lowering notes
+
+- Lowers AOT class/interface/enum existence checks for literal names.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function trait_exists(string $trait, bool $autoload): bool
+function trait_exists(string $trait, bool $autoload = true): bool
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function trait_exists(string $trait, bool $autoload): bool
## Cross-references
- [User reference for `trait_exists()`](../../../php/builtins/class/trait_exists.md)
-
diff --git a/docs/internals/builtins/date/checkdate.md b/docs/internals/builtins/date/checkdate.md
index 8811075285..db4899f487 100644
--- a/docs/internals/builtins/date/checkdate.md
+++ b/docs/internals/builtins/date/checkdate.md
@@ -2,15 +2,15 @@
title: "checkdate() — internals"
description: "Compiler internals for checkdate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 68
+ order: 85
---
## `checkdate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:163](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L163) (`lower_checkdate`)
+- **Signature**: [`src/builtins/system/checkdate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/checkdate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:163](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L163) (`lower_checkdate`)
- **Function symbol**: `lower_checkdate()`
@@ -40,4 +40,3 @@ function checkdate(int $month, int $day, int $year): bool
## Cross-references
- [User reference for `checkdate()`](../../../php/builtins/date/checkdate.md)
-
diff --git a/docs/internals/builtins/date/date.md b/docs/internals/builtins/date/date.md
index 279496d460..284b39e58b 100644
--- a/docs/internals/builtins/date/date.md
+++ b/docs/internals/builtins/date/date.md
@@ -2,15 +2,15 @@
title: "date() — internals"
description: "Compiler internals for date(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 69
+ order: 86
---
## `date()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L22) (`lower_date`)
+- **Signature**: [`src/builtins/system/date.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/date.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L22) (`lower_date`)
- **Function symbol**: `lower_date()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function date(string $format, int $timestamp): string
+function date(string $format, int $timestamp = null): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function date(string $format, int $timestamp): string
## Cross-references
- [User reference for `date()`](../../../php/builtins/date/date.md)
-
diff --git a/docs/internals/builtins/date/date_default_timezone_get.md b/docs/internals/builtins/date/date_default_timezone_get.md
index c8810568b9..cc1cbfbdb6 100644
--- a/docs/internals/builtins/date/date_default_timezone_get.md
+++ b/docs/internals/builtins/date/date_default_timezone_get.md
@@ -2,15 +2,15 @@
title: "date_default_timezone_get() — internals"
description: "Compiler internals for date_default_timezone_get(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 70
+ order: 87
---
## `date_default_timezone_get()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L70) (`lower_date_default_timezone_get`)
+- **Signature**: [`src/builtins/system/date_default_timezone_get.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/date_default_timezone_get.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L70) (`lower_date_default_timezone_get`)
- **Function symbol**: `lower_date_default_timezone_get()`
@@ -39,4 +39,3 @@ function date_default_timezone_get(): string
## Cross-references
- [User reference for `date_default_timezone_get()`](../../../php/builtins/date/date_default_timezone_get.md)
-
diff --git a/docs/internals/builtins/date/date_default_timezone_set.md b/docs/internals/builtins/date/date_default_timezone_set.md
index 4a12cd0ec4..9cfee9f4a2 100644
--- a/docs/internals/builtins/date/date_default_timezone_set.md
+++ b/docs/internals/builtins/date/date_default_timezone_set.md
@@ -2,15 +2,15 @@
title: "date_default_timezone_set() — internals"
description: "Compiler internals for date_default_timezone_set(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 71
+ order: 88
---
## `date_default_timezone_set()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:84](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L84) (`lower_date_default_timezone_set`)
+- **Signature**: [`src/builtins/system/date_default_timezone_set.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/date_default_timezone_set.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:84](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L84) (`lower_date_default_timezone_set`)
- **Function symbol**: `lower_date_default_timezone_set()`
@@ -42,4 +42,3 @@ function date_default_timezone_set(string $timezoneId): bool
## Cross-references
- [User reference for `date_default_timezone_set()`](../../../php/builtins/date/date_default_timezone_set.md)
-
diff --git a/docs/internals/builtins/date/getdate.md b/docs/internals/builtins/date/getdate.md
index 4b58944763..f13d4477cd 100644
--- a/docs/internals/builtins/date/getdate.md
+++ b/docs/internals/builtins/date/getdate.md
@@ -2,15 +2,15 @@
title: "getdate() — internals"
description: "Compiler internals for getdate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 72
+ order: 89
---
## `getdate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:183](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L183) (`lower_getdate`)
+- **Signature**: [`src/builtins/system/getdate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/getdate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:183](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L183) (`lower_getdate`)
- **Function symbol**: `lower_getdate()`
@@ -31,7 +31,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function getdate(int $timestamp): array
+function getdate(int $timestamp = null): array
```
## What the type checker enforces
@@ -41,4 +41,3 @@ function getdate(int $timestamp): array
## Cross-references
- [User reference for `getdate()`](../../../php/builtins/date/getdate.md)
-
diff --git a/docs/internals/builtins/date/gmdate.md b/docs/internals/builtins/date/gmdate.md
index 6db0c7c0a8..a1d773144b 100644
--- a/docs/internals/builtins/date/gmdate.md
+++ b/docs/internals/builtins/date/gmdate.md
@@ -2,15 +2,15 @@
title: "gmdate() — internals"
description: "Compiler internals for gmdate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 73
+ order: 90
---
## `gmdate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:33](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L33) (`lower_gmdate`)
+- **Signature**: [`src/builtins/system/gmdate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/gmdate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:33](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L33) (`lower_gmdate`)
- **Function symbol**: `lower_gmdate()`
@@ -29,7 +29,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function gmdate(string $format, int $timestamp): string
+function gmdate(string $format, int $timestamp = null): string
```
## What the type checker enforces
@@ -39,4 +39,3 @@ function gmdate(string $format, int $timestamp): string
## Cross-references
- [User reference for `gmdate()`](../../../php/builtins/date/gmdate.md)
-
diff --git a/docs/internals/builtins/date/gmmktime.md b/docs/internals/builtins/date/gmmktime.md
index 2ec3bc56be..e711735072 100644
--- a/docs/internals/builtins/date/gmmktime.md
+++ b/docs/internals/builtins/date/gmmktime.md
@@ -2,15 +2,15 @@
title: "gmmktime() — internals"
description: "Compiler internals for gmmktime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 74
+ order: 91
---
## `gmmktime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L151) (`lower_gmmktime`)
+- **Signature**: [`src/builtins/system/gmmktime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/gmmktime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L151) (`lower_gmmktime`)
- **Function symbol**: `lower_gmmktime()`
@@ -40,4 +40,3 @@ function gmmktime(int $hour, int $minute, int $second, int $month, int $day, int
## Cross-references
- [User reference for `gmmktime()`](../../../php/builtins/date/gmmktime.md)
-
diff --git a/docs/internals/builtins/date/hrtime.md b/docs/internals/builtins/date/hrtime.md
index 6d1fbd9624..fb9cb930cc 100644
--- a/docs/internals/builtins/date/hrtime.md
+++ b/docs/internals/builtins/date/hrtime.md
@@ -2,15 +2,15 @@
title: "hrtime() — internals"
description: "Compiler internals for hrtime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 75
+ order: 92
---
## `hrtime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:247](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L247) (`lower_hrtime`)
+- **Signature**: [`src/builtins/system/hrtime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/hrtime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:247](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L247) (`lower_hrtime`)
- **Function symbol**: `lower_hrtime()`
@@ -31,7 +31,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function hrtime(bool $as_number): mixed
+function hrtime(bool $as_number = false): mixed
```
## What the type checker enforces
@@ -41,4 +41,3 @@ function hrtime(bool $as_number): mixed
## Cross-references
- [User reference for `hrtime()`](../../../php/builtins/date/hrtime.md)
-
diff --git a/docs/internals/builtins/date/localtime.md b/docs/internals/builtins/date/localtime.md
index 0ec5a9b471..38d517a792 100644
--- a/docs/internals/builtins/date/localtime.md
+++ b/docs/internals/builtins/date/localtime.md
@@ -2,15 +2,15 @@
title: "localtime() — internals"
description: "Compiler internals for localtime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 76
+ order: 93
---
## `localtime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:220](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L220) (`lower_localtime`)
+- **Signature**: [`src/builtins/system/localtime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/localtime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:220](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L220) (`lower_localtime`)
- **Function symbol**: `lower_localtime()`
@@ -32,7 +32,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function localtime(int $timestamp, bool $associative): array
+function localtime(int $timestamp = -1, bool $associative = false): array
```
## What the type checker enforces
@@ -42,4 +42,3 @@ function localtime(int $timestamp, bool $associative): array
## Cross-references
- [User reference for `localtime()`](../../../php/builtins/date/localtime.md)
-
diff --git a/docs/internals/builtins/date/microtime.md b/docs/internals/builtins/date/microtime.md
index 4aa630697a..cf145e0101 100644
--- a/docs/internals/builtins/date/microtime.md
+++ b/docs/internals/builtins/date/microtime.md
@@ -2,15 +2,15 @@
title: "microtime() — internals"
description: "Compiler internals for microtime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 77
+ order: 94
---
## `microtime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:111](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L111) (`lower_microtime`)
+- **Signature**: [`src/builtins/system/microtime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/microtime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:111](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L111) (`lower_microtime`)
- **Function symbol**: `lower_microtime()`
@@ -35,7 +35,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function microtime(bool $as_float): int
+function microtime(bool $as_float = false): mixed
```
## What the type checker enforces
@@ -45,4 +45,3 @@ function microtime(bool $as_float): int
## Cross-references
- [User reference for `microtime()`](../../../php/builtins/date/microtime.md)
-
diff --git a/docs/internals/builtins/date/mktime.md b/docs/internals/builtins/date/mktime.md
index b464dd2d2b..58eb3e8b67 100644
--- a/docs/internals/builtins/date/mktime.md
+++ b/docs/internals/builtins/date/mktime.md
@@ -2,15 +2,15 @@
title: "mktime() — internals"
description: "Compiler internals for mktime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 78
+ order: 95
---
## `mktime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:140](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L140) (`lower_mktime`)
+- **Signature**: [`src/builtins/system/mktime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/mktime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:140](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L140) (`lower_mktime`)
- **Function symbol**: `lower_mktime()`
@@ -38,4 +38,3 @@ function mktime(int $hour, int $minute, int $second, int $month, int $day, int $
## Cross-references
- [User reference for `mktime()`](../../../php/builtins/date/mktime.md)
-
diff --git a/docs/internals/builtins/date/strtotime.md b/docs/internals/builtins/date/strtotime.md
index 7b16234b40..8075a0c1e5 100644
--- a/docs/internals/builtins/date/strtotime.md
+++ b/docs/internals/builtins/date/strtotime.md
@@ -2,15 +2,15 @@
title: "strtotime() — internals"
description: "Compiler internals for strtotime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 79
+ order: 96
---
## `strtotime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:487](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L487) (`lower_strtotime`)
+- **Signature**: [`src/builtins/system/strtotime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/strtotime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:487](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L487) (`lower_strtotime`)
- **Function symbol**: `lower_strtotime()`
@@ -32,7 +32,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function strtotime(string $datetime, int $baseTimestamp): mixed
+function strtotime(string $datetime, int $baseTimestamp = null): mixed
```
## What the type checker enforces
@@ -42,4 +42,3 @@ function strtotime(string $datetime, int $baseTimestamp): mixed
## Cross-references
- [User reference for `strtotime()`](../../../php/builtins/date/strtotime.md)
-
diff --git a/docs/internals/builtins/date/time.md b/docs/internals/builtins/date/time.md
index 512717f62a..6a5cb71832 100644
--- a/docs/internals/builtins/date/time.md
+++ b/docs/internals/builtins/date/time.md
@@ -2,15 +2,15 @@
title: "time() — internals"
description: "Compiler internals for time(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 80
+ order: 97
---
## `time()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:615](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L615) (`lower_time`)
+- **Signature**: [`src/builtins/system/time.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/time.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:615](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L615) (`lower_time`)
- **Function symbol**: `lower_time()`
@@ -36,4 +36,3 @@ function time(): int
## Cross-references
- [User reference for `time()`](../../../php/builtins/date/time.md)
-
diff --git a/docs/internals/builtins/filesystem/basename.md b/docs/internals/builtins/filesystem/basename.md
index 854f86e7a5..9c4395d000 100644
--- a/docs/internals/builtins/filesystem/basename.md
+++ b/docs/internals/builtins/filesystem/basename.md
@@ -2,15 +2,15 @@
title: "basename() — internals"
description: "Compiler internals for basename(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 81
+ order: 98
---
## `basename()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3893](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3893) (`lower_basename`)
+- **Signature**: [`src/builtins/io/basename.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/basename.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4536](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4536) (`lower_basename`)
- **Function symbol**: `lower_basename()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function basename(string $path, string $suffix): string
+function basename(string $path, string $suffix = ''): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function basename(string $path, string $suffix): string
## Cross-references
- [User reference for `basename()`](../../../php/builtins/filesystem/basename.md)
-
diff --git a/docs/internals/builtins/filesystem/chdir.md b/docs/internals/builtins/filesystem/chdir.md
index a691b4ffe5..29f810231c 100644
--- a/docs/internals/builtins/filesystem/chdir.md
+++ b/docs/internals/builtins/filesystem/chdir.md
@@ -2,15 +2,15 @@
title: "chdir() — internals"
description: "Compiler internals for chdir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 82
+ order: 99
---
## `chdir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3795](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3795) (`lower_chdir`)
+- **Signature**: [`src/builtins/io/chdir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/chdir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4438](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4438) (`lower_chdir`)
- **Function symbol**: `lower_chdir()`
@@ -40,4 +40,3 @@ function chdir(string $directory): bool
## Cross-references
- [User reference for `chdir()`](../../../php/builtins/filesystem/chdir.md)
-
diff --git a/docs/internals/builtins/filesystem/chgrp.md b/docs/internals/builtins/filesystem/chgrp.md
index efb67cbf05..088d98a680 100644
--- a/docs/internals/builtins/filesystem/chgrp.md
+++ b/docs/internals/builtins/filesystem/chgrp.md
@@ -2,15 +2,15 @@
title: "chgrp() — internals"
description: "Compiler internals for chgrp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 83
+ order: 100
---
## `chgrp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3835](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3835) (`lower_chgrp`)
+- **Signature**: [`src/builtins/io/chgrp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/chgrp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4478](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4478) (`lower_chgrp`)
- **Function symbol**: `lower_chgrp()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function chgrp(string $filename, int $group): bool
+function chgrp(string $filename, string $group): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function chgrp(string $filename, int $group): bool
## Cross-references
- [User reference for `chgrp()`](../../../php/builtins/filesystem/chgrp.md)
-
diff --git a/docs/internals/builtins/filesystem/chmod.md b/docs/internals/builtins/filesystem/chmod.md
index ec128c5d43..3b5e76e916 100644
--- a/docs/internals/builtins/filesystem/chmod.md
+++ b/docs/internals/builtins/filesystem/chmod.md
@@ -2,15 +2,15 @@
title: "chmod() — internals"
description: "Compiler internals for chmod(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 84
+ order: 101
---
## `chmod()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3825](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3825) (`lower_chmod`)
+- **Signature**: [`src/builtins/io/chmod.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/chmod.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4468](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4468) (`lower_chmod`)
- **Function symbol**: `lower_chmod()`
@@ -35,4 +35,3 @@ function chmod(string $filename, int $permissions): bool
## Cross-references
- [User reference for `chmod()`](../../../php/builtins/filesystem/chmod.md)
-
diff --git a/docs/internals/builtins/filesystem/chown.md b/docs/internals/builtins/filesystem/chown.md
index b8b83876b7..ac86b3b4e9 100644
--- a/docs/internals/builtins/filesystem/chown.md
+++ b/docs/internals/builtins/filesystem/chown.md
@@ -2,15 +2,15 @@
title: "chown() — internals"
description: "Compiler internals for chown(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 85
+ order: 102
---
## `chown()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3830](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3830) (`lower_chown`)
+- **Signature**: [`src/builtins/io/chown.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/chown.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4473](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4473) (`lower_chown`)
- **Function symbol**: `lower_chown()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function chown(string $filename, int $user): bool
+function chown(string $filename, string $user): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function chown(string $filename, int $user): bool
## Cross-references
- [User reference for `chown()`](../../../php/builtins/filesystem/chown.md)
-
diff --git a/docs/internals/builtins/filesystem/clearstatcache.md b/docs/internals/builtins/filesystem/clearstatcache.md
index 45ff7c3bd8..2417f80455 100644
--- a/docs/internals/builtins/filesystem/clearstatcache.md
+++ b/docs/internals/builtins/filesystem/clearstatcache.md
@@ -2,15 +2,15 @@
title: "clearstatcache() — internals"
description: "Compiler internals for clearstatcache(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 86
+ order: 103
---
## `clearstatcache()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4935](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4935) (`lower_clearstatcache`)
+- **Signature**: [`src/builtins/io/clearstatcache.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/clearstatcache.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5578](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5578) (`lower_clearstatcache`)
- **Function symbol**: `lower_clearstatcache()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function clearstatcache(bool $clear_realpath_cache, string $filename): void
+function clearstatcache(bool $clear_realpath_cache = false, string $filename = ''): void
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function clearstatcache(bool $clear_realpath_cache, string $filename): void
## Cross-references
- [User reference for `clearstatcache()`](../../../php/builtins/filesystem/clearstatcache.md)
-
diff --git a/docs/internals/builtins/filesystem/copy.md b/docs/internals/builtins/filesystem/copy.md
index 3ae63b649a..dfc16a31fa 100644
--- a/docs/internals/builtins/filesystem/copy.md
+++ b/docs/internals/builtins/filesystem/copy.md
@@ -2,15 +2,15 @@
title: "copy() — internals"
description: "Compiler internals for copy(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 87
+ order: 104
---
## `copy()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3800](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3800) (`lower_copy`)
+- **Signature**: [`src/builtins/io/copy.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/copy.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4443](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4443) (`lower_copy`)
- **Function symbol**: `lower_copy()`
@@ -29,14 +29,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function copy(string $from, string $to, mixed $context): bool
+function copy(string $from, string $to): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `copy()`](../../../php/builtins/filesystem/copy.md)
-
diff --git a/docs/internals/builtins/filesystem/dirname.md b/docs/internals/builtins/filesystem/dirname.md
index 3586eeeb02..6c38a29489 100644
--- a/docs/internals/builtins/filesystem/dirname.md
+++ b/docs/internals/builtins/filesystem/dirname.md
@@ -2,15 +2,15 @@
title: "dirname() — internals"
description: "Compiler internals for dirname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 88
+ order: 105
---
## `dirname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3932](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3932) (`lower_dirname`)
+- **Signature**: [`src/builtins/io/dirname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/dirname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4575](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4575) (`lower_dirname`)
- **Function symbol**: `lower_dirname()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function dirname(string $path, int $levels): string
+function dirname(string $path, int $levels = 1): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function dirname(string $path, int $levels): string
## Cross-references
- [User reference for `dirname()`](../../../php/builtins/filesystem/dirname.md)
-
diff --git a/docs/internals/builtins/filesystem/disk_free_space.md b/docs/internals/builtins/filesystem/disk_free_space.md
index 38eb6e1bf4..8243b6f2e8 100644
--- a/docs/internals/builtins/filesystem/disk_free_space.md
+++ b/docs/internals/builtins/filesystem/disk_free_space.md
@@ -2,15 +2,15 @@
title: "disk_free_space() — internals"
description: "Compiler internals for disk_free_space(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 89
+ order: 106
---
## `disk_free_space()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3149](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3149) (`lower_disk_free_space`)
+- **Signature**: [`src/builtins/io/disk_free_space.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/disk_free_space.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3370](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3370) (`lower_disk_free_space`)
- **Function symbol**: `lower_disk_free_space()`
@@ -36,4 +36,3 @@ function disk_free_space(string $directory): float
## Cross-references
- [User reference for `disk_free_space()`](../../../php/builtins/filesystem/disk_free_space.md)
-
diff --git a/docs/internals/builtins/filesystem/disk_total_space.md b/docs/internals/builtins/filesystem/disk_total_space.md
index c3a3aba1fb..85d7f85e99 100644
--- a/docs/internals/builtins/filesystem/disk_total_space.md
+++ b/docs/internals/builtins/filesystem/disk_total_space.md
@@ -2,15 +2,15 @@
title: "disk_total_space() — internals"
description: "Compiler internals for disk_total_space(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 90
+ order: 107
---
## `disk_total_space()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3157](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3157) (`lower_disk_total_space`)
+- **Signature**: [`src/builtins/io/disk_total_space.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/disk_total_space.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3378](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3378) (`lower_disk_total_space`)
- **Function symbol**: `lower_disk_total_space()`
@@ -36,4 +36,3 @@ function disk_total_space(string $directory): float
## Cross-references
- [User reference for `disk_total_space()`](../../../php/builtins/filesystem/disk_total_space.md)
-
diff --git a/docs/internals/builtins/filesystem/file_exists.md b/docs/internals/builtins/filesystem/file_exists.md
index 843bf435be..a73bca3467 100644
--- a/docs/internals/builtins/filesystem/file_exists.md
+++ b/docs/internals/builtins/filesystem/file_exists.md
@@ -2,15 +2,15 @@
title: "file_exists() — internals"
description: "Compiler internals for file_exists(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 91
+ order: 108
---
## `file_exists()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3756](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3756) (`lower_file_exists`)
+- **Signature**: [`src/builtins/io/file_exists.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/file_exists.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4399](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4399) (`lower_file_exists`)
- **Function symbol**: `lower_file_exists()`
@@ -37,4 +37,3 @@ function file_exists(string $filename): bool
## Cross-references
- [User reference for `file_exists()`](../../../php/builtins/filesystem/file_exists.md)
-
diff --git a/docs/internals/builtins/filesystem/fileatime.md b/docs/internals/builtins/filesystem/fileatime.md
index 86d9c6c6fa..7233c8305e 100644
--- a/docs/internals/builtins/filesystem/fileatime.md
+++ b/docs/internals/builtins/filesystem/fileatime.md
@@ -2,15 +2,15 @@
title: "fileatime() — internals"
description: "Compiler internals for fileatime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 92
+ order: 109
---
## `fileatime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4825](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4825) (`lower_fileatime`)
+- **Signature**: [`src/builtins/io/fileatime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fileatime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5468](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5468) (`lower_fileatime`)
- **Function symbol**: `lower_fileatime()`
@@ -39,4 +39,3 @@ function fileatime(string $filename): mixed
## Cross-references
- [User reference for `fileatime()`](../../../php/builtins/filesystem/fileatime.md)
-
diff --git a/docs/internals/builtins/filesystem/filectime.md b/docs/internals/builtins/filesystem/filectime.md
index f5e40499b5..7731c01108 100644
--- a/docs/internals/builtins/filesystem/filectime.md
+++ b/docs/internals/builtins/filesystem/filectime.md
@@ -2,15 +2,15 @@
title: "filectime() — internals"
description: "Compiler internals for filectime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 93
+ order: 110
---
## `filectime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4833](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4833) (`lower_filectime`)
+- **Signature**: [`src/builtins/io/filectime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/filectime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5476](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5476) (`lower_filectime`)
- **Function symbol**: `lower_filectime()`
@@ -39,4 +39,3 @@ function filectime(string $filename): mixed
## Cross-references
- [User reference for `filectime()`](../../../php/builtins/filesystem/filectime.md)
-
diff --git a/docs/internals/builtins/filesystem/filegroup.md b/docs/internals/builtins/filesystem/filegroup.md
index cfdba2ed3d..ac29c14fc7 100644
--- a/docs/internals/builtins/filesystem/filegroup.md
+++ b/docs/internals/builtins/filesystem/filegroup.md
@@ -2,15 +2,15 @@
title: "filegroup() — internals"
description: "Compiler internals for filegroup(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 94
+ order: 111
---
## `filegroup()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4857](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4857) (`lower_filegroup`)
+- **Signature**: [`src/builtins/io/filegroup.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/filegroup.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5500](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5500) (`lower_filegroup`)
- **Function symbol**: `lower_filegroup()`
@@ -39,4 +39,3 @@ function filegroup(string $filename): mixed
## Cross-references
- [User reference for `filegroup()`](../../../php/builtins/filesystem/filegroup.md)
-
diff --git a/docs/internals/builtins/filesystem/fileinode.md b/docs/internals/builtins/filesystem/fileinode.md
index 7db6c6a68a..c7a99169fb 100644
--- a/docs/internals/builtins/filesystem/fileinode.md
+++ b/docs/internals/builtins/filesystem/fileinode.md
@@ -2,15 +2,15 @@
title: "fileinode() — internals"
description: "Compiler internals for fileinode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 95
+ order: 112
---
## `fileinode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4865](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4865) (`lower_fileinode`)
+- **Signature**: [`src/builtins/io/fileinode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fileinode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5508](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5508) (`lower_fileinode`)
- **Function symbol**: `lower_fileinode()`
@@ -39,4 +39,3 @@ function fileinode(string $filename): mixed
## Cross-references
- [User reference for `fileinode()`](../../../php/builtins/filesystem/fileinode.md)
-
diff --git a/docs/internals/builtins/filesystem/filemtime.md b/docs/internals/builtins/filesystem/filemtime.md
index 02a2436f7b..6a800f621d 100644
--- a/docs/internals/builtins/filesystem/filemtime.md
+++ b/docs/internals/builtins/filesystem/filemtime.md
@@ -2,15 +2,15 @@
title: "filemtime() — internals"
description: "Compiler internals for filemtime(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 96
+ order: 113
---
## `filemtime()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4789](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4789) (`lower_filemtime`)
+- **Signature**: [`src/builtins/io/filemtime.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/filemtime.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5432](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5432) (`lower_filemtime`)
- **Function symbol**: `lower_filemtime()`
@@ -40,4 +40,3 @@ function filemtime(string $filename): int
## Cross-references
- [User reference for `filemtime()`](../../../php/builtins/filesystem/filemtime.md)
-
diff --git a/docs/internals/builtins/filesystem/fileowner.md b/docs/internals/builtins/filesystem/fileowner.md
index ec8843b075..e2be28445b 100644
--- a/docs/internals/builtins/filesystem/fileowner.md
+++ b/docs/internals/builtins/filesystem/fileowner.md
@@ -2,15 +2,15 @@
title: "fileowner() — internals"
description: "Compiler internals for fileowner(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 97
+ order: 114
---
## `fileowner()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4849](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4849) (`lower_fileowner`)
+- **Signature**: [`src/builtins/io/fileowner.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fileowner.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5492](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5492) (`lower_fileowner`)
- **Function symbol**: `lower_fileowner()`
@@ -38,4 +38,3 @@ function fileowner(string $filename): mixed
## Cross-references
- [User reference for `fileowner()`](../../../php/builtins/filesystem/fileowner.md)
-
diff --git a/docs/internals/builtins/filesystem/fileperms.md b/docs/internals/builtins/filesystem/fileperms.md
index 44c46a4746..cf6a95e897 100644
--- a/docs/internals/builtins/filesystem/fileperms.md
+++ b/docs/internals/builtins/filesystem/fileperms.md
@@ -2,15 +2,15 @@
title: "fileperms() — internals"
description: "Compiler internals for fileperms(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 98
+ order: 115
---
## `fileperms()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4841](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4841) (`lower_fileperms`)
+- **Signature**: [`src/builtins/io/fileperms.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fileperms.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5484](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5484) (`lower_fileperms`)
- **Function symbol**: `lower_fileperms()`
@@ -39,4 +39,3 @@ function fileperms(string $filename): mixed
## Cross-references
- [User reference for `fileperms()`](../../../php/builtins/filesystem/fileperms.md)
-
diff --git a/docs/internals/builtins/filesystem/filesize.md b/docs/internals/builtins/filesystem/filesize.md
index f70542477c..38060c85ff 100644
--- a/docs/internals/builtins/filesystem/filesize.md
+++ b/docs/internals/builtins/filesystem/filesize.md
@@ -2,15 +2,15 @@
title: "filesize() — internals"
description: "Compiler internals for filesize(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 99
+ order: 116
---
## `filesize()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4781](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4781) (`lower_filesize`)
+- **Signature**: [`src/builtins/io/filesize.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/filesize.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5424](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5424) (`lower_filesize`)
- **Function symbol**: `lower_filesize()`
@@ -39,4 +39,3 @@ function filesize(string $filename): int
## Cross-references
- [User reference for `filesize()`](../../../php/builtins/filesystem/filesize.md)
-
diff --git a/docs/internals/builtins/filesystem/filetype.md b/docs/internals/builtins/filesystem/filetype.md
index ccbea7f9a9..c22edde1f6 100644
--- a/docs/internals/builtins/filesystem/filetype.md
+++ b/docs/internals/builtins/filesystem/filetype.md
@@ -2,15 +2,15 @@
title: "filetype() — internals"
description: "Compiler internals for filetype(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 100
+ order: 117
---
## `filetype()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4873](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4873) (`lower_filetype`)
+- **Signature**: [`src/builtins/io/filetype.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/filetype.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5516](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5516) (`lower_filetype`)
- **Function symbol**: `lower_filetype()`
@@ -38,4 +38,3 @@ function filetype(string $filename): mixed
## Cross-references
- [User reference for `filetype()`](../../../php/builtins/filesystem/filetype.md)
-
diff --git a/docs/internals/builtins/filesystem/fnmatch.md b/docs/internals/builtins/filesystem/fnmatch.md
index fc023122d6..1d00eb2c06 100644
--- a/docs/internals/builtins/filesystem/fnmatch.md
+++ b/docs/internals/builtins/filesystem/fnmatch.md
@@ -2,15 +2,15 @@
title: "fnmatch() — internals"
description: "Compiler internals for fnmatch(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 101
+ order: 118
---
## `fnmatch()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3960](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3960) (`lower_fnmatch`)
+- **Signature**: [`src/builtins/io/fnmatch.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fnmatch.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4603](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4603) (`lower_fnmatch`)
- **Function symbol**: `lower_fnmatch()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function fnmatch(string $pattern, string $filename, int $flags): bool
+function fnmatch(string $pattern, string $filename, int $flags = 0): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function fnmatch(string $pattern, string $filename, int $flags): bool
## Cross-references
- [User reference for `fnmatch()`](../../../php/builtins/filesystem/fnmatch.md)
-
diff --git a/docs/internals/builtins/filesystem/getcwd.md b/docs/internals/builtins/filesystem/getcwd.md
index 1653a8184a..9243fb1ab3 100644
--- a/docs/internals/builtins/filesystem/getcwd.md
+++ b/docs/internals/builtins/filesystem/getcwd.md
@@ -2,15 +2,15 @@
title: "getcwd() — internals"
description: "Compiler internals for getcwd(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 102
+ order: 119
---
## `getcwd()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4753](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4753) (`lower_getcwd`)
+- **Signature**: [`src/builtins/io/getcwd.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/getcwd.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5396](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5396) (`lower_getcwd`)
- **Function symbol**: `lower_getcwd()`
@@ -37,4 +37,3 @@ function getcwd(): string
## Cross-references
- [User reference for `getcwd()`](../../../php/builtins/filesystem/getcwd.md)
-
diff --git a/docs/internals/builtins/filesystem/getenv.md b/docs/internals/builtins/filesystem/getenv.md
index c2e14e58db..0fac463983 100644
--- a/docs/internals/builtins/filesystem/getenv.md
+++ b/docs/internals/builtins/filesystem/getenv.md
@@ -2,15 +2,15 @@
title: "getenv() — internals"
description: "Compiler internals for getenv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 103
+ order: 120
---
## `getenv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:645](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L645) (`lower_getenv`)
+- **Signature**: [`src/builtins/system/getenv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/getenv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:645](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L645) (`lower_getenv`)
- **Function symbol**: `lower_getenv()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function getenv(string $name, bool $local_only): mixed
+function getenv(string $name): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `getenv()`](../../../php/builtins/filesystem/getenv.md)
-
diff --git a/docs/internals/builtins/filesystem/glob.md b/docs/internals/builtins/filesystem/glob.md
index 6d6e582b00..f55d70dc80 100644
--- a/docs/internals/builtins/filesystem/glob.md
+++ b/docs/internals/builtins/filesystem/glob.md
@@ -2,15 +2,15 @@
title: "glob() — internals"
description: "Compiler internals for glob(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 104
+ order: 121
---
## `glob()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3820](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3820) (`lower_glob`)
+- **Signature**: [`src/builtins/io/glob.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/glob.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4463](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4463) (`lower_glob`)
- **Function symbol**: `lower_glob()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function glob(string $pattern, int $flags): array
+function glob(string $pattern): array
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `glob()`](../../../php/builtins/filesystem/glob.md)
-
diff --git a/docs/internals/builtins/filesystem/is_dir.md b/docs/internals/builtins/filesystem/is_dir.md
index 07e7e3ab02..8f699adcbb 100644
--- a/docs/internals/builtins/filesystem/is_dir.md
+++ b/docs/internals/builtins/filesystem/is_dir.md
@@ -2,15 +2,15 @@
title: "is_dir() — internals"
description: "Compiler internals for is_dir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 105
+ order: 122
---
## `is_dir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4957](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4957) (`lower_is_dir`)
+- **Signature**: [`src/builtins/io/is_dir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_dir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5600](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5600) (`lower_is_dir`)
- **Function symbol**: `lower_is_dir()`
@@ -38,4 +38,3 @@ function is_dir(string $filename): bool
## Cross-references
- [User reference for `is_dir()`](../../../php/builtins/filesystem/is_dir.md)
-
diff --git a/docs/internals/builtins/filesystem/is_executable.md b/docs/internals/builtins/filesystem/is_executable.md
index 584811a677..9550fdfcb2 100644
--- a/docs/internals/builtins/filesystem/is_executable.md
+++ b/docs/internals/builtins/filesystem/is_executable.md
@@ -2,15 +2,15 @@
title: "is_executable() — internals"
description: "Compiler internals for is_executable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 106
+ order: 123
---
## `is_executable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4989](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4989) (`lower_is_executable`)
+- **Signature**: [`src/builtins/io/is_executable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_executable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5632](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5632) (`lower_is_executable`)
- **Function symbol**: `lower_is_executable()`
@@ -39,4 +39,3 @@ function is_executable(string $filename): bool
## Cross-references
- [User reference for `is_executable()`](../../../php/builtins/filesystem/is_executable.md)
-
diff --git a/docs/internals/builtins/filesystem/is_file.md b/docs/internals/builtins/filesystem/is_file.md
index a9b04ae600..4b2a35ea45 100644
--- a/docs/internals/builtins/filesystem/is_file.md
+++ b/docs/internals/builtins/filesystem/is_file.md
@@ -2,15 +2,15 @@
title: "is_file() — internals"
description: "Compiler internals for is_file(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 107
+ order: 124
---
## `is_file()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4949](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4949) (`lower_is_file`)
+- **Signature**: [`src/builtins/io/is_file.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_file.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5592](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5592) (`lower_is_file`)
- **Function symbol**: `lower_is_file()`
@@ -38,4 +38,3 @@ function is_file(string $filename): bool
## Cross-references
- [User reference for `is_file()`](../../../php/builtins/filesystem/is_file.md)
-
diff --git a/docs/internals/builtins/filesystem/is_link.md b/docs/internals/builtins/filesystem/is_link.md
index 2810bbd883..26df330e09 100644
--- a/docs/internals/builtins/filesystem/is_link.md
+++ b/docs/internals/builtins/filesystem/is_link.md
@@ -2,15 +2,15 @@
title: "is_link() — internals"
description: "Compiler internals for is_link(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 108
+ order: 125
---
## `is_link()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4997](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4997) (`lower_is_link`)
+- **Signature**: [`src/builtins/io/is_link.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_link.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5640](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5640) (`lower_is_link`)
- **Function symbol**: `lower_is_link()`
@@ -39,4 +39,3 @@ function is_link(string $filename): bool
## Cross-references
- [User reference for `is_link()`](../../../php/builtins/filesystem/is_link.md)
-
diff --git a/docs/internals/builtins/filesystem/is_readable.md b/docs/internals/builtins/filesystem/is_readable.md
index ada5dc3167..0e61b1965f 100644
--- a/docs/internals/builtins/filesystem/is_readable.md
+++ b/docs/internals/builtins/filesystem/is_readable.md
@@ -2,15 +2,15 @@
title: "is_readable() — internals"
description: "Compiler internals for is_readable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 109
+ order: 126
---
## `is_readable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4965](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4965) (`lower_is_readable`)
+- **Signature**: [`src/builtins/io/is_readable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_readable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5608](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5608) (`lower_is_readable`)
- **Function symbol**: `lower_is_readable()`
@@ -38,4 +38,3 @@ function is_readable(string $filename): bool
## Cross-references
- [User reference for `is_readable()`](../../../php/builtins/filesystem/is_readable.md)
-
diff --git a/docs/internals/builtins/filesystem/is_writable.md b/docs/internals/builtins/filesystem/is_writable.md
index ebe3f17694..f20c80490c 100644
--- a/docs/internals/builtins/filesystem/is_writable.md
+++ b/docs/internals/builtins/filesystem/is_writable.md
@@ -2,15 +2,15 @@
title: "is_writable() — internals"
description: "Compiler internals for is_writable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 110
+ order: 127
---
## `is_writable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4973](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4973) (`lower_is_writable`)
+- **Signature**: [`src/builtins/io/is_writable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_writable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5616](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5616) (`lower_is_writable`)
- **Function symbol**: `lower_is_writable()`
@@ -38,4 +38,3 @@ function is_writable(string $filename): bool
## Cross-references
- [User reference for `is_writable()`](../../../php/builtins/filesystem/is_writable.md)
-
diff --git a/docs/internals/builtins/filesystem/is_writeable.md b/docs/internals/builtins/filesystem/is_writeable.md
index cb88216284..906ddbb7fe 100644
--- a/docs/internals/builtins/filesystem/is_writeable.md
+++ b/docs/internals/builtins/filesystem/is_writeable.md
@@ -2,15 +2,15 @@
title: "is_writeable() — internals"
description: "Compiler internals for is_writeable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 111
+ order: 128
---
## `is_writeable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4981](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4981) (`lower_is_writeable`)
+- **Signature**: [`src/builtins/io/is_writeable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/is_writeable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5624](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5624) (`lower_is_writeable`)
- **Function symbol**: `lower_is_writeable()`
@@ -38,4 +38,3 @@ function is_writeable(string $filename): bool
## Cross-references
- [User reference for `is_writeable()`](../../../php/builtins/filesystem/is_writeable.md)
-
diff --git a/docs/internals/builtins/filesystem/lchgrp.md b/docs/internals/builtins/filesystem/lchgrp.md
index 7ebb947b94..054d954188 100644
--- a/docs/internals/builtins/filesystem/lchgrp.md
+++ b/docs/internals/builtins/filesystem/lchgrp.md
@@ -2,15 +2,15 @@
title: "lchgrp() — internals"
description: "Compiler internals for lchgrp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 112
+ order: 129
---
## `lchgrp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3845](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3845) (`lower_lchgrp`)
+- **Signature**: [`src/builtins/io/lchgrp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/lchgrp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4488](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4488) (`lower_lchgrp`)
- **Function symbol**: `lower_lchgrp()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function lchgrp(string $filename, int $group): bool
+function lchgrp(string $filename, string $group): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function lchgrp(string $filename, int $group): bool
## Cross-references
- [User reference for `lchgrp()`](../../../php/builtins/filesystem/lchgrp.md)
-
diff --git a/docs/internals/builtins/filesystem/lchown.md b/docs/internals/builtins/filesystem/lchown.md
index 8bc4487266..d4f2cf70d6 100644
--- a/docs/internals/builtins/filesystem/lchown.md
+++ b/docs/internals/builtins/filesystem/lchown.md
@@ -2,15 +2,15 @@
title: "lchown() — internals"
description: "Compiler internals for lchown(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 113
+ order: 130
---
## `lchown()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3840](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3840) (`lower_lchown`)
+- **Signature**: [`src/builtins/io/lchown.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/lchown.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4483](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4483) (`lower_lchown`)
- **Function symbol**: `lower_lchown()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function lchown(string $filename, int $user): bool
+function lchown(string $filename, string $user): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function lchown(string $filename, int $user): bool
## Cross-references
- [User reference for `lchown()`](../../../php/builtins/filesystem/lchown.md)
-
diff --git a/docs/internals/builtins/filesystem/link.md b/docs/internals/builtins/filesystem/link.md
index 8e07bc9b6e..4e2421a4cd 100644
--- a/docs/internals/builtins/filesystem/link.md
+++ b/docs/internals/builtins/filesystem/link.md
@@ -2,15 +2,15 @@
title: "link() — internals"
description: "Compiler internals for link(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 114
+ order: 131
---
## `link()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4810](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4810) (`lower_link`)
+- **Signature**: [`src/builtins/io/link.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/link.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5453](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5453) (`lower_link`)
- **Function symbol**: `lower_link()`
@@ -39,4 +39,3 @@ function link(string $target, string $link): bool
## Cross-references
- [User reference for `link()`](../../../php/builtins/filesystem/link.md)
-
diff --git a/docs/internals/builtins/filesystem/linkinfo.md b/docs/internals/builtins/filesystem/linkinfo.md
index d62186b9e1..bec27ff255 100644
--- a/docs/internals/builtins/filesystem/linkinfo.md
+++ b/docs/internals/builtins/filesystem/linkinfo.md
@@ -2,15 +2,15 @@
title: "linkinfo() — internals"
description: "Compiler internals for linkinfo(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 115
+ order: 132
---
## `linkinfo()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4797](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4797) (`lower_linkinfo`)
+- **Signature**: [`src/builtins/io/linkinfo.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/linkinfo.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5440](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5440) (`lower_linkinfo`)
- **Function symbol**: `lower_linkinfo()`
@@ -39,4 +39,3 @@ function linkinfo(string $path): int
## Cross-references
- [User reference for `linkinfo()`](../../../php/builtins/filesystem/linkinfo.md)
-
diff --git a/docs/internals/builtins/filesystem/lstat.md b/docs/internals/builtins/filesystem/lstat.md
index bc2bf1c008..1adfae4bb3 100644
--- a/docs/internals/builtins/filesystem/lstat.md
+++ b/docs/internals/builtins/filesystem/lstat.md
@@ -2,15 +2,15 @@
title: "lstat() — internals"
description: "Compiler internals for lstat(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 116
+ order: 133
---
## `lstat()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4891](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4891) (`lower_lstat`)
+- **Signature**: [`src/builtins/io/lstat.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/lstat.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5534](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5534) (`lower_lstat`)
- **Function symbol**: `lower_lstat()`
@@ -37,4 +37,3 @@ function lstat(string $filename): mixed
## Cross-references
- [User reference for `lstat()`](../../../php/builtins/filesystem/lstat.md)
-
diff --git a/docs/internals/builtins/filesystem/mkdir.md b/docs/internals/builtins/filesystem/mkdir.md
index b5bab2aee2..4bc31dbde1 100644
--- a/docs/internals/builtins/filesystem/mkdir.md
+++ b/docs/internals/builtins/filesystem/mkdir.md
@@ -2,15 +2,15 @@
title: "mkdir() — internals"
description: "Compiler internals for mkdir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 117
+ order: 134
---
## `mkdir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3785](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3785) (`lower_mkdir`)
+- **Signature**: [`src/builtins/io/mkdir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/mkdir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4428](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4428) (`lower_mkdir`)
- **Function symbol**: `lower_mkdir()`
@@ -30,14 +30,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function mkdir(string $directory, int $permissions, bool $recursive, bool $context): bool
+function mkdir(string $directory): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 4 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `mkdir()`](../../../php/builtins/filesystem/mkdir.md)
-
diff --git a/docs/internals/builtins/filesystem/pathinfo.md b/docs/internals/builtins/filesystem/pathinfo.md
index f276e07d5f..ebd9883a0c 100644
--- a/docs/internals/builtins/filesystem/pathinfo.md
+++ b/docs/internals/builtins/filesystem/pathinfo.md
@@ -2,15 +2,15 @@
title: "pathinfo() — internals"
description: "Compiler internals for pathinfo(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 118
+ order: 135
---
## `pathinfo()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4001](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4001) (`lower_pathinfo`)
+- **Signature**: [`src/builtins/io/pathinfo.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/pathinfo.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4644](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4644) (`lower_pathinfo`)
- **Function symbol**: `lower_pathinfo()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function pathinfo(string $path, int $flags): mixed
+function pathinfo(string $path, int $flags = 15): array
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function pathinfo(string $path, int $flags): mixed
## Cross-references
- [User reference for `pathinfo()`](../../../php/builtins/filesystem/pathinfo.md)
-
diff --git a/docs/internals/builtins/filesystem/putenv.md b/docs/internals/builtins/filesystem/putenv.md
index a6e1716242..39db571ebf 100644
--- a/docs/internals/builtins/filesystem/putenv.md
+++ b/docs/internals/builtins/filesystem/putenv.md
@@ -2,15 +2,15 @@
title: "putenv() — internals"
description: "Compiler internals for putenv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 119
+ order: 136
---
## `putenv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:657](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L657) (`lower_putenv`)
+- **Signature**: [`src/builtins/system/putenv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/putenv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:657](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L657) (`lower_putenv`)
- **Function symbol**: `lower_putenv()`
@@ -36,4 +36,3 @@ function putenv(string $assignment): bool
## Cross-references
- [User reference for `putenv()`](../../../php/builtins/filesystem/putenv.md)
-
diff --git a/docs/internals/builtins/filesystem/readfile.md b/docs/internals/builtins/filesystem/readfile.md
index 594785f43e..ba9ab3f8b9 100644
--- a/docs/internals/builtins/filesystem/readfile.md
+++ b/docs/internals/builtins/filesystem/readfile.md
@@ -2,15 +2,15 @@
title: "readfile() — internals"
description: "Compiler internals for readfile(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 120
+ order: 137
---
## `readfile()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:193](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L193) (`lower_readfile`)
+- **Signature**: [`src/builtins/io/readfile.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/readfile.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:300](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L300) (`lower_readfile`)
- **Function symbol**: `lower_readfile()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function readfile(string $filename, bool $use_include_path, mixed $context): mixed
+function readfile(string $filename): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `readfile()`](../../../php/builtins/filesystem/readfile.md)
-
diff --git a/docs/internals/builtins/filesystem/readlink.md b/docs/internals/builtins/filesystem/readlink.md
index cd37d759cd..9b67cd14b3 100644
--- a/docs/internals/builtins/filesystem/readlink.md
+++ b/docs/internals/builtins/filesystem/readlink.md
@@ -2,15 +2,15 @@
title: "readlink() — internals"
description: "Compiler internals for readlink(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 121
+ order: 138
---
## `readlink()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4815](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4815) (`lower_readlink`)
+- **Signature**: [`src/builtins/io/readlink.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/readlink.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5458](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5458) (`lower_readlink`)
- **Function symbol**: `lower_readlink()`
@@ -39,4 +39,3 @@ function readlink(string $path): mixed
## Cross-references
- [User reference for `readlink()`](../../../php/builtins/filesystem/readlink.md)
-
diff --git a/docs/internals/builtins/filesystem/realpath.md b/docs/internals/builtins/filesystem/realpath.md
index c47a307d45..eaee752b33 100644
--- a/docs/internals/builtins/filesystem/realpath.md
+++ b/docs/internals/builtins/filesystem/realpath.md
@@ -2,15 +2,15 @@
title: "realpath() — internals"
description: "Compiler internals for realpath(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 122
+ order: 139
---
## `realpath()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3465](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3465) (`lower_realpath`)
+- **Signature**: [`src/builtins/io/realpath.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/realpath.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3690](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3690) (`lower_realpath`)
- **Function symbol**: `lower_realpath()`
@@ -36,4 +36,3 @@ function realpath(string $path): mixed
## Cross-references
- [User reference for `realpath()`](../../../php/builtins/filesystem/realpath.md)
-
diff --git a/docs/internals/builtins/filesystem/realpath_cache_get.md b/docs/internals/builtins/filesystem/realpath_cache_get.md
index 9dec55a0ba..e32cb410b5 100644
--- a/docs/internals/builtins/filesystem/realpath_cache_get.md
+++ b/docs/internals/builtins/filesystem/realpath_cache_get.md
@@ -2,15 +2,15 @@
title: "realpath_cache_get() — internals"
description: "Compiler internals for realpath_cache_get(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 123
+ order: 140
---
## `realpath_cache_get()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3475](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3475) (`lower_realpath_cache_get`)
+- **Signature**: [`src/builtins/io/realpath_cache_get.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/realpath_cache_get.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3700](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3700) (`lower_realpath_cache_get`)
- **Function symbol**: `lower_realpath_cache_get()`
@@ -35,4 +35,3 @@ function realpath_cache_get(): array
## Cross-references
- [User reference for `realpath_cache_get()`](../../../php/builtins/filesystem/realpath_cache_get.md)
-
diff --git a/docs/internals/builtins/filesystem/realpath_cache_size.md b/docs/internals/builtins/filesystem/realpath_cache_size.md
index 19b0b4e179..113cb67e63 100644
--- a/docs/internals/builtins/filesystem/realpath_cache_size.md
+++ b/docs/internals/builtins/filesystem/realpath_cache_size.md
@@ -2,15 +2,15 @@
title: "realpath_cache_size() — internals"
description: "Compiler internals for realpath_cache_size(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 124
+ order: 141
---
## `realpath_cache_size()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3485](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3485) (`lower_realpath_cache_size`)
+- **Signature**: [`src/builtins/io/realpath_cache_size.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/realpath_cache_size.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3710](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3710) (`lower_realpath_cache_size`)
- **Function symbol**: `lower_realpath_cache_size()`
@@ -35,4 +35,3 @@ function realpath_cache_size(): int
## Cross-references
- [User reference for `realpath_cache_size()`](../../../php/builtins/filesystem/realpath_cache_size.md)
-
diff --git a/docs/internals/builtins/filesystem/rename.md b/docs/internals/builtins/filesystem/rename.md
index b743a82d5c..33acbcfc62 100644
--- a/docs/internals/builtins/filesystem/rename.md
+++ b/docs/internals/builtins/filesystem/rename.md
@@ -2,15 +2,15 @@
title: "rename() — internals"
description: "Compiler internals for rename(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 125
+ order: 142
---
## `rename()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3805](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3805) (`lower_rename`)
+- **Signature**: [`src/builtins/io/rename.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/rename.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4448](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4448) (`lower_rename`)
- **Function symbol**: `lower_rename()`
@@ -28,14 +28,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function rename(string $from, string $to, mixed $context): bool
+function rename(string $from, string $to): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `rename()`](../../../php/builtins/filesystem/rename.md)
-
diff --git a/docs/internals/builtins/filesystem/rmdir.md b/docs/internals/builtins/filesystem/rmdir.md
index 7fb455562d..6add2d8250 100644
--- a/docs/internals/builtins/filesystem/rmdir.md
+++ b/docs/internals/builtins/filesystem/rmdir.md
@@ -2,15 +2,15 @@
title: "rmdir() — internals"
description: "Compiler internals for rmdir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 126
+ order: 143
---
## `rmdir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3790](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3790) (`lower_rmdir`)
+- **Signature**: [`src/builtins/io/rmdir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/rmdir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4433](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4433) (`lower_rmdir`)
- **Function symbol**: `lower_rmdir()`
@@ -30,14 +30,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function rmdir(string $directory, mixed $context = null): bool
+function rmdir(string $directory): bool
```
## What the type checker enforces
-- **Arity**: takes 1–2 arguments (1 optional).
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `rmdir()`](../../../php/builtins/filesystem/rmdir.md)
-
diff --git a/docs/internals/builtins/filesystem/scandir.md b/docs/internals/builtins/filesystem/scandir.md
index bdceddebf0..1225c0afbe 100644
--- a/docs/internals/builtins/filesystem/scandir.md
+++ b/docs/internals/builtins/filesystem/scandir.md
@@ -2,15 +2,15 @@
title: "scandir() — internals"
description: "Compiler internals for scandir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 127
+ order: 144
---
## `scandir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3815](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3815) (`lower_scandir`)
+- **Signature**: [`src/builtins/io/scandir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/scandir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4458](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4458) (`lower_scandir`)
- **Function symbol**: `lower_scandir()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function scandir(string $directory, int $sorting_order, mixed $context): array
+function scandir(string $directory): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `scandir()`](../../../php/builtins/filesystem/scandir.md)
-
diff --git a/docs/internals/builtins/filesystem/stat.md b/docs/internals/builtins/filesystem/stat.md
index a67e1a45a2..0969fc70a2 100644
--- a/docs/internals/builtins/filesystem/stat.md
+++ b/docs/internals/builtins/filesystem/stat.md
@@ -2,15 +2,15 @@
title: "stat() — internals"
description: "Compiler internals for stat(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 128
+ order: 145
---
## `stat()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4886](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4886) (`lower_stat`)
+- **Signature**: [`src/builtins/io/stat.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stat.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5529](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5529) (`lower_stat`)
- **Function symbol**: `lower_stat()`
@@ -38,4 +38,3 @@ function stat(string $filename): mixed
## Cross-references
- [User reference for `stat()`](../../../php/builtins/filesystem/stat.md)
-
diff --git a/docs/internals/builtins/filesystem/symlink.md b/docs/internals/builtins/filesystem/symlink.md
index c2a0df2600..4930e27f74 100644
--- a/docs/internals/builtins/filesystem/symlink.md
+++ b/docs/internals/builtins/filesystem/symlink.md
@@ -2,15 +2,15 @@
title: "symlink() — internals"
description: "Compiler internals for symlink(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 129
+ order: 146
---
## `symlink()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4805](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4805) (`lower_symlink`)
+- **Signature**: [`src/builtins/io/symlink.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/symlink.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5448](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5448) (`lower_symlink`)
- **Function symbol**: `lower_symlink()`
@@ -39,4 +39,3 @@ function symlink(string $target, string $link): bool
## Cross-references
- [User reference for `symlink()`](../../../php/builtins/filesystem/symlink.md)
-
diff --git a/docs/internals/builtins/filesystem/sys_get_temp_dir.md b/docs/internals/builtins/filesystem/sys_get_temp_dir.md
index f2e4c85954..7c45622094 100644
--- a/docs/internals/builtins/filesystem/sys_get_temp_dir.md
+++ b/docs/internals/builtins/filesystem/sys_get_temp_dir.md
@@ -2,15 +2,15 @@
title: "sys_get_temp_dir() — internals"
description: "Compiler internals for sys_get_temp_dir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 130
+ order: 147
---
## `sys_get_temp_dir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4760](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4760) (`lower_sys_get_temp_dir`)
+- **Signature**: [`src/builtins/io/sys_get_temp_dir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/sys_get_temp_dir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5403](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5403) (`lower_sys_get_temp_dir`)
- **Function symbol**: `lower_sys_get_temp_dir()`
@@ -36,4 +36,3 @@ function sys_get_temp_dir(): string
## Cross-references
- [User reference for `sys_get_temp_dir()`](../../../php/builtins/filesystem/sys_get_temp_dir.md)
-
diff --git a/docs/internals/builtins/filesystem/tempnam.md b/docs/internals/builtins/filesystem/tempnam.md
index ba83498b41..d1de170ba3 100644
--- a/docs/internals/builtins/filesystem/tempnam.md
+++ b/docs/internals/builtins/filesystem/tempnam.md
@@ -2,15 +2,15 @@
title: "tempnam() — internals"
description: "Compiler internals for tempnam(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 131
+ order: 148
---
## `tempnam()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3810](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3810) (`lower_tempnam`)
+- **Signature**: [`src/builtins/io/tempnam.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/tempnam.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4453](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4453) (`lower_tempnam`)
- **Function symbol**: `lower_tempnam()`
@@ -38,4 +38,3 @@ function tempnam(string $directory, string $prefix): string
## Cross-references
- [User reference for `tempnam()`](../../../php/builtins/filesystem/tempnam.md)
-
diff --git a/docs/internals/builtins/filesystem/tmpfile.md b/docs/internals/builtins/filesystem/tmpfile.md
index d9d299c1ad..1c72e490e0 100644
--- a/docs/internals/builtins/filesystem/tmpfile.md
+++ b/docs/internals/builtins/filesystem/tmpfile.md
@@ -2,15 +2,15 @@
title: "tmpfile() — internals"
description: "Compiler internals for tmpfile(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 132
+ order: 149
---
## `tmpfile()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4773](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4773) (`lower_tmpfile`)
+- **Signature**: [`src/builtins/io/tmpfile.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/tmpfile.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5416](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5416) (`lower_tmpfile`)
- **Function symbol**: `lower_tmpfile()`
@@ -38,4 +38,3 @@ function tmpfile(): mixed
## Cross-references
- [User reference for `tmpfile()`](../../../php/builtins/filesystem/tmpfile.md)
-
diff --git a/docs/internals/builtins/filesystem/touch.md b/docs/internals/builtins/filesystem/touch.md
index 8d95d2188c..9a4ebb029c 100644
--- a/docs/internals/builtins/filesystem/touch.md
+++ b/docs/internals/builtins/filesystem/touch.md
@@ -2,15 +2,15 @@
title: "touch() — internals"
description: "Compiler internals for touch(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 133
+ order: 150
---
## `touch()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3880](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3880) (`lower_touch`)
+- **Signature**: [`src/builtins/io/touch.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/touch.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4523](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4523) (`lower_touch`)
- **Function symbol**: `lower_touch()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function touch(string $filename, int $mtime, int $atime): bool
+function touch(string $filename, int $mtime = null, int $atime = null): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function touch(string $filename, int $mtime, int $atime): bool
## Cross-references
- [User reference for `touch()`](../../../php/builtins/filesystem/touch.md)
-
diff --git a/docs/internals/builtins/filesystem/umask.md b/docs/internals/builtins/filesystem/umask.md
index c4cbd0d73f..50b45bae18 100644
--- a/docs/internals/builtins/filesystem/umask.md
+++ b/docs/internals/builtins/filesystem/umask.md
@@ -2,15 +2,15 @@
title: "umask() — internals"
description: "Compiler internals for umask(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 134
+ order: 151
---
## `umask()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3850](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3850) (`lower_umask`)
+- **Signature**: [`src/builtins/io/umask.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/umask.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4493](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4493) (`lower_umask`)
- **Function symbol**: `lower_umask()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function umask(int $mask): int
+function umask(int $mask = null): int
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function umask(int $mask): int
## Cross-references
- [User reference for `umask()`](../../../php/builtins/filesystem/umask.md)
-
diff --git a/docs/internals/builtins/filesystem/unlink.md b/docs/internals/builtins/filesystem/unlink.md
index ee48594f90..fb9df6ff4a 100644
--- a/docs/internals/builtins/filesystem/unlink.md
+++ b/docs/internals/builtins/filesystem/unlink.md
@@ -2,15 +2,15 @@
title: "unlink() — internals"
description: "Compiler internals for unlink(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 135
+ order: 152
---
## `unlink()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3764](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3764) (`lower_unlink`)
+- **Signature**: [`src/builtins/io/unlink.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/unlink.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:4407](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L4407) (`lower_unlink`)
- **Function symbol**: `lower_unlink()`
@@ -38,4 +38,3 @@ function unlink(string $filename): bool
## Cross-references
- [User reference for `unlink()`](../../../php/builtins/filesystem/unlink.md)
-
diff --git a/docs/internals/builtins/io/closedir.md b/docs/internals/builtins/io/closedir.md
index 228c19b98a..4a8ad88cc8 100644
--- a/docs/internals/builtins/io/closedir.md
+++ b/docs/internals/builtins/io/closedir.md
@@ -2,15 +2,15 @@
title: "closedir() — internals"
description: "Compiler internals for closedir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 136
+ order: 153
---
## `closedir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3351](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3351) (`lower_closedir`)
+- **Signature**: [`src/builtins/io/closedir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/closedir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3572](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3572) (`lower_closedir`)
- **Function symbol**: `lower_closedir()`
@@ -39,4 +39,3 @@ function closedir(resource $dir_handle): void
## Cross-references
- [User reference for `closedir()`](../../../php/builtins/io/closedir.md)
-
diff --git a/docs/internals/builtins/io/fclose.md b/docs/internals/builtins/io/fclose.md
index b198d7d33d..07d61b2ec2 100644
--- a/docs/internals/builtins/io/fclose.md
+++ b/docs/internals/builtins/io/fclose.md
@@ -2,15 +2,15 @@
title: "fclose() — internals"
description: "Compiler internals for fclose(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 137
+ order: 154
---
## `fclose()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2488](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2488) (`lower_fclose`)
+- **Signature**: [`src/builtins/io/fclose.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fclose.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2707](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2707) (`lower_fclose`)
- **Function symbol**: `lower_fclose()`
@@ -35,4 +35,3 @@ function fclose(resource $stream): bool
## Cross-references
- [User reference for `fclose()`](../../../php/builtins/io/fclose.md)
-
diff --git a/docs/internals/builtins/io/fdatasync.md b/docs/internals/builtins/io/fdatasync.md
index 255d86aa87..43ad181d29 100644
--- a/docs/internals/builtins/io/fdatasync.md
+++ b/docs/internals/builtins/io/fdatasync.md
@@ -2,15 +2,15 @@
title: "fdatasync() — internals"
description: "Compiler internals for fdatasync(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 138
+ order: 155
---
## `fdatasync()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3083](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3083) (`lower_fdatasync`)
+- **Signature**: [`src/builtins/io/fdatasync.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fdatasync.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3304](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3304) (`lower_fdatasync`)
- **Function symbol**: `lower_fdatasync()`
@@ -36,4 +36,3 @@ function fdatasync(resource $stream): bool
## Cross-references
- [User reference for `fdatasync()`](../../../php/builtins/io/fdatasync.md)
-
diff --git a/docs/internals/builtins/io/feof.md b/docs/internals/builtins/io/feof.md
index 8b90419d3c..e24c273b01 100644
--- a/docs/internals/builtins/io/feof.md
+++ b/docs/internals/builtins/io/feof.md
@@ -2,15 +2,15 @@
title: "feof() — internals"
description: "Compiler internals for feof(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 139
+ order: 156
---
## `feof()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2900](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2900) (`lower_feof`)
+- **Signature**: [`src/builtins/io/feof.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/feof.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3121](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3121) (`lower_feof`)
- **Function symbol**: `lower_feof()`
@@ -37,4 +37,3 @@ function feof(resource $stream): bool
## Cross-references
- [User reference for `feof()`](../../../php/builtins/io/feof.md)
-
diff --git a/docs/internals/builtins/io/fflush.md b/docs/internals/builtins/io/fflush.md
index 207a4723b8..2fc2d1aade 100644
--- a/docs/internals/builtins/io/fflush.md
+++ b/docs/internals/builtins/io/fflush.md
@@ -2,15 +2,15 @@
title: "fflush() — internals"
description: "Compiler internals for fflush(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 140
+ order: 157
---
## `fflush()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3045](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3045) (`lower_fflush`)
+- **Signature**: [`src/builtins/io/fflush.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fflush.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3266](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3266) (`lower_fflush`)
- **Function symbol**: `lower_fflush()`
@@ -36,4 +36,3 @@ function fflush(resource $stream): bool
## Cross-references
- [User reference for `fflush()`](../../../php/builtins/io/fflush.md)
-
diff --git a/docs/internals/builtins/io/fgetc.md b/docs/internals/builtins/io/fgetc.md
index 0895a2d6f8..8a79b3f773 100644
--- a/docs/internals/builtins/io/fgetc.md
+++ b/docs/internals/builtins/io/fgetc.md
@@ -2,15 +2,15 @@
title: "fgetc() — internals"
description: "Compiler internals for fgetc(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 141
+ order: 158
---
## `fgetc()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2759](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2759) (`lower_fgetc`)
+- **Signature**: [`src/builtins/io/fgetc.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fgetc.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2980](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2980) (`lower_fgetc`)
- **Function symbol**: `lower_fgetc()`
@@ -37,4 +37,3 @@ function fgetc(resource $stream): mixed
## Cross-references
- [User reference for `fgetc()`](../../../php/builtins/io/fgetc.md)
-
diff --git a/docs/internals/builtins/io/fgetcsv.md b/docs/internals/builtins/io/fgetcsv.md
index 7498ea8a72..9e7afe5ddd 100644
--- a/docs/internals/builtins/io/fgetcsv.md
+++ b/docs/internals/builtins/io/fgetcsv.md
@@ -2,15 +2,15 @@
title: "fgetcsv() — internals"
description: "Compiler internals for fgetcsv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 142
+ order: 159
---
## `fgetcsv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2772](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2772) (`lower_fgetcsv`)
+- **Signature**: [`src/builtins/io/fgetcsv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fgetcsv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2993](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2993) (`lower_fgetcsv`)
- **Function symbol**: `lower_fgetcsv()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function fgetcsv(resource $stream, int $length, string $separator, string $enclosure, string $escape): array
+function fgetcsv(resource $stream, int $length = null, string $separator = ','): array
```
## What the type checker enforces
-- **Arity**: takes 3–5 arguments (2 optional).
+- **Arity**: takes 1–3 arguments (2 optional).
## Cross-references
- [User reference for `fgetcsv()`](../../../php/builtins/io/fgetcsv.md)
-
diff --git a/docs/internals/builtins/io/fgets.md b/docs/internals/builtins/io/fgets.md
index dcfad29556..b87a1aeb18 100644
--- a/docs/internals/builtins/io/fgets.md
+++ b/docs/internals/builtins/io/fgets.md
@@ -2,15 +2,15 @@
title: "fgets() — internals"
description: "Compiler internals for fgets(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 143
+ order: 160
---
## `fgets()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2746](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2746) (`lower_fgets`)
+- **Signature**: [`src/builtins/io/fgets.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fgets.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2967](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2967) (`lower_fgets`)
- **Function symbol**: `lower_fgets()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function fgets(resource $stream, int $length): mixed
+function fgets(resource $stream): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `fgets()`](../../../php/builtins/io/fgets.md)
-
diff --git a/docs/internals/builtins/io/file.md b/docs/internals/builtins/io/file.md
index 6aa5cf1907..53664951e9 100644
--- a/docs/internals/builtins/io/file.md
+++ b/docs/internals/builtins/io/file.md
@@ -2,15 +2,15 @@
title: "file() — internals"
description: "Compiler internals for file(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 144
+ order: 161
---
## `file()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3460](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3460) (`lower_file`)
+- **Signature**: [`src/builtins/io/file.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/file.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3685](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3685) (`lower_file`)
- **Function symbol**: `lower_file()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function file(string $filename, int $flags, mixed $context): array
+function file(string $filename): array
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `file()`](../../../php/builtins/io/file.md)
-
diff --git a/docs/internals/builtins/io/file_get_contents.md b/docs/internals/builtins/io/file_get_contents.md
index a39f076629..341051a308 100644
--- a/docs/internals/builtins/io/file_get_contents.md
+++ b/docs/internals/builtins/io/file_get_contents.md
@@ -2,15 +2,15 @@
title: "file_get_contents() — internals"
description: "Compiler internals for file_get_contents(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 145
+ order: 162
---
## `file_get_contents()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:39](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L39) (`lower_file_get_contents`)
+- **Signature**: [`src/builtins/io/file_get_contents.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/file_get_contents.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:39](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L39) (`lower_file_get_contents`)
- **Function symbol**: `lower_file_get_contents()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function file_get_contents(string $filename, bool $use_include_path, mixed $context, int $offset, int $length): mixed
+function file_get_contents(string $filename): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 5 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `file_get_contents()`](../../../php/builtins/io/file_get_contents.md)
-
diff --git a/docs/internals/builtins/io/file_put_contents.md b/docs/internals/builtins/io/file_put_contents.md
index 3334689517..a0dd17d11b 100644
--- a/docs/internals/builtins/io/file_put_contents.md
+++ b/docs/internals/builtins/io/file_put_contents.md
@@ -2,15 +2,15 @@
title: "file_put_contents() — internals"
description: "Compiler internals for file_put_contents(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 146
+ order: 163
---
## `file_put_contents()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3502](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3502) (`lower_file_put_contents`)
+- **Signature**: [`src/builtins/io/file_put_contents.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/file_put_contents.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3727](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3727) (`lower_file_put_contents`)
- **Function symbol**: `lower_file_put_contents()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function file_put_contents(string $filename, mixed $data, int $flags = 0, mixed $context = null): int
+function file_put_contents(string $filename, string $data): int
```
## What the type checker enforces
-- **Arity**: takes 2–4 arguments (2 optional).
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `file_put_contents()`](../../../php/builtins/io/file_put_contents.md)
-
diff --git a/docs/internals/builtins/io/flock.md b/docs/internals/builtins/io/flock.md
index 9253d4f6df..05e1b70504 100644
--- a/docs/internals/builtins/io/flock.md
+++ b/docs/internals/builtins/io/flock.md
@@ -2,15 +2,15 @@
title: "flock() — internals"
description: "Compiler internals for flock(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 147
+ order: 164
---
## `flock()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3088](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3088) (`lower_flock`)
+- **Signature**: [`src/builtins/io/flock.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/flock.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3309](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3309) (`lower_flock`)
- **Function symbol**: `lower_flock()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function flock(resource $stream, int $operation, bool $would_block): bool
+function flock(resource $stream, int $operation, bool $would_block = null): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function flock(resource $stream, int $operation, bool $would_block): bool
## Cross-references
- [User reference for `flock()`](../../../php/builtins/io/flock.md)
-
diff --git a/docs/internals/builtins/io/fopen.md b/docs/internals/builtins/io/fopen.md
index 9caf8c0787..f4af5776b2 100644
--- a/docs/internals/builtins/io/fopen.md
+++ b/docs/internals/builtins/io/fopen.md
@@ -2,15 +2,15 @@
title: "fopen() — internals"
description: "Compiler internals for fopen(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 148
+ order: 165
---
## `fopen()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:233](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L233) (`lower_fopen`)
+- **Signature**: [`src/builtins/io/fopen.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fopen.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:340](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L340) (`lower_fopen`)
- **Function symbol**: `lower_fopen()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function fopen(string $filename, string $mode, bool $use_include_path, mixed $context): mixed
+function fopen(string $filename, string $mode, bool $use_include_path = false, mixed $context = null): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function fopen(string $filename, string $mode, bool $use_include_path, mixed $co
## Cross-references
- [User reference for `fopen()`](../../../php/builtins/io/fopen.md)
-
diff --git a/docs/internals/builtins/io/fpassthru.md b/docs/internals/builtins/io/fpassthru.md
index 366176778b..fc5dd43810 100644
--- a/docs/internals/builtins/io/fpassthru.md
+++ b/docs/internals/builtins/io/fpassthru.md
@@ -2,15 +2,15 @@
title: "fpassthru() — internals"
description: "Compiler internals for fpassthru(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 149
+ order: 166
---
## `fpassthru()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2806](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2806) (`lower_fpassthru`)
+- **Signature**: [`src/builtins/io/fpassthru.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fpassthru.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3027](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3027) (`lower_fpassthru`)
- **Function symbol**: `lower_fpassthru()`
@@ -37,4 +37,3 @@ function fpassthru(resource $stream): int
## Cross-references
- [User reference for `fpassthru()`](../../../php/builtins/io/fpassthru.md)
-
diff --git a/docs/internals/builtins/io/fprintf.md b/docs/internals/builtins/io/fprintf.md
index 9a6dd0c3b4..25c68e9a11 100644
--- a/docs/internals/builtins/io/fprintf.md
+++ b/docs/internals/builtins/io/fprintf.md
@@ -2,15 +2,15 @@
title: "fprintf() — internals"
description: "Compiler internals for fprintf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 150
+ order: 167
---
## `fprintf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2641](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2641) (`lower_fprintf`)
+- **Signature**: [`src/builtins/io/fprintf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fprintf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2862](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2862) (`lower_fprintf`)
- **Function symbol**: `lower_fprintf()`
@@ -37,4 +37,3 @@ function fprintf(resource $stream, string $format, ...$values): int
## Cross-references
- [User reference for `fprintf()`](../../../php/builtins/io/fprintf.md)
-
diff --git a/docs/internals/builtins/io/fputcsv.md b/docs/internals/builtins/io/fputcsv.md
index 88cfb17351..88071b7e26 100644
--- a/docs/internals/builtins/io/fputcsv.md
+++ b/docs/internals/builtins/io/fputcsv.md
@@ -2,15 +2,15 @@
title: "fputcsv() — internals"
description: "Compiler internals for fputcsv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 151
+ order: 168
---
## `fputcsv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2784](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2784) (`lower_fputcsv`)
+- **Signature**: [`src/builtins/io/fputcsv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fputcsv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3005](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3005) (`lower_fputcsv`)
- **Function symbol**: `lower_fputcsv()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function fputcsv(resource $stream, array $fields, string $separator = ',', string $enclosure = '"', string $escape = '\\', string $eol = '\n'): int
+function fputcsv(resource $stream, array $fields, string $separator = ',', string $enclosure = '"'): int
```
## What the type checker enforces
-- **Arity**: takes 2–6 arguments (4 optional).
+- **Arity**: takes 2–4 arguments (2 optional).
## Cross-references
- [User reference for `fputcsv()`](../../../php/builtins/io/fputcsv.md)
-
diff --git a/docs/internals/builtins/io/fread.md b/docs/internals/builtins/io/fread.md
index 5469f8cd75..3718fd98c9 100644
--- a/docs/internals/builtins/io/fread.md
+++ b/docs/internals/builtins/io/fread.md
@@ -2,15 +2,15 @@
title: "fread() — internals"
description: "Compiler internals for fread(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 152
+ order: 169
---
## `fread()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2595](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2595) (`lower_fread`)
+- **Signature**: [`src/builtins/io/fread.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fread.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2816](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2816) (`lower_fread`)
- **Function symbol**: `lower_fread()`
@@ -36,4 +36,3 @@ function fread(resource $stream, int $length): string
## Cross-references
- [User reference for `fread()`](../../../php/builtins/io/fread.md)
-
diff --git a/docs/internals/builtins/io/fscanf.md b/docs/internals/builtins/io/fscanf.md
index a011eac1cc..ac174d4b98 100644
--- a/docs/internals/builtins/io/fscanf.md
+++ b/docs/internals/builtins/io/fscanf.md
@@ -2,15 +2,15 @@
title: "fscanf() — internals"
description: "Compiler internals for fscanf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 153
+ order: 170
---
## `fscanf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2717](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2717) (`lower_fscanf`)
+- **Signature**: [`src/builtins/io/fscanf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fscanf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2938](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2938) (`lower_fscanf`)
- **Function symbol**: `lower_fscanf()`
@@ -38,4 +38,3 @@ function fscanf(resource $stream, string $format, ...$vars): array
## Cross-references
- [User reference for `fscanf()`](../../../php/builtins/io/fscanf.md)
-
diff --git a/docs/internals/builtins/io/fseek.md b/docs/internals/builtins/io/fseek.md
index 934711515c..ce92f7c77f 100644
--- a/docs/internals/builtins/io/fseek.md
+++ b/docs/internals/builtins/io/fseek.md
@@ -2,15 +2,15 @@
title: "fseek() — internals"
description: "Compiler internals for fseek(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 154
+ order: 171
---
## `fseek()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2951](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2951) (`lower_fseek`)
+- **Signature**: [`src/builtins/io/fseek.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fseek.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3172](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3172) (`lower_fseek`)
- **Function symbol**: `lower_fseek()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function fseek(resource $stream, int $offset, int $whence): int
+function fseek(resource $stream, int $offset, int $whence = 0): int
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function fseek(resource $stream, int $offset, int $whence): int
## Cross-references
- [User reference for `fseek()`](../../../php/builtins/io/fseek.md)
-
diff --git a/docs/internals/builtins/io/fstat.md b/docs/internals/builtins/io/fstat.md
index 8d2660c612..b92d1d8b31 100644
--- a/docs/internals/builtins/io/fstat.md
+++ b/docs/internals/builtins/io/fstat.md
@@ -2,15 +2,15 @@
title: "fstat() — internals"
description: "Compiler internals for fstat(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 155
+ order: 172
---
## `fstat()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:4896](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L4896) (`lower_fstat`)
+- **Signature**: [`src/builtins/io/fstat.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fstat.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:5539](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L5539) (`lower_fstat`)
- **Function symbol**: `lower_fstat()`
@@ -36,4 +36,3 @@ function fstat(resource $stream): mixed
## Cross-references
- [User reference for `fstat()`](../../../php/builtins/io/fstat.md)
-
diff --git a/docs/internals/builtins/io/fsync.md b/docs/internals/builtins/io/fsync.md
index b51b8e59eb..5338aab7cd 100644
--- a/docs/internals/builtins/io/fsync.md
+++ b/docs/internals/builtins/io/fsync.md
@@ -2,15 +2,15 @@
title: "fsync() — internals"
description: "Compiler internals for fsync(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 156
+ order: 173
---
## `fsync()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3040](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3040) (`lower_fsync`)
+- **Signature**: [`src/builtins/io/fsync.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fsync.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3261](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3261) (`lower_fsync`)
- **Function symbol**: `lower_fsync()`
@@ -37,4 +37,3 @@ function fsync(resource $stream): bool
## Cross-references
- [User reference for `fsync()`](../../../php/builtins/io/fsync.md)
-
diff --git a/docs/internals/builtins/io/ftell.md b/docs/internals/builtins/io/ftell.md
index 7905e87db3..fce93e4672 100644
--- a/docs/internals/builtins/io/ftell.md
+++ b/docs/internals/builtins/io/ftell.md
@@ -2,15 +2,15 @@
title: "ftell() — internals"
description: "Compiler internals for ftell(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 157
+ order: 174
---
## `ftell()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2912](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2912) (`lower_ftell`)
+- **Signature**: [`src/builtins/io/ftell.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/ftell.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3133](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3133) (`lower_ftell`)
- **Function symbol**: `lower_ftell()`
@@ -36,4 +36,3 @@ function ftell(resource $stream): int
## Cross-references
- [User reference for `ftell()`](../../../php/builtins/io/ftell.md)
-
diff --git a/docs/internals/builtins/io/ftruncate.md b/docs/internals/builtins/io/ftruncate.md
index 223ee36ee7..8a71ce2a37 100644
--- a/docs/internals/builtins/io/ftruncate.md
+++ b/docs/internals/builtins/io/ftruncate.md
@@ -2,15 +2,15 @@
title: "ftruncate() — internals"
description: "Compiler internals for ftruncate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 158
+ order: 175
---
## `ftruncate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2989](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2989) (`lower_ftruncate`)
+- **Signature**: [`src/builtins/io/ftruncate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/ftruncate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3210](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3210) (`lower_ftruncate`)
- **Function symbol**: `lower_ftruncate()`
@@ -35,4 +35,3 @@ function ftruncate(resource $stream, int $size): bool
## Cross-references
- [User reference for `ftruncate()`](../../../php/builtins/io/ftruncate.md)
-
diff --git a/docs/internals/builtins/io/fwrite.md b/docs/internals/builtins/io/fwrite.md
index 1e221c0391..85551c288d 100644
--- a/docs/internals/builtins/io/fwrite.md
+++ b/docs/internals/builtins/io/fwrite.md
@@ -2,15 +2,15 @@
title: "fwrite() — internals"
description: "Compiler internals for fwrite(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 159
+ order: 176
---
## `fwrite()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2617](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2617) (`lower_fwrite`)
+- **Signature**: [`src/builtins/io/fwrite.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fwrite.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2838](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2838) (`lower_fwrite`)
- **Function symbol**: `lower_fwrite()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function fwrite(resource $stream, string $data, int $length): int
+function fwrite(resource $stream, string $data): int
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `fwrite()`](../../../php/builtins/io/fwrite.md)
-
diff --git a/docs/internals/builtins/io/gethostbyaddr.md b/docs/internals/builtins/io/gethostbyaddr.md
index ebea3fe9b4..49ad6a17cc 100644
--- a/docs/internals/builtins/io/gethostbyaddr.md
+++ b/docs/internals/builtins/io/gethostbyaddr.md
@@ -2,15 +2,15 @@
title: "gethostbyaddr() — internals"
description: "Compiler internals for gethostbyaddr(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 160
+ order: 177
---
## `gethostbyaddr()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3210](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3210) (`lower_gethostbyaddr`)
+- **Signature**: [`src/builtins/io/gethostbyaddr.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/gethostbyaddr.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3431](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3431) (`lower_gethostbyaddr`)
- **Function symbol**: `lower_gethostbyaddr()`
@@ -37,4 +37,3 @@ function gethostbyaddr(string $ip): mixed
## Cross-references
- [User reference for `gethostbyaddr()`](../../../php/builtins/io/gethostbyaddr.md)
-
diff --git a/docs/internals/builtins/io/gethostbyname.md b/docs/internals/builtins/io/gethostbyname.md
index 63c159f132..74394b4e6b 100644
--- a/docs/internals/builtins/io/gethostbyname.md
+++ b/docs/internals/builtins/io/gethostbyname.md
@@ -2,15 +2,15 @@
title: "gethostbyname() — internals"
description: "Compiler internals for gethostbyname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 161
+ order: 178
---
## `gethostbyname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3198](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3198) (`lower_gethostbyname`)
+- **Signature**: [`src/builtins/io/gethostbyname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/gethostbyname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3419](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3419) (`lower_gethostbyname`)
- **Function symbol**: `lower_gethostbyname()`
@@ -37,4 +37,3 @@ function gethostbyname(string $hostname): string
## Cross-references
- [User reference for `gethostbyname()`](../../../php/builtins/io/gethostbyname.md)
-
diff --git a/docs/internals/builtins/io/gethostname.md b/docs/internals/builtins/io/gethostname.md
index f28d5c4846..f5dbcabf0e 100644
--- a/docs/internals/builtins/io/gethostname.md
+++ b/docs/internals/builtins/io/gethostname.md
@@ -2,15 +2,15 @@
title: "gethostname() — internals"
description: "Compiler internals for gethostname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 162
+ order: 179
---
## `gethostname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3188](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3188) (`lower_gethostname`)
+- **Signature**: [`src/builtins/io/gethostname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/gethostname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3409](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3409) (`lower_gethostname`)
- **Function symbol**: `lower_gethostname()`
@@ -38,4 +38,3 @@ function gethostname(): string
## Cross-references
- [User reference for `gethostname()`](../../../php/builtins/io/gethostname.md)
-
diff --git a/docs/internals/builtins/io/getprotobyname.md b/docs/internals/builtins/io/getprotobyname.md
index 0d86583f36..85c99a16db 100644
--- a/docs/internals/builtins/io/getprotobyname.md
+++ b/docs/internals/builtins/io/getprotobyname.md
@@ -2,15 +2,15 @@
title: "getprotobyname() — internals"
description: "Compiler internals for getprotobyname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 163
+ order: 180
---
## `getprotobyname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3223](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3223) (`lower_getprotobyname`)
+- **Signature**: [`src/builtins/io/getprotobyname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/getprotobyname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3444](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3444) (`lower_getprotobyname`)
- **Function symbol**: `lower_getprotobyname()`
@@ -36,4 +36,3 @@ function getprotobyname(string $protocol): mixed
## Cross-references
- [User reference for `getprotobyname()`](../../../php/builtins/io/getprotobyname.md)
-
diff --git a/docs/internals/builtins/io/getprotobynumber.md b/docs/internals/builtins/io/getprotobynumber.md
index 9372def8e6..f2603bff3f 100644
--- a/docs/internals/builtins/io/getprotobynumber.md
+++ b/docs/internals/builtins/io/getprotobynumber.md
@@ -2,15 +2,15 @@
title: "getprotobynumber() — internals"
description: "Compiler internals for getprotobynumber(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 164
+ order: 181
---
## `getprotobynumber()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3246](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3246) (`lower_getprotobynumber`)
+- **Signature**: [`src/builtins/io/getprotobynumber.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/getprotobynumber.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3467](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3467) (`lower_getprotobynumber`)
- **Function symbol**: `lower_getprotobynumber()`
@@ -36,4 +36,3 @@ function getprotobynumber(int $protocol): mixed
## Cross-references
- [User reference for `getprotobynumber()`](../../../php/builtins/io/getprotobynumber.md)
-
diff --git a/docs/internals/builtins/io/getservbyname.md b/docs/internals/builtins/io/getservbyname.md
index dfac1b3625..cf829bb720 100644
--- a/docs/internals/builtins/io/getservbyname.md
+++ b/docs/internals/builtins/io/getservbyname.md
@@ -2,15 +2,15 @@
title: "getservbyname() — internals"
description: "Compiler internals for getservbyname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 165
+ order: 182
---
## `getservbyname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3265](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3265) (`lower_getservbyname`)
+- **Signature**: [`src/builtins/io/getservbyname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/getservbyname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3486](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3486) (`lower_getservbyname`)
- **Function symbol**: `lower_getservbyname()`
@@ -36,4 +36,3 @@ function getservbyname(string $service, string $protocol): mixed
## Cross-references
- [User reference for `getservbyname()`](../../../php/builtins/io/getservbyname.md)
-
diff --git a/docs/internals/builtins/io/getservbyport.md b/docs/internals/builtins/io/getservbyport.md
index 58046bbac1..1e37fd6dda 100644
--- a/docs/internals/builtins/io/getservbyport.md
+++ b/docs/internals/builtins/io/getservbyport.md
@@ -2,15 +2,15 @@
title: "getservbyport() — internals"
description: "Compiler internals for getservbyport(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 166
+ order: 183
---
## `getservbyport()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3296](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3296) (`lower_getservbyport`)
+- **Signature**: [`src/builtins/io/getservbyport.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/getservbyport.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3517](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3517) (`lower_getservbyport`)
- **Function symbol**: `lower_getservbyport()`
@@ -36,4 +36,3 @@ function getservbyport(int $port, string $protocol): mixed
## Cross-references
- [User reference for `getservbyport()`](../../../php/builtins/io/getservbyport.md)
-
diff --git a/docs/internals/builtins/io/hash_file.md b/docs/internals/builtins/io/hash_file.md
index cbc99e8293..7c594feab1 100644
--- a/docs/internals/builtins/io/hash_file.md
+++ b/docs/internals/builtins/io/hash_file.md
@@ -2,15 +2,15 @@
title: "hash_file() — internals"
description: "Compiler internals for hash_file(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 167
+ order: 184
---
## `hash_file()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:180](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L180) (`lower_hash_file`)
+- **Signature**: [`src/builtins/io/hash_file.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/hash_file.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:287](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L287) (`lower_hash_file`)
- **Function symbol**: `lower_hash_file()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): mixed
+function hash_file(string $algo, string $filename, bool $binary = false): mixed
```
## What the type checker enforces
-- **Arity**: takes 2–4 arguments (2 optional).
+- **Arity**: takes 2–3 arguments (1 optional).
## Cross-references
- [User reference for `hash_file()`](../../../php/builtins/io/hash_file.md)
-
diff --git a/docs/internals/builtins/io/opendir.md b/docs/internals/builtins/io/opendir.md
index 0b9983dc92..26d9d4b37e 100644
--- a/docs/internals/builtins/io/opendir.md
+++ b/docs/internals/builtins/io/opendir.md
@@ -2,15 +2,15 @@
title: "opendir() — internals"
description: "Compiler internals for opendir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 168
+ order: 185
---
## `opendir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3326](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3326) (`lower_opendir`)
+- **Signature**: [`src/builtins/io/opendir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/opendir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3547](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3547) (`lower_opendir`)
- **Function symbol**: `lower_opendir()`
@@ -38,4 +38,3 @@ function opendir(string $directory): mixed
## Cross-references
- [User reference for `opendir()`](../../../php/builtins/io/opendir.md)
-
diff --git a/docs/internals/builtins/io/readdir.md b/docs/internals/builtins/io/readdir.md
index bf0f3a143d..b6c1f0da7e 100644
--- a/docs/internals/builtins/io/readdir.md
+++ b/docs/internals/builtins/io/readdir.md
@@ -2,15 +2,15 @@
title: "readdir() — internals"
description: "Compiler internals for readdir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 169
+ order: 186
---
## `readdir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3336](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3336) (`lower_readdir`)
+- **Signature**: [`src/builtins/io/readdir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/readdir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3557](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3557) (`lower_readdir`)
- **Function symbol**: `lower_readdir()`
@@ -39,4 +39,3 @@ function readdir(resource $dir_handle): mixed
## Cross-references
- [User reference for `readdir()`](../../../php/builtins/io/readdir.md)
-
diff --git a/docs/internals/builtins/io/rewind.md b/docs/internals/builtins/io/rewind.md
index ae06516ee4..6f63dbcf51 100644
--- a/docs/internals/builtins/io/rewind.md
+++ b/docs/internals/builtins/io/rewind.md
@@ -2,15 +2,15 @@
title: "rewind() — internals"
description: "Compiler internals for rewind(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 170
+ order: 187
---
## `rewind()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2975](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2975) (`lower_rewind`)
+- **Signature**: [`src/builtins/io/rewind.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/rewind.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3196](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3196) (`lower_rewind`)
- **Function symbol**: `lower_rewind()`
@@ -35,4 +35,3 @@ function rewind(resource $stream): bool
## Cross-references
- [User reference for `rewind()`](../../../php/builtins/io/rewind.md)
-
diff --git a/docs/internals/builtins/io/rewinddir.md b/docs/internals/builtins/io/rewinddir.md
index 4d49681959..185059fde0 100644
--- a/docs/internals/builtins/io/rewinddir.md
+++ b/docs/internals/builtins/io/rewinddir.md
@@ -2,15 +2,15 @@
title: "rewinddir() — internals"
description: "Compiler internals for rewinddir(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 171
+ order: 188
---
## `rewinddir()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3365](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3365) (`lower_rewinddir`)
+- **Signature**: [`src/builtins/io/rewinddir.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/rewinddir.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3588](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3588) (`lower_rewinddir`)
- **Function symbol**: `lower_rewinddir()`
@@ -37,4 +37,3 @@ function rewinddir(resource $dir_handle): void
## Cross-references
- [User reference for `rewinddir()`](../../../php/builtins/io/rewinddir.md)
-
diff --git a/docs/internals/builtins/io/stream_bucket_make_writeable.md b/docs/internals/builtins/io/stream_bucket_make_writeable.md
index 72e71e5842..6bb8d17674 100644
--- a/docs/internals/builtins/io/stream_bucket_make_writeable.md
+++ b/docs/internals/builtins/io/stream_bucket_make_writeable.md
@@ -2,15 +2,15 @@
title: "stream_bucket_make_writeable() — internals"
description: "Compiler internals for stream_bucket_make_writeable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 172
+ order: 189
---
## `stream_bucket_make_writeable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1768](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1768) (`lower_stream_bucket_make_writeable`)
+- **Signature**: [`src/builtins/io/stream_bucket_make_writeable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_bucket_make_writeable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1987](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1987) (`lower_stream_bucket_make_writeable`)
- **Function symbol**: `lower_stream_bucket_make_writeable()`
@@ -36,4 +36,3 @@ function stream_bucket_make_writeable(mixed $brigade): mixed
## Cross-references
- [User reference for `stream_bucket_make_writeable()`](../../../php/builtins/io/stream_bucket_make_writeable.md)
-
diff --git a/docs/internals/builtins/io/stream_bucket_new.md b/docs/internals/builtins/io/stream_bucket_new.md
index 773e434245..568382c658 100644
--- a/docs/internals/builtins/io/stream_bucket_new.md
+++ b/docs/internals/builtins/io/stream_bucket_new.md
@@ -2,15 +2,15 @@
title: "stream_bucket_new() — internals"
description: "Compiler internals for stream_bucket_new(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 173
+ order: 190
---
## `stream_bucket_new()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1751](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1751) (`lower_stream_bucket_new`)
+- **Signature**: [`src/builtins/io/stream_bucket_new.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_bucket_new.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1970](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1970) (`lower_stream_bucket_new`)
- **Function symbol**: `lower_stream_bucket_new()`
@@ -35,4 +35,3 @@ function stream_bucket_new(resource $stream, string $buffer): mixed
## Cross-references
- [User reference for `stream_bucket_new()`](../../../php/builtins/io/stream_bucket_new.md)
-
diff --git a/docs/internals/builtins/io/stream_context_create.md b/docs/internals/builtins/io/stream_context_create.md
index c105395341..294aed3021 100644
--- a/docs/internals/builtins/io/stream_context_create.md
+++ b/docs/internals/builtins/io/stream_context_create.md
@@ -2,15 +2,15 @@
title: "stream_context_create() — internals"
description: "Compiler internals for stream_context_create(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 174
+ order: 191
---
## `stream_context_create()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:951](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L951) (`lower_stream_context_create`)
+- **Signature**: [`src/builtins/io/stream_context_create.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_create.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1064](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1064) (`lower_stream_context_create`)
- **Function symbol**: `lower_stream_context_create()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_context_create(array $options, array $params): mixed
+function stream_context_create(array $options = null, array $params = null): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_context_create(array $options, array $params): mixed
## Cross-references
- [User reference for `stream_context_create()`](../../../php/builtins/io/stream_context_create.md)
-
diff --git a/docs/internals/builtins/io/stream_context_get_default.md b/docs/internals/builtins/io/stream_context_get_default.md
index 10f0138839..e8fa0170ef 100644
--- a/docs/internals/builtins/io/stream_context_get_default.md
+++ b/docs/internals/builtins/io/stream_context_get_default.md
@@ -2,15 +2,15 @@
title: "stream_context_get_default() — internals"
description: "Compiler internals for stream_context_get_default(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 175
+ order: 192
---
## `stream_context_get_default()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:965](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L965) (`lower_stream_context_get_default`)
+- **Signature**: [`src/builtins/io/stream_context_get_default.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_get_default.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1078](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1078) (`lower_stream_context_get_default`)
- **Function symbol**: `lower_stream_context_get_default()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_context_get_default(array $options): mixed
+function stream_context_get_default(array $options = null): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_context_get_default(array $options): mixed
## Cross-references
- [User reference for `stream_context_get_default()`](../../../php/builtins/io/stream_context_get_default.md)
-
diff --git a/docs/internals/builtins/io/stream_context_get_options.md b/docs/internals/builtins/io/stream_context_get_options.md
index 1c60e44a29..1bc88ab6fc 100644
--- a/docs/internals/builtins/io/stream_context_get_options.md
+++ b/docs/internals/builtins/io/stream_context_get_options.md
@@ -2,15 +2,15 @@
title: "stream_context_get_options() — internals"
description: "Compiler internals for stream_context_get_options(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 176
+ order: 193
---
## `stream_context_get_options()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1139](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1139) (`lower_stream_context_get_options`)
+- **Signature**: [`src/builtins/io/stream_context_get_options.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_get_options.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1252](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1252) (`lower_stream_context_get_options`)
- **Function symbol**: `lower_stream_context_get_options()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function stream_context_get_options(resource $stream_or_context): array
+function stream_context_get_options(resource $context): array
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function stream_context_get_options(resource $stream_or_context): array
## Cross-references
- [User reference for `stream_context_get_options()`](../../../php/builtins/io/stream_context_get_options.md)
-
diff --git a/docs/internals/builtins/io/stream_context_get_params.md b/docs/internals/builtins/io/stream_context_get_params.md
index d6b1897077..a336ce53ac 100644
--- a/docs/internals/builtins/io/stream_context_get_params.md
+++ b/docs/internals/builtins/io/stream_context_get_params.md
@@ -2,15 +2,15 @@
title: "stream_context_get_params() — internals"
description: "Compiler internals for stream_context_get_params(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 177
+ order: 194
---
## `stream_context_get_params()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1178](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1178) (`lower_stream_context_get_params`)
+- **Signature**: [`src/builtins/io/stream_context_get_params.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_get_params.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1291](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1291) (`lower_stream_context_get_params`)
- **Function symbol**: `lower_stream_context_get_params()`
@@ -35,4 +35,3 @@ function stream_context_get_params(resource $context): array
## Cross-references
- [User reference for `stream_context_get_params()`](../../../php/builtins/io/stream_context_get_params.md)
-
diff --git a/docs/internals/builtins/io/stream_context_set_default.md b/docs/internals/builtins/io/stream_context_set_default.md
index d9e388a080..11bc262f4a 100644
--- a/docs/internals/builtins/io/stream_context_set_default.md
+++ b/docs/internals/builtins/io/stream_context_set_default.md
@@ -2,15 +2,15 @@
title: "stream_context_set_default() — internals"
description: "Compiler internals for stream_context_set_default(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 178
+ order: 195
---
## `stream_context_set_default()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:975](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L975) (`lower_stream_context_set_default`)
+- **Signature**: [`src/builtins/io/stream_context_set_default.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_set_default.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1088](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1088) (`lower_stream_context_set_default`)
- **Function symbol**: `lower_stream_context_set_default()`
@@ -35,4 +35,3 @@ function stream_context_set_default(array $options): mixed
## Cross-references
- [User reference for `stream_context_set_default()`](../../../php/builtins/io/stream_context_set_default.md)
-
diff --git a/docs/internals/builtins/io/stream_context_set_option.md b/docs/internals/builtins/io/stream_context_set_option.md
index 10970cad66..fc81a0beaf 100644
--- a/docs/internals/builtins/io/stream_context_set_option.md
+++ b/docs/internals/builtins/io/stream_context_set_option.md
@@ -2,15 +2,15 @@
title: "stream_context_set_option() — internals"
description: "Compiler internals for stream_context_set_option(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 179
+ order: 196
---
## `stream_context_set_option()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:985](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L985) (`lower_stream_context_set_option`)
+- **Signature**: [`src/builtins/io/stream_context_set_option.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_set_option.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1098](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1098) (`lower_stream_context_set_option`)
- **Function symbol**: `lower_stream_context_set_option()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_context_set_option(resource $context, string $wrapper_or_options, string $option_name, mixed $value): bool
+function stream_context_set_option(resource $context, string $wrapper_or_options, string $option_name = null, mixed $value = null): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_context_set_option(resource $context, string $wrapper_or_options
## Cross-references
- [User reference for `stream_context_set_option()`](../../../php/builtins/io/stream_context_set_option.md)
-
diff --git a/docs/internals/builtins/io/stream_context_set_params.md b/docs/internals/builtins/io/stream_context_set_params.md
index a655098c77..adacd430ba 100644
--- a/docs/internals/builtins/io/stream_context_set_params.md
+++ b/docs/internals/builtins/io/stream_context_set_params.md
@@ -2,15 +2,15 @@
title: "stream_context_set_params() — internals"
description: "Compiler internals for stream_context_set_params(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 180
+ order: 197
---
## `stream_context_set_params()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1005](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1005) (`lower_stream_context_set_params`)
+- **Signature**: [`src/builtins/io/stream_context_set_params.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_context_set_params.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1118](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1118) (`lower_stream_context_set_params`)
- **Function symbol**: `lower_stream_context_set_params()`
@@ -35,4 +35,3 @@ function stream_context_set_params(resource $context, array $params): bool
## Cross-references
- [User reference for `stream_context_set_params()`](../../../php/builtins/io/stream_context_set_params.md)
-
diff --git a/docs/internals/builtins/io/stream_copy_to_stream.md b/docs/internals/builtins/io/stream_copy_to_stream.md
index 64a2c7be53..c33e7ed443 100644
--- a/docs/internals/builtins/io/stream_copy_to_stream.md
+++ b/docs/internals/builtins/io/stream_copy_to_stream.md
@@ -2,15 +2,15 @@
title: "stream_copy_to_stream() — internals"
description: "Compiler internals for stream_copy_to_stream(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 181
+ order: 198
---
## `stream_copy_to_stream()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1247](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1247) (`lower_stream_copy_to_stream`)
+- **Signature**: [`src/builtins/io/stream_copy_to_stream.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_copy_to_stream.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1360](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1360) (`lower_stream_copy_to_stream`)
- **Function symbol**: `lower_stream_copy_to_stream()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_copy_to_stream(resource $from, resource $to, int $length, int $offset): mixed
+function stream_copy_to_stream(resource $from, resource $to, int $length = null, int $offset = -1): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_copy_to_stream(resource $from, resource $to, int $length, int $o
## Cross-references
- [User reference for `stream_copy_to_stream()`](../../../php/builtins/io/stream_copy_to_stream.md)
-
diff --git a/docs/internals/builtins/io/stream_filter_register.md b/docs/internals/builtins/io/stream_filter_register.md
index f4a9b49a0d..4871ac00d3 100644
--- a/docs/internals/builtins/io/stream_filter_register.md
+++ b/docs/internals/builtins/io/stream_filter_register.md
@@ -2,15 +2,15 @@
title: "stream_filter_register() — internals"
description: "Compiler internals for stream_filter_register(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 182
+ order: 199
---
## `stream_filter_register()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1408](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1408) (`lower_stream_filter_register`)
+- **Signature**: [`src/builtins/io/stream_filter_register.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_filter_register.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1521](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1521) (`lower_stream_filter_register`)
- **Function symbol**: `lower_stream_filter_register()`
@@ -36,4 +36,3 @@ function stream_filter_register(string $filter_name, string $class): bool
## Cross-references
- [User reference for `stream_filter_register()`](../../../php/builtins/io/stream_filter_register.md)
-
diff --git a/docs/internals/builtins/io/stream_filter_remove.md b/docs/internals/builtins/io/stream_filter_remove.md
index bd8b9a29b5..4259c6188a 100644
--- a/docs/internals/builtins/io/stream_filter_remove.md
+++ b/docs/internals/builtins/io/stream_filter_remove.md
@@ -2,15 +2,15 @@
title: "stream_filter_remove() — internals"
description: "Compiler internals for stream_filter_remove(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 183
+ order: 200
---
## `stream_filter_remove()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1720](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1720) (`lower_stream_filter_remove`)
+- **Signature**: [`src/builtins/io/stream_filter_remove.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_filter_remove.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1939](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1939) (`lower_stream_filter_remove`)
- **Function symbol**: `lower_stream_filter_remove()`
@@ -36,4 +36,3 @@ function stream_filter_remove(resource $stream_filter): bool
## Cross-references
- [User reference for `stream_filter_remove()`](../../../php/builtins/io/stream_filter_remove.md)
-
diff --git a/docs/internals/builtins/io/stream_get_contents.md b/docs/internals/builtins/io/stream_get_contents.md
index e209c1eb10..2354839a01 100644
--- a/docs/internals/builtins/io/stream_get_contents.md
+++ b/docs/internals/builtins/io/stream_get_contents.md
@@ -2,15 +2,15 @@
title: "stream_get_contents() — internals"
description: "Compiler internals for stream_get_contents(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 184
+ order: 201
---
## `stream_get_contents()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1188](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1188) (`lower_stream_get_contents`)
+- **Signature**: [`src/builtins/io/stream_get_contents.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_contents.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1301](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1301) (`lower_stream_get_contents`)
- **Function symbol**: `lower_stream_get_contents()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_get_contents(resource $stream, int $length, int $offset): mixed
+function stream_get_contents(resource $stream, int $length = null, int $offset = -1): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_get_contents(resource $stream, int $length, int $offset): mixed
## Cross-references
- [User reference for `stream_get_contents()`](../../../php/builtins/io/stream_get_contents.md)
-
diff --git a/docs/internals/builtins/io/stream_get_filters.md b/docs/internals/builtins/io/stream_get_filters.md
index eed69910a5..eac2d3dabc 100644
--- a/docs/internals/builtins/io/stream_get_filters.md
+++ b/docs/internals/builtins/io/stream_get_filters.md
@@ -2,15 +2,15 @@
title: "stream_get_filters() — internals"
description: "Compiler internals for stream_get_filters(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 185
+ order: 202
---
## `stream_get_filters()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1380](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1380) (`lower_stream_get_filters`)
+- **Signature**: [`src/builtins/io/stream_get_filters.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_filters.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1493](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1493) (`lower_stream_get_filters`)
- **Function symbol**: `lower_stream_get_filters()`
@@ -35,4 +35,3 @@ function stream_get_filters(): array
## Cross-references
- [User reference for `stream_get_filters()`](../../../php/builtins/io/stream_get_filters.md)
-
diff --git a/docs/internals/builtins/io/stream_get_line.md b/docs/internals/builtins/io/stream_get_line.md
index 6d73f000e3..52b01cbfb2 100644
--- a/docs/internals/builtins/io/stream_get_line.md
+++ b/docs/internals/builtins/io/stream_get_line.md
@@ -2,15 +2,15 @@
title: "stream_get_line() — internals"
description: "Compiler internals for stream_get_line(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 186
+ order: 203
---
## `stream_get_line()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1280](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1280) (`lower_stream_get_line`)
+- **Signature**: [`src/builtins/io/stream_get_line.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_line.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1393](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1393) (`lower_stream_get_line`)
- **Function symbol**: `lower_stream_get_line()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_get_line(resource $stream, int $length, string $ending): string
+function stream_get_line(resource $stream, int $length, string $ending = ''): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_get_line(resource $stream, int $length, string $ending): string
## Cross-references
- [User reference for `stream_get_line()`](../../../php/builtins/io/stream_get_line.md)
-
diff --git a/docs/internals/builtins/io/stream_get_meta_data.md b/docs/internals/builtins/io/stream_get_meta_data.md
index 198fe9752b..c22e797cce 100644
--- a/docs/internals/builtins/io/stream_get_meta_data.md
+++ b/docs/internals/builtins/io/stream_get_meta_data.md
@@ -2,15 +2,15 @@
title: "stream_get_meta_data() — internals"
description: "Compiler internals for stream_get_meta_data(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 187
+ order: 204
---
## `stream_get_meta_data()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1333](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1333) (`lower_stream_get_meta_data`)
+- **Signature**: [`src/builtins/io/stream_get_meta_data.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_meta_data.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1446](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1446) (`lower_stream_get_meta_data`)
- **Function symbol**: `lower_stream_get_meta_data()`
@@ -36,4 +36,3 @@ function stream_get_meta_data(resource $stream): array
## Cross-references
- [User reference for `stream_get_meta_data()`](../../../php/builtins/io/stream_get_meta_data.md)
-
diff --git a/docs/internals/builtins/io/stream_get_transports.md b/docs/internals/builtins/io/stream_get_transports.md
index 424af49efe..8c08491264 100644
--- a/docs/internals/builtins/io/stream_get_transports.md
+++ b/docs/internals/builtins/io/stream_get_transports.md
@@ -2,15 +2,15 @@
title: "stream_get_transports() — internals"
description: "Compiler internals for stream_get_transports(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 188
+ order: 205
---
## `stream_get_transports()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1364](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1364) (`lower_stream_get_transports`)
+- **Signature**: [`src/builtins/io/stream_get_transports.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_transports.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1477](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1477) (`lower_stream_get_transports`)
- **Function symbol**: `lower_stream_get_transports()`
@@ -35,4 +35,3 @@ function stream_get_transports(): array
## Cross-references
- [User reference for `stream_get_transports()`](../../../php/builtins/io/stream_get_transports.md)
-
diff --git a/docs/internals/builtins/io/stream_get_wrappers.md b/docs/internals/builtins/io/stream_get_wrappers.md
index ddb2137eb4..f83d658932 100644
--- a/docs/internals/builtins/io/stream_get_wrappers.md
+++ b/docs/internals/builtins/io/stream_get_wrappers.md
@@ -2,15 +2,15 @@
title: "stream_get_wrappers() — internals"
description: "Compiler internals for stream_get_wrappers(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 189
+ order: 206
---
## `stream_get_wrappers()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1348](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1348) (`lower_stream_get_wrappers`)
+- **Signature**: [`src/builtins/io/stream_get_wrappers.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_get_wrappers.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1461](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1461) (`lower_stream_get_wrappers`)
- **Function symbol**: `lower_stream_get_wrappers()`
@@ -35,4 +35,3 @@ function stream_get_wrappers(): array
## Cross-references
- [User reference for `stream_get_wrappers()`](../../../php/builtins/io/stream_get_wrappers.md)
-
diff --git a/docs/internals/builtins/io/stream_is_local.md b/docs/internals/builtins/io/stream_is_local.md
index fdc05712a5..2a28e1703b 100644
--- a/docs/internals/builtins/io/stream_is_local.md
+++ b/docs/internals/builtins/io/stream_is_local.md
@@ -2,15 +2,15 @@
title: "stream_is_local() — internals"
description: "Compiler internals for stream_is_local(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 190
+ order: 207
---
## `stream_is_local()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1884](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1884) (`lower_stream_is_local`)
+- **Signature**: [`src/builtins/io/stream_is_local.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_is_local.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2103](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2103) (`lower_stream_is_local`)
- **Function symbol**: `lower_stream_is_local()`
@@ -35,4 +35,3 @@ function stream_is_local(resource $stream): bool
## Cross-references
- [User reference for `stream_is_local()`](../../../php/builtins/io/stream_is_local.md)
-
diff --git a/docs/internals/builtins/io/stream_isatty.md b/docs/internals/builtins/io/stream_isatty.md
index 212b5aa456..a5ee682754 100644
--- a/docs/internals/builtins/io/stream_isatty.md
+++ b/docs/internals/builtins/io/stream_isatty.md
@@ -2,15 +2,15 @@
title: "stream_isatty() — internals"
description: "Compiler internals for stream_isatty(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 191
+ order: 208
---
## `stream_isatty()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1908](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1908) (`lower_stream_isatty`)
+- **Signature**: [`src/builtins/io/stream_isatty.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_isatty.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2127](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2127) (`lower_stream_isatty`)
- **Function symbol**: `lower_stream_isatty()`
@@ -36,4 +36,3 @@ function stream_isatty(resource $stream): bool
## Cross-references
- [User reference for `stream_isatty()`](../../../php/builtins/io/stream_isatty.md)
-
diff --git a/docs/internals/builtins/io/stream_resolve_include_path.md b/docs/internals/builtins/io/stream_resolve_include_path.md
index fbbfae287d..12122e031c 100644
--- a/docs/internals/builtins/io/stream_resolve_include_path.md
+++ b/docs/internals/builtins/io/stream_resolve_include_path.md
@@ -2,15 +2,15 @@
title: "stream_resolve_include_path() — internals"
description: "Compiler internals for stream_resolve_include_path(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 192
+ order: 209
---
## `stream_resolve_include_path()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2142](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2142) (`lower_stream_resolve_include_path`)
+- **Signature**: [`src/builtins/io/stream_resolve_include_path.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_resolve_include_path.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2361](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2361) (`lower_stream_resolve_include_path`)
- **Function symbol**: `lower_stream_resolve_include_path()`
@@ -37,4 +37,3 @@ function stream_resolve_include_path(string $filename): mixed
## Cross-references
- [User reference for `stream_resolve_include_path()`](../../../php/builtins/io/stream_resolve_include_path.md)
-
diff --git a/docs/internals/builtins/io/stream_select.md b/docs/internals/builtins/io/stream_select.md
index af28d818a7..d5c0fc7e99 100644
--- a/docs/internals/builtins/io/stream_select.md
+++ b/docs/internals/builtins/io/stream_select.md
@@ -2,15 +2,15 @@
title: "stream_select() — internals"
description: "Compiler internals for stream_select(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 193
+ order: 210
---
## `stream_select()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2097](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2097) (`lower_stream_select`)
+- **Signature**: [`src/builtins/io/stream_select.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_select.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2316](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2316) (`lower_stream_select`)
- **Function symbol**: `lower_stream_select()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_select(array $read, array $write, array $except, int $seconds, int $microseconds): int
+function stream_select(array $read, array $write, array $except, int $seconds, int $microseconds = 0): int
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function stream_select(array $read, array $write, array $except, int $seconds, i
## Cross-references
- [User reference for `stream_select()`](../../../php/builtins/io/stream_select.md)
-
diff --git a/docs/internals/builtins/io/stream_set_blocking.md b/docs/internals/builtins/io/stream_set_blocking.md
index 098b585e38..c2971c593a 100644
--- a/docs/internals/builtins/io/stream_set_blocking.md
+++ b/docs/internals/builtins/io/stream_set_blocking.md
@@ -2,15 +2,15 @@
title: "stream_set_blocking() — internals"
description: "Compiler internals for stream_set_blocking(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 194
+ order: 211
---
## `stream_set_blocking()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1923](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1923) (`lower_stream_set_blocking`)
+- **Signature**: [`src/builtins/io/stream_set_blocking.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_set_blocking.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2142](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2142) (`lower_stream_set_blocking`)
- **Function symbol**: `lower_stream_set_blocking()`
@@ -37,4 +37,3 @@ function stream_set_blocking(resource $stream, bool $enable): bool
## Cross-references
- [User reference for `stream_set_blocking()`](../../../php/builtins/io/stream_set_blocking.md)
-
diff --git a/docs/internals/builtins/io/stream_set_chunk_size.md b/docs/internals/builtins/io/stream_set_chunk_size.md
index 29f2af81cf..3ebdaabd13 100644
--- a/docs/internals/builtins/io/stream_set_chunk_size.md
+++ b/docs/internals/builtins/io/stream_set_chunk_size.md
@@ -2,15 +2,15 @@
title: "stream_set_chunk_size() — internals"
description: "Compiler internals for stream_set_chunk_size(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 195
+ order: 212
---
## `stream_set_chunk_size()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1975](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1975) (`lower_stream_set_chunk_size`)
+- **Signature**: [`src/builtins/io/stream_set_chunk_size.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_set_chunk_size.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2194](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2194) (`lower_stream_set_chunk_size`)
- **Function symbol**: `lower_stream_set_chunk_size()`
@@ -35,4 +35,3 @@ function stream_set_chunk_size(resource $stream, int $size): int
## Cross-references
- [User reference for `stream_set_chunk_size()`](../../../php/builtins/io/stream_set_chunk_size.md)
-
diff --git a/docs/internals/builtins/io/stream_set_read_buffer.md b/docs/internals/builtins/io/stream_set_read_buffer.md
index 998b5671ad..e955cf8ecc 100644
--- a/docs/internals/builtins/io/stream_set_read_buffer.md
+++ b/docs/internals/builtins/io/stream_set_read_buffer.md
@@ -2,15 +2,15 @@
title: "stream_set_read_buffer() — internals"
description: "Compiler internals for stream_set_read_buffer(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 196
+ order: 213
---
## `stream_set_read_buffer()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2035](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2035) (`lower_stream_set_buffer`)
+- **Signature**: [`src/builtins/io/stream_set_read_buffer.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_set_read_buffer.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2254](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2254) (`lower_stream_set_buffer`)
- **Function symbol**: `lower_stream_set_buffer()`
@@ -35,4 +35,3 @@ function stream_set_read_buffer(resource $stream, int $size): int
## Cross-references
- [User reference for `stream_set_read_buffer()`](../../../php/builtins/io/stream_set_read_buffer.md)
-
diff --git a/docs/internals/builtins/io/stream_set_timeout.md b/docs/internals/builtins/io/stream_set_timeout.md
index 25b96ac7de..945078a811 100644
--- a/docs/internals/builtins/io/stream_set_timeout.md
+++ b/docs/internals/builtins/io/stream_set_timeout.md
@@ -2,15 +2,15 @@
title: "stream_set_timeout() — internals"
description: "Compiler internals for stream_set_timeout(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 197
+ order: 214
---
## `stream_set_timeout()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2048](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2048) (`lower_stream_set_timeout`)
+- **Signature**: [`src/builtins/io/stream_set_timeout.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_set_timeout.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2267](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2267) (`lower_stream_set_timeout`)
- **Function symbol**: `lower_stream_set_timeout()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_set_timeout(resource $stream, int $seconds, int $microseconds): bool
+function stream_set_timeout(resource $stream, int $seconds, int $microseconds = 0): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_set_timeout(resource $stream, int $seconds, int $microseconds):
## Cross-references
- [User reference for `stream_set_timeout()`](../../../php/builtins/io/stream_set_timeout.md)
-
diff --git a/docs/internals/builtins/io/stream_set_write_buffer.md b/docs/internals/builtins/io/stream_set_write_buffer.md
index 8848461a88..5687cee45a 100644
--- a/docs/internals/builtins/io/stream_set_write_buffer.md
+++ b/docs/internals/builtins/io/stream_set_write_buffer.md
@@ -2,15 +2,15 @@
title: "stream_set_write_buffer() — internals"
description: "Compiler internals for stream_set_write_buffer(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 198
+ order: 215
---
## `stream_set_write_buffer()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2035](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2035) (`lower_stream_set_buffer`)
+- **Signature**: [`src/builtins/io/stream_set_write_buffer.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_set_write_buffer.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2254](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2254) (`lower_stream_set_buffer`)
- **Function symbol**: `lower_stream_set_buffer()`
@@ -35,4 +35,3 @@ function stream_set_write_buffer(resource $stream, int $size): int
## Cross-references
- [User reference for `stream_set_write_buffer()`](../../../php/builtins/io/stream_set_write_buffer.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_accept.md b/docs/internals/builtins/io/stream_socket_accept.md
index cffac1881b..15bc1cd888 100644
--- a/docs/internals/builtins/io/stream_socket_accept.md
+++ b/docs/internals/builtins/io/stream_socket_accept.md
@@ -2,15 +2,15 @@
title: "stream_socket_accept() — internals"
description: "Compiler internals for stream_socket_accept(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 199
+ order: 216
---
## `stream_socket_accept()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2217](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2217) (`lower_stream_socket_accept`)
+- **Signature**: [`src/builtins/io/stream_socket_accept.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_accept.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2436](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2436) (`lower_stream_socket_accept`)
- **Function symbol**: `lower_stream_socket_accept()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function stream_socket_accept(resource $socket, float $timeout, string $peer_name): mixed
+function stream_socket_accept(resource $socket, float $timeout = null, string $peer_name = null): mixed
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function stream_socket_accept(resource $socket, float $timeout, string $peer_nam
## Cross-references
- [User reference for `stream_socket_accept()`](../../../php/builtins/io/stream_socket_accept.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_client.md b/docs/internals/builtins/io/stream_socket_client.md
index 71e8ce0fa2..86a0b421a4 100644
--- a/docs/internals/builtins/io/stream_socket_client.md
+++ b/docs/internals/builtins/io/stream_socket_client.md
@@ -2,15 +2,15 @@
title: "stream_socket_client() — internals"
description: "Compiler internals for stream_socket_client(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 200
+ order: 217
---
## `stream_socket_client()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2178](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2178) (`lower_stream_socket_client`)
+- **Signature**: [`src/builtins/io/stream_socket_client.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_client.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2397](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2397) (`lower_stream_socket_client`)
- **Function symbol**: `lower_stream_socket_client()`
@@ -27,15 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function stream_socket_client(string $address, int $error_code, int $error_message, string $timeout, float $flags): mixed
+function stream_socket_client(string $address): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 5 arguments.
-- **By-reference parameters**: `$error_code`, `$error_message`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `stream_socket_client()`](../../../php/builtins/io/stream_socket_client.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_enable_crypto.md b/docs/internals/builtins/io/stream_socket_enable_crypto.md
index b822ae31db..1b2277fb07 100644
--- a/docs/internals/builtins/io/stream_socket_enable_crypto.md
+++ b/docs/internals/builtins/io/stream_socket_enable_crypto.md
@@ -2,15 +2,15 @@
title: "stream_socket_enable_crypto() — internals"
description: "Compiler internals for stream_socket_enable_crypto(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 201
+ order: 218
---
## `stream_socket_enable_crypto()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2328](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2328) (`lower_stream_socket_enable_crypto`)
+- **Signature**: [`src/builtins/io/stream_socket_enable_crypto.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_enable_crypto.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2547](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2547) (`lower_stream_socket_enable_crypto`)
- **Function symbol**: `lower_stream_socket_enable_crypto()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto_method, resource $session_stream): bool
+function stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto_method = null, resource $session_stream = null): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto
## Cross-references
- [User reference for `stream_socket_enable_crypto()`](../../../php/builtins/io/stream_socket_enable_crypto.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_get_name.md b/docs/internals/builtins/io/stream_socket_get_name.md
index 678c984d21..0108165a9d 100644
--- a/docs/internals/builtins/io/stream_socket_get_name.md
+++ b/docs/internals/builtins/io/stream_socket_get_name.md
@@ -2,15 +2,15 @@
title: "stream_socket_get_name() — internals"
description: "Compiler internals for stream_socket_get_name(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 202
+ order: 219
---
## `stream_socket_get_name()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2277](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2277) (`lower_stream_socket_get_name`)
+- **Signature**: [`src/builtins/io/stream_socket_get_name.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_get_name.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2496](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2496) (`lower_stream_socket_get_name`)
- **Function symbol**: `lower_stream_socket_get_name()`
@@ -36,4 +36,3 @@ function stream_socket_get_name(resource $socket, bool $remote): mixed
## Cross-references
- [User reference for `stream_socket_get_name()`](../../../php/builtins/io/stream_socket_get_name.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_pair.md b/docs/internals/builtins/io/stream_socket_pair.md
index 901e21d473..d0cca12362 100644
--- a/docs/internals/builtins/io/stream_socket_pair.md
+++ b/docs/internals/builtins/io/stream_socket_pair.md
@@ -2,15 +2,15 @@
title: "stream_socket_pair() — internals"
description: "Compiler internals for stream_socket_pair(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 203
+ order: 220
---
## `stream_socket_pair()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2246](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2246) (`lower_stream_socket_pair`)
+- **Signature**: [`src/builtins/io/stream_socket_pair.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_pair.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2465](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2465) (`lower_stream_socket_pair`)
- **Function symbol**: `lower_stream_socket_pair()`
@@ -36,4 +36,3 @@ function stream_socket_pair(int $domain, int $type, int $protocol): mixed
## Cross-references
- [User reference for `stream_socket_pair()`](../../../php/builtins/io/stream_socket_pair.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_recvfrom.md b/docs/internals/builtins/io/stream_socket_recvfrom.md
index d58325726a..12ccb5cbcc 100644
--- a/docs/internals/builtins/io/stream_socket_recvfrom.md
+++ b/docs/internals/builtins/io/stream_socket_recvfrom.md
@@ -2,15 +2,15 @@
title: "stream_socket_recvfrom() — internals"
description: "Compiler internals for stream_socket_recvfrom(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 204
+ order: 221
---
## `stream_socket_recvfrom()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2380](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2380) (`lower_stream_socket_recvfrom`)
+- **Signature**: [`src/builtins/io/stream_socket_recvfrom.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_recvfrom.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2599](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2599) (`lower_stream_socket_recvfrom`)
- **Function symbol**: `lower_stream_socket_recvfrom()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_socket_recvfrom(resource $socket, int $length, int $flags, string $address): mixed
+function stream_socket_recvfrom(resource $socket, int $length, int $flags = 0, string $address = ''): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function stream_socket_recvfrom(resource $socket, int $length, int $flags, strin
## Cross-references
- [User reference for `stream_socket_recvfrom()`](../../../php/builtins/io/stream_socket_recvfrom.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_sendto.md b/docs/internals/builtins/io/stream_socket_sendto.md
index 5f29216821..0350af291b 100644
--- a/docs/internals/builtins/io/stream_socket_sendto.md
+++ b/docs/internals/builtins/io/stream_socket_sendto.md
@@ -2,15 +2,15 @@
title: "stream_socket_sendto() — internals"
description: "Compiler internals for stream_socket_sendto(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 205
+ order: 222
---
## `stream_socket_sendto()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2422](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2422) (`lower_stream_socket_sendto`)
+- **Signature**: [`src/builtins/io/stream_socket_sendto.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_sendto.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2641](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2641) (`lower_stream_socket_sendto`)
- **Function symbol**: `lower_stream_socket_sendto()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_socket_sendto(resource $socket, string $data, int $flags, string $address): mixed
+function stream_socket_sendto(resource $socket, string $data, int $flags = 0, string $address = ''): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function stream_socket_sendto(resource $socket, string $data, int $flags, string
## Cross-references
- [User reference for `stream_socket_sendto()`](../../../php/builtins/io/stream_socket_sendto.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_server.md b/docs/internals/builtins/io/stream_socket_server.md
index 51e3a05bb3..86922f9e4e 100644
--- a/docs/internals/builtins/io/stream_socket_server.md
+++ b/docs/internals/builtins/io/stream_socket_server.md
@@ -2,15 +2,15 @@
title: "stream_socket_server() — internals"
description: "Compiler internals for stream_socket_server(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 206
+ order: 223
---
## `stream_socket_server()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2155](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2155) (`lower_stream_socket_server`)
+- **Signature**: [`src/builtins/io/stream_socket_server.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_server.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2374](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2374) (`lower_stream_socket_server`)
- **Function symbol**: `lower_stream_socket_server()`
@@ -26,15 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function stream_socket_server(string $address, int $error_code, int $error_message): mixed
+function stream_socket_server(string $address): mixed
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
-- **By-reference parameters**: `$error_code`, `$error_message`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `stream_socket_server()`](../../../php/builtins/io/stream_socket_server.md)
-
diff --git a/docs/internals/builtins/io/stream_socket_shutdown.md b/docs/internals/builtins/io/stream_socket_shutdown.md
index 7920cb0291..e5eb60e571 100644
--- a/docs/internals/builtins/io/stream_socket_shutdown.md
+++ b/docs/internals/builtins/io/stream_socket_shutdown.md
@@ -2,15 +2,15 @@
title: "stream_socket_shutdown() — internals"
description: "Compiler internals for stream_socket_shutdown(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 207
+ order: 224
---
## `stream_socket_shutdown()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2303](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2303) (`lower_stream_socket_shutdown`)
+- **Signature**: [`src/builtins/io/stream_socket_shutdown.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_socket_shutdown.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2522](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2522) (`lower_stream_socket_shutdown`)
- **Function symbol**: `lower_stream_socket_shutdown()`
@@ -36,4 +36,3 @@ function stream_socket_shutdown(resource $stream, int $mode): bool
## Cross-references
- [User reference for `stream_socket_shutdown()`](../../../php/builtins/io/stream_socket_shutdown.md)
-
diff --git a/docs/internals/builtins/io/stream_supports_lock.md b/docs/internals/builtins/io/stream_supports_lock.md
index a735df223d..82a20d07f7 100644
--- a/docs/internals/builtins/io/stream_supports_lock.md
+++ b/docs/internals/builtins/io/stream_supports_lock.md
@@ -2,15 +2,15 @@
title: "stream_supports_lock() — internals"
description: "Compiler internals for stream_supports_lock(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 208
+ order: 225
---
## `stream_supports_lock()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:1896](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L1896) (`lower_stream_supports_lock`)
+- **Signature**: [`src/builtins/io/stream_supports_lock.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_supports_lock.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2115](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2115) (`lower_stream_supports_lock`)
- **Function symbol**: `lower_stream_supports_lock()`
@@ -36,4 +36,3 @@ function stream_supports_lock(resource $stream): bool
## Cross-references
- [User reference for `stream_supports_lock()`](../../../php/builtins/io/stream_supports_lock.md)
-
diff --git a/docs/internals/builtins/io/stream_wrapper_register.md b/docs/internals/builtins/io/stream_wrapper_register.md
index 65fb148907..4ed40920b3 100644
--- a/docs/internals/builtins/io/stream_wrapper_register.md
+++ b/docs/internals/builtins/io/stream_wrapper_register.md
@@ -2,15 +2,15 @@
title: "stream_wrapper_register() — internals"
description: "Compiler internals for stream_wrapper_register(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 209
+ order: 226
---
## `stream_wrapper_register()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:887](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L887) (`lower_stream_wrapper_register`)
+- **Signature**: [`src/builtins/io/stream_wrapper_register.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_wrapper_register.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1000](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1000) (`lower_stream_wrapper_register`)
- **Function symbol**: `lower_stream_wrapper_register()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function stream_wrapper_register(string $protocol, string $class, int $flags): bool
+function stream_wrapper_register(string $protocol, string $class, int $flags = 0): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function stream_wrapper_register(string $protocol, string $class, int $flags): b
## Cross-references
- [User reference for `stream_wrapper_register()`](../../../php/builtins/io/stream_wrapper_register.md)
-
diff --git a/docs/internals/builtins/io/stream_wrapper_restore.md b/docs/internals/builtins/io/stream_wrapper_restore.md
index 9bb85aa7d5..8e82185c1a 100644
--- a/docs/internals/builtins/io/stream_wrapper_restore.md
+++ b/docs/internals/builtins/io/stream_wrapper_restore.md
@@ -2,15 +2,15 @@
title: "stream_wrapper_restore() — internals"
description: "Compiler internals for stream_wrapper_restore(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 210
+ order: 227
---
## `stream_wrapper_restore()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:939](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L939) (`lower_stream_wrapper_restore`)
+- **Signature**: [`src/builtins/io/stream_wrapper_restore.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_wrapper_restore.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1052](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1052) (`lower_stream_wrapper_restore`)
- **Function symbol**: `lower_stream_wrapper_restore()`
@@ -35,4 +35,3 @@ function stream_wrapper_restore(string $protocol): bool
## Cross-references
- [User reference for `stream_wrapper_restore()`](../../../php/builtins/io/stream_wrapper_restore.md)
-
diff --git a/docs/internals/builtins/io/stream_wrapper_unregister.md b/docs/internals/builtins/io/stream_wrapper_unregister.md
index 3cbc4cfddc..aaa6b3b57b 100644
--- a/docs/internals/builtins/io/stream_wrapper_unregister.md
+++ b/docs/internals/builtins/io/stream_wrapper_unregister.md
@@ -2,15 +2,15 @@
title: "stream_wrapper_unregister() — internals"
description: "Compiler internals for stream_wrapper_unregister(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 211
+ order: 228
---
## `stream_wrapper_unregister()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:917](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L917) (`lower_stream_wrapper_unregister`)
+- **Signature**: [`src/builtins/io/stream_wrapper_unregister.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_wrapper_unregister.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1030](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1030) (`lower_stream_wrapper_unregister`)
- **Function symbol**: `lower_stream_wrapper_unregister()`
@@ -36,4 +36,3 @@ function stream_wrapper_unregister(string $protocol): bool
## Cross-references
- [User reference for `stream_wrapper_unregister()`](../../../php/builtins/io/stream_wrapper_unregister.md)
-
diff --git a/docs/internals/builtins/io/vfprintf.md b/docs/internals/builtins/io/vfprintf.md
index 9543234e03..3a7fdd8e50 100644
--- a/docs/internals/builtins/io/vfprintf.md
+++ b/docs/internals/builtins/io/vfprintf.md
@@ -2,15 +2,15 @@
title: "vfprintf() — internals"
description: "Compiler internals for vfprintf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 212
+ order: 229
---
## `vfprintf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:2677](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L2677) (`lower_vfprintf`)
+- **Signature**: [`src/builtins/io/vfprintf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/vfprintf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2898](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2898) (`lower_vfprintf`)
- **Function symbol**: `lower_vfprintf()`
@@ -37,4 +37,3 @@ function vfprintf(resource $stream, string $format, array $values): int
## Cross-references
- [User reference for `vfprintf()`](../../../php/builtins/io/vfprintf.md)
-
diff --git a/docs/internals/builtins/json/json_decode.md b/docs/internals/builtins/json/json_decode.md
index f27c39bd5f..9c7bdef87c 100644
--- a/docs/internals/builtins/json/json_decode.md
+++ b/docs/internals/builtins/json/json_decode.md
@@ -2,15 +2,15 @@
title: "json_decode() — internals"
description: "Compiler internals for json_decode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 213
+ order: 230
---
## `json_decode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/json.rs`:30](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/json.rs#L30) (`lower_json_decode`)
+- **Signature**: [`src/builtins/system/json_decode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/json_decode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/json.rs`:30](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/json.rs#L30) (`lower_json_decode`)
- **Function symbol**: `lower_json_decode()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function json_decode(string $json, bool $associative, int $depth, int $flags): mixed
+function json_decode(string $json, bool $associative = null, int $depth = 512, int $flags = 0): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function json_decode(string $json, bool $associative, int $depth, int $flags): m
## Cross-references
- [User reference for `json_decode()`](../../../php/builtins/json/json_decode.md)
-
diff --git a/docs/internals/builtins/json/json_encode.md b/docs/internals/builtins/json/json_encode.md
index 363ee53b45..0b4a9d768a 100644
--- a/docs/internals/builtins/json/json_encode.md
+++ b/docs/internals/builtins/json/json_encode.md
@@ -2,15 +2,15 @@
title: "json_encode() — internals"
description: "Compiler internals for json_encode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 214
+ order: 231
---
## `json_encode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/json.rs`:52](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/json.rs#L52) (`lower_json_encode`)
+- **Signature**: [`src/builtins/system/json_encode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/json_encode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/json.rs`:52](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/json.rs#L52) (`lower_json_encode`)
- **Function symbol**: `lower_json_encode()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function json_encode(mixed $value, int $flags, int $depth): string
+function json_encode(mixed $value, int $flags = 0, int $depth = 512): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function json_encode(mixed $value, int $flags, int $depth): string
## Cross-references
- [User reference for `json_encode()`](../../../php/builtins/json/json_encode.md)
-
diff --git a/docs/internals/builtins/json/json_last_error.md b/docs/internals/builtins/json/json_last_error.md
index 51c232863f..0e1d486891 100644
--- a/docs/internals/builtins/json/json_last_error.md
+++ b/docs/internals/builtins/json/json_last_error.md
@@ -2,15 +2,15 @@
title: "json_last_error() — internals"
description: "Compiler internals for json_last_error(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 215
+ order: 232
---
## `json_last_error()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/json.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/json.rs#L70) (`lower_json_last_error`)
+- **Signature**: [`src/builtins/system/json_last_error.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/json_last_error.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/json.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/json.rs#L70) (`lower_json_last_error`)
- **Function symbol**: `lower_json_last_error()`
@@ -36,4 +36,3 @@ function json_last_error(): int
## Cross-references
- [User reference for `json_last_error()`](../../../php/builtins/json/json_last_error.md)
-
diff --git a/docs/internals/builtins/json/json_last_error_msg.md b/docs/internals/builtins/json/json_last_error_msg.md
index 1697ef9b23..cb96b19c43 100644
--- a/docs/internals/builtins/json/json_last_error_msg.md
+++ b/docs/internals/builtins/json/json_last_error_msg.md
@@ -2,15 +2,15 @@
title: "json_last_error_msg() — internals"
description: "Compiler internals for json_last_error_msg(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 216
+ order: 233
---
## `json_last_error_msg()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/json.rs`:85](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/json.rs#L85) (`lower_json_last_error_msg`)
+- **Signature**: [`src/builtins/system/json_last_error_msg.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/json_last_error_msg.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/json.rs`:85](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/json.rs#L85) (`lower_json_last_error_msg`)
- **Function symbol**: `lower_json_last_error_msg()`
@@ -37,4 +37,3 @@ function json_last_error_msg(): string
## Cross-references
- [User reference for `json_last_error_msg()`](../../../php/builtins/json/json_last_error_msg.md)
-
diff --git a/docs/internals/builtins/json/json_validate.md b/docs/internals/builtins/json/json_validate.md
index b9caf8b8e4..3fad80ebc2 100644
--- a/docs/internals/builtins/json/json_validate.md
+++ b/docs/internals/builtins/json/json_validate.md
@@ -2,15 +2,15 @@
title: "json_validate() — internals"
description: "Compiler internals for json_validate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 217
+ order: 234
---
## `json_validate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/json.rs`:95](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/json.rs#L95) (`lower_json_validate`)
+- **Signature**: [`src/builtins/system/json_validate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/json_validate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/json.rs`:95](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/json.rs#L95) (`lower_json_validate`)
- **Function symbol**: `lower_json_validate()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function json_validate(string $json, int $depth, int $flags): bool
+function json_validate(string $json, int $depth = 512, int $flags = 0): bool
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function json_validate(string $json, int $depth, int $flags): bool
## Cross-references
- [User reference for `json_validate()`](../../../php/builtins/json/json_validate.md)
-
diff --git a/docs/internals/builtins/math/abs.md b/docs/internals/builtins/math/abs.md
index f7301e4f7b..7922313ede 100644
--- a/docs/internals/builtins/math/abs.md
+++ b/docs/internals/builtins/math/abs.md
@@ -2,15 +2,15 @@
title: "abs() — internals"
description: "Compiler internals for abs(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 218
+ order: 235
---
## `abs()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:43](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L43) (`lower_abs`)
+- **Signature**: [`src/builtins/math/abs.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/abs.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:43](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L43) (`lower_abs`)
- **Function symbol**: `lower_abs()`
@@ -36,4 +36,3 @@ function abs(int $num): mixed
## Cross-references
- [User reference for `abs()`](../../../php/builtins/math/abs.md)
-
diff --git a/docs/internals/builtins/math/acos.md b/docs/internals/builtins/math/acos.md
index 26347fa3dc..1d800b1477 100644
--- a/docs/internals/builtins/math/acos.md
+++ b/docs/internals/builtins/math/acos.md
@@ -2,18 +2,22 @@
title: "acos() — internals"
description: "Compiler internals for acos(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 219
+ order: 236
---
## `acos()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/acos.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/acos.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function acos(float $num): float
## Cross-references
- [User reference for `acos()`](../../../php/builtins/math/acos.md)
-
diff --git a/docs/internals/builtins/math/asin.md b/docs/internals/builtins/math/asin.md
index acb654de50..849ba063e2 100644
--- a/docs/internals/builtins/math/asin.md
+++ b/docs/internals/builtins/math/asin.md
@@ -2,18 +2,22 @@
title: "asin() — internals"
description: "Compiler internals for asin(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 220
+ order: 237
---
## `asin()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/asin.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/asin.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function asin(float $num): float
## Cross-references
- [User reference for `asin()`](../../../php/builtins/math/asin.md)
-
diff --git a/docs/internals/builtins/math/atan.md b/docs/internals/builtins/math/atan.md
index 2b8295d3f5..f60bbb0fe2 100644
--- a/docs/internals/builtins/math/atan.md
+++ b/docs/internals/builtins/math/atan.md
@@ -2,18 +2,22 @@
title: "atan() — internals"
description: "Compiler internals for atan(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 221
+ order: 238
---
## `atan()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/atan.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/atan.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function atan(float $num): float
## Cross-references
- [User reference for `atan()`](../../../php/builtins/math/atan.md)
-
diff --git a/docs/internals/builtins/math/atan2.md b/docs/internals/builtins/math/atan2.md
index 3f75ba226e..e45ecf6a8c 100644
--- a/docs/internals/builtins/math/atan2.md
+++ b/docs/internals/builtins/math/atan2.md
@@ -2,15 +2,15 @@
title: "atan2() — internals"
description: "Compiler internals for atan2(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 222
+ order: 239
---
## `atan2()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/libm.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/libm.rs#L35) (`lower_atan2`)
+- **Signature**: [`src/builtins/math/atan2.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/atan2.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L35) (`lower_atan2`)
- **Function symbol**: `lower_atan2()`
@@ -35,4 +35,3 @@ function atan2(float $y, float $x): float
## Cross-references
- [User reference for `atan2()`](../../../php/builtins/math/atan2.md)
-
diff --git a/docs/internals/builtins/math/ceil.md b/docs/internals/builtins/math/ceil.md
index 6c923654c2..cfaa80576d 100644
--- a/docs/internals/builtins/math/ceil.md
+++ b/docs/internals/builtins/math/ceil.md
@@ -2,15 +2,15 @@
title: "ceil() — internals"
description: "Compiler internals for ceil(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 223
+ order: 240
---
## `ceil()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L75) (`lower_ceil`)
+- **Signature**: [`src/builtins/math/ceil.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/ceil.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L75) (`lower_ceil`)
- **Function symbol**: `lower_ceil()`
@@ -35,4 +35,3 @@ function ceil(float $num): float
## Cross-references
- [User reference for `ceil()`](../../../php/builtins/math/ceil.md)
-
diff --git a/docs/internals/builtins/math/clamp.md b/docs/internals/builtins/math/clamp.md
index d4d7b706c4..ca4f8a19b3 100644
--- a/docs/internals/builtins/math/clamp.md
+++ b/docs/internals/builtins/math/clamp.md
@@ -2,15 +2,15 @@
title: "clamp() — internals"
description: "Compiler internals for clamp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 224
+ order: 241
---
## `clamp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:80](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L80) (`lower_clamp`)
+- **Signature**: [`src/builtins/math/clamp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/clamp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:80](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L80) (`lower_clamp`)
- **Function symbol**: `lower_clamp()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function clamp(int $value, int $min, int $max): string
+function clamp(int $value, int $min, int $max): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function clamp(int $value, int $min, int $max): string
## Cross-references
- [User reference for `clamp()`](../../../php/builtins/math/clamp.md)
-
diff --git a/docs/internals/builtins/math/cos.md b/docs/internals/builtins/math/cos.md
index 3f2608e3f2..0e995e6cc9 100644
--- a/docs/internals/builtins/math/cos.md
+++ b/docs/internals/builtins/math/cos.md
@@ -2,18 +2,22 @@
title: "cos() — internals"
description: "Compiler internals for cos(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 225
+ order: 242
---
## `cos()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/cos.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/cos.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function cos(float $num): float
## Cross-references
- [User reference for `cos()`](../../../php/builtins/math/cos.md)
-
diff --git a/docs/internals/builtins/math/cosh.md b/docs/internals/builtins/math/cosh.md
index 20e816ee2f..1ce7156dbe 100644
--- a/docs/internals/builtins/math/cosh.md
+++ b/docs/internals/builtins/math/cosh.md
@@ -2,18 +2,22 @@
title: "cosh() — internals"
description: "Compiler internals for cosh(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 226
+ order: 243
---
## `cosh()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/cosh.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/cosh.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function cosh(float $num): float
## Cross-references
- [User reference for `cosh()`](../../../php/builtins/math/cosh.md)
-
diff --git a/docs/internals/builtins/math/deg2rad.md b/docs/internals/builtins/math/deg2rad.md
index 4e8b49d1fa..57aa0dc01e 100644
--- a/docs/internals/builtins/math/deg2rad.md
+++ b/docs/internals/builtins/math/deg2rad.md
@@ -2,15 +2,15 @@
title: "deg2rad() — internals"
description: "Compiler internals for deg2rad(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 227
+ order: 244
---
## `deg2rad()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/libm.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/libm.rs#L75) (`lower_deg2rad`)
+- **Signature**: [`src/builtins/math/deg2rad.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/deg2rad.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L75) (`lower_deg2rad`)
- **Function symbol**: `lower_deg2rad()`
@@ -35,4 +35,3 @@ function deg2rad(float $num): float
## Cross-references
- [User reference for `deg2rad()`](../../../php/builtins/math/deg2rad.md)
-
diff --git a/docs/internals/builtins/math/exp.md b/docs/internals/builtins/math/exp.md
index be3eda0e50..8e55435630 100644
--- a/docs/internals/builtins/math/exp.md
+++ b/docs/internals/builtins/math/exp.md
@@ -2,18 +2,22 @@
title: "exp() — internals"
description: "Compiler internals for exp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 228
+ order: 245
---
## `exp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/exp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/exp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function exp(float $num): float
## Cross-references
- [User reference for `exp()`](../../../php/builtins/math/exp.md)
-
diff --git a/docs/internals/builtins/math/fdiv.md b/docs/internals/builtins/math/fdiv.md
index 2430877b10..2779c26376 100644
--- a/docs/internals/builtins/math/fdiv.md
+++ b/docs/internals/builtins/math/fdiv.md
@@ -2,15 +2,15 @@
title: "fdiv() — internals"
description: "Compiler internals for fdiv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 229
+ order: 246
---
## `fdiv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/binary.rs`:60](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/binary.rs#L60) (`lower_fdiv`)
+- **Signature**: [`src/builtins/math/fdiv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/fdiv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/binary.rs`:67](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/binary.rs#L67) (`lower_fdiv`)
- **Function symbol**: `lower_fdiv()`
@@ -35,4 +35,3 @@ function fdiv(float $num1, float $num2): float
## Cross-references
- [User reference for `fdiv()`](../../../php/builtins/math/fdiv.md)
-
diff --git a/docs/internals/builtins/math/floor.md b/docs/internals/builtins/math/floor.md
index 547ba440e9..c197992643 100644
--- a/docs/internals/builtins/math/floor.md
+++ b/docs/internals/builtins/math/floor.md
@@ -2,15 +2,15 @@
title: "floor() — internals"
description: "Compiler internals for floor(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 230
+ order: 247
---
## `floor()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L70) (`lower_floor`)
+- **Signature**: [`src/builtins/math/floor.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/floor.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:70](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L70) (`lower_floor`)
- **Function symbol**: `lower_floor()`
@@ -35,4 +35,3 @@ function floor(float $num): float
## Cross-references
- [User reference for `floor()`](../../../php/builtins/math/floor.md)
-
diff --git a/docs/internals/builtins/math/fmod.md b/docs/internals/builtins/math/fmod.md
index 1b3dc05637..9ad291188d 100644
--- a/docs/internals/builtins/math/fmod.md
+++ b/docs/internals/builtins/math/fmod.md
@@ -2,15 +2,15 @@
title: "fmod() — internals"
description: "Compiler internals for fmod(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 231
+ order: 248
---
## `fmod()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/binary.rs`:85](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/binary.rs#L85) (`lower_fmod`)
+- **Signature**: [`src/builtins/math/fmod.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/fmod.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/binary.rs`:92](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/binary.rs#L92) (`lower_fmod`)
- **Function symbol**: `lower_fmod()`
@@ -35,4 +35,3 @@ function fmod(float $num1, float $num2): float
## Cross-references
- [User reference for `fmod()`](../../../php/builtins/math/fmod.md)
-
diff --git a/docs/internals/builtins/math/hypot.md b/docs/internals/builtins/math/hypot.md
index ad0fe933b0..37b485883f 100644
--- a/docs/internals/builtins/math/hypot.md
+++ b/docs/internals/builtins/math/hypot.md
@@ -2,15 +2,15 @@
title: "hypot() — internals"
description: "Compiler internals for hypot(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 232
+ order: 249
---
## `hypot()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/libm.rs`:43](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/libm.rs#L43) (`lower_hypot`)
+- **Signature**: [`src/builtins/math/hypot.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/hypot.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:43](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L43) (`lower_hypot`)
- **Function symbol**: `lower_hypot()`
@@ -35,4 +35,3 @@ function hypot(float $x, float $y): float
## Cross-references
- [User reference for `hypot()`](../../../php/builtins/math/hypot.md)
-
diff --git a/docs/internals/builtins/math/intdiv.md b/docs/internals/builtins/math/intdiv.md
index 71703b1fdb..8121f26dc3 100644
--- a/docs/internals/builtins/math/intdiv.md
+++ b/docs/internals/builtins/math/intdiv.md
@@ -2,15 +2,15 @@
title: "intdiv() — internals"
description: "Compiler internals for intdiv(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 233
+ order: 250
---
## `intdiv()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/binary.rs`:21](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/binary.rs#L21) (`lower_intdiv`)
+- **Signature**: [`src/builtins/math/intdiv.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/intdiv.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/binary.rs`:23](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/binary.rs#L23) (`lower_intdiv`)
- **Function symbol**: `lower_intdiv()`
@@ -35,4 +35,3 @@ function intdiv(int $num1, int $num2): int
## Cross-references
- [User reference for `intdiv()`](../../../php/builtins/math/intdiv.md)
-
diff --git a/docs/internals/builtins/math/is_finite.md b/docs/internals/builtins/math/is_finite.md
index dff7e9964f..1ce482dba4 100644
--- a/docs/internals/builtins/math/is_finite.md
+++ b/docs/internals/builtins/math/is_finite.md
@@ -2,15 +2,15 @@
title: "is_finite() — internals"
description: "Compiler internals for is_finite(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 234
+ order: 251
---
## `is_finite()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:169](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L169) (`lower_is_finite`)
+- **Signature**: [`src/builtins/types/is_finite.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_finite.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:169](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L169) (`lower_is_finite`)
- **Function symbol**: `lower_is_finite()`
@@ -35,4 +35,3 @@ function is_finite(float $num): bool
## Cross-references
- [User reference for `is_finite()`](../../../php/builtins/math/is_finite.md)
-
diff --git a/docs/internals/builtins/math/is_infinite.md b/docs/internals/builtins/math/is_infinite.md
index a4bda4349e..341d462e3f 100644
--- a/docs/internals/builtins/math/is_infinite.md
+++ b/docs/internals/builtins/math/is_infinite.md
@@ -2,15 +2,15 @@
title: "is_infinite() — internals"
description: "Compiler internals for is_infinite(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 235
+ order: 252
---
## `is_infinite()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:132](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L132) (`lower_is_infinite`)
+- **Signature**: [`src/builtins/types/is_infinite.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_infinite.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:132](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L132) (`lower_is_infinite`)
- **Function symbol**: `lower_is_infinite()`
@@ -35,4 +35,3 @@ function is_infinite(float $num): bool
## Cross-references
- [User reference for `is_infinite()`](../../../php/builtins/math/is_infinite.md)
-
diff --git a/docs/internals/builtins/math/is_nan.md b/docs/internals/builtins/math/is_nan.md
index 014931d616..3d54cef485 100644
--- a/docs/internals/builtins/math/is_nan.md
+++ b/docs/internals/builtins/math/is_nan.md
@@ -2,15 +2,15 @@
title: "is_nan() — internals"
description: "Compiler internals for is_nan(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 236
+ order: 253
---
## `is_nan()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:113](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L113) (`lower_is_nan`)
+- **Signature**: [`src/builtins/types/is_nan.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_nan.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:113](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L113) (`lower_is_nan`)
- **Function symbol**: `lower_is_nan()`
@@ -35,4 +35,3 @@ function is_nan(float $num): bool
## Cross-references
- [User reference for `is_nan()`](../../../php/builtins/math/is_nan.md)
-
diff --git a/docs/internals/builtins/math/log.md b/docs/internals/builtins/math/log.md
index 1606887942..11999ebe30 100644
--- a/docs/internals/builtins/math/log.md
+++ b/docs/internals/builtins/math/log.md
@@ -2,15 +2,15 @@
title: "log() — internals"
description: "Compiler internals for log(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 237
+ order: 254
---
## `log()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/libm.rs`:51](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/libm.rs#L51) (`lower_log`)
+- **Signature**: [`src/builtins/math/log.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/log.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:51](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L51) (`lower_log`)
- **Function symbol**: `lower_log()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function log(float $num, float $base): float
+function log(float $num, float $base = 2.718281828459045): float
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function log(float $num, float $base): float
## Cross-references
- [User reference for `log()`](../../../php/builtins/math/log.md)
-
diff --git a/docs/internals/builtins/math/log10.md b/docs/internals/builtins/math/log10.md
index afba603649..b02032e611 100644
--- a/docs/internals/builtins/math/log10.md
+++ b/docs/internals/builtins/math/log10.md
@@ -2,18 +2,22 @@
title: "log10() — internals"
description: "Compiler internals for log10(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 238
+ order: 255
---
## `log10()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/log10.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/log10.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function log10(float $num): float
## Cross-references
- [User reference for `log10()`](../../../php/builtins/math/log10.md)
-
diff --git a/docs/internals/builtins/math/log2.md b/docs/internals/builtins/math/log2.md
index 6426d8dca1..ffa1dbca15 100644
--- a/docs/internals/builtins/math/log2.md
+++ b/docs/internals/builtins/math/log2.md
@@ -2,18 +2,22 @@
title: "log2() — internals"
description: "Compiler internals for log2(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 239
+ order: 256
---
## `log2()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/log2.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/log2.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function log2(float $num): float
## Cross-references
- [User reference for `log2()`](../../../php/builtins/math/log2.md)
-
diff --git a/docs/internals/builtins/math/max.md b/docs/internals/builtins/math/max.md
index e2a8df6d0c..0a31bbee79 100644
--- a/docs/internals/builtins/math/max.md
+++ b/docs/internals/builtins/math/max.md
@@ -2,15 +2,15 @@
title: "max() — internals"
description: "Compiler internals for max(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 240
+ order: 257
---
## `max()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:204](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L204) (`lower_min_max`)
+- **Signature**: [`src/builtins/math/max.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/max.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:204](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L204) (`lower_min_max`)
- **Function symbol**: `lower_min_max()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function max(mixed $value, ...$values): float
+function max(mixed $value, ...$values): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function max(mixed $value, ...$values): float
## Cross-references
- [User reference for `max()`](../../../php/builtins/math/max.md)
-
diff --git a/docs/internals/builtins/math/min.md b/docs/internals/builtins/math/min.md
index f9fe453464..d39f3d4b3e 100644
--- a/docs/internals/builtins/math/min.md
+++ b/docs/internals/builtins/math/min.md
@@ -2,15 +2,15 @@
title: "min() — internals"
description: "Compiler internals for min(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 241
+ order: 258
---
## `min()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:204](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L204) (`lower_min_max`)
+- **Signature**: [`src/builtins/math/min.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/min.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:204](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L204) (`lower_min_max`)
- **Function symbol**: `lower_min_max()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function min(mixed $value, ...$values): float
+function min(mixed $value, ...$values): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function min(mixed $value, ...$values): float
## Cross-references
- [User reference for `min()`](../../../php/builtins/math/min.md)
-
diff --git a/docs/internals/builtins/math/mt_rand.md b/docs/internals/builtins/math/mt_rand.md
index c7f3edcc13..468eb0ea11 100644
--- a/docs/internals/builtins/math/mt_rand.md
+++ b/docs/internals/builtins/math/mt_rand.md
@@ -2,21 +2,26 @@
title: "mt_rand() — internals"
description: "Compiler internals for mt_rand(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 242
+ order: 259
---
## `mt_rand()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/mt_rand.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/mt_rand.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/random.rs`:21](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/random.rs#L21) (`lower_rand`)
+- **Function symbol**: `lower_rand()`
+### Lowering notes
+
+- Lowers `rand()` and `mt_rand()` with either zero args or an inclusive range.
+
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_random_u32`
## Signature summary
@@ -31,4 +36,3 @@ function mt_rand(int $min, int $max): int
## Cross-references
- [User reference for `mt_rand()`](../../../php/builtins/math/mt_rand.md)
-
diff --git a/docs/internals/builtins/math/pi.md b/docs/internals/builtins/math/pi.md
index d08cd290c8..16b2a09935 100644
--- a/docs/internals/builtins/math/pi.md
+++ b/docs/internals/builtins/math/pi.md
@@ -2,21 +2,21 @@
title: "pi() — internals"
description: "Compiler internals for pi(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 243
+ order: 260
---
## `pi()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:596](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L596) (`lower_pi`)
+- **Signature**: [`src/builtins/math/pi.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/pi.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:240](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L240) (`lower_pi`)
- **Function symbol**: `lower_pi()`
### Lowering notes
-- Lowers `pi()` as the same data-section float constant used by the legacy backend.
+- Lowers `pi()` as a data-section float constant.
## Runtime helpers
@@ -35,4 +35,3 @@ function pi(): float
## Cross-references
- [User reference for `pi()`](../../../php/builtins/math/pi.md)
-
diff --git a/docs/internals/builtins/math/pow.md b/docs/internals/builtins/math/pow.md
index f3a8668d08..e0996e87bf 100644
--- a/docs/internals/builtins/math/pow.md
+++ b/docs/internals/builtins/math/pow.md
@@ -2,15 +2,15 @@
title: "pow() — internals"
description: "Compiler internals for pow(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 244
+ order: 261
---
## `pow()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/binary.rs`:114](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/binary.rs#L114) (`lower_pow`)
+- **Signature**: [`src/builtins/math/pow.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/pow.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/binary.rs`:121](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/binary.rs#L121) (`lower_pow`)
- **Function symbol**: `lower_pow()`
@@ -35,4 +35,3 @@ function pow(float $num, float $exponent): float
## Cross-references
- [User reference for `pow()`](../../../php/builtins/math/pow.md)
-
diff --git a/docs/internals/builtins/math/rad2deg.md b/docs/internals/builtins/math/rad2deg.md
index c3130e4dfe..621a148d0f 100644
--- a/docs/internals/builtins/math/rad2deg.md
+++ b/docs/internals/builtins/math/rad2deg.md
@@ -2,15 +2,15 @@
title: "rad2deg() — internals"
description: "Compiler internals for rad2deg(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 245
+ order: 262
---
## `rad2deg()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/libm.rs`:83](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/libm.rs#L83) (`lower_rad2deg`)
+- **Signature**: [`src/builtins/math/rad2deg.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/rad2deg.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:83](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L83) (`lower_rad2deg`)
- **Function symbol**: `lower_rad2deg()`
@@ -35,4 +35,3 @@ function rad2deg(float $num): float
## Cross-references
- [User reference for `rad2deg()`](../../../php/builtins/math/rad2deg.md)
-
diff --git a/docs/internals/builtins/math/rand.md b/docs/internals/builtins/math/rand.md
index 0f0d368aa7..6aa48de0d2 100644
--- a/docs/internals/builtins/math/rand.md
+++ b/docs/internals/builtins/math/rand.md
@@ -2,21 +2,26 @@
title: "rand() — internals"
description: "Compiler internals for rand(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 246
+ order: 263
---
## `rand()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/rand.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/rand.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/random.rs`:21](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/random.rs#L21) (`lower_rand`)
+- **Function symbol**: `lower_rand()`
+### Lowering notes
+
+- Lowers `rand()` and `mt_rand()` with either zero args or an inclusive range.
+
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_random_u32`
## Signature summary
@@ -31,4 +36,3 @@ function rand(int $min, int $max): int
## Cross-references
- [User reference for `rand()`](../../../php/builtins/math/rand.md)
-
diff --git a/docs/internals/builtins/math/random_bytes.md b/docs/internals/builtins/math/random_bytes.md
new file mode 100644
index 0000000000..8c32fe8a32
--- /dev/null
+++ b/docs/internals/builtins/math/random_bytes.md
@@ -0,0 +1,44 @@
+---
+title: "random_bytes() — internals"
+description: "Compiler internals for random_bytes(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 264
+---
+
+## `random_bytes()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/math/random_bytes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/random_bytes.rs)
+- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/random.rs`:58](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/random.rs#L58) (`lower_random_bytes`)
+- **Function symbol**: `lower_random_bytes()`
+
+
+### Lowering notes
+
+- Lowers `random_bytes()` into an owned CSPRNG binary string of the given length.
+- Materializes the single length operand as an integer, passes it to the
+- `__rt_random_bytes` runtime helper (length in `x0` on AArch64, `rdi` on
+- x86_64), and stores the returned owned string result (`x1`/`x2` on AArch64,
+- `rax`/`rdx` on x86_64) into the instruction's result slot. The runtime helper
+- owns allocation, the cryptographic fill, and the fatal paths for a length
+- below 1 or an unavailable entropy source.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_random_bytes`
+
+## Signature summary
+
+```php
+function random_bytes(int $length): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- [User reference for `random_bytes()`](../../../php/builtins/math/random_bytes.md)
diff --git a/docs/internals/builtins/math/random_int.md b/docs/internals/builtins/math/random_int.md
index 59b9d8c246..90c988932a 100644
--- a/docs/internals/builtins/math/random_int.md
+++ b/docs/internals/builtins/math/random_int.md
@@ -2,15 +2,15 @@
title: "random_int() — internals"
description: "Compiler internals for random_int(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 247
+ order: 264
---
## `random_int()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math/random.rs`:40](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math/random.rs#L40) (`lower_random_int`)
+- **Signature**: [`src/builtins/math/random_int.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/random_int.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/random.rs`:40](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/random.rs#L40) (`lower_random_int`)
- **Function symbol**: `lower_random_int()`
@@ -35,4 +35,3 @@ function random_int(int $min, int $max): int
## Cross-references
- [User reference for `random_int()`](../../../php/builtins/math/random_int.md)
-
diff --git a/docs/internals/builtins/math/round.md b/docs/internals/builtins/math/round.md
index 13ab162501..7cdea6d462 100644
--- a/docs/internals/builtins/math/round.md
+++ b/docs/internals/builtins/math/round.md
@@ -2,15 +2,15 @@
title: "round() — internals"
description: "Compiler internals for round(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 248
+ order: 266
---
## `round()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:186](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L186) (`lower_round`)
+- **Signature**: [`src/builtins/math/round.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/round.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:186](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L186) (`lower_round`)
- **Function symbol**: `lower_round()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function round(float $num, int $precision): float
+function round(float $num, int $precision = 0): float
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function round(float $num, int $precision): float
## Cross-references
- [User reference for `round()`](../../../php/builtins/math/round.md)
-
diff --git a/docs/internals/builtins/math/sin.md b/docs/internals/builtins/math/sin.md
index c7ae73c578..ea3754ab6c 100644
--- a/docs/internals/builtins/math/sin.md
+++ b/docs/internals/builtins/math/sin.md
@@ -2,18 +2,22 @@
title: "sin() — internals"
description: "Compiler internals for sin(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 249
+ order: 267
---
## `sin()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/sin.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/sin.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function sin(float $num): float
## Cross-references
- [User reference for `sin()`](../../../php/builtins/math/sin.md)
-
diff --git a/docs/internals/builtins/math/sinh.md b/docs/internals/builtins/math/sinh.md
index a163be2535..486c00031d 100644
--- a/docs/internals/builtins/math/sinh.md
+++ b/docs/internals/builtins/math/sinh.md
@@ -2,18 +2,22 @@
title: "sinh() — internals"
description: "Compiler internals for sinh(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 250
+ order: 268
---
## `sinh()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/sinh.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/sinh.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function sinh(float $num): float
## Cross-references
- [User reference for `sinh()`](../../../php/builtins/math/sinh.md)
-
diff --git a/docs/internals/builtins/math/sqrt.md b/docs/internals/builtins/math/sqrt.md
index 6d4136edb1..c185414a37 100644
--- a/docs/internals/builtins/math/sqrt.md
+++ b/docs/internals/builtins/math/sqrt.md
@@ -2,15 +2,15 @@
title: "sqrt() — internals"
description: "Compiler internals for sqrt(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 251
+ order: 269
---
## `sqrt()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/math.rs`:97](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/math.rs#L97) (`lower_sqrt`)
+- **Signature**: [`src/builtins/math/sqrt.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/sqrt.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math.rs`:97](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math.rs#L97) (`lower_sqrt`)
- **Function symbol**: `lower_sqrt()`
@@ -35,4 +35,3 @@ function sqrt(float $num): float
## Cross-references
- [User reference for `sqrt()`](../../../php/builtins/math/sqrt.md)
-
diff --git a/docs/internals/builtins/math/tan.md b/docs/internals/builtins/math/tan.md
index 51c42b04e1..6794cf0a54 100644
--- a/docs/internals/builtins/math/tan.md
+++ b/docs/internals/builtins/math/tan.md
@@ -2,18 +2,22 @@
title: "tan() — internals"
description: "Compiler internals for tan(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 252
+ order: 270
---
## `tan()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/tan.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/tan.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function tan(float $num): float
## Cross-references
- [User reference for `tan()`](../../../php/builtins/math/tan.md)
-
diff --git a/docs/internals/builtins/math/tanh.md b/docs/internals/builtins/math/tanh.md
index 3163cce60e..c4c88eb396 100644
--- a/docs/internals/builtins/math/tanh.md
+++ b/docs/internals/builtins/math/tanh.md
@@ -2,18 +2,22 @@
title: "tanh() — internals"
description: "Compiler internals for tanh(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 253
+ order: 271
---
## `tanh()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/math/tanh.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/math/tanh.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/math/libm.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/math/libm.rs#L22) (`lower_unary_libm`)
+- **Function symbol**: `lower_unary_libm()`
+### Lowering notes
+
+- Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function tanh(float $num): float
## Cross-references
- [User reference for `tanh()`](../../../php/builtins/math/tanh.md)
-
diff --git a/docs/internals/builtins/misc/buffer_new.md b/docs/internals/builtins/misc/buffer_new.md
index 69e9edf6fe..1dda6a2d93 100644
--- a/docs/internals/builtins/misc/buffer_new.md
+++ b/docs/internals/builtins/misc/buffer_new.md
@@ -2,7 +2,7 @@
title: "buffer_new() — internals"
description: "Compiler internals for buffer_new(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 257
+ order: 272
---
## `buffer_new()` — internals
@@ -31,4 +31,3 @@ function buffer_new(int $length): mixed
## Cross-references
- [User reference for `buffer_new()`](../../../php/builtins/misc/buffer_new.md)
-
diff --git a/docs/internals/builtins/misc/call_user_func.md b/docs/internals/builtins/misc/call_user_func.md
deleted file mode 100644
index ef30267e5e..0000000000
--- a/docs/internals/builtins/misc/call_user_func.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-title: "call_user_func() — internals"
-description: "Compiler internals for call_user_func(): lowering path, type checks, and runtime helpers."
-sidebar:
- order: 258
----
-
-## `call_user_func()` — internals
-
-## Where it lives
-
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
-
-
-## Runtime helpers
-
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
-
-## Signature summary
-
-```php
-function call_user_func(callable $callback, ...$args): mixed
-```
-
-## What the type checker enforces
-
-- **Arity**: takes exactly 1 argument.
-- **Variadic**: collects excess arguments into `$args`.
-
-## Cross-references
-
-- [User reference for `call_user_func()`](../../../php/builtins/misc/call_user_func.md)
-
diff --git a/docs/internals/builtins/misc/call_user_func_array.md b/docs/internals/builtins/misc/call_user_func_array.md
deleted file mode 100644
index a558856fad..0000000000
--- a/docs/internals/builtins/misc/call_user_func_array.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-title: "call_user_func_array() — internals"
-description: "Compiler internals for call_user_func_array(): lowering path, type checks, and runtime helpers."
-sidebar:
- order: 259
----
-
-## `call_user_func_array()` — internals
-
-## Where it lives
-
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
-
-
-## Runtime helpers
-
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
-
-## Signature summary
-
-```php
-function call_user_func_array(callable $callback, array $args): mixed
-```
-
-## What the type checker enforces
-
-- **Arity**: takes exactly 2 arguments.
-
-## Cross-references
-
-- [User reference for `call_user_func_array()`](../../../php/builtins/misc/call_user_func_array.md)
-
diff --git a/docs/internals/builtins/misc/define.md b/docs/internals/builtins/misc/define.md
index 09a870ad7b..a9e5610097 100644
--- a/docs/internals/builtins/misc/define.md
+++ b/docs/internals/builtins/misc/define.md
@@ -2,21 +2,21 @@
title: "define() — internals"
description: "Compiler internals for define(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 260
+ order: 273
---
## `define()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:549](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L549) (`lower_define`)
+- **Signature**: [`src/builtins/system/define.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/define.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:82](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L82) (`lower_define`)
- **Function symbol**: `lower_define()`
### Lowering notes
-- Lowers `define("NAME", value)` with the legacy duplicate-name runtime guard.
+- Lowers `define("NAME", value)` with the duplicate-name runtime guard.
## Runtime helpers
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function define(string $constant_name, mixed $value, bool $case_insensitive): bool
+function define(string $constant_name, mixed $value): bool
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `define()`](../../../php/builtins/misc/define.md)
-
diff --git a/docs/internals/builtins/misc/defined.md b/docs/internals/builtins/misc/defined.md
index 50f2a3ac93..73775cde67 100644
--- a/docs/internals/builtins/misc/defined.md
+++ b/docs/internals/builtins/misc/defined.md
@@ -2,15 +2,15 @@
title: "defined() — internals"
description: "Compiler internals for defined(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 261
+ order: 274
---
## `defined()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:744](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L744) (`lower_defined`)
+- **Signature**: [`src/builtins/system/defined.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/defined.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:261](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L261) (`lower_defined`)
- **Function symbol**: `lower_defined()`
@@ -35,4 +35,3 @@ function defined(string $constant_name): bool
## Cross-references
- [User reference for `defined()`](../../../php/builtins/misc/defined.md)
-
diff --git a/docs/internals/builtins/misc/empty.md b/docs/internals/builtins/misc/empty.md
index 9de298217f..4584c0290f 100644
--- a/docs/internals/builtins/misc/empty.md
+++ b/docs/internals/builtins/misc/empty.md
@@ -2,7 +2,7 @@
title: "empty() — internals"
description: "Compiler internals for empty(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 262
+ order: 275
---
## `empty()` — internals
@@ -10,7 +10,7 @@ sidebar:
## Where it lives
- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1096](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1096) (`lower_empty`)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:618](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L618) (`lower_empty`)
- **Function symbol**: `lower_empty()`
@@ -36,4 +36,3 @@ function empty(mixed $value): bool
## Cross-references
- [User reference for `empty()`](../../../php/builtins/misc/empty.md)
-
diff --git a/docs/internals/builtins/misc/header.md b/docs/internals/builtins/misc/header.md
index db1cb11bac..d57a5fe98c 100644
--- a/docs/internals/builtins/misc/header.md
+++ b/docs/internals/builtins/misc/header.md
@@ -2,15 +2,15 @@
title: "header() — internals"
description: "Compiler internals for header(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 263
+ order: 276
---
## `header()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:289](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L289) (`lower_header`)
+- **Signature**: [`src/builtins/system/header.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/header.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:289](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L289) (`lower_header`)
- **Function symbol**: `lower_header()`
@@ -31,7 +31,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function header(mixed $header, mixed $replace, mixed $response_code): void
+function header(string $header, bool $replace = true, int $response_code = 0): void
```
## What the type checker enforces
@@ -41,4 +41,3 @@ function header(mixed $header, mixed $replace, mixed $response_code): void
## Cross-references
- [User reference for `header()`](../../../php/builtins/misc/header.md)
-
diff --git a/docs/internals/builtins/misc/http_response_code.md b/docs/internals/builtins/misc/http_response_code.md
index f4e81d3959..d42da3c558 100644
--- a/docs/internals/builtins/misc/http_response_code.md
+++ b/docs/internals/builtins/misc/http_response_code.md
@@ -2,15 +2,15 @@
title: "http_response_code() — internals"
description: "Compiler internals for http_response_code(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 264
+ order: 277
---
## `http_response_code()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:264](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L264) (`lower_http_response_code`)
+- **Signature**: [`src/builtins/system/http_response_code.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/http_response_code.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:264](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L264) (`lower_http_response_code`)
- **Function symbol**: `lower_http_response_code()`
@@ -30,7 +30,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function http_response_code(mixed $response_code): int
+function http_response_code(int $response_code = 0): int
```
## What the type checker enforces
@@ -40,4 +40,3 @@ function http_response_code(mixed $response_code): int
## Cross-references
- [User reference for `http_response_code()`](../../../php/builtins/misc/http_response_code.md)
-
diff --git a/docs/internals/builtins/misc/isset.md b/docs/internals/builtins/misc/isset.md
index 4ad04b8410..b580985d37 100644
--- a/docs/internals/builtins/misc/isset.md
+++ b/docs/internals/builtins/misc/isset.md
@@ -2,7 +2,7 @@
title: "isset() — internals"
description: "Compiler internals for isset(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 265
+ order: 278
---
## `isset()` — internals
@@ -10,7 +10,7 @@ sidebar:
## Where it lives
- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/isset.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/isset.rs#L24) (`lower_isset`)
+- **Lowering**: [`src/codegen/lower_inst/builtins/isset.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/isset.rs#L24) (`lower_isset`)
- **Function symbol**: `lower_isset()`
@@ -36,4 +36,3 @@ function isset(mixed $var, ...$vars): bool
## Cross-references
- [User reference for `isset()`](../../../php/builtins/misc/isset.md)
-
diff --git a/docs/internals/builtins/misc/php_uname.md b/docs/internals/builtins/misc/php_uname.md
index 5146a53839..0e02bd290c 100644
--- a/docs/internals/builtins/misc/php_uname.md
+++ b/docs/internals/builtins/misc/php_uname.md
@@ -2,15 +2,15 @@
title: "php_uname() — internals"
description: "Compiler internals for php_uname(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 266
+ order: 279
---
## `php_uname()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:672](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L672) (`lower_php_uname`)
+- **Signature**: [`src/builtins/system/php_uname.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/php_uname.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:672](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L672) (`lower_php_uname`)
- **Function symbol**: `lower_php_uname()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function php_uname(string $mode): string
+function php_uname(string $mode = 'a'): string
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function php_uname(string $mode): string
## Cross-references
- [User reference for `php_uname()`](../../../php/builtins/misc/php_uname.md)
-
diff --git a/docs/internals/builtins/misc/phpversion.md b/docs/internals/builtins/misc/phpversion.md
index 0244314646..455c9c1aa7 100644
--- a/docs/internals/builtins/misc/phpversion.md
+++ b/docs/internals/builtins/misc/phpversion.md
@@ -2,15 +2,15 @@
title: "phpversion() — internals"
description: "Compiler internals for phpversion(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 267
+ order: 280
---
## `phpversion()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:734](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L734) (`lower_phpversion`)
+- **Signature**: [`src/builtins/system/phpversion.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/phpversion.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:251](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L251) (`lower_phpversion`)
- **Function symbol**: `lower_phpversion()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function phpversion(string $extension = null): string
+function phpversion(): string
```
## What the type checker enforces
-- **Arity**: takes 0–1 arguments (1 optional).
+- **Arity**: takes no arguments.
## Cross-references
- [User reference for `phpversion()`](../../../php/builtins/misc/phpversion.md)
-
diff --git a/docs/internals/builtins/misc/print_r.md b/docs/internals/builtins/misc/print_r.md
index e54c254b0c..98a9ccf0f0 100644
--- a/docs/internals/builtins/misc/print_r.md
+++ b/docs/internals/builtins/misc/print_r.md
@@ -2,15 +2,15 @@
title: "print_r() — internals"
description: "Compiler internals for print_r(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 268
+ order: 281
---
## `print_r()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/debug.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/debug.rs#L24) (`lower_print_r`)
+- **Signature**: [`src/builtins/io/print_r.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/print_r.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/debug.rs`:24](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/debug.rs#L24) (`lower_print_r`)
- **Function symbol**: `lower_print_r()`
@@ -25,15 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function print_r(...$values): void
+function print_r(mixed $value): void
```
## What the type checker enforces
-- **Arity**: takes no arguments.
-- **Variadic**: collects excess arguments into `$values`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `print_r()`](../../../php/builtins/misc/print_r.md)
-
diff --git a/docs/internals/builtins/misc/serialize.md b/docs/internals/builtins/misc/serialize.md
new file mode 100644
index 0000000000..58e96c3e1d
--- /dev/null
+++ b/docs/internals/builtins/misc/serialize.md
@@ -0,0 +1,43 @@
+---
+title: "serialize() — internals"
+description: "Compiler internals for serialize(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 282
+---
+
+## `serialize()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/system/serialize.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/serialize.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/serialize.rs`:33](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/serialize.rs#L33) (`lower_serialize`)
+- **Function symbol**: `lower_serialize()`
+
+
+### Lowering notes
+
+- Lowers `serialize($value)` into the shared serialize runtime helper.
+- Scalar static types are formatted directly through `__rt_serialize_value`; a
+- Mixed/Union argument is unboxed and dispatched by `__rt_serialize_mixed`.
+- Non-scalar static types (arrays/objects) are not yet supported and are rejected.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_serialize_begin`
+- `__rt_serialize_mixed`
+- `__rt_serialize_value`
+
+## Signature summary
+
+```php
+function serialize(mixed $value): string
+```
+
+## What the type checker enforces
+
+- **Arity**: takes exactly 1 argument.
+
+## Cross-references
+
+- [User reference for `serialize()`](../../../php/builtins/misc/serialize.md)
diff --git a/docs/internals/builtins/misc/unserialize.md b/docs/internals/builtins/misc/unserialize.md
new file mode 100644
index 0000000000..ce2667acfd
--- /dev/null
+++ b/docs/internals/builtins/misc/unserialize.md
@@ -0,0 +1,43 @@
+---
+title: "unserialize() — internals"
+description: "Compiler internals for unserialize(): lowering path, type checks, and runtime helpers."
+sidebar:
+ order: 283
+---
+
+## `unserialize()` — internals
+
+## Where it lives
+
+- **Signature**: [`src/builtins/system/unserialize.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/unserialize.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/serialize.rs`:164](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/serialize.rs#L164) (`lower_unserialize`)
+- **Function symbol**: `lower_unserialize()`
+
+
+### Lowering notes
+
+- Lowers `unserialize($data, $options?)` into the shared unserialize runtime helper.
+- The source string is parsed by `__rt_unserialize_mixed`; a null result pointer
+- (parse error or unsupported wire form) is boxed as PHP `false`. The optional
+- `$options` argument is accepted but currently ignored.
+
+## Runtime helpers
+
+The following runtime helpers are referenced:
+- `__rt_mixed_cast_string`
+- `__rt_unserialize_begin`
+- `__rt_unserialize_mixed`
+
+## Signature summary
+
+```php
+function unserialize(string $data, mixed $options = []): mixed
+```
+
+## What the type checker enforces
+
+- **Arity**: takes 1–2 arguments (1 optional).
+
+## Cross-references
+
+- [User reference for `unserialize()`](../../../php/builtins/misc/unserialize.md)
diff --git a/docs/internals/builtins/misc/unset.md b/docs/internals/builtins/misc/unset.md
index d530ea5f9d..cfa29e94fa 100644
--- a/docs/internals/builtins/misc/unset.md
+++ b/docs/internals/builtins/misc/unset.md
@@ -2,7 +2,7 @@
title: "unset() — internals"
description: "Compiler internals for unset(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 269
+ order: 284
---
## `unset()` — internals
@@ -10,7 +10,7 @@ sidebar:
## Where it lives
- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:48](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L48) (`lower_unset_builtin`)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:48](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L48) (`lower_unset_builtin`)
- **Function symbol**: `lower_unset_builtin()`
@@ -36,4 +36,3 @@ function unset(mixed $var, ...$vars): void
## Cross-references
- [User reference for `unset()`](../../../php/builtins/misc/unset.md)
-
diff --git a/docs/internals/builtins/misc/var_dump.md b/docs/internals/builtins/misc/var_dump.md
index 375de96f66..2a9981d77b 100644
--- a/docs/internals/builtins/misc/var_dump.md
+++ b/docs/internals/builtins/misc/var_dump.md
@@ -2,15 +2,15 @@
title: "var_dump() — internals"
description: "Compiler internals for var_dump(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 270
+ order: 285
---
## `var_dump()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/debug.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/debug.rs#L35) (`lower_var_dump`)
+- **Signature**: [`src/builtins/io/var_dump.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/var_dump.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/debug.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/debug.rs#L35) (`lower_var_dump`)
- **Function symbol**: `lower_var_dump()`
@@ -25,15 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function var_dump(...$values): void
+function var_dump(mixed $value): void
```
## What the type checker enforces
-- **Arity**: takes no arguments.
-- **Variadic**: collects excess arguments into `$values`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `var_dump()`](../../../php/builtins/misc/var_dump.md)
-
diff --git a/docs/internals/builtins/pointer/ptr.md b/docs/internals/builtins/pointer/ptr.md
index 1f77ef5b5b..33eafec841 100644
--- a/docs/internals/builtins/pointer/ptr.md
+++ b/docs/internals/builtins/pointer/ptr.md
@@ -2,15 +2,15 @@
title: "ptr() — internals"
description: "Compiler internals for ptr(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 271
+ order: 286
---
## `ptr()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L25) (`lower_ptr`)
+- **Signature**: [`src/builtins/pointers/ptr.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L25) (`lower_ptr`)
- **Function symbol**: `lower_ptr()`
@@ -35,4 +35,3 @@ function ptr(mixed $value): mixed
## Cross-references
- [User reference for `ptr()`](../../../php/builtins/pointer/ptr.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_get.md b/docs/internals/builtins/pointer/ptr_get.md
index dcc82ef40b..bd233c1b2f 100644
--- a/docs/internals/builtins/pointer/ptr_get.md
+++ b/docs/internals/builtins/pointer/ptr_get.md
@@ -2,15 +2,15 @@
title: "ptr_get() — internals"
description: "Compiler internals for ptr_get(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 272
+ order: 287
---
## `ptr_get()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:109](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L109) (`lower_ptr_get`)
+- **Signature**: [`src/builtins/pointers/ptr_get.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_get.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:109](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L109) (`lower_ptr_get`)
- **Function symbol**: `lower_ptr_get()`
@@ -35,4 +35,3 @@ function ptr_get(pointer $pointer): int
## Cross-references
- [User reference for `ptr_get()`](../../../php/builtins/pointer/ptr_get.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_is_null.md b/docs/internals/builtins/pointer/ptr_is_null.md
index 6bbf36553d..fef096c1d9 100644
--- a/docs/internals/builtins/pointer/ptr_is_null.md
+++ b/docs/internals/builtins/pointer/ptr_is_null.md
@@ -2,15 +2,15 @@
title: "ptr_is_null() — internals"
description: "Compiler internals for ptr_is_null(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 273
+ order: 288
---
## `ptr_is_null()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:56](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L56) (`lower_ptr_is_null`)
+- **Signature**: [`src/builtins/pointers/ptr_is_null.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_is_null.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:56](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L56) (`lower_ptr_is_null`)
- **Function symbol**: `lower_ptr_is_null()`
@@ -35,4 +35,3 @@ function ptr_is_null(pointer $pointer): bool
## Cross-references
- [User reference for `ptr_is_null()`](../../../php/builtins/pointer/ptr_is_null.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_null.md b/docs/internals/builtins/pointer/ptr_null.md
index ced645e9d5..18835ffd50 100644
--- a/docs/internals/builtins/pointer/ptr_null.md
+++ b/docs/internals/builtins/pointer/ptr_null.md
@@ -2,15 +2,15 @@
title: "ptr_null() — internals"
description: "Compiler internals for ptr_null(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 274
+ order: 289
---
## `ptr_null()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:49](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L49) (`lower_ptr_null`)
+- **Signature**: [`src/builtins/pointers/ptr_null.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_null.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:49](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L49) (`lower_ptr_null`)
- **Function symbol**: `lower_ptr_null()`
@@ -35,4 +35,3 @@ function ptr_null(): mixed
## Cross-references
- [User reference for `ptr_null()`](../../../php/builtins/pointer/ptr_null.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_offset.md b/docs/internals/builtins/pointer/ptr_offset.md
index 89d582d276..22bfae92ac 100644
--- a/docs/internals/builtins/pointer/ptr_offset.md
+++ b/docs/internals/builtins/pointer/ptr_offset.md
@@ -2,15 +2,15 @@
title: "ptr_offset() — internals"
description: "Compiler internals for ptr_offset(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 275
+ order: 290
---
## `ptr_offset()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:86](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L86) (`lower_ptr_offset`)
+- **Signature**: [`src/builtins/pointers/ptr_offset.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_offset.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:86](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L86) (`lower_ptr_offset`)
- **Function symbol**: `lower_ptr_offset()`
@@ -35,4 +35,3 @@ function ptr_offset(pointer $pointer, int $offset): mixed
## Cross-references
- [User reference for `ptr_offset()`](../../../php/builtins/pointer/ptr_offset.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_read16.md b/docs/internals/builtins/pointer/ptr_read16.md
index 982fc21296..e3d14d3f76 100644
--- a/docs/internals/builtins/pointer/ptr_read16.md
+++ b/docs/internals/builtins/pointer/ptr_read16.md
@@ -2,15 +2,15 @@
title: "ptr_read16() — internals"
description: "Compiler internals for ptr_read16(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 276
+ order: 291
---
## `ptr_read16()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:124](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L124) (`lower_ptr_read16`)
+- **Signature**: [`src/builtins/pointers/ptr_read16.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_read16.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:124](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L124) (`lower_ptr_read16`)
- **Function symbol**: `lower_ptr_read16()`
@@ -36,4 +36,3 @@ function ptr_read16(pointer $pointer): int
## Cross-references
- [User reference for `ptr_read16()`](../../../php/builtins/pointer/ptr_read16.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_read32.md b/docs/internals/builtins/pointer/ptr_read32.md
index bd376ef247..1a815882ee 100644
--- a/docs/internals/builtins/pointer/ptr_read32.md
+++ b/docs/internals/builtins/pointer/ptr_read32.md
@@ -2,15 +2,15 @@
title: "ptr_read32() — internals"
description: "Compiler internals for ptr_read32(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 277
+ order: 292
---
## `ptr_read32()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:129](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L129) (`lower_ptr_read32`)
+- **Signature**: [`src/builtins/pointers/ptr_read32.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_read32.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:129](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L129) (`lower_ptr_read32`)
- **Function symbol**: `lower_ptr_read32()`
@@ -36,4 +36,3 @@ function ptr_read32(pointer $pointer): int
## Cross-references
- [User reference for `ptr_read32()`](../../../php/builtins/pointer/ptr_read32.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_read8.md b/docs/internals/builtins/pointer/ptr_read8.md
index 3d4acf7caa..5770540c95 100644
--- a/docs/internals/builtins/pointer/ptr_read8.md
+++ b/docs/internals/builtins/pointer/ptr_read8.md
@@ -2,15 +2,15 @@
title: "ptr_read8() — internals"
description: "Compiler internals for ptr_read8(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 278
+ order: 293
---
## `ptr_read8()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:119](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L119) (`lower_ptr_read8`)
+- **Signature**: [`src/builtins/pointers/ptr_read8.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_read8.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:119](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L119) (`lower_ptr_read8`)
- **Function symbol**: `lower_ptr_read8()`
@@ -35,4 +35,3 @@ function ptr_read8(pointer $pointer): int
## Cross-references
- [User reference for `ptr_read8()`](../../../php/builtins/pointer/ptr_read8.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_read_string.md b/docs/internals/builtins/pointer/ptr_read_string.md
index 50e8b0cd90..d7e8257cd8 100644
--- a/docs/internals/builtins/pointer/ptr_read_string.md
+++ b/docs/internals/builtins/pointer/ptr_read_string.md
@@ -2,15 +2,15 @@
title: "ptr_read_string() — internals"
description: "Compiler internals for ptr_read_string(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 279
+ order: 294
---
## `ptr_read_string()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:134](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L134) (`lower_ptr_read_string`)
+- **Signature**: [`src/builtins/pointers/ptr_read_string.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_read_string.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:134](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L134) (`lower_ptr_read_string`)
- **Function symbol**: `lower_ptr_read_string()`
@@ -36,4 +36,3 @@ function ptr_read_string(pointer $pointer, int $length): string
## Cross-references
- [User reference for `ptr_read_string()`](../../../php/builtins/pointer/ptr_read_string.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_set.md b/docs/internals/builtins/pointer/ptr_set.md
index 9e2acbfdf7..3b86f5c22c 100644
--- a/docs/internals/builtins/pointer/ptr_set.md
+++ b/docs/internals/builtins/pointer/ptr_set.md
@@ -2,15 +2,15 @@
title: "ptr_set() — internals"
description: "Compiler internals for ptr_set(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 280
+ order: 295
---
## `ptr_set()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:114](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L114) (`lower_ptr_set`)
+- **Signature**: [`src/builtins/pointers/ptr_set.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_set.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:114](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L114) (`lower_ptr_set`)
- **Function symbol**: `lower_ptr_set()`
@@ -35,4 +35,3 @@ function ptr_set(pointer $pointer, mixed $value): void
## Cross-references
- [User reference for `ptr_set()`](../../../php/builtins/pointer/ptr_set.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_sizeof.md b/docs/internals/builtins/pointer/ptr_sizeof.md
index 91a149dcbd..cf5fb0e3cf 100644
--- a/docs/internals/builtins/pointer/ptr_sizeof.md
+++ b/docs/internals/builtins/pointer/ptr_sizeof.md
@@ -2,15 +2,15 @@
title: "ptr_sizeof() — internals"
description: "Compiler internals for ptr_sizeof(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 281
+ order: 296
---
## `ptr_sizeof()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L75) (`lower_ptr_sizeof`)
+- **Signature**: [`src/builtins/pointers/ptr_sizeof.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_sizeof.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:75](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L75) (`lower_ptr_sizeof`)
- **Function symbol**: `lower_ptr_sizeof()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function ptr_sizeof(string $type): mixed
+function ptr_sizeof(string $type): int
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function ptr_sizeof(string $type): mixed
## Cross-references
- [User reference for `ptr_sizeof()`](../../../php/builtins/pointer/ptr_sizeof.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_write16.md b/docs/internals/builtins/pointer/ptr_write16.md
index 32ebfd1f48..f7a50dd294 100644
--- a/docs/internals/builtins/pointer/ptr_write16.md
+++ b/docs/internals/builtins/pointer/ptr_write16.md
@@ -2,15 +2,15 @@
title: "ptr_write16() — internals"
description: "Compiler internals for ptr_write16(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 282
+ order: 297
---
## `ptr_write16()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:161](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L161) (`lower_ptr_write16`)
+- **Signature**: [`src/builtins/pointers/ptr_write16.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_write16.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:161](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L161) (`lower_ptr_write16`)
- **Function symbol**: `lower_ptr_write16()`
@@ -36,4 +36,3 @@ function ptr_write16(pointer $pointer, int $value): void
## Cross-references
- [User reference for `ptr_write16()`](../../../php/builtins/pointer/ptr_write16.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_write32.md b/docs/internals/builtins/pointer/ptr_write32.md
index 8b6845a590..bdb4202848 100644
--- a/docs/internals/builtins/pointer/ptr_write32.md
+++ b/docs/internals/builtins/pointer/ptr_write32.md
@@ -2,15 +2,15 @@
title: "ptr_write32() — internals"
description: "Compiler internals for ptr_write32(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 283
+ order: 298
---
## `ptr_write32()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:166](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L166) (`lower_ptr_write32`)
+- **Signature**: [`src/builtins/pointers/ptr_write32.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_write32.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:166](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L166) (`lower_ptr_write32`)
- **Function symbol**: `lower_ptr_write32()`
@@ -36,4 +36,3 @@ function ptr_write32(pointer $pointer, int $value): void
## Cross-references
- [User reference for `ptr_write32()`](../../../php/builtins/pointer/ptr_write32.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_write8.md b/docs/internals/builtins/pointer/ptr_write8.md
index 8946480827..8b2c9267a4 100644
--- a/docs/internals/builtins/pointer/ptr_write8.md
+++ b/docs/internals/builtins/pointer/ptr_write8.md
@@ -2,15 +2,15 @@
title: "ptr_write8() — internals"
description: "Compiler internals for ptr_write8(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 284
+ order: 299
---
## `ptr_write8()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:156](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L156) (`lower_ptr_write8`)
+- **Signature**: [`src/builtins/pointers/ptr_write8.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_write8.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:156](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L156) (`lower_ptr_write8`)
- **Function symbol**: `lower_ptr_write8()`
@@ -35,4 +35,3 @@ function ptr_write8(pointer $pointer, int $value): void
## Cross-references
- [User reference for `ptr_write8()`](../../../php/builtins/pointer/ptr_write8.md)
-
diff --git a/docs/internals/builtins/pointer/ptr_write_string.md b/docs/internals/builtins/pointer/ptr_write_string.md
index fced27d4cd..4d719ec3d6 100644
--- a/docs/internals/builtins/pointer/ptr_write_string.md
+++ b/docs/internals/builtins/pointer/ptr_write_string.md
@@ -2,15 +2,15 @@
title: "ptr_write_string() — internals"
description: "Compiler internals for ptr_write_string(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 285
+ order: 300
---
## `ptr_write_string()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/pointers.rs`:171](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/pointers.rs#L171) (`lower_ptr_write_string`)
+- **Signature**: [`src/builtins/pointers/ptr_write_string.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/pointers/ptr_write_string.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/pointers.rs`:171](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/pointers.rs#L171) (`lower_ptr_write_string`)
- **Function symbol**: `lower_ptr_write_string()`
@@ -36,4 +36,3 @@ function ptr_write_string(pointer $pointer, string $string): int
## Cross-references
- [User reference for `ptr_write_string()`](../../../php/builtins/pointer/ptr_write_string.md)
-
diff --git a/docs/internals/builtins/process/die.md b/docs/internals/builtins/process/die.md
index 2dc4acbc48..a1c0c531ee 100644
--- a/docs/internals/builtins/process/die.md
+++ b/docs/internals/builtins/process/die.md
@@ -2,7 +2,7 @@
title: "die() — internals"
description: "Compiler internals for die(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 286
+ order: 301
---
## `die()` — internals
@@ -31,4 +31,3 @@ function die(int $status): void
## Cross-references
- [User reference for `die()`](../../../php/builtins/process/die.md)
-
diff --git a/docs/internals/builtins/process/exec.md b/docs/internals/builtins/process/exec.md
index 0d39321c3c..44432db163 100644
--- a/docs/internals/builtins/process/exec.md
+++ b/docs/internals/builtins/process/exec.md
@@ -2,15 +2,15 @@
title: "exec() — internals"
description: "Compiler internals for exec(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 287
+ order: 302
---
## `exec()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:690](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L690) (`lower_exec`)
+- **Signature**: [`src/builtins/system/exec.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/exec.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:690](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L690) (`lower_exec`)
- **Function symbol**: `lower_exec()`
@@ -25,15 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function exec(string $command, array $output, int $result_code): string
+function exec(string $command): string
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
-- **By-reference parameters**: `$output`, `$result_code`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `exec()`](../../../php/builtins/process/exec.md)
-
diff --git a/docs/internals/builtins/process/exit.md b/docs/internals/builtins/process/exit.md
index eea2adb3b2..a768b10347 100644
--- a/docs/internals/builtins/process/exit.md
+++ b/docs/internals/builtins/process/exit.md
@@ -2,7 +2,7 @@
title: "exit() — internals"
description: "Compiler internals for exit(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 288
+ order: 303
---
## `exit()` — internals
@@ -31,4 +31,3 @@ function exit(int $status): void
## Cross-references
- [User reference for `exit()`](../../../php/builtins/process/exit.md)
-
diff --git a/docs/internals/builtins/process/passthru.md b/docs/internals/builtins/process/passthru.md
index 43789b8549..06276d88f6 100644
--- a/docs/internals/builtins/process/passthru.md
+++ b/docs/internals/builtins/process/passthru.md
@@ -2,15 +2,15 @@
title: "passthru() — internals"
description: "Compiler internals for passthru(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 289
+ order: 304
---
## `passthru()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:714](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L714) (`lower_passthru`)
+- **Signature**: [`src/builtins/system/passthru.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/passthru.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:714](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L714) (`lower_passthru`)
- **Function symbol**: `lower_passthru()`
@@ -27,15 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function passthru(string $command, int $result_code): void
+function passthru(string $command): void
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
-- **By-reference parameters**: `$result_code`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `passthru()`](../../../php/builtins/process/passthru.md)
-
diff --git a/docs/internals/builtins/process/pclose.md b/docs/internals/builtins/process/pclose.md
index f8a495d3b5..a48b4f5cf9 100644
--- a/docs/internals/builtins/process/pclose.md
+++ b/docs/internals/builtins/process/pclose.md
@@ -2,15 +2,15 @@
title: "pclose() — internals"
description: "Compiler internals for pclose(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 290
+ order: 305
---
## `pclose()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3407](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3407) (`lower_pclose`)
+- **Signature**: [`src/builtins/io/pclose.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/pclose.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3630](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3630) (`lower_pclose`)
- **Function symbol**: `lower_pclose()`
@@ -36,4 +36,3 @@ function pclose(resource $handle): int
## Cross-references
- [User reference for `pclose()`](../../../php/builtins/process/pclose.md)
-
diff --git a/docs/internals/builtins/process/popen.md b/docs/internals/builtins/process/popen.md
index 0ffa3ab135..f7411ab5b0 100644
--- a/docs/internals/builtins/process/popen.md
+++ b/docs/internals/builtins/process/popen.md
@@ -2,15 +2,15 @@
title: "popen() — internals"
description: "Compiler internals for popen(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 291
+ order: 306
---
## `popen()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:3379](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L3379) (`lower_popen`)
+- **Signature**: [`src/builtins/io/popen.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/popen.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3602](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3602) (`lower_popen`)
- **Function symbol**: `lower_popen()`
@@ -36,4 +36,3 @@ function popen(string $command, string $mode): mixed
## Cross-references
- [User reference for `popen()`](../../../php/builtins/process/popen.md)
-
diff --git a/docs/internals/builtins/process/readline.md b/docs/internals/builtins/process/readline.md
index 7e35fbc3fe..1fcff99246 100644
--- a/docs/internals/builtins/process/readline.md
+++ b/docs/internals/builtins/process/readline.md
@@ -2,15 +2,15 @@
title: "readline() — internals"
description: "Compiler internals for readline(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 292
+ order: 307
---
## `readline()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/io.rs`:203](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/io.rs#L203) (`lower_readline`)
+- **Signature**: [`src/builtins/io/readline.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/readline.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:310](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L310) (`lower_readline`)
- **Function symbol**: `lower_readline()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function readline(string $prompt): mixed
+function readline(string $prompt = null): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function readline(string $prompt): mixed
## Cross-references
- [User reference for `readline()`](../../../php/builtins/process/readline.md)
-
diff --git a/docs/internals/builtins/process/shell_exec.md b/docs/internals/builtins/process/shell_exec.md
index fdcd90c079..a93a1590a2 100644
--- a/docs/internals/builtins/process/shell_exec.md
+++ b/docs/internals/builtins/process/shell_exec.md
@@ -2,15 +2,15 @@
title: "shell_exec() — internals"
description: "Compiler internals for shell_exec(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 293
+ order: 308
---
## `shell_exec()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:698](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L698) (`lower_shell_exec`)
+- **Signature**: [`src/builtins/system/shell_exec.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/shell_exec.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:698](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L698) (`lower_shell_exec`)
- **Function symbol**: `lower_shell_exec()`
@@ -35,4 +35,3 @@ function shell_exec(string $command): string
## Cross-references
- [User reference for `shell_exec()`](../../../php/builtins/process/shell_exec.md)
-
diff --git a/docs/internals/builtins/process/sleep.md b/docs/internals/builtins/process/sleep.md
index eeffa0102c..47d9f295b1 100644
--- a/docs/internals/builtins/process/sleep.md
+++ b/docs/internals/builtins/process/sleep.md
@@ -2,15 +2,15 @@
title: "sleep() — internals"
description: "Compiler internals for sleep(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 294
+ order: 309
---
## `sleep()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:473](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L473) (`lower_sleep`)
+- **Signature**: [`src/builtins/system/sleep.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/sleep.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:473](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L473) (`lower_sleep`)
- **Function symbol**: `lower_sleep()`
@@ -36,4 +36,3 @@ function sleep(int $seconds): int
## Cross-references
- [User reference for `sleep()`](../../../php/builtins/process/sleep.md)
-
diff --git a/docs/internals/builtins/process/system.md b/docs/internals/builtins/process/system.md
index 29f3917f0e..11f10b11b8 100644
--- a/docs/internals/builtins/process/system.md
+++ b/docs/internals/builtins/process/system.md
@@ -2,21 +2,21 @@
title: "system() — internals"
description: "Compiler internals for system(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 295
+ order: 310
---
## `system()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:706](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L706) (`lower_system`)
+- **Signature**: [`src/builtins/system/system.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/system.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:706](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L706) (`lower_system`)
- **Function symbol**: `lower_system()`
### Lowering notes
-- Lowers `system(command)` through libc `system()` and returns the legacy empty string result.
+- Lowers `system(command)` through libc `system()` and returns the compiler's empty string result.
## Runtime helpers
@@ -26,15 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function system(string $command, int $result_code): string
+function system(string $command): string
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
-- **By-reference parameters**: `$result_code`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `system()`](../../../php/builtins/process/system.md)
-
diff --git a/docs/internals/builtins/process/usleep.md b/docs/internals/builtins/process/usleep.md
index 3368c4957c..062ece31b8 100644
--- a/docs/internals/builtins/process/usleep.md
+++ b/docs/internals/builtins/process/usleep.md
@@ -2,15 +2,15 @@
title: "usleep() — internals"
description: "Compiler internals for usleep(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 296
+ order: 311
---
## `usleep()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/system.rs`:625](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/system.rs#L625) (`lower_usleep`)
+- **Signature**: [`src/builtins/system/usleep.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/usleep.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/system.rs`:625](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/system.rs#L625) (`lower_usleep`)
- **Function symbol**: `lower_usleep()`
@@ -36,4 +36,3 @@ function usleep(int $microseconds): void
## Cross-references
- [User reference for `usleep()`](../../../php/builtins/process/usleep.md)
-
diff --git a/docs/internals/builtins/regex/preg_match.md b/docs/internals/builtins/regex/preg_match.md
index 41368a5da4..3ddfb2d52f 100644
--- a/docs/internals/builtins/regex/preg_match.md
+++ b/docs/internals/builtins/regex/preg_match.md
@@ -2,15 +2,15 @@
title: "preg_match() — internals"
description: "Compiler internals for preg_match(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 297
+ order: 312
---
## `preg_match()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/regex.rs`:28](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/regex.rs#L28) (`lower_preg_match`)
+- **Signature**: [`src/builtins/system/preg_match.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/preg_match.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/regex.rs`:28](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/regex.rs#L28) (`lower_preg_match`)
- **Function symbol**: `lower_preg_match()`
@@ -22,12 +22,13 @@ sidebar:
The following runtime helpers are referenced:
- `__rt_preg_match`
+- `__rt_preg_match_all`
- `__rt_preg_match_capture`
## Signature summary
```php
-function preg_match(string $pattern, string $subject, array $matches): int
+function preg_match(string $pattern, string $subject, array $matches = []): int
```
## What the type checker enforces
@@ -38,4 +39,3 @@ function preg_match(string $pattern, string $subject, array $matches): int
## Cross-references
- [User reference for `preg_match()`](../../../php/builtins/regex/preg_match.md)
-
diff --git a/docs/internals/builtins/regex/preg_match_all.md b/docs/internals/builtins/regex/preg_match_all.md
index 6817788715..95d701c2b3 100644
--- a/docs/internals/builtins/regex/preg_match_all.md
+++ b/docs/internals/builtins/regex/preg_match_all.md
@@ -2,15 +2,15 @@
title: "preg_match_all() — internals"
description: "Compiler internals for preg_match_all(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 298
+ order: 313
---
## `preg_match_all()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/regex.rs`:52](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/regex.rs#L52) (`lower_preg_match_all`)
+- **Signature**: [`src/builtins/system/preg_match_all.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/preg_match_all.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/regex.rs`:49](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/regex.rs#L49) (`lower_preg_match_all`)
- **Function symbol**: `lower_preg_match_all()`
@@ -22,19 +22,18 @@ sidebar:
The following runtime helpers are referenced:
- `__rt_preg_match_all`
+- `__rt_preg_replace`
## Signature summary
```php
-function preg_match_all(string $pattern, string $subject, array $matches): int
+function preg_match_all(string $pattern, string $subject): int
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
-- **By-reference parameters**: `$matches`.
+- **Arity**: takes exactly 2 arguments.
## Cross-references
- [User reference for `preg_match_all()`](../../../php/builtins/regex/preg_match_all.md)
-
diff --git a/docs/internals/builtins/regex/preg_replace.md b/docs/internals/builtins/regex/preg_replace.md
index 205f9d2a82..7b2b8b2a04 100644
--- a/docs/internals/builtins/regex/preg_replace.md
+++ b/docs/internals/builtins/regex/preg_replace.md
@@ -2,15 +2,15 @@
title: "preg_replace() — internals"
description: "Compiler internals for preg_replace(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 299
+ order: 314
---
## `preg_replace()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/regex.rs`:65](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/regex.rs#L65) (`lower_preg_replace`)
+- **Signature**: [`src/builtins/system/preg_replace.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/preg_replace.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/regex.rs`:62](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/regex.rs#L62) (`lower_preg_replace`)
- **Function symbol**: `lower_preg_replace()`
@@ -26,15 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function preg_replace(string $pattern, string $replacement, string $subject, int $limit = -1, int $count = null): string
+function preg_replace(string $pattern, string $replacement, string $subject): string
```
## What the type checker enforces
-- **Arity**: takes 3–5 arguments (2 optional).
-- **By-reference parameters**: `$count`.
+- **Arity**: takes exactly 3 arguments.
## Cross-references
- [User reference for `preg_replace()`](../../../php/builtins/regex/preg_replace.md)
-
diff --git a/docs/internals/builtins/regex/preg_replace_callback.md b/docs/internals/builtins/regex/preg_replace_callback.md
index d9bf700beb..32af71799a 100644
--- a/docs/internals/builtins/regex/preg_replace_callback.md
+++ b/docs/internals/builtins/regex/preg_replace_callback.md
@@ -2,15 +2,15 @@
title: "preg_replace_callback() — internals"
description: "Compiler internals for preg_replace_callback(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 300
+ order: 315
---
## `preg_replace_callback()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/regex.rs`:90](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/regex.rs#L90) (`lower_preg_replace_callback`)
+- **Signature**: [`src/builtins/callables/preg_replace_callback.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/callables/preg_replace_callback.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/regex.rs`:84](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/regex.rs#L84) (`lower_preg_replace_callback`)
- **Function symbol**: `lower_preg_replace_callback()`
@@ -26,15 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function preg_replace_callback(string $pattern, callable $callback, string $subject, int $limit = -1, int $count = null, int $flags = 0): array
+function preg_replace_callback(string $pattern, callable $callback, string $subject): string
```
## What the type checker enforces
-- **Arity**: takes 3–6 arguments (3 optional).
-- **By-reference parameters**: `$count`.
+- **Arity**: takes exactly 3 arguments.
## Cross-references
- [User reference for `preg_replace_callback()`](../../../php/builtins/regex/preg_replace_callback.md)
-
diff --git a/docs/internals/builtins/regex/preg_split.md b/docs/internals/builtins/regex/preg_split.md
index c5cdbfc66b..d938228255 100644
--- a/docs/internals/builtins/regex/preg_split.md
+++ b/docs/internals/builtins/regex/preg_split.md
@@ -2,15 +2,15 @@
title: "preg_split() — internals"
description: "Compiler internals for preg_split(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 301
+ order: 316
---
## `preg_split()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/regex.rs`:374](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/regex.rs#L374) (`lower_preg_split`)
+- **Signature**: [`src/builtins/system/preg_split.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/system/preg_split.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/regex.rs`:388](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/regex.rs#L388) (`lower_preg_split`)
- **Function symbol**: `lower_preg_split()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function preg_split(string $pattern, string $subject, int $limit, int $flags): array
+function preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function preg_split(string $pattern, string $subject, int $limit, int $flags): a
## Cross-references
- [User reference for `preg_split()`](../../../php/builtins/regex/preg_split.md)
-
diff --git a/docs/internals/builtins/spl/iterator_apply.md b/docs/internals/builtins/spl/iterator_apply.md
index 9fb6274624..af330644c9 100644
--- a/docs/internals/builtins/spl/iterator_apply.md
+++ b/docs/internals/builtins/spl/iterator_apply.md
@@ -2,15 +2,15 @@
title: "iterator_apply() — internals"
description: "Compiler internals for iterator_apply(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 302
+ order: 317
---
## `iterator_apply()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:289](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L289) (`lower_iterator_apply`)
+- **Signature**: [`src/builtins/spl/iterator_apply.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/iterator_apply.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:290](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L290) (`lower_iterator_apply`)
- **Function symbol**: `lower_iterator_apply()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function iterator_apply(traversable $iterator, callable $callback, array $args): int
+function iterator_apply(traversable $iterator, callable $callback, array $args = null): int
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function iterator_apply(traversable $iterator, callable $callback, array $args):
## Cross-references
- [User reference for `iterator_apply()`](../../../php/builtins/spl/iterator_apply.md)
-
diff --git a/docs/internals/builtins/spl/iterator_count.md b/docs/internals/builtins/spl/iterator_count.md
index 048229bea5..3369e6d572 100644
--- a/docs/internals/builtins/spl/iterator_count.md
+++ b/docs/internals/builtins/spl/iterator_count.md
@@ -2,15 +2,15 @@
title: "iterator_count() — internals"
description: "Compiler internals for iterator_count(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 303
+ order: 318
---
## `iterator_count()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:236](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L236) (`lower_iterator_count`)
+- **Signature**: [`src/builtins/spl/iterator_count.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/iterator_count.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:237](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L237) (`lower_iterator_count`)
- **Function symbol**: `lower_iterator_count()`
@@ -35,4 +35,3 @@ function iterator_count(traversable $iterator): int
## Cross-references
- [User reference for `iterator_count()`](../../../php/builtins/spl/iterator_count.md)
-
diff --git a/docs/internals/builtins/spl/iterator_to_array.md b/docs/internals/builtins/spl/iterator_to_array.md
index 5bce3fea3b..36fb10db33 100644
--- a/docs/internals/builtins/spl/iterator_to_array.md
+++ b/docs/internals/builtins/spl/iterator_to_array.md
@@ -2,15 +2,15 @@
title: "iterator_to_array() — internals"
description: "Compiler internals for iterator_to_array(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 304
+ order: 319
---
## `iterator_to_array()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:265](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L265) (`lower_iterator_to_array`)
+- **Signature**: [`src/builtins/spl/iterator_to_array.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/iterator_to_array.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:266](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L266) (`lower_iterator_to_array`)
- **Function symbol**: `lower_iterator_to_array()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function iterator_to_array(traversable $iterator, bool $preserve_keys): array
+function iterator_to_array(traversable $iterator, bool $preserve_keys = true): array
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function iterator_to_array(traversable $iterator, bool $preserve_keys): array
## Cross-references
- [User reference for `iterator_to_array()`](../../../php/builtins/spl/iterator_to_array.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload.md b/docs/internals/builtins/spl/spl_autoload.md
index d4f2f00d6b..12b8cfb504 100644
--- a/docs/internals/builtins/spl/spl_autoload.md
+++ b/docs/internals/builtins/spl/spl_autoload.md
@@ -2,15 +2,15 @@
title: "spl_autoload() — internals"
description: "Compiler internals for spl_autoload(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 305
+ order: 320
---
## `spl_autoload()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:150](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L150) (`lower_spl_autoload_void`)
+- **Signature**: [`src/builtins/spl/spl_autoload.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L151) (`lower_spl_autoload_void`)
- **Function symbol**: `lower_spl_autoload_void()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function spl_autoload(string $class, string $file_extensions): void
+function spl_autoload(string $class, string $file_extensions = null): void
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function spl_autoload(string $class, string $file_extensions): void
## Cross-references
- [User reference for `spl_autoload()`](../../../php/builtins/spl/spl_autoload.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload_call.md b/docs/internals/builtins/spl/spl_autoload_call.md
index cf316c669f..2ef338a9b1 100644
--- a/docs/internals/builtins/spl/spl_autoload_call.md
+++ b/docs/internals/builtins/spl/spl_autoload_call.md
@@ -2,15 +2,15 @@
title: "spl_autoload_call() — internals"
description: "Compiler internals for spl_autoload_call(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 306
+ order: 321
---
## `spl_autoload_call()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:150](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L150) (`lower_spl_autoload_void`)
+- **Signature**: [`src/builtins/spl/spl_autoload_call.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload_call.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L151) (`lower_spl_autoload_void`)
- **Function symbol**: `lower_spl_autoload_void()`
@@ -35,4 +35,3 @@ function spl_autoload_call(string $class): void
## Cross-references
- [User reference for `spl_autoload_call()`](../../../php/builtins/spl/spl_autoload_call.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload_extensions.md b/docs/internals/builtins/spl/spl_autoload_extensions.md
index aadca45247..0543d518ab 100644
--- a/docs/internals/builtins/spl/spl_autoload_extensions.md
+++ b/docs/internals/builtins/spl/spl_autoload_extensions.md
@@ -2,21 +2,21 @@
title: "spl_autoload_extensions() — internals"
description: "Compiler internals for spl_autoload_extensions(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 307
+ order: 322
---
## `spl_autoload_extensions()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:177](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L177) (`lower_spl_autoload_extensions`)
+- **Signature**: [`src/builtins/spl/spl_autoload_extensions.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload_extensions.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:178](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L178) (`lower_spl_autoload_extensions`)
- **Function symbol**: `lower_spl_autoload_extensions()`
### Lowering notes
-- Lowers `spl_autoload_extensions()` against the legacy mutable extension globals.
+- Lowers `spl_autoload_extensions()` against the mutable extension globals.
## Runtime helpers
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function spl_autoload_extensions(string $file_extensions): string
+function spl_autoload_extensions(string $file_extensions = null): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function spl_autoload_extensions(string $file_extensions): string
## Cross-references
- [User reference for `spl_autoload_extensions()`](../../../php/builtins/spl/spl_autoload_extensions.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload_functions.md b/docs/internals/builtins/spl/spl_autoload_functions.md
index 182d22825f..7292c1ec14 100644
--- a/docs/internals/builtins/spl/spl_autoload_functions.md
+++ b/docs/internals/builtins/spl/spl_autoload_functions.md
@@ -2,15 +2,15 @@
title: "spl_autoload_functions() — internals"
description: "Compiler internals for spl_autoload_functions(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 308
+ order: 323
---
## `spl_autoload_functions()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:166](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L166) (`lower_spl_autoload_functions`)
+- **Signature**: [`src/builtins/spl/spl_autoload_functions.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload_functions.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:167](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L167) (`lower_spl_autoload_functions`)
- **Function symbol**: `lower_spl_autoload_functions()`
@@ -35,4 +35,3 @@ function spl_autoload_functions(): array
## Cross-references
- [User reference for `spl_autoload_functions()`](../../../php/builtins/spl/spl_autoload_functions.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload_register.md b/docs/internals/builtins/spl/spl_autoload_register.md
index f8af69eb06..82819be366 100644
--- a/docs/internals/builtins/spl/spl_autoload_register.md
+++ b/docs/internals/builtins/spl/spl_autoload_register.md
@@ -2,15 +2,15 @@
title: "spl_autoload_register() — internals"
description: "Compiler internals for spl_autoload_register(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 309
+ order: 324
---
## `spl_autoload_register()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:134](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L134) (`lower_spl_autoload_bool`)
+- **Signature**: [`src/builtins/spl/spl_autoload_register.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload_register.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:135](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L135) (`lower_spl_autoload_bool`)
- **Function symbol**: `lower_spl_autoload_bool()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function spl_autoload_register(callable $callback, bool $throw, bool $prepend): bool
+function spl_autoload_register(callable $callback = null, bool $throw = true, bool $prepend = false): bool
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function spl_autoload_register(callable $callback, bool $throw, bool $prepend):
## Cross-references
- [User reference for `spl_autoload_register()`](../../../php/builtins/spl/spl_autoload_register.md)
-
diff --git a/docs/internals/builtins/spl/spl_autoload_unregister.md b/docs/internals/builtins/spl/spl_autoload_unregister.md
index 1aa51ff1f4..1acd8c791b 100644
--- a/docs/internals/builtins/spl/spl_autoload_unregister.md
+++ b/docs/internals/builtins/spl/spl_autoload_unregister.md
@@ -2,15 +2,15 @@
title: "spl_autoload_unregister() — internals"
description: "Compiler internals for spl_autoload_unregister(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 310
+ order: 325
---
## `spl_autoload_unregister()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:134](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L134) (`lower_spl_autoload_bool`)
+- **Signature**: [`src/builtins/spl/spl_autoload_unregister.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_autoload_unregister.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:135](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L135) (`lower_spl_autoload_bool`)
- **Function symbol**: `lower_spl_autoload_bool()`
@@ -35,4 +35,3 @@ function spl_autoload_unregister(callable $callback): bool
## Cross-references
- [User reference for `spl_autoload_unregister()`](../../../php/builtins/spl/spl_autoload_unregister.md)
-
diff --git a/docs/internals/builtins/spl/spl_classes.md b/docs/internals/builtins/spl/spl_classes.md
index 47424960ea..0cecb8c6c0 100644
--- a/docs/internals/builtins/spl/spl_classes.md
+++ b/docs/internals/builtins/spl/spl_classes.md
@@ -2,15 +2,15 @@
title: "spl_classes() — internals"
description: "Compiler internals for spl_classes(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 311
+ order: 326
---
## `spl_classes()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:205](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L205) (`lower_spl_classes`)
+- **Signature**: [`src/builtins/spl/spl_classes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_classes.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:206](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L206) (`lower_spl_classes`)
- **Function symbol**: `lower_spl_classes()`
@@ -36,4 +36,3 @@ function spl_classes(): array
## Cross-references
- [User reference for `spl_classes()`](../../../php/builtins/spl/spl_classes.md)
-
diff --git a/docs/internals/builtins/spl/spl_object_hash.md b/docs/internals/builtins/spl/spl_object_hash.md
index 6996ea3891..352d981b05 100644
--- a/docs/internals/builtins/spl/spl_object_hash.md
+++ b/docs/internals/builtins/spl/spl_object_hash.md
@@ -2,15 +2,15 @@
title: "spl_object_hash() — internals"
description: "Compiler internals for spl_object_hash(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 312
+ order: 327
---
## `spl_object_hash()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:225](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L225) (`lower_spl_object_hash`)
+- **Signature**: [`src/builtins/spl/spl_object_hash.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_object_hash.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:226](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L226) (`lower_spl_object_hash`)
- **Function symbol**: `lower_spl_object_hash()`
@@ -36,4 +36,3 @@ function spl_object_hash(object $object): string
## Cross-references
- [User reference for `spl_object_hash()`](../../../php/builtins/spl/spl_object_hash.md)
-
diff --git a/docs/internals/builtins/spl/spl_object_id.md b/docs/internals/builtins/spl/spl_object_id.md
index 1fdcb7eab0..c2aa448aaa 100644
--- a/docs/internals/builtins/spl/spl_object_id.md
+++ b/docs/internals/builtins/spl/spl_object_id.md
@@ -2,15 +2,15 @@
title: "spl_object_id() — internals"
description: "Compiler internals for spl_object_id(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 313
+ order: 328
---
## `spl_object_id()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/spl.rs`:215](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/spl.rs#L215) (`lower_spl_object_id`)
+- **Signature**: [`src/builtins/spl/spl_object_id.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/spl/spl_object_id.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/spl.rs`:216](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/spl.rs#L216) (`lower_spl_object_id`)
- **Function symbol**: `lower_spl_object_id()`
@@ -36,4 +36,3 @@ function spl_object_id(object $object): int
## Cross-references
- [User reference for `spl_object_id()`](../../../php/builtins/spl/spl_object_id.md)
-
diff --git a/docs/internals/builtins/streams/fsockopen.md b/docs/internals/builtins/streams/fsockopen.md
index 99e2cb1b22..9b8cc53bce 100644
--- a/docs/internals/builtins/streams/fsockopen.md
+++ b/docs/internals/builtins/streams/fsockopen.md
@@ -2,18 +2,22 @@
title: "fsockopen() — internals"
description: "Compiler internals for fsockopen(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 314
+ order: 329
---
## `fsockopen()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/fsockopen.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/fsockopen.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3644](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3644) (`lower_fsockopen`)
+- **Function symbol**: `lower_fsockopen()`
+### Lowering notes
+
+- Lowers `fsockopen(host, port, errno?, errstr?, timeout?)`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function fsockopen(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed
+function fsockopen(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed
```
## What the type checker enforces
@@ -32,4 +36,3 @@ function fsockopen(string $hostname, int $port, int $error_code, string $error_m
## Cross-references
- [User reference for `fsockopen()`](../../../php/builtins/streams/fsockopen.md)
-
diff --git a/docs/internals/builtins/streams/pfsockopen.md b/docs/internals/builtins/streams/pfsockopen.md
index 44c2689324..ab93c38766 100644
--- a/docs/internals/builtins/streams/pfsockopen.md
+++ b/docs/internals/builtins/streams/pfsockopen.md
@@ -2,18 +2,22 @@
title: "pfsockopen() — internals"
description: "Compiler internals for pfsockopen(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 315
+ order: 330
---
## `pfsockopen()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/pfsockopen.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/pfsockopen.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:3644](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L3644) (`lower_fsockopen`)
+- **Function symbol**: `lower_fsockopen()`
+### Lowering notes
+
+- Lowers `fsockopen(host, port, errno?, errstr?, timeout?)`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function pfsockopen(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed
+function pfsockopen(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed
```
## What the type checker enforces
@@ -32,4 +36,3 @@ function pfsockopen(string $hostname, int $port, int $error_code, string $error_
## Cross-references
- [User reference for `pfsockopen()`](../../../php/builtins/streams/pfsockopen.md)
-
diff --git a/docs/internals/builtins/streams/stream_bucket_append.md b/docs/internals/builtins/streams/stream_bucket_append.md
index 5c0a44fed4..76fd3ee3ce 100644
--- a/docs/internals/builtins/streams/stream_bucket_append.md
+++ b/docs/internals/builtins/streams/stream_bucket_append.md
@@ -2,18 +2,22 @@
title: "stream_bucket_append() — internals"
description: "Compiler internals for stream_bucket_append(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 316
+ order: 331
---
## `stream_bucket_append()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/stream_bucket_append.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_bucket_append.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2064](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2064) (`lower_stream_bucket_append_or_prepend`)
+- **Function symbol**: `lower_stream_bucket_append_or_prepend()`
+### Lowering notes
+
+- Lowers `stream_bucket_append` and `stream_bucket_prepend` over the `_buckets` array.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function stream_bucket_append(mixed $brigade, mixed $bucket): void
## Cross-references
- [User reference for `stream_bucket_append()`](../../../php/builtins/streams/stream_bucket_append.md)
-
diff --git a/docs/internals/builtins/streams/stream_bucket_prepend.md b/docs/internals/builtins/streams/stream_bucket_prepend.md
index bf6e2fa63c..cf79876f04 100644
--- a/docs/internals/builtins/streams/stream_bucket_prepend.md
+++ b/docs/internals/builtins/streams/stream_bucket_prepend.md
@@ -2,18 +2,22 @@
title: "stream_bucket_prepend() — internals"
description: "Compiler internals for stream_bucket_prepend(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 317
+ order: 332
---
## `stream_bucket_prepend()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/stream_bucket_prepend.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_bucket_prepend.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:2064](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L2064) (`lower_stream_bucket_append_or_prepend`)
+- **Function symbol**: `lower_stream_bucket_append_or_prepend()`
+### Lowering notes
+
+- Lowers `stream_bucket_append` and `stream_bucket_prepend` over the `_buckets` array.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function stream_bucket_prepend(mixed $brigade, mixed $bucket): void
## Cross-references
- [User reference for `stream_bucket_prepend()`](../../../php/builtins/streams/stream_bucket_prepend.md)
-
diff --git a/docs/internals/builtins/streams/stream_filter_append.md b/docs/internals/builtins/streams/stream_filter_append.md
index 367e9e6694..76ee9847ca 100644
--- a/docs/internals/builtins/streams/stream_filter_append.md
+++ b/docs/internals/builtins/streams/stream_filter_append.md
@@ -2,18 +2,22 @@
title: "stream_filter_append() — internals"
description: "Compiler internals for stream_filter_append(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 318
+ order: 333
---
## `stream_filter_append()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/stream_filter_append.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_filter_append.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1550](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1550) (`lower_stream_filter_attach`)
+- **Function symbol**: `lower_stream_filter_attach()`
+### Lowering notes
+
+- Lowers `stream_filter_append` and `stream_filter_prepend`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_filter_append(resource $stream, string $filter_name, int $mode, mixed $params): mixed
+function stream_filter_append(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function stream_filter_append(resource $stream, string $filter_name, int $mode,
## Cross-references
- [User reference for `stream_filter_append()`](../../../php/builtins/streams/stream_filter_append.md)
-
diff --git a/docs/internals/builtins/streams/stream_filter_prepend.md b/docs/internals/builtins/streams/stream_filter_prepend.md
index 9c023e3f5d..0eba5d1e4b 100644
--- a/docs/internals/builtins/streams/stream_filter_prepend.md
+++ b/docs/internals/builtins/streams/stream_filter_prepend.md
@@ -2,18 +2,22 @@
title: "stream_filter_prepend() — internals"
description: "Compiler internals for stream_filter_prepend(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 319
+ order: 334
---
## `stream_filter_prepend()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/io/stream_filter_prepend.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/io/stream_filter_prepend.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/io.rs`:1550](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/io.rs#L1550) (`lower_stream_filter_attach`)
+- **Function symbol**: `lower_stream_filter_attach()`
+### Lowering notes
+
+- Lowers `stream_filter_append` and `stream_filter_prepend`.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function stream_filter_prepend(resource $stream, string $filter_name, int $mode, mixed $params): mixed
+function stream_filter_prepend(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function stream_filter_prepend(resource $stream, string $filter_name, int $mode,
## Cross-references
- [User reference for `stream_filter_prepend()`](../../../php/builtins/streams/stream_filter_prepend.md)
-
diff --git a/docs/internals/builtins/string/addslashes.md b/docs/internals/builtins/string/addslashes.md
index a7ada21270..690a7b9338 100644
--- a/docs/internals/builtins/string/addslashes.md
+++ b/docs/internals/builtins/string/addslashes.md
@@ -2,15 +2,15 @@
title: "addslashes() — internals"
description: "Compiler internals for addslashes(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 320
+ order: 335
---
## `addslashes()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/addslashes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/addslashes.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function addslashes(string $string): string
## Cross-references
- [User reference for `addslashes()`](../../../php/builtins/string/addslashes.md)
-
diff --git a/docs/internals/builtins/string/base64_decode.md b/docs/internals/builtins/string/base64_decode.md
index 5f5bc3372e..231a64d493 100644
--- a/docs/internals/builtins/string/base64_decode.md
+++ b/docs/internals/builtins/string/base64_decode.md
@@ -2,15 +2,15 @@
title: "base64_decode() — internals"
description: "Compiler internals for base64_decode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 321
+ order: 336
---
## `base64_decode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/base64_decode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/base64_decode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function base64_decode(string $string, bool $strict): string
+function base64_decode(string $string): string
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `base64_decode()`](../../../php/builtins/string/base64_decode.md)
-
diff --git a/docs/internals/builtins/string/base64_encode.md b/docs/internals/builtins/string/base64_encode.md
index 0e658dd981..f4ff51e4ea 100644
--- a/docs/internals/builtins/string/base64_encode.md
+++ b/docs/internals/builtins/string/base64_encode.md
@@ -2,15 +2,15 @@
title: "base64_encode() — internals"
description: "Compiler internals for base64_encode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 322
+ order: 337
---
## `base64_encode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/base64_encode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/base64_encode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function base64_encode(string $string): string
## Cross-references
- [User reference for `base64_encode()`](../../../php/builtins/string/base64_encode.md)
-
diff --git a/docs/internals/builtins/string/bin2hex.md b/docs/internals/builtins/string/bin2hex.md
index c1ebe36b2b..46c485805e 100644
--- a/docs/internals/builtins/string/bin2hex.md
+++ b/docs/internals/builtins/string/bin2hex.md
@@ -2,15 +2,15 @@
title: "bin2hex() — internals"
description: "Compiler internals for bin2hex(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 323
+ order: 338
---
## `bin2hex()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/bin2hex.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/bin2hex.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function bin2hex(string $string): string
## Cross-references
- [User reference for `bin2hex()`](../../../php/builtins/string/bin2hex.md)
-
diff --git a/docs/internals/builtins/string/chop.md b/docs/internals/builtins/string/chop.md
index 91a762cd0a..555fc89fef 100644
--- a/docs/internals/builtins/string/chop.md
+++ b/docs/internals/builtins/string/chop.md
@@ -2,18 +2,22 @@
title: "chop() — internals"
description: "Compiler internals for chop(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 324
+ order: 339
---
## `chop()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/string/chop.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/chop.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
+- **Function symbol**: `lower_trim_like()`
+### Lowering notes
+
+- Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function chop(string $string, string $characters): string
+function chop(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function chop(string $string, string $characters): string
## Cross-references
- [User reference for `chop()`](../../../php/builtins/string/chop.md)
-
diff --git a/docs/internals/builtins/string/chr.md b/docs/internals/builtins/string/chr.md
index 886f2f619f..fd5e4b8454 100644
--- a/docs/internals/builtins/string/chr.md
+++ b/docs/internals/builtins/string/chr.md
@@ -2,15 +2,15 @@
title: "chr() — internals"
description: "Compiler internals for chr(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 325
+ order: 340
---
## `chr()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:858](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L858) (`lower_chr`)
+- **Signature**: [`src/builtins/string/chr.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/chr.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:858](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L858) (`lower_chr`)
- **Function symbol**: `lower_chr()`
@@ -36,4 +36,3 @@ function chr(int $codepoint): string
## Cross-references
- [User reference for `chr()`](../../../php/builtins/string/chr.md)
-
diff --git a/docs/internals/builtins/string/crc32.md b/docs/internals/builtins/string/crc32.md
index 21d0d2273f..86282c894d 100644
--- a/docs/internals/builtins/string/crc32.md
+++ b/docs/internals/builtins/string/crc32.md
@@ -2,15 +2,15 @@
title: "crc32() — internals"
description: "Compiler internals for crc32(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 326
+ order: 341
---
## `crc32()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:348](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L348) (`lower_crc32`)
+- **Signature**: [`src/builtins/string/crc32.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/crc32.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:348](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L348) (`lower_crc32`)
- **Function symbol**: `lower_crc32()`
@@ -39,4 +39,3 @@ function crc32(string $string): int
## Cross-references
- [User reference for `crc32()`](../../../php/builtins/string/crc32.md)
-
diff --git a/docs/internals/builtins/string/explode.md b/docs/internals/builtins/string/explode.md
index ee1e878736..eea918f14b 100644
--- a/docs/internals/builtins/string/explode.md
+++ b/docs/internals/builtins/string/explode.md
@@ -2,15 +2,15 @@
title: "explode() — internals"
description: "Compiler internals for explode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 327
+ order: 342
---
## `explode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L151) (`lower_explode`)
+- **Signature**: [`src/builtins/string/explode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/explode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:151](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L151) (`lower_explode`)
- **Function symbol**: `lower_explode()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function explode(string $separator, string $string, int $limit): array
+function explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function explode(string $separator, string $string, int $limit): array
## Cross-references
- [User reference for `explode()`](../../../php/builtins/string/explode.md)
-
diff --git a/docs/internals/builtins/string/grapheme_strrev.md b/docs/internals/builtins/string/grapheme_strrev.md
index 9fa28db932..17f141bbda 100644
--- a/docs/internals/builtins/string/grapheme_strrev.md
+++ b/docs/internals/builtins/string/grapheme_strrev.md
@@ -2,15 +2,15 @@
title: "grapheme_strrev() — internals"
description: "Compiler internals for grapheme_strrev(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 328
+ order: 343
---
## `grapheme_strrev()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:88](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L88) (`lower_grapheme_strrev`)
+- **Signature**: [`src/builtins/string/grapheme_strrev.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/grapheme_strrev.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:88](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L88) (`lower_grapheme_strrev`)
- **Function symbol**: `lower_grapheme_strrev()`
@@ -37,4 +37,3 @@ function grapheme_strrev(string $string): mixed
## Cross-references
- [User reference for `grapheme_strrev()`](../../../php/builtins/string/grapheme_strrev.md)
-
diff --git a/docs/internals/builtins/string/gzcompress.md b/docs/internals/builtins/string/gzcompress.md
index 6f3aa0fbd3..eec09f15dc 100644
--- a/docs/internals/builtins/string/gzcompress.md
+++ b/docs/internals/builtins/string/gzcompress.md
@@ -2,15 +2,15 @@
title: "gzcompress() — internals"
description: "Compiler internals for gzcompress(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 329
+ order: 344
---
## `gzcompress()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:402](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L402) (`lower_gzcompress`)
+- **Signature**: [`src/builtins/string/gzcompress.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/gzcompress.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:402](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L402) (`lower_gzcompress`)
- **Function symbol**: `lower_gzcompress()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function gzcompress(string $data, int $level, int $encoding): string
+function gzcompress(string $data, int $level = -1): string
```
## What the type checker enforces
-- **Arity**: takes 2–3 arguments (1 optional).
+- **Arity**: takes 1–2 arguments (1 optional).
## Cross-references
- [User reference for `gzcompress()`](../../../php/builtins/string/gzcompress.md)
-
diff --git a/docs/internals/builtins/string/gzdeflate.md b/docs/internals/builtins/string/gzdeflate.md
index a0fc254667..1e062282dd 100644
--- a/docs/internals/builtins/string/gzdeflate.md
+++ b/docs/internals/builtins/string/gzdeflate.md
@@ -2,15 +2,15 @@
title: "gzdeflate() — internals"
description: "Compiler internals for gzdeflate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 330
+ order: 345
---
## `gzdeflate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:418](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L418) (`lower_gzdeflate`)
+- **Signature**: [`src/builtins/string/gzdeflate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/gzdeflate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:418](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L418) (`lower_gzdeflate`)
- **Function symbol**: `lower_gzdeflate()`
@@ -25,14 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function gzdeflate(string $data, int $level, int $encoding): string
+function gzdeflate(string $data, int $level = -1): string
```
## What the type checker enforces
-- **Arity**: takes 2–3 arguments (1 optional).
+- **Arity**: takes 1–2 arguments (1 optional).
## Cross-references
- [User reference for `gzdeflate()`](../../../php/builtins/string/gzdeflate.md)
-
diff --git a/docs/internals/builtins/string/gzinflate.md b/docs/internals/builtins/string/gzinflate.md
index f543f8bad0..0a2695fdec 100644
--- a/docs/internals/builtins/string/gzinflate.md
+++ b/docs/internals/builtins/string/gzinflate.md
@@ -2,15 +2,15 @@
title: "gzinflate() — internals"
description: "Compiler internals for gzinflate(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 331
+ order: 346
---
## `gzinflate()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:436](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L436) (`lower_gzinflate`)
+- **Signature**: [`src/builtins/string/gzinflate.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/gzinflate.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:436](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L436) (`lower_gzinflate`)
- **Function symbol**: `lower_gzinflate()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function gzinflate(string $data, int $max_length): string
+function gzinflate(string $data, int $max_length = 0): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function gzinflate(string $data, int $max_length): string
## Cross-references
- [User reference for `gzinflate()`](../../../php/builtins/string/gzinflate.md)
-
diff --git a/docs/internals/builtins/string/gzuncompress.md b/docs/internals/builtins/string/gzuncompress.md
index 2b30331848..bf4f949e70 100644
--- a/docs/internals/builtins/string/gzuncompress.md
+++ b/docs/internals/builtins/string/gzuncompress.md
@@ -2,15 +2,15 @@
title: "gzuncompress() — internals"
description: "Compiler internals for gzuncompress(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 332
+ order: 347
---
## `gzuncompress()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:457](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L457) (`lower_gzuncompress`)
+- **Signature**: [`src/builtins/string/gzuncompress.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/gzuncompress.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:457](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L457) (`lower_gzuncompress`)
- **Function symbol**: `lower_gzuncompress()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function gzuncompress(string $data, int $max_length): string
+function gzuncompress(string $data, int $max_length = 0): mixed
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function gzuncompress(string $data, int $max_length): string
## Cross-references
- [User reference for `gzuncompress()`](../../../php/builtins/string/gzuncompress.md)
-
diff --git a/docs/internals/builtins/string/hash.md b/docs/internals/builtins/string/hash.md
index b9e8a60878..c2ed6097fe 100644
--- a/docs/internals/builtins/string/hash.md
+++ b/docs/internals/builtins/string/hash.md
@@ -2,15 +2,15 @@
title: "hash() — internals"
description: "Compiler internals for hash(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 333
+ order: 348
---
## `hash()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:209](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L209) (`lower_hash`)
+- **Signature**: [`src/builtins/string/hash.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:209](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L209) (`lower_hash`)
- **Function symbol**: `lower_hash()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function hash(string $algo, string $data, bool $binary = false, array $options = []): string
+function hash(string $algo, string $data, bool $binary = false): string
```
## What the type checker enforces
-- **Arity**: takes 2–4 arguments (2 optional).
+- **Arity**: takes 2–3 arguments (1 optional).
## Cross-references
- [User reference for `hash()`](../../../php/builtins/string/hash.md)
-
diff --git a/docs/internals/builtins/string/hash_algos.md b/docs/internals/builtins/string/hash_algos.md
index 581755cc25..8a612acaee 100644
--- a/docs/internals/builtins/string/hash_algos.md
+++ b/docs/internals/builtins/string/hash_algos.md
@@ -2,15 +2,15 @@
title: "hash_algos() — internals"
description: "Compiler internals for hash_algos(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 334
+ order: 349
---
## `hash_algos()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:254](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L254) (`lower_hash_algos`)
+- **Signature**: [`src/builtins/string/hash_algos.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_algos.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:254](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L254) (`lower_hash_algos`)
- **Function symbol**: `lower_hash_algos()`
@@ -37,4 +37,3 @@ function hash_algos(): array
## Cross-references
- [User reference for `hash_algos()`](../../../php/builtins/string/hash_algos.md)
-
diff --git a/docs/internals/builtins/string/hash_copy.md b/docs/internals/builtins/string/hash_copy.md
index 56f648fa37..31dcbe408c 100644
--- a/docs/internals/builtins/string/hash_copy.md
+++ b/docs/internals/builtins/string/hash_copy.md
@@ -2,15 +2,15 @@
title: "hash_copy() — internals"
description: "Compiler internals for hash_copy(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 335
+ order: 350
---
## `hash_copy()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:333](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L333) (`lower_hash_copy`)
+- **Signature**: [`src/builtins/string/hash_copy.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_copy.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:333](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L333) (`lower_hash_copy`)
- **Function symbol**: `lower_hash_copy()`
@@ -39,4 +39,3 @@ function hash_copy(resource $context): mixed
## Cross-references
- [User reference for `hash_copy()`](../../../php/builtins/string/hash_copy.md)
-
diff --git a/docs/internals/builtins/string/hash_equals.md b/docs/internals/builtins/string/hash_equals.md
index b8b682f426..090ffbb03f 100644
--- a/docs/internals/builtins/string/hash_equals.md
+++ b/docs/internals/builtins/string/hash_equals.md
@@ -2,15 +2,15 @@
title: "hash_equals() — internals"
description: "Compiler internals for hash_equals(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 336
+ order: 351
---
## `hash_equals()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:247](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L247) (`lower_hash_equals`)
+- **Signature**: [`src/builtins/string/hash_equals.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_equals.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:247](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L247) (`lower_hash_equals`)
- **Function symbol**: `lower_hash_equals()`
@@ -38,4 +38,3 @@ function hash_equals(string $known_string, string $user_string): bool
## Cross-references
- [User reference for `hash_equals()`](../../../php/builtins/string/hash_equals.md)
-
diff --git a/docs/internals/builtins/string/hash_final.md b/docs/internals/builtins/string/hash_final.md
index 6e55303c6f..b4ce2847b6 100644
--- a/docs/internals/builtins/string/hash_final.md
+++ b/docs/internals/builtins/string/hash_final.md
@@ -2,15 +2,15 @@
title: "hash_final() — internals"
description: "Compiler internals for hash_final(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 337
+ order: 352
---
## `hash_final()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:302](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L302) (`lower_hash_final`)
+- **Signature**: [`src/builtins/string/hash_final.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_final.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:302](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L302) (`lower_hash_final`)
- **Function symbol**: `lower_hash_final()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function hash_final(resource $context, bool $binary): string
+function hash_final(resource $context, bool $binary = false): string
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function hash_final(resource $context, bool $binary): string
## Cross-references
- [User reference for `hash_final()`](../../../php/builtins/string/hash_final.md)
-
diff --git a/docs/internals/builtins/string/hash_hmac.md b/docs/internals/builtins/string/hash_hmac.md
index 0889f92e4b..0b27e37b08 100644
--- a/docs/internals/builtins/string/hash_hmac.md
+++ b/docs/internals/builtins/string/hash_hmac.md
@@ -2,15 +2,15 @@
title: "hash_hmac() — internals"
description: "Compiler internals for hash_hmac(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 338
+ order: 353
---
## `hash_hmac()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:228](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L228) (`lower_hash_hmac`)
+- **Signature**: [`src/builtins/string/hash_hmac.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_hmac.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:228](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L228) (`lower_hash_hmac`)
- **Function symbol**: `lower_hash_hmac()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function hash_hmac(string $algo, string $data, string $key, bool $binary): string
+function hash_hmac(string $algo, string $data, string $key, bool $binary = false): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function hash_hmac(string $algo, string $data, string $key, bool $binary): strin
## Cross-references
- [User reference for `hash_hmac()`](../../../php/builtins/string/hash_hmac.md)
-
diff --git a/docs/internals/builtins/string/hash_init.md b/docs/internals/builtins/string/hash_init.md
index 502d3eb97b..db7ba10da8 100644
--- a/docs/internals/builtins/string/hash_init.md
+++ b/docs/internals/builtins/string/hash_init.md
@@ -2,15 +2,15 @@
title: "hash_init() — internals"
description: "Compiler internals for hash_init(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 339
+ order: 354
---
## `hash_init()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:266](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L266) (`lower_hash_init`)
+- **Signature**: [`src/builtins/string/hash_init.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_init.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:266](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L266) (`lower_hash_init`)
- **Function symbol**: `lower_hash_init()`
@@ -26,14 +26,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function hash_init(string $algo, int $flags = 0, string $key = '', array $options = []): mixed
+function hash_init(string $algo, int $flags = 0, string $key = ''): mixed
```
## What the type checker enforces
-- **Arity**: takes 1–4 arguments (3 optional).
+- **Arity**: takes 1–3 arguments (2 optional).
## Cross-references
- [User reference for `hash_init()`](../../../php/builtins/string/hash_init.md)
-
diff --git a/docs/internals/builtins/string/hash_update.md b/docs/internals/builtins/string/hash_update.md
index d690762753..979c991b56 100644
--- a/docs/internals/builtins/string/hash_update.md
+++ b/docs/internals/builtins/string/hash_update.md
@@ -2,15 +2,15 @@
title: "hash_update() — internals"
description: "Compiler internals for hash_update(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 340
+ order: 355
---
## `hash_update()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:277](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L277) (`lower_hash_update`)
+- **Signature**: [`src/builtins/string/hash_update.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hash_update.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:277](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L277) (`lower_hash_update`)
- **Function symbol**: `lower_hash_update()`
@@ -36,4 +36,3 @@ function hash_update(resource $context, string $data): bool
## Cross-references
- [User reference for `hash_update()`](../../../php/builtins/string/hash_update.md)
-
diff --git a/docs/internals/builtins/string/hex2bin.md b/docs/internals/builtins/string/hex2bin.md
index 952cf150fb..ddcd104988 100644
--- a/docs/internals/builtins/string/hex2bin.md
+++ b/docs/internals/builtins/string/hex2bin.md
@@ -2,15 +2,15 @@
title: "hex2bin() — internals"
description: "Compiler internals for hex2bin(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 341
+ order: 356
---
## `hex2bin()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/hex2bin.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/hex2bin.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function hex2bin(string $string): string
## Cross-references
- [User reference for `hex2bin()`](../../../php/builtins/string/hex2bin.md)
-
diff --git a/docs/internals/builtins/string/html_entity_decode.md b/docs/internals/builtins/string/html_entity_decode.md
index cfe59af056..89a7f64f26 100644
--- a/docs/internals/builtins/string/html_entity_decode.md
+++ b/docs/internals/builtins/string/html_entity_decode.md
@@ -2,15 +2,15 @@
title: "html_entity_decode() — internals"
description: "Compiler internals for html_entity_decode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 342
+ order: 357
---
## `html_entity_decode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/html_entity_decode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/html_entity_decode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function html_entity_decode(string $string, int $flags, string $encoding): string
+function html_entity_decode(string $string): string
```
## What the type checker enforces
-- **Arity**: takes exactly 3 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `html_entity_decode()`](../../../php/builtins/string/html_entity_decode.md)
-
diff --git a/docs/internals/builtins/string/htmlentities.md b/docs/internals/builtins/string/htmlentities.md
index 9d19ae0798..4e0cd701ff 100644
--- a/docs/internals/builtins/string/htmlentities.md
+++ b/docs/internals/builtins/string/htmlentities.md
@@ -2,15 +2,15 @@
title: "htmlentities() — internals"
description: "Compiler internals for htmlentities(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 343
+ order: 358
---
## `htmlentities()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/htmlentities.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/htmlentities.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function htmlentities(string $string, int $flags, string $encoding, bool $double_encode): string
+function htmlentities(string $string): string
```
## What the type checker enforces
-- **Arity**: takes exactly 4 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `htmlentities()`](../../../php/builtins/string/htmlentities.md)
-
diff --git a/docs/internals/builtins/string/htmlspecialchars.md b/docs/internals/builtins/string/htmlspecialchars.md
index 44f63bf6b8..b93173302b 100644
--- a/docs/internals/builtins/string/htmlspecialchars.md
+++ b/docs/internals/builtins/string/htmlspecialchars.md
@@ -2,15 +2,15 @@
title: "htmlspecialchars() — internals"
description: "Compiler internals for htmlspecialchars(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 344
+ order: 359
---
## `htmlspecialchars()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/htmlspecialchars.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/htmlspecialchars.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function htmlspecialchars(string $string, int $flags, string $encoding, bool $double_encode): string
+function htmlspecialchars(string $string): string
```
## What the type checker enforces
-- **Arity**: takes exactly 4 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `htmlspecialchars()`](../../../php/builtins/string/htmlspecialchars.md)
-
diff --git a/docs/internals/builtins/string/implode.md b/docs/internals/builtins/string/implode.md
index 1dd74500fa..07148e9201 100644
--- a/docs/internals/builtins/string/implode.md
+++ b/docs/internals/builtins/string/implode.md
@@ -2,15 +2,15 @@
title: "implode() — internals"
description: "Compiler internals for implode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 345
+ order: 360
---
## `implode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:192](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L192) (`lower_implode`)
+- **Signature**: [`src/builtins/string/implode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/implode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:192](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L192) (`lower_implode`)
- **Function symbol**: `lower_implode()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function implode(string $separator, array $array): string
+function implode(string $separator, array $array = null): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function implode(string $separator, array $array): string
## Cross-references
- [User reference for `implode()`](../../../php/builtins/string/implode.md)
-
diff --git a/docs/internals/builtins/string/inet_ntop.md b/docs/internals/builtins/string/inet_ntop.md
index f7dba19d13..06ac904870 100644
--- a/docs/internals/builtins/string/inet_ntop.md
+++ b/docs/internals/builtins/string/inet_ntop.md
@@ -2,15 +2,15 @@
title: "inet_ntop() — internals"
description: "Compiler internals for inet_ntop(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 346
+ order: 361
---
## `inet_ntop()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:497](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L497) (`lower_inet`)
+- **Signature**: [`src/builtins/string/inet_ntop.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/inet_ntop.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:497](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L497) (`lower_inet`)
- **Function symbol**: `lower_inet()`
@@ -36,4 +36,3 @@ function inet_ntop(string $ip): mixed
## Cross-references
- [User reference for `inet_ntop()`](../../../php/builtins/string/inet_ntop.md)
-
diff --git a/docs/internals/builtins/string/inet_pton.md b/docs/internals/builtins/string/inet_pton.md
index eaa52a5f8e..b234f68d13 100644
--- a/docs/internals/builtins/string/inet_pton.md
+++ b/docs/internals/builtins/string/inet_pton.md
@@ -2,15 +2,15 @@
title: "inet_pton() — internals"
description: "Compiler internals for inet_pton(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 347
+ order: 362
---
## `inet_pton()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:497](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L497) (`lower_inet`)
+- **Signature**: [`src/builtins/string/inet_pton.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/inet_pton.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:497](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L497) (`lower_inet`)
- **Function symbol**: `lower_inet()`
@@ -36,4 +36,3 @@ function inet_pton(string $ip): mixed
## Cross-references
- [User reference for `inet_pton()`](../../../php/builtins/string/inet_pton.md)
-
diff --git a/docs/internals/builtins/string/ip2long.md b/docs/internals/builtins/string/ip2long.md
index 20c8c624e6..90f6b900b9 100644
--- a/docs/internals/builtins/string/ip2long.md
+++ b/docs/internals/builtins/string/ip2long.md
@@ -2,15 +2,15 @@
title: "ip2long() — internals"
description: "Compiler internals for ip2long(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 348
+ order: 363
---
## `ip2long()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:488](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L488) (`lower_ip2long`)
+- **Signature**: [`src/builtins/string/ip2long.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ip2long.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:488](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L488) (`lower_ip2long`)
- **Function symbol**: `lower_ip2long()`
@@ -37,4 +37,3 @@ function ip2long(string $ip): mixed
## Cross-references
- [User reference for `ip2long()`](../../../php/builtins/string/ip2long.md)
-
diff --git a/docs/internals/builtins/string/lcfirst.md b/docs/internals/builtins/string/lcfirst.md
index dda4a914e0..90a115bc7d 100644
--- a/docs/internals/builtins/string/lcfirst.md
+++ b/docs/internals/builtins/string/lcfirst.md
@@ -2,15 +2,15 @@
title: "lcfirst() — internals"
description: "Compiler internals for lcfirst(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 349
+ order: 364
---
## `lcfirst()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:104](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L104) (`lower_lcfirst`)
+- **Signature**: [`src/builtins/string/lcfirst.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/lcfirst.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:104](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L104) (`lower_lcfirst`)
- **Function symbol**: `lower_lcfirst()`
@@ -36,4 +36,3 @@ function lcfirst(string $string): string
## Cross-references
- [User reference for `lcfirst()`](../../../php/builtins/string/lcfirst.md)
-
diff --git a/docs/internals/builtins/string/long2ip.md b/docs/internals/builtins/string/long2ip.md
index 69030b1718..0e104cd876 100644
--- a/docs/internals/builtins/string/long2ip.md
+++ b/docs/internals/builtins/string/long2ip.md
@@ -2,15 +2,15 @@
title: "long2ip() — internals"
description: "Compiler internals for long2ip(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 350
+ order: 365
---
## `long2ip()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:476](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L476) (`lower_long2ip`)
+- **Signature**: [`src/builtins/string/long2ip.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/long2ip.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:476](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L476) (`lower_long2ip`)
- **Function symbol**: `lower_long2ip()`
@@ -37,4 +37,3 @@ function long2ip(int $ip): string
## Cross-references
- [User reference for `long2ip()`](../../../php/builtins/string/long2ip.md)
-
diff --git a/docs/internals/builtins/string/ltrim.md b/docs/internals/builtins/string/ltrim.md
index ef7d971ac8..36b3a638e8 100644
--- a/docs/internals/builtins/string/ltrim.md
+++ b/docs/internals/builtins/string/ltrim.md
@@ -2,15 +2,15 @@
title: "ltrim() — internals"
description: "Compiler internals for ltrim(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 351
+ order: 366
---
## `ltrim()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
+- **Signature**: [`src/builtins/string/ltrim.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ltrim.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
- **Function symbol**: `lower_trim_like()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function ltrim(string $string, string $characters): string
+function ltrim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function ltrim(string $string, string $characters): string
## Cross-references
- [User reference for `ltrim()`](../../../php/builtins/string/ltrim.md)
-
diff --git a/docs/internals/builtins/string/md5.md b/docs/internals/builtins/string/md5.md
index 03d42d867c..d5e038a304 100644
--- a/docs/internals/builtins/string/md5.md
+++ b/docs/internals/builtins/string/md5.md
@@ -2,15 +2,15 @@
title: "md5() — internals"
description: "Compiler internals for md5(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 352
+ order: 367
---
## `md5()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:355](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L355) (`lower_md5`)
+- **Signature**: [`src/builtins/string/md5.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/md5.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:355](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L355) (`lower_md5`)
- **Function symbol**: `lower_md5()`
@@ -28,7 +28,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function md5(string $string, bool $binary): string
+function md5(string $string, bool $binary = false): string
```
## What the type checker enforces
@@ -38,4 +38,3 @@ function md5(string $string, bool $binary): string
## Cross-references
- [User reference for `md5()`](../../../php/builtins/string/md5.md)
-
diff --git a/docs/internals/builtins/string/nl2br.md b/docs/internals/builtins/string/nl2br.md
index 0ca2ac3b4f..297298b345 100644
--- a/docs/internals/builtins/string/nl2br.md
+++ b/docs/internals/builtins/string/nl2br.md
@@ -2,15 +2,15 @@
title: "nl2br() — internals"
description: "Compiler internals for nl2br(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 353
+ order: 368
---
## `nl2br()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/nl2br.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/nl2br.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function nl2br(string $string, bool $use_xhtml): string
+function nl2br(string $string): string
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `nl2br()`](../../../php/builtins/string/nl2br.md)
-
diff --git a/docs/internals/builtins/string/number_format.md b/docs/internals/builtins/string/number_format.md
index c0edd06463..25bda575da 100644
--- a/docs/internals/builtins/string/number_format.md
+++ b/docs/internals/builtins/string/number_format.md
@@ -2,15 +2,15 @@
title: "number_format() — internals"
description: "Compiler internals for number_format(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 354
+ order: 369
---
## `number_format()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:875](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L875) (`lower_number_format`)
+- **Signature**: [`src/builtins/string/number_format.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/number_format.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:875](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L875) (`lower_number_format`)
- **Function symbol**: `lower_number_format()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function number_format(float $num, int $decimals, string $decimal_separator, string $thousands_separator): string
+function number_format(float $num, int $decimals = 0, string $decimal_separator = '.', string $thousands_separator = ','): string
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function number_format(float $num, int $decimals, string $decimal_separator, str
## Cross-references
- [User reference for `number_format()`](../../../php/builtins/string/number_format.md)
-
diff --git a/docs/internals/builtins/string/ord.md b/docs/internals/builtins/string/ord.md
index ed53e5e680..6f748d42d5 100644
--- a/docs/internals/builtins/string/ord.md
+++ b/docs/internals/builtins/string/ord.md
@@ -2,15 +2,15 @@
title: "ord() — internals"
description: "Compiler internals for ord(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 355
+ order: 370
---
## `ord()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:834](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L834) (`lower_ord`)
+- **Signature**: [`src/builtins/string/ord.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ord.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:834](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L834) (`lower_ord`)
- **Function symbol**: `lower_ord()`
@@ -35,4 +35,3 @@ function ord(string $character): int
## Cross-references
- [User reference for `ord()`](../../../php/builtins/string/ord.md)
-
diff --git a/docs/internals/builtins/string/printf.md b/docs/internals/builtins/string/printf.md
index c51beb26be..c132d37790 100644
--- a/docs/internals/builtins/string/printf.md
+++ b/docs/internals/builtins/string/printf.md
@@ -2,15 +2,15 @@
title: "printf() — internals"
description: "Compiler internals for printf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 356
+ order: 371
---
## `printf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:517](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L517) (`lower_printf`)
+- **Signature**: [`src/builtins/string/printf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/printf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:517](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L517) (`lower_printf`)
- **Function symbol**: `lower_printf()`
@@ -37,4 +37,3 @@ function printf(string $format, ...$values): int
## Cross-references
- [User reference for `printf()`](../../../php/builtins/string/printf.md)
-
diff --git a/docs/internals/builtins/string/rawurldecode.md b/docs/internals/builtins/string/rawurldecode.md
index e479c24987..8d1743e949 100644
--- a/docs/internals/builtins/string/rawurldecode.md
+++ b/docs/internals/builtins/string/rawurldecode.md
@@ -2,15 +2,15 @@
title: "rawurldecode() — internals"
description: "Compiler internals for rawurldecode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 357
+ order: 372
---
## `rawurldecode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/rawurldecode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/rawurldecode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function rawurldecode(string $string): string
## Cross-references
- [User reference for `rawurldecode()`](../../../php/builtins/string/rawurldecode.md)
-
diff --git a/docs/internals/builtins/string/rawurlencode.md b/docs/internals/builtins/string/rawurlencode.md
index 62a8252a0f..63c5abfc9e 100644
--- a/docs/internals/builtins/string/rawurlencode.md
+++ b/docs/internals/builtins/string/rawurlencode.md
@@ -2,15 +2,15 @@
title: "rawurlencode() — internals"
description: "Compiler internals for rawurlencode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 358
+ order: 373
---
## `rawurlencode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/rawurlencode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/rawurlencode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function rawurlencode(string $string): string
## Cross-references
- [User reference for `rawurlencode()`](../../../php/builtins/string/rawurlencode.md)
-
diff --git a/docs/internals/builtins/string/rtrim.md b/docs/internals/builtins/string/rtrim.md
index eb9efd109b..bab7799890 100644
--- a/docs/internals/builtins/string/rtrim.md
+++ b/docs/internals/builtins/string/rtrim.md
@@ -2,18 +2,22 @@
title: "rtrim() — internals"
description: "Compiler internals for rtrim(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 359
+ order: 374
---
## `rtrim()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/string/rtrim.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/rtrim.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
+- **Function symbol**: `lower_trim_like()`
+### Lowering notes
+
+- Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function rtrim(string $string, string $characters): string
+function rtrim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
## What the type checker enforces
@@ -31,4 +35,3 @@ function rtrim(string $string, string $characters): string
## Cross-references
- [User reference for `rtrim()`](../../../php/builtins/string/rtrim.md)
-
diff --git a/docs/internals/builtins/string/sha1.md b/docs/internals/builtins/string/sha1.md
index 368ec729fc..7e809a1990 100644
--- a/docs/internals/builtins/string/sha1.md
+++ b/docs/internals/builtins/string/sha1.md
@@ -2,15 +2,15 @@
title: "sha1() — internals"
description: "Compiler internals for sha1(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 360
+ order: 375
---
## `sha1()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:360](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L360) (`lower_sha1`)
+- **Signature**: [`src/builtins/string/sha1.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/sha1.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:360](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L360) (`lower_sha1`)
- **Function symbol**: `lower_sha1()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function sha1(string $string, bool $binary): string
+function sha1(string $string, bool $binary = false): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function sha1(string $string, bool $binary): string
## Cross-references
- [User reference for `sha1()`](../../../php/builtins/string/sha1.md)
-
diff --git a/docs/internals/builtins/string/sprintf.md b/docs/internals/builtins/string/sprintf.md
index 2a2dee4b54..b66c8e5ff0 100644
--- a/docs/internals/builtins/string/sprintf.md
+++ b/docs/internals/builtins/string/sprintf.md
@@ -2,15 +2,15 @@
title: "sprintf() — internals"
description: "Compiler internals for sprintf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 361
+ order: 376
---
## `sprintf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:511](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L511) (`lower_sprintf`)
+- **Signature**: [`src/builtins/string/sprintf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/sprintf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:511](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L511) (`lower_sprintf`)
- **Function symbol**: `lower_sprintf()`
@@ -37,4 +37,3 @@ function sprintf(string $format, ...$values): string
## Cross-references
- [User reference for `sprintf()`](../../../php/builtins/string/sprintf.md)
-
diff --git a/docs/internals/builtins/string/sscanf.md b/docs/internals/builtins/string/sscanf.md
index a91af74076..7aec227847 100644
--- a/docs/internals/builtins/string/sscanf.md
+++ b/docs/internals/builtins/string/sscanf.md
@@ -2,15 +2,15 @@
title: "sscanf() — internals"
description: "Compiler internals for sscanf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 362
+ order: 377
---
## `sscanf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:163](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L163) (`lower_sscanf`)
+- **Signature**: [`src/builtins/string/sscanf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/sscanf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:163](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L163) (`lower_sscanf`)
- **Function symbol**: `lower_sscanf()`
@@ -38,4 +38,3 @@ function sscanf(string $string, string $format, ...$vars): array
## Cross-references
- [User reference for `sscanf()`](../../../php/builtins/string/sscanf.md)
-
diff --git a/docs/internals/builtins/string/str_contains.md b/docs/internals/builtins/string/str_contains.md
index e2843efbfc..bfadc6a13f 100644
--- a/docs/internals/builtins/string/str_contains.md
+++ b/docs/internals/builtins/string/str_contains.md
@@ -2,15 +2,15 @@
title: "str_contains() — internals"
description: "Compiler internals for str_contains(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 363
+ order: 378
---
## `str_contains()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:682](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L682) (`lower_str_contains`)
+- **Signature**: [`src/builtins/string/str_contains.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_contains.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:682](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L682) (`lower_str_contains`)
- **Function symbol**: `lower_str_contains()`
@@ -36,4 +36,3 @@ function str_contains(string $haystack, string $needle): bool
## Cross-references
- [User reference for `str_contains()`](../../../php/builtins/string/str_contains.md)
-
diff --git a/docs/internals/builtins/string/str_ends_with.md b/docs/internals/builtins/string/str_ends_with.md
index 789deb4d81..fc8a7c8af4 100644
--- a/docs/internals/builtins/string/str_ends_with.md
+++ b/docs/internals/builtins/string/str_ends_with.md
@@ -2,15 +2,15 @@
title: "str_ends_with() — internals"
description: "Compiler internals for str_ends_with(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 364
+ order: 379
---
## `str_ends_with()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
+- **Signature**: [`src/builtins/string/str_ends_with.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_ends_with.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
- **Function symbol**: `lower_binary_string_runtime()`
@@ -36,4 +36,3 @@ function str_ends_with(string $haystack, string $needle): bool
## Cross-references
- [User reference for `str_ends_with()`](../../../php/builtins/string/str_ends_with.md)
-
diff --git a/docs/internals/builtins/string/str_ireplace.md b/docs/internals/builtins/string/str_ireplace.md
index f04ceecfe7..6986a05e28 100644
--- a/docs/internals/builtins/string/str_ireplace.md
+++ b/docs/internals/builtins/string/str_ireplace.md
@@ -2,18 +2,22 @@
title: "str_ireplace() — internals"
description: "Compiler internals for str_ireplace(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 365
+ order: 380
---
## `str_ireplace()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/string/str_ireplace.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_ireplace.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:780](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L780) (`lower_string_replace`)
+- **Function symbol**: `lower_string_replace()`
+### Lowering notes
+
+- Lowers `str_replace()`/`str_ireplace()` with three string operands.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -21,15 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function str_ireplace(mixed $search, mixed $replace, mixed $subject, int $count): mixed
+function str_ireplace(string $search, string $replace, string $subject, int $count = null): string
```
## What the type checker enforces
- **Arity**: takes 3–4 arguments (1 optional).
-- **By-reference parameters**: `$count`.
## Cross-references
- [User reference for `str_ireplace()`](../../../php/builtins/string/str_ireplace.md)
-
diff --git a/docs/internals/builtins/string/str_pad.md b/docs/internals/builtins/string/str_pad.md
index 1cfd662aaf..8cf5dd497d 100644
--- a/docs/internals/builtins/string/str_pad.md
+++ b/docs/internals/builtins/string/str_pad.md
@@ -2,15 +2,15 @@
title: "str_pad() — internals"
description: "Compiler internals for str_pad(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 366
+ order: 381
---
## `str_pad()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:818](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L818) (`lower_str_pad`)
+- **Signature**: [`src/builtins/string/str_pad.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_pad.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:818](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L818) (`lower_str_pad`)
- **Function symbol**: `lower_str_pad()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function str_pad(string $string, int $length, string $pad_string, int $pad_type): string
+function str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = 1): string
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function str_pad(string $string, int $length, string $pad_string, int $pad_type)
## Cross-references
- [User reference for `str_pad()`](../../../php/builtins/string/str_pad.md)
-
diff --git a/docs/internals/builtins/string/str_repeat.md b/docs/internals/builtins/string/str_repeat.md
index 71b2ea8ce1..89526a8a7c 100644
--- a/docs/internals/builtins/string/str_repeat.md
+++ b/docs/internals/builtins/string/str_repeat.md
@@ -2,15 +2,15 @@
title: "str_repeat() — internals"
description: "Compiler internals for str_repeat(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 367
+ order: 382
---
## `str_repeat()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:746](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L746) (`lower_str_repeat`)
+- **Signature**: [`src/builtins/string/str_repeat.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_repeat.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:746](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L746) (`lower_str_repeat`)
- **Function symbol**: `lower_str_repeat()`
@@ -36,4 +36,3 @@ function str_repeat(string $string, int $times): string
## Cross-references
- [User reference for `str_repeat()`](../../../php/builtins/string/str_repeat.md)
-
diff --git a/docs/internals/builtins/string/str_replace.md b/docs/internals/builtins/string/str_replace.md
index 1ed9805908..88ae2a2e33 100644
--- a/docs/internals/builtins/string/str_replace.md
+++ b/docs/internals/builtins/string/str_replace.md
@@ -2,15 +2,15 @@
title: "str_replace() — internals"
description: "Compiler internals for str_replace(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 368
+ order: 383
---
## `str_replace()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:780](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L780) (`lower_string_replace`)
+- **Signature**: [`src/builtins/string/str_replace.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_replace.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:780](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L780) (`lower_string_replace`)
- **Function symbol**: `lower_string_replace()`
@@ -25,15 +25,13 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function str_replace(string $search, string $replace, string $subject, int $count): mixed
+function str_replace(string $search, string $replace, string $subject, int $count = null): string
```
## What the type checker enforces
- **Arity**: takes 3–4 arguments (1 optional).
-- **By-reference parameters**: `$count`.
## Cross-references
- [User reference for `str_replace()`](../../../php/builtins/string/str_replace.md)
-
diff --git a/docs/internals/builtins/string/str_split.md b/docs/internals/builtins/string/str_split.md
index a57daf8485..2eeb07c3a2 100644
--- a/docs/internals/builtins/string/str_split.md
+++ b/docs/internals/builtins/string/str_split.md
@@ -2,15 +2,15 @@
title: "str_split() — internals"
description: "Compiler internals for str_split(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 369
+ order: 384
---
## `str_split()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:176](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L176) (`lower_str_split`)
+- **Signature**: [`src/builtins/string/str_split.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_split.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:176](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L176) (`lower_str_split`)
- **Function symbol**: `lower_str_split()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function str_split(string $string, int $length): array
+function str_split(string $string, int $length = 1): array
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function str_split(string $string, int $length): array
## Cross-references
- [User reference for `str_split()`](../../../php/builtins/string/str_split.md)
-
diff --git a/docs/internals/builtins/string/str_starts_with.md b/docs/internals/builtins/string/str_starts_with.md
index f20f94185f..1b0164c51d 100644
--- a/docs/internals/builtins/string/str_starts_with.md
+++ b/docs/internals/builtins/string/str_starts_with.md
@@ -2,15 +2,15 @@
title: "str_starts_with() — internals"
description: "Compiler internals for str_starts_with(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 370
+ order: 385
---
## `str_starts_with()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
+- **Signature**: [`src/builtins/string/str_starts_with.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/str_starts_with.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
- **Function symbol**: `lower_binary_string_runtime()`
@@ -36,4 +36,3 @@ function str_starts_with(string $haystack, string $needle): bool
## Cross-references
- [User reference for `str_starts_with()`](../../../php/builtins/string/str_starts_with.md)
-
diff --git a/docs/internals/builtins/string/strcasecmp.md b/docs/internals/builtins/string/strcasecmp.md
index f79ad7cbbe..b5fdbd33bf 100644
--- a/docs/internals/builtins/string/strcasecmp.md
+++ b/docs/internals/builtins/string/strcasecmp.md
@@ -2,21 +2,26 @@
title: "strcasecmp() — internals"
description: "Compiler internals for strcasecmp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 371
+ order: 386
---
## `strcasecmp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/string/strcasecmp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strcasecmp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
+- **Function symbol**: `lower_binary_string_runtime()`
+### Lowering notes
+
+- Lowers a two-argument string builtin that directly delegates to a runtime helper.
+
## Runtime helpers
-_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
+The following runtime helpers are referenced:
+- `__rt_explode`
## Signature summary
@@ -31,4 +36,3 @@ function strcasecmp(string $string1, string $string2): int
## Cross-references
- [User reference for `strcasecmp()`](../../../php/builtins/string/strcasecmp.md)
-
diff --git a/docs/internals/builtins/string/strcmp.md b/docs/internals/builtins/string/strcmp.md
index 3cf3f19e66..280ae03df5 100644
--- a/docs/internals/builtins/string/strcmp.md
+++ b/docs/internals/builtins/string/strcmp.md
@@ -2,15 +2,15 @@
title: "strcmp() — internals"
description: "Compiler internals for strcmp(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 372
+ order: 387
---
## `strcmp()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
+- **Signature**: [`src/builtins/string/strcmp.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strcmp.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:139](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L139) (`lower_binary_string_runtime`)
- **Function symbol**: `lower_binary_string_runtime()`
@@ -36,4 +36,3 @@ function strcmp(string $string1, string $string2): int
## Cross-references
- [User reference for `strcmp()`](../../../php/builtins/string/strcmp.md)
-
diff --git a/docs/internals/builtins/string/stripslashes.md b/docs/internals/builtins/string/stripslashes.md
index c261edb6e5..55a40a0cc3 100644
--- a/docs/internals/builtins/string/stripslashes.md
+++ b/docs/internals/builtins/string/stripslashes.md
@@ -2,15 +2,15 @@
title: "stripslashes() — internals"
description: "Compiler internals for stripslashes(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 373
+ order: 388
---
## `stripslashes()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/stripslashes.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/stripslashes.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function stripslashes(string $string): string
## Cross-references
- [User reference for `stripslashes()`](../../../php/builtins/string/stripslashes.md)
-
diff --git a/docs/internals/builtins/string/strlen.md b/docs/internals/builtins/string/strlen.md
index 688a27f92c..cd37f070bc 100644
--- a/docs/internals/builtins/string/strlen.md
+++ b/docs/internals/builtins/string/strlen.md
@@ -2,15 +2,15 @@
title: "strlen() — internals"
description: "Compiler internals for strlen(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 374
+ order: 389
---
## `strlen()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:971](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L971) (`lower_strlen`)
+- **Signature**: [`src/builtins/string/strlen.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strlen.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:493](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L493) (`lower_strlen`)
- **Function symbol**: `lower_strlen()`
@@ -36,4 +36,3 @@ function strlen(string $string): int
## Cross-references
- [User reference for `strlen()`](../../../php/builtins/string/strlen.md)
-
diff --git a/docs/internals/builtins/string/strpos.md b/docs/internals/builtins/string/strpos.md
index a4a902618a..b8f17df65f 100644
--- a/docs/internals/builtins/string/strpos.md
+++ b/docs/internals/builtins/string/strpos.md
@@ -2,15 +2,15 @@
title: "strpos() — internals"
description: "Compiler internals for strpos(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 375
+ order: 390
---
## `strpos()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:700](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L700) (`lower_string_position`)
+- **Signature**: [`src/builtins/string/strpos.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strpos.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:700](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L700) (`lower_string_position`)
- **Function symbol**: `lower_string_position()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function strpos(string $haystack, string $needle, int $offset): mixed
+function strpos(string $haystack, string $needle, int $offset = 0): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function strpos(string $haystack, string $needle, int $offset): mixed
## Cross-references
- [User reference for `strpos()`](../../../php/builtins/string/strpos.md)
-
diff --git a/docs/internals/builtins/string/strrev.md b/docs/internals/builtins/string/strrev.md
index 1af9a48b10..584b6b8adf 100644
--- a/docs/internals/builtins/string/strrev.md
+++ b/docs/internals/builtins/string/strrev.md
@@ -2,15 +2,15 @@
title: "strrev() — internals"
description: "Compiler internals for strrev(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 376
+ order: 391
---
## `strrev()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/strrev.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strrev.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function strrev(string $string): string
## Cross-references
- [User reference for `strrev()`](../../../php/builtins/string/strrev.md)
-
diff --git a/docs/internals/builtins/string/strrpos.md b/docs/internals/builtins/string/strrpos.md
index ae0372563e..dae2268cce 100644
--- a/docs/internals/builtins/string/strrpos.md
+++ b/docs/internals/builtins/string/strrpos.md
@@ -2,15 +2,15 @@
title: "strrpos() — internals"
description: "Compiler internals for strrpos(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 377
+ order: 392
---
## `strrpos()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:700](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L700) (`lower_string_position`)
+- **Signature**: [`src/builtins/string/strrpos.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strrpos.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:700](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L700) (`lower_string_position`)
- **Function symbol**: `lower_string_position()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function strrpos(string $haystack, string $needle, int $offset): mixed
+function strrpos(string $haystack, string $needle, int $offset = 0): mixed
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function strrpos(string $haystack, string $needle, int $offset): mixed
## Cross-references
- [User reference for `strrpos()`](../../../php/builtins/string/strrpos.md)
-
diff --git a/docs/internals/builtins/string/strstr.md b/docs/internals/builtins/string/strstr.md
index 2fe73e65f4..3b807047a0 100644
--- a/docs/internals/builtins/string/strstr.md
+++ b/docs/internals/builtins/string/strstr.md
@@ -2,15 +2,15 @@
title: "strstr() — internals"
description: "Compiler internals for strstr(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 378
+ order: 393
---
## `strstr()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:762](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L762) (`lower_strstr`)
+- **Signature**: [`src/builtins/string/strstr.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strstr.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:762](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L762) (`lower_strstr`)
- **Function symbol**: `lower_strstr()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function strstr(string $haystack, string $needle, bool $before_needle): string
+function strstr(string $haystack, string $needle, bool $before_needle = false): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function strstr(string $haystack, string $needle, bool $before_needle): string
## Cross-references
- [User reference for `strstr()`](../../../php/builtins/string/strstr.md)
-
diff --git a/docs/internals/builtins/string/strtolower.md b/docs/internals/builtins/string/strtolower.md
index e6bc81c16f..4d0d7ecebd 100644
--- a/docs/internals/builtins/string/strtolower.md
+++ b/docs/internals/builtins/string/strtolower.md
@@ -2,15 +2,15 @@
title: "strtolower() — internals"
description: "Compiler internals for strtolower(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 379
+ order: 394
---
## `strtolower()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/strtolower.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strtolower.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function strtolower(string $string): string
## Cross-references
- [User reference for `strtolower()`](../../../php/builtins/string/strtolower.md)
-
diff --git a/docs/internals/builtins/string/strtoupper.md b/docs/internals/builtins/string/strtoupper.md
index ece1e909dd..89fae1815a 100644
--- a/docs/internals/builtins/string/strtoupper.md
+++ b/docs/internals/builtins/string/strtoupper.md
@@ -2,15 +2,15 @@
title: "strtoupper() — internals"
description: "Compiler internals for strtoupper(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 380
+ order: 395
---
## `strtoupper()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/strtoupper.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/strtoupper.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function strtoupper(string $string): string
## Cross-references
- [User reference for `strtoupper()`](../../../php/builtins/string/strtoupper.md)
-
diff --git a/docs/internals/builtins/string/substr.md b/docs/internals/builtins/string/substr.md
index baa7b3204c..e1e389729e 100644
--- a/docs/internals/builtins/string/substr.md
+++ b/docs/internals/builtins/string/substr.md
@@ -2,15 +2,15 @@
title: "substr() — internals"
description: "Compiler internals for substr(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 381
+ order: 396
---
## `substr()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:713](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L713) (`lower_substr`)
+- **Signature**: [`src/builtins/string/substr.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/substr.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:713](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L713) (`lower_substr`)
- **Function symbol**: `lower_substr()`
@@ -26,7 +26,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function substr(string $string, int $offset, int $length): string
+function substr(string $string, int $offset, int $length = null): string
```
## What the type checker enforces
@@ -36,4 +36,3 @@ function substr(string $string, int $offset, int $length): string
## Cross-references
- [User reference for `substr()`](../../../php/builtins/string/substr.md)
-
diff --git a/docs/internals/builtins/string/substr_replace.md b/docs/internals/builtins/string/substr_replace.md
index 876a69dd32..4a12ebc291 100644
--- a/docs/internals/builtins/string/substr_replace.md
+++ b/docs/internals/builtins/string/substr_replace.md
@@ -2,15 +2,15 @@
title: "substr_replace() — internals"
description: "Compiler internals for substr_replace(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 382
+ order: 397
---
## `substr_replace()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:730](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L730) (`lower_substr_replace`)
+- **Signature**: [`src/builtins/string/substr_replace.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/substr_replace.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:730](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L730) (`lower_substr_replace`)
- **Function symbol**: `lower_substr_replace()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function substr_replace(string $string, string $replace, int $offset, int $length): string
+function substr_replace(string $string, string $replace, int $offset, int $length = null): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function substr_replace(string $string, string $replace, int $offset, int $lengt
## Cross-references
- [User reference for `substr_replace()`](../../../php/builtins/string/substr_replace.md)
-
diff --git a/docs/internals/builtins/string/trim.md b/docs/internals/builtins/string/trim.md
index 826d2a50bc..3f0d39136f 100644
--- a/docs/internals/builtins/string/trim.md
+++ b/docs/internals/builtins/string/trim.md
@@ -2,15 +2,15 @@
title: "trim() — internals"
description: "Compiler internals for trim(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 383
+ order: 398
---
## `trim()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
+- **Signature**: [`src/builtins/string/trim.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/trim.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:112](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L112) (`lower_trim_like`)
- **Function symbol**: `lower_trim_like()`
@@ -25,7 +25,7 @@ _No direct `__rt_*` helpers captured — the lowering is inlined or routes throu
## Signature summary
```php
-function trim(string $string, string $characters): string
+function trim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
## What the type checker enforces
@@ -35,4 +35,3 @@ function trim(string $string, string $characters): string
## Cross-references
- [User reference for `trim()`](../../../php/builtins/string/trim.md)
-
diff --git a/docs/internals/builtins/string/ucfirst.md b/docs/internals/builtins/string/ucfirst.md
index 8a27dfb2d9..af5cea25a6 100644
--- a/docs/internals/builtins/string/ucfirst.md
+++ b/docs/internals/builtins/string/ucfirst.md
@@ -2,15 +2,15 @@
title: "ucfirst() — internals"
description: "Compiler internals for ucfirst(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 384
+ order: 399
---
## `ucfirst()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:96](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L96) (`lower_ucfirst`)
+- **Signature**: [`src/builtins/string/ucfirst.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ucfirst.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:96](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L96) (`lower_ucfirst`)
- **Function symbol**: `lower_ucfirst()`
@@ -36,4 +36,3 @@ function ucfirst(string $string): string
## Cross-references
- [User reference for `ucfirst()`](../../../php/builtins/string/ucfirst.md)
-
diff --git a/docs/internals/builtins/string/ucwords.md b/docs/internals/builtins/string/ucwords.md
index 637986b82f..c88ddec735 100644
--- a/docs/internals/builtins/string/ucwords.md
+++ b/docs/internals/builtins/string/ucwords.md
@@ -2,15 +2,15 @@
title: "ucwords() — internals"
description: "Compiler internals for ucwords(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 385
+ order: 400
---
## `ucwords()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/ucwords.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ucwords.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function ucwords(string $string, string $separators): string
+function ucwords(string $string, string $separators = ' \t\r\n\x0c\x0b'): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function ucwords(string $string, string $separators): string
## Cross-references
- [User reference for `ucwords()`](../../../php/builtins/string/ucwords.md)
-
diff --git a/docs/internals/builtins/string/urldecode.md b/docs/internals/builtins/string/urldecode.md
index d28fd70d37..83364251b5 100644
--- a/docs/internals/builtins/string/urldecode.md
+++ b/docs/internals/builtins/string/urldecode.md
@@ -2,15 +2,15 @@
title: "urldecode() — internals"
description: "Compiler internals for urldecode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 386
+ order: 401
---
## `urldecode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/urldecode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/urldecode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function urldecode(string $string): string
## Cross-references
- [User reference for `urldecode()`](../../../php/builtins/string/urldecode.md)
-
diff --git a/docs/internals/builtins/string/urlencode.md b/docs/internals/builtins/string/urlencode.md
index 11b6df1f6f..cda48a6b38 100644
--- a/docs/internals/builtins/string/urlencode.md
+++ b/docs/internals/builtins/string/urlencode.md
@@ -2,15 +2,15 @@
title: "urlencode() — internals"
description: "Compiler internals for urlencode(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 387
+ order: 402
---
## `urlencode()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
+- **Signature**: [`src/builtins/string/urlencode.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/urlencode.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:76](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L76) (`lower_unary_string_runtime`)
- **Function symbol**: `lower_unary_string_runtime()`
@@ -37,4 +37,3 @@ function urlencode(string $string): string
## Cross-references
- [User reference for `urlencode()`](../../../php/builtins/string/urlencode.md)
-
diff --git a/docs/internals/builtins/string/vprintf.md b/docs/internals/builtins/string/vprintf.md
index 349aeb3a52..30b3ad65ab 100644
--- a/docs/internals/builtins/string/vprintf.md
+++ b/docs/internals/builtins/string/vprintf.md
@@ -2,15 +2,15 @@
title: "vprintf() — internals"
description: "Compiler internals for vprintf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 388
+ order: 403
---
## `vprintf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:530](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L530) (`lower_vprintf`)
+- **Signature**: [`src/builtins/string/vprintf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/vprintf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:530](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L530) (`lower_vprintf`)
- **Function symbol**: `lower_vprintf()`
@@ -36,4 +36,3 @@ function vprintf(string $format, array $values): int
## Cross-references
- [User reference for `vprintf()`](../../../php/builtins/string/vprintf.md)
-
diff --git a/docs/internals/builtins/string/vsprintf.md b/docs/internals/builtins/string/vsprintf.md
index 7d74f946b0..c63db8bedf 100644
--- a/docs/internals/builtins/string/vsprintf.md
+++ b/docs/internals/builtins/string/vsprintf.md
@@ -2,15 +2,15 @@
title: "vsprintf() — internals"
description: "Compiler internals for vsprintf(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 389
+ order: 404
---
## `vsprintf()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:524](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L524) (`lower_vsprintf`)
+- **Signature**: [`src/builtins/string/vsprintf.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/vsprintf.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:524](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L524) (`lower_vsprintf`)
- **Function symbol**: `lower_vsprintf()`
@@ -36,4 +36,3 @@ function vsprintf(string $format, array $values): string
## Cross-references
- [User reference for `vsprintf()`](../../../php/builtins/string/vsprintf.md)
-
diff --git a/docs/internals/builtins/string/wordwrap.md b/docs/internals/builtins/string/wordwrap.md
index 627dab0014..127c959e39 100644
--- a/docs/internals/builtins/string/wordwrap.md
+++ b/docs/internals/builtins/string/wordwrap.md
@@ -2,15 +2,15 @@
title: "wordwrap() — internals"
description: "Compiler internals for wordwrap(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 390
+ order: 405
---
## `wordwrap()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/strings.rs`:802](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/strings.rs#L802) (`lower_wordwrap`)
+- **Signature**: [`src/builtins/string/wordwrap.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/wordwrap.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/strings.rs`:802](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/strings.rs#L802) (`lower_wordwrap`)
- **Function symbol**: `lower_wordwrap()`
@@ -27,7 +27,7 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function wordwrap(string $string, int $width, string $break, bool $cut_long_words): string
+function wordwrap(string $string, int $width = 75, string $break = '\n', bool $cut_long_words = false): string
```
## What the type checker enforces
@@ -37,4 +37,3 @@ function wordwrap(string $string, int $width, string $break, bool $cut_long_word
## Cross-references
- [User reference for `wordwrap()`](../../../php/builtins/string/wordwrap.md)
-
diff --git a/docs/internals/builtins/type/boolval.md b/docs/internals/builtins/type/boolval.md
index ad8e381159..34c4221dc1 100644
--- a/docs/internals/builtins/type/boolval.md
+++ b/docs/internals/builtins/type/boolval.md
@@ -2,15 +2,15 @@
title: "boolval() — internals"
description: "Compiler internals for boolval(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 391
+ order: 406
---
## `boolval()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1064](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1064) (`lower_boolval`)
+- **Signature**: [`src/builtins/types/boolval.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/boolval.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:586](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L586) (`lower_boolval`)
- **Function symbol**: `lower_boolval()`
@@ -35,4 +35,3 @@ function boolval(mixed $value): bool
## Cross-references
- [User reference for `boolval()`](../../../php/builtins/type/boolval.md)
-
diff --git a/docs/internals/builtins/type/ctype_alnum.md b/docs/internals/builtins/type/ctype_alnum.md
index 35882f881d..988bd80b55 100644
--- a/docs/internals/builtins/type/ctype_alnum.md
+++ b/docs/internals/builtins/type/ctype_alnum.md
@@ -2,15 +2,15 @@
title: "ctype_alnum() — internals"
description: "Compiler internals for ctype_alnum(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 392
+ order: 407
---
## `ctype_alnum()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/ctype.rs`:30](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/ctype.rs#L30) (`lower_ctype_alnum`)
+- **Signature**: [`src/builtins/string/ctype_alnum.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ctype_alnum.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/ctype.rs`:30](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/ctype.rs#L30) (`lower_ctype_alnum`)
- **Function symbol**: `lower_ctype_alnum()`
@@ -35,4 +35,3 @@ function ctype_alnum(string $text): bool
## Cross-references
- [User reference for `ctype_alnum()`](../../../php/builtins/type/ctype_alnum.md)
-
diff --git a/docs/internals/builtins/type/ctype_alpha.md b/docs/internals/builtins/type/ctype_alpha.md
index db33a05b3e..4586834d6d 100644
--- a/docs/internals/builtins/type/ctype_alpha.md
+++ b/docs/internals/builtins/type/ctype_alpha.md
@@ -2,15 +2,15 @@
title: "ctype_alpha() — internals"
description: "Compiler internals for ctype_alpha(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 393
+ order: 408
---
## `ctype_alpha()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/ctype.rs`:20](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/ctype.rs#L20) (`lower_ctype_alpha`)
+- **Signature**: [`src/builtins/string/ctype_alpha.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ctype_alpha.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/ctype.rs`:20](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/ctype.rs#L20) (`lower_ctype_alpha`)
- **Function symbol**: `lower_ctype_alpha()`
@@ -35,4 +35,3 @@ function ctype_alpha(string $text): bool
## Cross-references
- [User reference for `ctype_alpha()`](../../../php/builtins/type/ctype_alpha.md)
-
diff --git a/docs/internals/builtins/type/ctype_digit.md b/docs/internals/builtins/type/ctype_digit.md
index 804af113a5..dd6e72eb47 100644
--- a/docs/internals/builtins/type/ctype_digit.md
+++ b/docs/internals/builtins/type/ctype_digit.md
@@ -2,15 +2,15 @@
title: "ctype_digit() — internals"
description: "Compiler internals for ctype_digit(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 394
+ order: 409
---
## `ctype_digit()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/ctype.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/ctype.rs#L25) (`lower_ctype_digit`)
+- **Signature**: [`src/builtins/string/ctype_digit.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ctype_digit.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/ctype.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/ctype.rs#L25) (`lower_ctype_digit`)
- **Function symbol**: `lower_ctype_digit()`
@@ -35,4 +35,3 @@ function ctype_digit(string $text): bool
## Cross-references
- [User reference for `ctype_digit()`](../../../php/builtins/type/ctype_digit.md)
-
diff --git a/docs/internals/builtins/type/ctype_space.md b/docs/internals/builtins/type/ctype_space.md
index 6dc9fd9444..fede58f818 100644
--- a/docs/internals/builtins/type/ctype_space.md
+++ b/docs/internals/builtins/type/ctype_space.md
@@ -2,15 +2,15 @@
title: "ctype_space() — internals"
description: "Compiler internals for ctype_space(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 395
+ order: 410
---
## `ctype_space()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/ctype.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/ctype.rs#L35) (`lower_ctype_space`)
+- **Signature**: [`src/builtins/string/ctype_space.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/string/ctype_space.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/ctype.rs`:35](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/ctype.rs#L35) (`lower_ctype_space`)
- **Function symbol**: `lower_ctype_space()`
@@ -35,4 +35,3 @@ function ctype_space(string $text): bool
## Cross-references
- [User reference for `ctype_space()`](../../../php/builtins/type/ctype_space.md)
-
diff --git a/docs/internals/builtins/type/floatval.md b/docs/internals/builtins/type/floatval.md
index f29b36075e..e79832821b 100644
--- a/docs/internals/builtins/type/floatval.md
+++ b/docs/internals/builtins/type/floatval.md
@@ -2,15 +2,15 @@
title: "floatval() — internals"
description: "Compiler internals for floatval(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 396
+ order: 411
---
## `floatval()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1034](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1034) (`lower_floatval`)
+- **Signature**: [`src/builtins/types/floatval.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/floatval.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:556](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L556) (`lower_floatval`)
- **Function symbol**: `lower_floatval()`
@@ -36,4 +36,3 @@ function floatval(mixed $value): float
## Cross-references
- [User reference for `floatval()`](../../../php/builtins/type/floatval.md)
-
diff --git a/docs/internals/builtins/type/get_resource_id.md b/docs/internals/builtins/type/get_resource_id.md
index 5c93138589..5d8df3089e 100644
--- a/docs/internals/builtins/type/get_resource_id.md
+++ b/docs/internals/builtins/type/get_resource_id.md
@@ -2,15 +2,15 @@
title: "get_resource_id() — internals"
description: "Compiler internals for get_resource_id(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 397
+ order: 412
---
## `get_resource_id()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:424](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L424) (`lower_get_resource_id`)
+- **Signature**: [`src/builtins/types/get_resource_id.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/get_resource_id.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:424](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L424) (`lower_get_resource_id`)
- **Function symbol**: `lower_get_resource_id()`
@@ -35,4 +35,3 @@ function get_resource_id(resource $resource): int
## Cross-references
- [User reference for `get_resource_id()`](../../../php/builtins/type/get_resource_id.md)
-
diff --git a/docs/internals/builtins/type/get_resource_type.md b/docs/internals/builtins/type/get_resource_type.md
index 8b7519a022..2febdf1a4d 100644
--- a/docs/internals/builtins/type/get_resource_type.md
+++ b/docs/internals/builtins/type/get_resource_type.md
@@ -2,15 +2,15 @@
title: "get_resource_type() — internals"
description: "Compiler internals for get_resource_type(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 398
+ order: 413
---
## `get_resource_type()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:412](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L412) (`lower_get_resource_type`)
+- **Signature**: [`src/builtins/types/get_resource_type.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/get_resource_type.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:412](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L412) (`lower_get_resource_type`)
- **Function symbol**: `lower_get_resource_type()`
@@ -35,4 +35,3 @@ function get_resource_type(resource $resource): string
## Cross-references
- [User reference for `get_resource_type()`](../../../php/builtins/type/get_resource_type.md)
-
diff --git a/docs/internals/builtins/type/gettype.md b/docs/internals/builtins/type/gettype.md
index 3da66f0b62..a8c97e14b7 100644
--- a/docs/internals/builtins/type/gettype.md
+++ b/docs/internals/builtins/type/gettype.md
@@ -2,15 +2,15 @@
title: "gettype() — internals"
description: "Compiler internals for gettype(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 399
+ order: 414
---
## `gettype()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:612](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L612) (`lower_gettype`)
+- **Signature**: [`src/builtins/types/gettype.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/gettype.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:129](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L129) (`lower_gettype`)
- **Function symbol**: `lower_gettype()`
@@ -35,4 +35,3 @@ function gettype(mixed $value): string
## Cross-references
- [User reference for `gettype()`](../../../php/builtins/type/gettype.md)
-
diff --git a/docs/internals/builtins/type/intval.md b/docs/internals/builtins/type/intval.md
index d755c6f9c5..3a348dfe89 100644
--- a/docs/internals/builtins/type/intval.md
+++ b/docs/internals/builtins/type/intval.md
@@ -2,15 +2,15 @@
title: "intval() — internals"
description: "Compiler internals for intval(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 400
+ order: 415
---
## `intval()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1001](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1001) (`lower_intval`)
+- **Signature**: [`src/builtins/types/intval.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/intval.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:523](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L523) (`lower_intval`)
- **Function symbol**: `lower_intval()`
@@ -27,14 +27,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function intval(mixed $value, int $base): int
+function intval(mixed $value): int
```
## What the type checker enforces
-- **Arity**: takes exactly 2 arguments.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `intval()`](../../../php/builtins/type/intval.md)
-
diff --git a/docs/internals/builtins/type/is_array.md b/docs/internals/builtins/type/is_array.md
index 1ffaadf14f..2fb0727e6f 100644
--- a/docs/internals/builtins/type/is_array.md
+++ b/docs/internals/builtins/type/is_array.md
@@ -2,15 +2,15 @@
title: "is_array() — internals"
description: "Compiler internals for is_array(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 401
+ order: 416
---
## `is_array()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1480](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1480) (`lower_is_array`)
+- **Signature**: [`src/builtins/types/is_array.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_array.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:1002](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L1002) (`lower_is_array`)
- **Function symbol**: `lower_is_array()`
@@ -37,4 +37,3 @@ function is_array(mixed $value): bool
## Cross-references
- [User reference for `is_array()`](../../../php/builtins/type/is_array.md)
-
diff --git a/docs/internals/builtins/type/is_bool.md b/docs/internals/builtins/type/is_bool.md
index 83fef96bff..7553078da4 100644
--- a/docs/internals/builtins/type/is_bool.md
+++ b/docs/internals/builtins/type/is_bool.md
@@ -2,18 +2,22 @@
title: "is_bool() — internals"
description: "Compiler internals for is_bool(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 402
+ order: 417
---
## `is_bool()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/types/is_bool.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_bool.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:736](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L736) (`lower_static_type_predicate`)
+- **Function symbol**: `lower_static_type_predicate()`
+### Lowering notes
+
+- Lowers a static `is_*` predicate for concrete non-Mixed values.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function is_bool(mixed $value): bool
## Cross-references
- [User reference for `is_bool()`](../../../php/builtins/type/is_bool.md)
-
diff --git a/docs/internals/builtins/type/is_callable.md b/docs/internals/builtins/type/is_callable.md
index 77b58190d6..8f21568224 100644
--- a/docs/internals/builtins/type/is_callable.md
+++ b/docs/internals/builtins/type/is_callable.md
@@ -2,15 +2,15 @@
title: "is_callable() — internals"
description: "Compiler internals for is_callable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 403
+ order: 418
---
## `is_callable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:802](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L802) (`lower_is_callable`)
+- **Signature**: [`src/builtins/types/is_callable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_callable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:319](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L319) (`lower_is_callable`)
- **Function symbol**: `lower_is_callable()`
@@ -28,15 +28,13 @@ The following runtime helpers are referenced:
## Signature summary
```php
-function is_callable(mixed $value, bool $syntax_only = false, string $callable_name = null): bool
+function is_callable(mixed $value): bool
```
## What the type checker enforces
-- **Arity**: takes 1–3 arguments (2 optional).
-- **By-reference parameters**: `$callable_name`.
+- **Arity**: takes exactly 1 argument.
## Cross-references
- [User reference for `is_callable()`](../../../php/builtins/type/is_callable.md)
-
diff --git a/docs/internals/builtins/type/is_float.md b/docs/internals/builtins/type/is_float.md
index 83af6295f6..304877e8d7 100644
--- a/docs/internals/builtins/type/is_float.md
+++ b/docs/internals/builtins/type/is_float.md
@@ -2,18 +2,22 @@
title: "is_float() — internals"
description: "Compiler internals for is_float(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 404
+ order: 419
---
## `is_float()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/types/is_float.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_float.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:736](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L736) (`lower_static_type_predicate`)
+- **Function symbol**: `lower_static_type_predicate()`
+### Lowering notes
+
+- Lowers a static `is_*` predicate for concrete non-Mixed values.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function is_float(mixed $value): bool
## Cross-references
- [User reference for `is_float()`](../../../php/builtins/type/is_float.md)
-
diff --git a/docs/internals/builtins/type/is_int.md b/docs/internals/builtins/type/is_int.md
index 704b6d2912..0655d3dca4 100644
--- a/docs/internals/builtins/type/is_int.md
+++ b/docs/internals/builtins/type/is_int.md
@@ -2,18 +2,22 @@
title: "is_int() — internals"
description: "Compiler internals for is_int(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 405
+ order: 420
---
## `is_int()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/types/is_int.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_int.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:736](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L736) (`lower_static_type_predicate`)
+- **Function symbol**: `lower_static_type_predicate()`
+### Lowering notes
+
+- Lowers a static `is_*` predicate for concrete non-Mixed values.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function is_int(mixed $value): bool
## Cross-references
- [User reference for `is_int()`](../../../php/builtins/type/is_int.md)
-
diff --git a/docs/internals/builtins/type/is_iterable.md b/docs/internals/builtins/type/is_iterable.md
index 127d997372..adaa2e8c4e 100644
--- a/docs/internals/builtins/type/is_iterable.md
+++ b/docs/internals/builtins/type/is_iterable.md
@@ -2,15 +2,15 @@
title: "is_iterable() — internals"
description: "Compiler internals for is_iterable(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 406
+ order: 421
---
## `is_iterable()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1272](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1272) (`lower_is_iterable`)
+- **Signature**: [`src/builtins/types/is_iterable.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_iterable.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:794](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L794) (`lower_is_iterable`)
- **Function symbol**: `lower_is_iterable()`
@@ -35,4 +35,3 @@ function is_iterable(mixed $value): bool
## Cross-references
- [User reference for `is_iterable()`](../../../php/builtins/type/is_iterable.md)
-
diff --git a/docs/internals/builtins/type/is_null.md b/docs/internals/builtins/type/is_null.md
index c8a30c86cc..99011917bb 100644
--- a/docs/internals/builtins/type/is_null.md
+++ b/docs/internals/builtins/type/is_null.md
@@ -2,18 +2,22 @@
title: "is_null() — internals"
description: "Compiler internals for is_null(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 407
+ order: 422
---
## `is_null()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/types/is_null.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_null.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:992](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L992) (`lower_is_null_builtin`)
+- **Function symbol**: `lower_is_null_builtin()`
+### Lowering notes
+
+- Lowers `is_null()` for concrete scalar values and boxed Mixed payloads.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function is_null(mixed $value): bool
## Cross-references
- [User reference for `is_null()`](../../../php/builtins/type/is_null.md)
-
diff --git a/docs/internals/builtins/type/is_numeric.md b/docs/internals/builtins/type/is_numeric.md
index d0f726ca35..2b521d9b0b 100644
--- a/docs/internals/builtins/type/is_numeric.md
+++ b/docs/internals/builtins/type/is_numeric.md
@@ -2,15 +2,15 @@
title: "is_numeric() — internals"
description: "Compiler internals for is_numeric(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 408
+ order: 423
---
## `is_numeric()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/is_numeric.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/is_numeric.rs#L22) (`lower_is_numeric`)
+- **Signature**: [`src/builtins/types/is_numeric.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_numeric.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/is_numeric.rs`:22](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/is_numeric.rs#L22) (`lower_is_numeric`)
- **Function symbol**: `lower_is_numeric()`
@@ -35,4 +35,3 @@ function is_numeric(mixed $value): bool
## Cross-references
- [User reference for `is_numeric()`](../../../php/builtins/type/is_numeric.md)
-
diff --git a/docs/internals/builtins/type/is_object.md b/docs/internals/builtins/type/is_object.md
index 941b646244..e42568766c 100644
--- a/docs/internals/builtins/type/is_object.md
+++ b/docs/internals/builtins/type/is_object.md
@@ -2,15 +2,15 @@
title: "is_object() — internals"
description: "Compiler internals for is_object(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 409
+ order: 424
---
## `is_object()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1495](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1495) (`lower_is_object`)
+- **Signature**: [`src/builtins/types/is_object.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_object.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:1017](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L1017) (`lower_is_object`)
- **Function symbol**: `lower_is_object()`
@@ -36,4 +36,3 @@ function is_object(mixed $value): bool
## Cross-references
- [User reference for `is_object()`](../../../php/builtins/type/is_object.md)
-
diff --git a/docs/internals/builtins/type/is_resource.md b/docs/internals/builtins/type/is_resource.md
index 817cd16c09..4a83fd77e7 100644
--- a/docs/internals/builtins/type/is_resource.md
+++ b/docs/internals/builtins/type/is_resource.md
@@ -2,15 +2,15 @@
title: "is_resource() — internals"
description: "Compiler internals for is_resource(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 410
+ order: 425
---
## `is_resource()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:400](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L400) (`lower_is_resource`)
+- **Signature**: [`src/builtins/types/is_resource.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_resource.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:400](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L400) (`lower_is_resource`)
- **Function symbol**: `lower_is_resource()`
@@ -35,4 +35,3 @@ function is_resource(mixed $value): bool
## Cross-references
- [User reference for `is_resource()`](../../../php/builtins/type/is_resource.md)
-
diff --git a/docs/internals/builtins/type/is_scalar.md b/docs/internals/builtins/type/is_scalar.md
index 22e2b7b26a..4a73bcd86d 100644
--- a/docs/internals/builtins/type/is_scalar.md
+++ b/docs/internals/builtins/type/is_scalar.md
@@ -2,15 +2,15 @@
title: "is_scalar() — internals"
description: "Compiler internals for is_scalar(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 411
+ order: 426
---
## `is_scalar()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins.rs`:1511](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins.rs#L1511) (`lower_is_scalar`)
+- **Signature**: [`src/builtins/types/is_scalar.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_scalar.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:1033](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L1033) (`lower_is_scalar`)
- **Function symbol**: `lower_is_scalar()`
@@ -37,4 +37,3 @@ function is_scalar(mixed $value): bool
## Cross-references
- [User reference for `is_scalar()`](../../../php/builtins/type/is_scalar.md)
-
diff --git a/docs/internals/builtins/type/is_string.md b/docs/internals/builtins/type/is_string.md
index a41110d5eb..56a26f75f6 100644
--- a/docs/internals/builtins/type/is_string.md
+++ b/docs/internals/builtins/type/is_string.md
@@ -2,18 +2,22 @@
title: "is_string() — internals"
description: "Compiler internals for is_string(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 412
+ order: 427
---
## `is_string()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`(not lowered)`:0]()
-- **Function symbol**: `(none — type-checker only)()`
+- **Signature**: [`src/builtins/types/is_string.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/is_string.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins.rs`:736](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins.rs#L736) (`lower_static_type_predicate`)
+- **Function symbol**: `lower_static_type_predicate()`
+### Lowering notes
+
+- Lowers a static `is_*` predicate for concrete non-Mixed values.
+
## Runtime helpers
_No direct `__rt_*` helpers captured — the lowering is inlined or routes through another builtin._
@@ -31,4 +35,3 @@ function is_string(mixed $value): bool
## Cross-references
- [User reference for `is_string()`](../../../php/builtins/type/is_string.md)
-
diff --git a/docs/internals/builtins/type/settype.md b/docs/internals/builtins/type/settype.md
index 2ae248b18c..bb36d472c8 100644
--- a/docs/internals/builtins/type/settype.md
+++ b/docs/internals/builtins/type/settype.md
@@ -2,15 +2,15 @@
title: "settype() — internals"
description: "Compiler internals for settype(): lowering path, type checks, and runtime helpers."
sidebar:
- order: 413
+ order: 428
---
## `settype()` — internals
## Where it lives
-- **Signature**: [`src/types/signatures.rs`](https://github.com/illegalstudio/elephc/blob/main/src/types/signatures.rs)
-- **Lowering**: [`src/codegen_ir/lower_inst/builtins/types.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen_ir/lower_inst/builtins/types.rs#L25) (`lower_settype`)
+- **Signature**: [`src/builtins/types/settype.rs`](https://github.com/illegalstudio/elephc/blob/main/src/builtins/types/settype.rs)
+- **Lowering**: [`src/codegen/lower_inst/builtins/types.rs`:25](https://github.com/illegalstudio/elephc/blob/main/src/codegen/lower_inst/builtins/types.rs#L25) (`lower_settype`)
- **Function symbol**: `lower_settype()`
@@ -36,4 +36,3 @@ function settype(mixed $var, string $type): bool
## Cross-references
- [User reference for `settype()`](../../../php/builtins/type/settype.md)
-
diff --git a/docs/internals/how-elephc-works.md b/docs/internals/how-elephc-works.md
index 1c065b1b20..efe80d9024 100644
--- a/docs/internals/how-elephc-works.md
+++ b/docs/internals/how-elephc-works.md
@@ -291,9 +291,16 @@ The exact textual IR contains value ids, types, ownership, spans, and terminator
## Phase 16: Code generation
-**Files:** `src/codegen_ir/`, plus shared `src/codegen/abi/`, `src/codegen/runtime/`, and target helpers — See [The Code Generator](the-codegen.md) for details.
-
-The EIR backend emits assembly for the selected target. For ordinary control flow this is mostly straight-line branches and labels; for `try` / `catch` / `finally`, the compiler additionally emits handler records and resume labels around `_setjmp` / `_longjmp`-based exception unwinding. The legacy AST backend remains available only through `--ast-backend`; new PHP-visible behavior is expected to go through EIR. By this point our running example has already lost the `if` shell, so the AArch64 form is simpler than the original source (simplified, with comments):
+**Files:** `src/codegen/`, plus shared `src/codegen_support/abi/`,
+`src/codegen_support/runtime/`, and target helpers — See
+[The Code Generator](the-codegen.md) for details.
+
+The EIR backend emits assembly for the selected target. For ordinary control
+flow this is mostly straight-line branches and labels; for `try` / `catch` /
+`finally`, the compiler additionally emits handler records and resume labels
+around `_setjmp` / `_longjmp`-based exception unwinding. By this point our
+running example has already lost the `if` shell, so the AArch64 form is simpler
+than the original source (simplified, with comments):
```asm
.global _main
diff --git a/docs/internals/memory-model.md b/docs/internals/memory-model.md
index 9dc0c6562a..782d87dac6 100644
--- a/docs/internals/memory-model.md
+++ b/docs/internals/memory-model.md
@@ -107,9 +107,9 @@ For heap-backed values, stack slots also carry compile-time ownership metadata i
elephc has two representations for PHP `null` in scalar slots, selected per compilation by
`--null-repr=sentinel|tagged` (or `ELEPHC_NULL_REPR`). The tagged representation is the
-default; the sentinel is the legacy opt-out.
+default; the sentinel is the compatibility opt-out.
-#### The in-band sentinel (legacy opt-out)
+#### The in-band sentinel (compatibility opt-out)
`null` is represented as the integer `0x7FFFFFFFFFFFFFFE` (`PHP_INT_MAX - 1`). Because every
64-bit pattern is a valid PHP int, this sentinel collides with the real integer
@@ -150,9 +150,9 @@ cmp x1, #8 ; runtime tag 8 = PHP null
b.eq value_is_null
```
-A tagged null carries the legacy sentinel as its payload word, so boxing it into a Mixed
-cell produces exactly the legacy `{tag 8, sentinel}` words and un-audited consumers degrade
-to the legacy behavior. `?int` parameters, returns, and properties keep their boxed Mixed
+A tagged null carries the sentinel as its payload word, so boxing it into a Mixed
+cell produces `{tag 8, sentinel}` words and un-audited consumers degrade
+to sentinel behavior. `?int` parameters, returns, and properties keep their boxed Mixed
representation under both modes.
### Pointer values
@@ -376,7 +376,7 @@ Associative arrays use a separate heap-allocated structure: an open-addressing h
|---|---|---|
| `count` | 8 bytes | Number of occupied entries |
| `capacity` | 8 bytes | Total number of slots |
-| `val_type` | 8 bytes | Coarse value-type summary (0=int, 1=str, 2=float, 3=bool, 4=array, 5=assoc, 6=object, 7=mixed) |
+| `val_type` | 8 bytes | Coarse value-type summary (0=int, 1=str, 2=float, 3=bool, 4=array, 5=assoc, 6=object, 7=mixed, 8=null) |
| `head` | 8 bytes | Slot index of the first inserted entry, or `-1` when empty |
| `tail` | 8 bytes | Slot index of the most recently inserted entry, or `-1` when empty |
@@ -585,6 +585,8 @@ The naming pattern comes from `static_property_symbol(...)`. Inherited static pr
| Fiber scheduler state | `_fiber_current`, `_fiber_main_saved_sp`, `_fiber_main_saved_exc`, `_fiber_main_saved_call_frame` = 32 bytes total | Fixed-size current-fiber and main-frame resume bookkeeping |
| Runtime diagnostics | `_rt_diag_suppression` = 8 bytes total | Fixed-size warning-suppression depth used by `@` and exception unwinding |
| JSON state | `_json_last_error`, `_json_active_flags`, `_json_active_depth`, `_json_indent_depth`, `_json_depth_limit`, `_json_validate_idx`, `_json_validate_ptr`, `_json_validate_len`, `_json_decode_assoc`, `_json_error_source_ptr`, `_json_error_location_active`, `_json_error_line`, `_json_error_column` = 104 bytes total | Fixed-size bookkeeping for JSON calls and decode error locations |
+| Serialize/unserialize state | `_ser_value_counter`, `_ser_obj_count`, `_unser_count` = 8 bytes each; `_ser_obj_ptrs`, `_ser_obj_idxs`, `_unser_values` = 512KB each | `serialize()` object-dedup counters/maps and `unserialize()` reference registry; overflow degrades gracefully (serialize stops deduping, unserialize fails the ref) |
+| Date/time state | `_strtotime_clock`, `_php_default_tz_len` = 8 bytes each; `_php_tz_env`, `_php_tz_save` = 264 bytes each | `strtotime()` clock override plus default-timezone (`date_default_timezone_*`) env/save buffers and stored identifier length |
| CLI globals | `_global_argc`, `_global_argv` = 16 bytes total | Fixed-size bookkeeping |
| User globals | 16 bytes per `global $var` slot | Grows with number of referenced globals |
| Static vars | 24 bytes per `static $var` (`16 + 8 init flag`) | Grows with number of declared static locals |
@@ -595,7 +597,7 @@ The naming pattern comes from `static_property_symbol(...)`. Inherited static pr
| Stream filter scratch | `_stream_filter_buf`, `_stream_grow_scratch` = 64KB each | Scratch space for stream filters, including length-growing filters such as base64 and quoted-printable encoders |
| Stream context and callbacks | `_stream_context_options`, `_stream_notification_callback`, `_stream_connect_host`, `_stream_open_opened_path_scratch`, `_url_stat_matched` | Current stream-context options hash, notification callback, TLS peer host, wrapper opened-path scratch, and wrapper url_stat match flag |
| TLS and crypto function slots | `_elephc_tls_*_fn`, `_zlib_*_fn`, `_bz2_*_fn`, `_phar_zlib_*_fn`, `_phar_bz2_*_fn`, `_iconv_*_fn`, `_elephc_crypto_*_fn` = 8 bytes per slot | Late-bound function pointers so programs only link optional TLS/compression/iconv/crypto support when a call site publishes the symbol |
-| HTTP/HTTPS/FTP buffers | `_http_resp_buf`, `_https_resp_buf`, `_user_wrapper_drain_buf`, `_phar_write_out` = 1MB each; `_http_req_scratch` = 8KB; `_http_redirect_path_buf`, `_fgc_url_retr` = 2KB each; `_fgc_url_addr`, `_fsockopen_addr` = 512 bytes each; `_ftp_resp_buf` = 4KB; `_ftp_data_addr`, `_ftp_cmd_scratch` = 64 bytes each | Protocol-specific response, request, redirect, FTP, wrapper, and PHAR writer scratch buffers |
+| HTTP/HTTPS/FTP buffers | `_http_resp_buf`, `_https_resp_buf`, `_user_wrapper_drain_buf`, `_phar_write_out` = 1MB each; `_http_req_scratch` = 8KB; `_http_redirect_path_buf`, `_fgc_url_retr` = 2KB each; `_fgc_url_addr`, `_fsockopen_addr` = 512 bytes each; `_ftp_resp_buf` = 4KB; `_ftp_data_addr`, `_ftp_cmd_scratch` = 64 bytes each; `_ftp_use_tls` = 8 bytes (FTPS handshake flag) | Protocol-specific response, request, redirect, FTP/FTPS, wrapper, and PHAR writer scratch buffers and flags |
| HTTP active context | `_http_active_ignore_errors`, `_http_active_max_redirects`, `_http_active_timeout_seconds`, `_http_active_proxy_ptr`, `_http_active_proxy_len`, `_http_active_host_ptr`, `_http_active_host_len`, `_http_redirect_path_len` | Fixed-size state shared between HTTP request construction and redirect/open helpers |
| Socket address scratch | `_recvfrom_addr_ptr`, `_recvfrom_addr_len`, `_accept_peer_ptr`, `_accept_peer_len` = 8 bytes each | Stores peer/address strings returned through by-reference socket parameters |
| Protocol/service lookup buffers | `_protoent_buf` = 32KB, `_servent_buf` = 1MB | Scratch buffers for protocol and service database lookups |
@@ -617,7 +619,8 @@ elephc uses a **free-list allocator with reference counting plus a targeted cycl
7. **String buffer reset** — the concat buffer resets at each statement, with strings that need to survive copied to heap via `__rt_str_persist`
8. **Stack memory** — automatically reclaimed when functions return
9. **Generator frame release** — Generator frames participate in object refcounting, with a custom deep-free branch for their frame slots and delegated iterator
-10. **Process exit** — all memory reclaimed by the OS
+10. **Resource scope-cleanup** — Mixed-boxed resources (tag 9) carry a resource-kind subtype in the high payload word, and `__rt_mixed_free_deep` runs the matching destructor when the box is released: kind 1 = native stream fd (`close()`), kind 2 = HashContext handle (`elephc_crypto_free` through `__rt_hash_ctx_free`), kind 3 = `popen` pipe (`__rt_pclose`, which closes the `FILE*` and reaps the child), kind 4 = `opendir` stream (`__rt_closedir`). Kind 0 resources (generic resources) are skipped, and every fd-backed kind also skips handles `>= 0x40000000` — synthetic wrapper handles and the `-1` sentinel that an explicit `fclose`/`pclose`/`closedir` stamps into the box so the descriptor is never released twice (even if its fd number was reused). Alias safety comes from the Mixed box refcount — `$b = $a` increfs the box, so only the last release triggers the destructor
+11. **Process exit** — all memory reclaimed by the OS
### What is NOT freed
@@ -628,6 +631,8 @@ elephc uses a **free-list allocator with reference counting plus a targeted cycl
- **Container-copying builtins** no longer blindly duplicate borrowed heap handles for common nested payload paths: refcounted runtime variants now retain values before new arrays/hash tables take ownership (`array` literals with spreads, `array_merge`, `array_chunk`, `array_slice`, `array_reverse`, `array_pad`, `array_unique`, `array_splice`, `array_diff`, `array_intersect`, `array_filter`, `array_fill`, `array_combine`, `array_fill_keys`)
- **Regression coverage now explicitly exercises** local aliases, borrowed nested-container returns, `Owned`/`Borrowed` control-flow merges, and scope-exit paths so future ownership work has focused tripwires instead of relying only on large end-to-end suites
- **Raw/off-heap ownership cycles** are still outside the collector. `ptr` values, extern-managed buffers, and raw helper allocations (`kind=0`) are not traversed just because an address exists somewhere
+- **Kind-0 resources** (generic/unknown resource kind, including synthetic user-wrapper handles `>= 0x40000000`) are not auto-freed by the Mixed deep-free path — their lifecycle remains managed by the wrapper layer or the user's explicit `close()` call. Kinds 1–4 (native stream fd, HashContext, `popen` pipe, `opendir` stream) are auto-released at scope exit
+- **HashContext reuse after `hash_final()`** is memory-safe but not PHP-equivalent: `elephc_crypto_final` finalizes a *clone* and leaves the original handle live and owned by its Mixed box, so the box's kind-2 destructor frees it exactly once. A second `hash_final()` or a `hash_update()`/`hash_copy()` on the same handle therefore does not double-free or use-after-free (where PHP throws "Supplied resource is not a valid Hash Context resource"), it simply keeps hashing the still-live context (documented in `src/codegen/runtime/strings/hash_context.rs`)
### Targeted cycle collection
diff --git a/docs/internals/the-codegen.md b/docs/internals/the-codegen.md
index 17417f7424..4af8188e3a 100644
--- a/docs/internals/the-codegen.md
+++ b/docs/internals/the-codegen.md
@@ -1,1187 +1,88 @@
---
title: "The Code Generator"
-description: "How typed AST nodes become native assembly for the selected target."
+description: "How EIR becomes target assembly and links against the shared runtime."
sidebar:
order: 7
---
-**Source:** default backend `src/ir_lower/`, `src/ir/`, and `src/codegen_ir/`; shared target/runtime infrastructure under `src/codegen/abi/`, `src/codegen/runtime/`, `src/codegen/platform/`, `src/codegen/emit.rs`, and `src/codegen/data_section.rs`; frozen legacy AST backend under `src/codegen/expr.rs`, `src/codegen/expr/`, `src/codegen/stmt.rs`, `src/codegen/stmt/`, `src/codegen/functions/`, and `src/codegen/builtins/`; intrinsic method registry: `src/intrinsics.rs`
-
-The code generator (codegen) is the heart of the compiler. The default path lowers the checked and optimized AST into EIR first, then emits native assembly text for the selected target from that EIR. The temporary `--ast-backend` fallback still walks the checked AST directly and emits assembly while the legacy emitter remains in-tree.
-
-elephc currently supports more than one backend. AArch64 is still the clearest reference path in the codebase and in this document, while Linux `x86_64` is also a supported backend that goes through the same high-level lowering pipeline.
-
-Most snippets below use AArch64 because the instruction forms are compact and the surrounding docs already explain them in detail. When a section talks about target-specific ABI or runtime behavior, it calls out Linux `x86_64` explicitly.
-
-For an introduction to AArch64, see [Introduction to ARM64 Assembly](arm64-assembly.md).
-
-## Overview
-
-In the default path, `src/ir_lower/` walks the checked optimized AST, produces validated EIR, and `src/codegen_ir/` emits assembly for each EIR function, instruction, and terminator. The CLI's main output is the **user program assembly**; the shared runtime helpers are usually assembled separately and reused from the runtime object cache. The user-facing `.s` file still has this structure:
-
-```asm
-.global _main
-.align 2
-
-; --- user-defined functions ---
-_fn_factorial:
- ...
- ret
-
-; --- class methods ---
-_method_Point_move:
- ...
- ret
-
-; --- main program ---
-_main:
- ; prologue (stack frame setup)
- ; global argc/argv initialization
- ; program statements
- ; epilogue (exit syscall)
-
-; --- deferred closures emitted after _main ---
-_closure_1:
- ...
- ret
-
-; --- data section ---
-.data
-_str_0: .ascii "hello"
-_float_0: .quad 0x400921FB54442D18
-
-; --- source markers used by --source-map ---
-; @src line=12 col=5
-```
-
-Trait composition does not add a separate runtime dispatch layer. Traits are flattened into each concrete class during type checking, then inheritance metadata is layered on top. Codegen still emits `_method_Class_method` / `_static_Class_method` labels, but instance calls now use vtable slots keyed by `class_id` so child overrides work through inherited methods.
-
-The exact directives and symbol decoration vary by target. The example above is intentionally AArch64-flavored, but the same structural phases apply on Linux `x86_64`.
-
-When you call the legacy library-style `codegen::generate(...)` entry point, elephc still exposes both pieces explicitly as `(user_asm, runtime_asm)`. The normal CLI path lowers to EIR, calls `codegen_ir::generate_user_asm_from_ir_with_options(...)`, and links against the runtime-object cache so repeated compiles do not have to reassemble the same shared runtime text every time.
-
-## The Emitter
-
-**File:** `src/codegen/emit.rs`
-
-The `Emitter` is a simple string buffer with helper methods:
-
-| Method | Output |
+**Source:** assembly emitter `src/codegen/`; EIR lowering `src/ir_lower/`;
+IR model and validation `src/ir/`; shared runtime/ABI support
+`src/codegen_support/`.
+
+Codegen is a single EIR pipeline. The checked and optimized AST is always
+lowered into EIR, IR passes run over that module, and `src/codegen/` emits the
+user assembly for the selected target.
+
+## Pipeline Position
+
+```text
+PHP source
+ -> Lexer
+ -> Parser
+ -> Magic constants
+ -> Conditional compilation
+ -> Resolver / autoload
+ -> NameResolver
+ -> AST constant folding
+ -> Type checker / warnings
+ -> AST optimizer passes
+ -> AST -> EIR lowering
+ -> EIR validation
+ -> EIR optimization passes
+ -> EIR -> target assembly
+ -> runtime cache
+ -> assembler / linker
+ -> binary or cdylib
+```
+
+`--emit-ir` stops after lowering and IR optimization, printing the textual EIR.
+Normal builds continue through `codegen::generate_user_asm_from_ir_with_options`
+and link the resulting user object against the cached runtime object.
+
+## Module Layout
+
+| Path | Responsibility |
|---|---|
-| `instruction("mov x0, #42")` | ` mov x0, #42\n` (indented) |
-| `label("_main")` | `_main:\n` |
-| `comment("load variable")` | ` ; load variable\n` |
-| `raw(".global _main")` | `.global _main\n` (no indent) |
-| `blank()` | `\n` |
-
-All assembly is built as text, then written to the `.s` file.
-
-Statement emission also injects source markers of the form `@src line= col=`. They are ignored by the assembler as comments, but the CLI can later scan them to build a simple source-map sidecar file when `--source-map` is enabled.
-
-## Runtime split, cache, and source maps
-
-The compiler's codegen/runtime handoff now has three distinct artifacts:
-
-1. **User assembly** — emitted by the selected backend into the per-build `.s` file
-2. **Runtime object** — assembled from the shared runtime once and cached under `~/.cache/elephc/` (or `XDG_CACHE_HOME`) using the compiler version, target, heap size, and generated runtime assembly hash in the filename
-3. **Optional source map** — a JSON sidecar generated from `@src` markers embedded in the user assembly comments
-
-This means normal CLI builds no longer concatenate the runtime text into every output assembly file before assembling. Instead, they:
-
-- prepare or reuse the cached runtime object
-- assemble only the user `.s` file into `file.o`
-- link `file.o` against the cached runtime object
-
-The source-map file is intentionally simple. Today it stores a list of `(asm_line, php_line, php_col)` entries so tools and humans can correlate generated assembly back to the original PHP statements without needing full DWARF debug info.
-
-The AST optimizer intentionally still runs before backend selection. By the time the default EIR backend runs, constant expressions and some dead control-flow have already been removed, and EIR adds the function-wide value and control-flow shape that the linear-scan register allocator (`src/ir_passes/`, see [The IR](the-ir.md)) and future IR optimizations rely on. The legacy AST backend sees the same optimized AST when `--ast-backend` is selected.
-
-## Emit modes: executable vs cdylib
-
-Codegen runs in one of two emit modes selected by the `--emit` flag. `executable` (the default) produces the standalone-binary shape described throughout this page: a `main` entry point, top-level statements, and a process-exit epilogue. `cdylib` produces a shared library instead: no `main` body is emitted, and after the user functions a set of C-ABI trampolines is appended for every `#[Export]`-marked function plus the four `elephc_*` lifecycle entry points (see [Shared Libraries](../beyond-php/cdylib.md)).
-
-Cdylib emission also switches the emitter into position-independent mode (`pic_data_refs`): global data references emitted through the `abi::symbols` helpers resolve through the GOT (`@GOTPCREL` on x86_64, `:got:`/`:got_lo12:` on AArch64) instead of direct PC-relative addressing, and the runtime object is generated and cached separately in a PIC variant. On ELF targets a final pass (`src/codegen/visibility.rs`) appends `.hidden` directives for every internal global so the `.so` exports only its public ABI and internal runtime state cannot be preempted across loaded modules.
-
-## The Context
-
-**File:** `src/codegen/context.rs`
-
-The `Context` tracks state during code generation:
-
-```rust
-pub struct Context {
- pub variables: HashMap, // variable → type + stack offset
- pub stack_offset: usize, // next available stack slot
- pub loop_stack: Vec, // for break/continue
- pub return_label: Option, // for early returns
- pub functions: HashMap,
- pub function_variant_groups: HashSet, // include-loaded function dispatchers
- pub deferred_closures: Vec, // closures emitted after current function
- pub deferred_fiber_wrappers: Vec,
- pub deferred_callback_wrappers: Vec,
- pub constants: HashMap, // compile-time constants
- pub global_vars: HashSet, // globals active in current scope
- pub static_vars: HashSet, // statics active in current scope
- pub ref_params: HashSet, // pass-by-reference params
- pub local_ref_cell_flags: HashMap, // compiler-created ref cells
- pub in_main: bool, // whether we're compiling top-level code
- pub all_global_var_names: HashSet,
- pub all_static_vars: HashMap<(String, String), PhpType>,
- pub closure_sigs: HashMap,
- pub callable_param_sigs: HashMap<(String, String), FunctionSig>,
- pub closure_captures: HashMap>,
- pub runtime_callable_builtin_wrappers: HashMap,
- pub runtime_callable_extern_wrappers: HashMap,
- pub runtime_callable_static_method_wrappers: HashMap,
- pub first_class_callable_targets: HashMap,
- pub variable_fcc_label: HashMap,
- pub classes: HashMap,
- pub interfaces: HashMap,
- pub traits: HashSet,
- pub enums: HashMap,
- pub packed_classes: HashMap,
- pub current_class: Option,
- pub extern_functions: HashMap,
- pub extern_classes: HashMap,
- pub extern_globals: HashMap,
- pub return_type: PhpType,
- pub activation_prev_offset: Option,
- pub activation_cleanup_offset: Option,
- pub activation_frame_base_offset: Option,
- pub pending_action_offset: Option,
- pub pending_target_offset: Option,
- pub nested_concat_offset_offset: Option,
- pub pending_return_value_offset: Option,
- pub try_slot_offsets: Vec,
- pub next_try_slot_idx: usize,
- pub finally_stack: Vec,
-}
-```
-
-Each variable has a `VarInfo`:
-
-```rust
-pub struct VarInfo {
- pub ty: PhpType, // current runtime storage type
- pub static_ty: PhpType, // declared/static type retained for checks and calls
- pub stack_offset: usize, // offset from frame pointer (x29)
- pub ownership: HeapOwnership, // NonHeap / Owned / Borrowed / MaybeOwned
- pub epilogue_cleanup_safe: bool, // false for locals populated through still-ambiguous control-flow/alias paths
-}
-```
-
-`HeapOwnership` is a codegen-only ownership lattice used for heap-backed values flowing through stack slots:
-
-- `NonHeap` — integers, floats, bools, null, resources, raw pointers
-- `Owned` — this slot definitely owns the current heap-backed value
-- `Borrowed` — this slot currently aliases heap storage owned elsewhere
-- `MaybeOwned` — control flow merged heap-backed paths with different ownership states
-
-The lattice is now threaded through the main local-variable paths. Function epilogues re-enable cleanup only for slots classified as `Owned` and still marked `epilogue_cleanup_safe`; locals coming from still-ambiguous control-flow or aliasing paths are intentionally skipped. Special aliases such as `$this`, by-reference params, globals, and statics are explicitly kept out of epilogue cleanup because the current frame does not own their storage. Builtins that duplicate containers now also dispatch to dedicated `_refcounted` runtime helpers when their element/value types are heap-backed, so nested array/hash/object/string payloads are retained before the new container becomes an owner.
-
-The exception-related fields let codegen thread `try` / `catch` / `finally` through non-local control flow. Function and `_main` frames publish activation records into the runtime cleanup stack, pre-allocate handler slots for `setjmp` buffers, and use `finally_stack` plus the `pending_*` slots to defer `return`, `break`, and `continue` until the innermost `finally` body has run.
-
-### Label generation
-
-`ctx.next_label("while")` produces `_while_1`, `_while_2`, etc. A global atomic counter ensures labels never collide across functions or compilation units.
-
-## The Data Section
-
-**File:** `src/codegen/data_section.rs`
-
-String literals and float constants are stored in the `.data` section:
-
-```rust
-pub struct DataSection {
- entries: Vec<(String, Vec)>, // string label → bytes
- float_entries: Vec<(String, u64)>, // float label → bit pattern
- counter: usize, // next unique label suffix
- dedup: HashMap, String>, // avoid duplicate strings
- float_dedup: HashMap, // avoid duplicate floats
-}
-```
-
-When the codegen encounters `"hello"`, it calls `data.add_string(b"hello")` which returns a label (`_str_0`) and length (`5`). Identical strings are deduplicated — two `"hello"` literals share the same label.
-
-Floats are stored as their raw 64-bit IEEE 754 bit patterns (`.quad` directive).
-
-## Legacy AST expression codegen
-
-**Files:** `src/codegen/expr.rs`, `src/codegen/expr/`
-
-The frozen `--ast-backend` path still uses `emit_expr()` to take an expression node and emit code that leaves the result in the standard registers. The default backend reaches the same ABI/runtime helpers through EIR lowering in `src/ir_lower/` and instruction lowering in `src/codegen_ir/`. The top-level legacy `expr.rs` file mainly dispatches into focused helpers under `expr/` such as `scalars.rs`, `variables.rs`, `binops/`, `arrays.rs`, `compare/`, `calls/`, and `objects/`.
-
-| Type | Result location |
-|---|---|
-| `Int` / `Bool` / `Void` / `Resource` | `x0` |
-| `Float` | `d0` |
-| `Str` | `x1` (pointer), `x2` (length) |
-| `Array` / `AssocArray` / `Iterable` | `x0` (heap pointer) |
-| `Mixed` | `x0` (pointer to boxed mixed cell) |
-| `Object` | `x0` (heap pointer) |
-| `Callable` / `Pointer` | `x0` |
-| `Buffer` / `Packed` | `x0` (heap pointer) |
-| `Union` | `x0` (same as Mixed — boxed runtime-tagged payload) |
-
-### Expression AST dispatch coverage
-
-The legacy expression dispatcher is intentionally thin. It routes each `ExprKind`
-variant into one of the focused lowering paths below, while the EIR path mirrors the same PHP-visible coverage through `src/ir_lower/expr/`:
-
-| Variants | Lowering path |
-|---|---|
-| `StringLiteral`, `IntLiteral`, `FloatLiteral`, `BoolLiteral`, `Null`, `Negate`, `Not`, `BitNot`, `Cast`, `Print`, `ErrorSuppress` | Scalar, coercion, stdout, and diagnostics helpers |
-| `Variable`, `This`, `PreIncrement`, `PostIncrement`, `PreDecrement`, `PostDecrement`, `Assignment` | Variable load/store and assignment-expression helpers |
-| `BinaryOp`, `InstanceOf`, `NullCoalesce`, `Pipe`, `Ternary`, `ShortTernary`, `Throw` | Operator, comparison, call-pipe, branch, and exception-aware expression helpers |
-| `ArrayLiteral`, `ArrayLiteralAssoc`, `ArrayAccess`, `Spread`, `Match` | Indexed-array, associative-array, unpacking, string-indexing, and match-expression helpers |
-| `FunctionCall`, `NamedArg`, `ClosureCall`, `ExprCall`, `Closure`, `FirstClassCallable` | Shared call-argument planner, closure wrappers, and callable dispatch helpers |
-| `ConstRef`, `ClassConstant`, `ScopedConstantAccess`, `MagicConstant` | Compile-time constant and class-constant loading. `MagicConstant` should already be lowered by the frontend before codegen. |
-| `NewObject`, `NewDynamic`, `NewScopedObject`, `NewDynamicObject`, `PropertyAccess`, `DynamicPropertyAccess`, `NullsafePropertyAccess`, `NullsafeDynamicPropertyAccess`, `StaticPropertyAccess`, `MethodCall`, `NullsafeMethodCall`, `StaticMethodCall` | Object allocation (including `new $var()` via `NewDynamic` and the internal runtime-class-string factory `NewDynamicObject`), property/member access, nullsafe chain lowering, vtable dispatch, and late-static-binding helpers |
-| `PtrCast`, `BufferNew`, `Yield`, `YieldFrom` | Pointer/buffer extensions and generator state-machine lowering |
-
-### Intrinsic Calls
-
-Most method calls use the normal class metadata path: receiver evaluation, argument materialization, vtable or direct-method target selection, then a call to the emitted PHP method body. A small set of runtime-managed core objects cannot use the synthetic PHP stubs as their real implementation. For those, `src/intrinsics.rs` records an `IntrinsicCall` entry keyed by PHP class and method name, and `src/codegen/expr/objects/dispatch/intrinsic.rs` emits the direct runtime-helper call after the normal receiver and argument setup has already run.
-
-Current intrinsic call sites cover `Fiber` instance/static APIs and the runtime-backed `Generator` method surface. User classes with the same method names are not affected because the lookup includes the resolved class name.
-
-### Literals
-
-```php
-42 → mov x0, #42
-3.14 → adrp x9, _float_0@PAGE / add x9, ... / ldr d0, [x9]
-"hello" → adrp x1, _str_0@PAGE / add x1, ... / mov x2, #5
-true → mov x0, #1
-null → movz x0, #0xFFFE / movk x0, ... (load null sentinel)
-```
-
-Large integers (> 65535 or negative) use `movz` + `movk` sequences. See [ARM64 Instruction Reference](arm64-instructions.md#loading-large-constants).
-
-### The push/pop pattern for binary operations
-
-Binary operations like `$a + $b` need both operands in registers simultaneously, but `emit_expr` uses the same registers for every expression. The solution: **push the left result onto the stack, evaluate the right, then pop the left back**.
-
-```php
-$a + $b
-```
-
-```asm
-; Step 1: evaluate left ($a)
-ldur x0, [x29, #-8] ; x0 = $a
-
-; Step 2: push left onto stack
-str x0, [sp, #-16]! ; save x0 to stack, decrement sp
-
-; Step 3: evaluate right ($b)
-ldur x0, [x29, #-16] ; x0 = $b (overwrites left!)
-
-; Step 4: pop left back into a different register
-ldr x1, [sp], #16 ; restore left into x1, increment sp
-
-; Step 5: operate
-add x0, x1, x0 ; x0 = left + right
-```
-
-For strings (which use two registers), the push saves both `x1` and `x2`, and the pop restores them to `x3` and `x4`.
-
-For floats, the push/pop uses `d0`/`d1`:
-
-```asm
-str d0, [sp, #-16]! ; push left float
-; ... evaluate right → d0 ...
-ldr d1, [sp], #16 ; pop left float into d1
-fadd d0, d1, d0 ; d0 = left + right
-```
-
-### Comparison operators
-
-Comparisons use `cmp` (integer) or `fcmp` (float) followed by `cset`:
-
-```php
-$x > 5
-```
-
-```asm
-; ... push $x, evaluate 5 ...
-cmp x1, x0 ; compare left with right
-cset x0, gt ; x0 = 1 if greater, 0 otherwise
-```
-
-The result is always `x0` with value 0 or 1 (`PhpType::Bool`).
-
-### Short-circuit logical operators
-
-`&&`, `||`, `and`, and `or` use **short-circuit evaluation** — the right side isn't evaluated if the left determines the result. `xor` is also a logical operator, but it evaluates both operands because exclusive OR needs both truthiness values.
-
-```php
-$a && $b
-```
-
-```asm
-; evaluate $a
-cmp x0, #0
-b.eq _sc_end_1 ; if $a is falsy, skip $b entirely (result = 0)
-; evaluate $b
-cmp x0, #0
-cset x0, ne ; result = whether $b is truthy
-_sc_end_1:
-```
-
-### String concatenation
-
-The `.` operator calls the runtime's `__rt_concat`:
-
-```php
-"hello" . " world"
-```
-
-```asm
-; push left string (x1, x2)
-; evaluate right string → x1, x2
-; pop left → x3, x4
-; call concat
-mov x3, ... ; left ptr
-mov x4, ... ; left len
-bl __rt_concat ; result → x1 (ptr), x2 (len)
-```
-
-See [The Runtime](the-runtime.md) for how `__rt_concat` works.
-
-### Bitwise operations
-
-The bitwise operators (`&`, `|`, `^`, `~`, `<<`, `>>`) operate on integers and emit single ARM64 instructions:
-
-```php
-$a & $b → and x0, x1, x0 // bitwise AND
-$a | $b → orr x0, x1, x0 // bitwise OR
-$a ^ $b → eor x0, x1, x0 // bitwise XOR
-$a << $b → lsl x0, x1, x0 // logical shift left
-$a >> $b → asr x0, x1, x0 // arithmetic shift right (preserves sign)
-~$a → mvn x0, x0 // bitwise complement (one's complement)
-```
-
-Like other binary operations, bitwise ops use the push/pop pattern — evaluate left, push, evaluate right, pop left, apply operation.
-
-### Spaceship operator
-
-The spaceship operator (`<=>`) returns -1, 0, or 1 depending on the comparison result. It uses conditional select instructions:
-
-```php
-$a <=> $b
-```
-
-```asm
-; ... push $a, evaluate $b ...
-cmp x1, x0 ; compare left with right
-cset x0, gt ; x0 = 1 if left > right, else 0
-csinv x0, x0, xzr, ge ; if left < right: x0 = ~0 = -1 (all ones)
-```
-
-`csinv` (conditional select invert) inverts `xzr` (the zero register) to produce -1 when the condition is not met.
-
-For floats, `fcmp` replaces `cmp`, but the same `cset`/`csinv` pattern applies.
-
-### Array union
-
-When both operands of `+` are arrays, codegen routes the expression to PHP array-union lowering instead of numeric addition. Indexed arrays call `__rt_array_union`, which clones the left operand and appends only the right-side numeric suffix whose keys are missing from the left. Associative arrays call `__rt_hash_union`, which clones the left hash, walks the right hash in insertion order, and inserts only keys that are absent from the clone. Mixed indexed/associative operands return a hash result: `__rt_array_hash_union` maps left indexed positions into integer hash keys before merging the right hash, while `__rt_hash_array_union` clones the left hash and probes right indexed positions as integer keys.
-
-### Null coalescing operator
-
-The `??` operator returns the left operand if it is non-null, otherwise the right:
-
-```php
-$x ?? "default"
-```
-
-```asm
-; evaluate $x
-; compare with null sentinel (0x7FFFFFFFFFFFFFFE)
-b.ne _nc_done_1 ; if not null, keep left value
-; evaluate "default" ; otherwise, use right side
-_nc_done_1:
-```
-
-The null check compares the value against the [null sentinel](memory-model.md). The operator is right-associative (`$a ?? $b ?? $c` = `$a ?? ($b ?? $c)`).
-
-Null coalescing assignment is parsed as `$x = $x ?? expr`, but assignment lowering recognizes that exact shape and emits a conditional store:
-
-```php
-$x ??= "default";
-```
-
-The generated code loads `$x`, branches past the assignment when it is non-null, and evaluates/stores the right-hand side only on the null path. This preserves PHP's `??=` short-circuit behavior and avoids rewriting an already-owned heap value back into the same local slot.
-
-### Pipe operator
-
-PHP 8.5 `value |> callable` lowers through `src/codegen/expr/calls/pipe.rs`.
-`emit_expr()` first stores the left-hand value into a hidden local slot so the
-left side is observably evaluated before the callable target. It then builds a
-synthetic one-argument call using that hidden local as the single positional
-argument.
-
-The pipe lowering delegates to the existing call paths whenever possible:
-first-class function targets become `FunctionCall`, first-class static method
-targets become `StaticMethodCall`, first-class instance method targets become
-`MethodCall`, local callable variables become `ClosureCall`, and other callable
-expressions become `ExprCall`. Argument planning, ABI materialization,
-ownership, and diagnostics therefore stay aligned with ordinary calls.
-
-### Error-control operator
-
-The `@` operator lowers to a scoped runtime diagnostic-suppression pair:
-
-1. Call `__rt_diag_push_suppression`
-2. Evaluate the operand normally
-3. Preserve the operand result in the appropriate ABI result shape
-4. Call `__rt_diag_pop_suppression`
-5. Restore the operand result
-
-The exception handler frame also snapshots the current suppression depth before `setjmp()` and restores it after a `longjmp()` into catch dispatch. That prevents a thrown expression inside `@` from leaking warning suppression into later code.
-
-### Nullsafe operator
-
-The `?->` operator lowers nullable receivers through the boxed mixed path used by nullable and union storage. Codegen flattens postfix chains that contain a nullsafe segment, evaluates the base once, and branches to a shared boxed-`null` result when a nullsafe receiver is null. That branch skips the rest of the chain, including later ordinary `->` segments, array indexes, method arguments, and callable arguments. If an ordinary segment later receives a real null value from the non-short-circuited path, it still follows PHP's warning or fatal behavior.
-
-### Type coercions
-
-When types need to match (e.g., int + float), the codegen inserts conversion instructions:
-
-```asm
-scvtf d0, x0 ; convert signed integer (x0) → double (d0)
-fcvtzs x0, d0 ; convert double (d0) → signed integer (x0)
-```
-
-The `.` (concat) operator also coerces non-strings:
-- `Int` → calls `__rt_itoa` to get a string
-- `Float` → calls `__rt_ftoa`
-- `Bool true` → string "1"
-- `Bool false` / `Null` → empty string (length 0)
-
-### Constant references
-
-```php
-const MAX = 100;
-echo MAX;
-```
-
-Constants declared with `const` or `define()` are resolved at compile time. When the codegen encounters a `ConstRef`, it looks up the constant's value and emits it as a literal — `mov x0, #100` for an integer, or loads a string label from the data section. `define()` call sites still emit a per-constant runtime seen flag so the call returns `true` only for the first runtime definition and returns `false` with a suppressible warning on duplicate attempts.
-
-Enum cases reuse the same idea, but through enum metadata instead of scalar constants: parser output uses `ExprKind::ScopedConstantAccess` for `Color::Red`, and codegen detects enum receivers to load the canonical enum-case symbol emitted in runtime data. Helper builtins such as `Enum::from()` / `Enum::tryFrom()` lower through the checker/codegen enum tables carried in `Context`; a missing `Enum::from()` value constructs a catchable `ValueError` with the PHP-compatible backing-value message.
-
-### Pointer values and casts
-
-Pointer expressions are carried in `x0` as plain 64-bit addresses:
-
-- `ptr($var)` computes the address of a stack or global slot and returns it in `x0`
-- `ptr_null()` loads the zero address
-- `ptr_cast($p)` only changes the static type tag seen by the checker, so codegen emits the inner expression and leaves the address unchanged
-- Pointer printing routes through `__rt_ptoa`, which formats the address as a `0x...` string before writing
-
-### Buffer allocation and packed hot-path access
-
-`buffer_new(len)` lowers directly from `ExprKind::BufferNew`: codegen evaluates the element count, loads the checked element stride from the type metadata, and calls `__rt_buffer_new`. The resulting pointer in `x0` references a contiguous `[length][stride][payload...]` block rather than a PHP array/hash structure.
-
-When `T` is a scalar POD type, reads and writes use direct address arithmetic from the buffer base plus `index * stride`. When `T` is a `packed class`, codegen combines the buffer element stride with the field offset from `packed_classes` metadata and emits direct typed loads/stores into the packed payload.
-
-### Function calls
-
-```php
-my_func($a, $b, $c)
-```
-
-1. Evaluate each argument and push results onto the stack
-2. Pop arguments into the correct ABI registers (`x0`-`x7` for ints, `d0`-`d7` for floats, two registers per string)
-3. If a heap-backed argument is being borrowed from an existing owner (for example a local variable or container read), retain it before passing it to the callee
-4. `bl _fn_my_func` — branch with link (saves return address)
-5. Result is in `x0`/`d0`/`x1`+`x2` depending on return type
-
-Named-argument calls split evaluation order from ABI order. `src/codegen/expr/calls/args.rs` evaluates source arguments left-to-right, stores any out-of-order values in temporary slots, validates spread prefixes after later named expressions have run, then materializes the final parameter list in ABI order. Spread prefixes before named arguments are evaluated once; multiple prefix spreads are combined before runtime length/overwrite checks, and too-short positional spreads for required parameters fail instead of reading beyond the array payload. Runtime associative-array spreads are dynamic named providers: they look up string keys by parameter name, fall back to numeric keys for positional slots, and let the per-parameter missing/default branch decide whether a required value is present. Built-in and extern named calls use the same source-order pre-evaluation step before their normalized positional emitters run; mutating built-ins mark their target parameter as ref-like so pre-evaluation does not redirect writes into a temporary. Extern calls preserve PHP source evaluation order first and only then load C ABI registers.
-
-## Closure codegen
-
-### Anonymous functions and arrow functions
-
-Closures (`function($x) { ... }`) and arrow functions (`fn($x) => ...`) are compiled as separate labeled functions, similar to user-defined functions. The key difference is **deferred emission** — the closure body is not emitted inline. Instead:
-
-1. **At the closure expression site**: the codegen generates a unique entry label (e.g., `_closure_1`), creates a static callable descriptor in `.data`, and loads the descriptor address into `x0`. The descriptor includes side records for signature/default/by-reference/variadic metadata, capture and hidden-parameter bindings, and invocation shape. The descriptor pointer is then stored in the variable's stack slot as a `Callable` (8 bytes).
-
-2. **The body is deferred**: the closure's parameter list, body statements, captured variables, and label are pushed onto `ctx.deferred_closures`. This avoids emitting function code in the middle of the current function's instruction stream.
-
-3. **After `_main`**: all deferred closures are emitted as standalone labeled functions (prologue, body, epilogue), just like user-defined functions.
-
-### `use` captures
-
-Closures can capture variables from the enclosing scope via `use ($var1, $var2)`:
-
-```php
-$greeting = "Hello";
-$fn = function($name) use ($greeting) {
- echo $greeting . " " . $name;
-};
-```
-
-Only explicit `use (...)` captures are stored in the AST and forwarded as hidden closure arguments. Arrow functions are still parsed as closures, but they use `is_arrow = true` with an empty `captures` list.
-
-The AST stores captured variable names in the `captures` field of the `Closure` expression. At the call site, captured variables are passed as **extra arguments** after the explicit arguments:
-
-1. **At the closure expression site**: the captured variable names and types are recorded in `ctx.closure_captures` alongside the deferred closure.
-2. **At the call site** (`$fn("World")`): the codegen looks up the captured variables, evaluates them from the caller's scope, and passes them as additional arguments after the explicit ones.
-3. **In the closure body**: the captured values arrive as extra parameters and are stored in local stack slots, making them accessible like regular local variables.
-
-This means captures are passed **by value** — modifying a captured variable inside the closure does not affect the outer scope (matching PHP semantics).
-
-### Closure calls
-
-When a closure variable is called (`$fn(1, 2)`), the codegen:
-
-1. Evaluates each argument and pushes results onto the stack
-2. Loads the closure descriptor from the variable's stack slot into `x9`
-3. Loads the native entry address from the descriptor's entry slot
-4. Pushes `x9` temporarily while popping arguments into ABI registers
-5. Pops `x9` back and calls `blr x9` — an indirect branch through a register
-
-`blr` (Branch with Link to Register) is like `bl` but the target address comes from a register rather than a label. This is what makes closures work — the compiler doesn't know at compile time which function will be called, so it uses an indirect jump.
-
-### Closures as callback arguments
-
-Built-in functions like `array_map`, `array_filter`, `array_reduce`, `array_walk`, `usort`, `uksort`, `uasort`, and `preg_replace_callback` accept callback values. PHP callable storage carries a descriptor pointer; callback runtimes receive the native entry loaded from that descriptor plus an optional environment pointer, then call the entry via `blr`.
-
-For captured closures passed through callback runtimes such as `array_map`, `array_filter`, `array_reduce`, `array_walk`, `usort`, `uksort`, `uasort`, and `preg_replace_callback`, codegen builds a temporary callback environment containing the original entry address plus its hidden `use (...)` values. The runtime passes that environment to a generated callback wrapper, and the wrapper re-materializes the original visible arguments plus hidden captures before calling the closure. `array_map()`, `array_filter()`, `array_reduce()`, `array_walk()`, `usort()`, `uksort()`, `uasort()`, and `preg_replace_callback()` route descriptor-valued callable variables and `callable` parameters through descriptor-backed callback wrappers, so stored closure captures and first-class-callable receiver environments come from the descriptor rather than from source locals that may have changed. Those callback runtimes also select descriptor cases for runtime callable-array variables such as `[$object, $method]` or `[$class, $method]`, then build the same descriptor callback environment after the runtime receiver/class and method strings match a public method. These runtimes can also route branch-shaped captured callable expressions through descriptor-backed callback wrappers: the environment stores the selected descriptor, the wrapper boxes visible arguments into a temporary Mixed argument array, invokes the descriptor's uniform invoker, casts or discards the boxed result for the callback runtime, and preserves runtime-loop callee-saved registers around the generated invoker. `CallbackFilterIterator` and `RecursiveCallbackFilterIterator` use a heap-backed variant of that descriptor environment so branch-selected captured descriptors and runtime-selected callable-array variable or literal descriptors stay attached to the iterator object and recursive child filters. `iterator_apply()` uses the same uniform descriptor invoker for branch-shaped captured callbacks and runtime-selected callable-array variables, with its callback-argument array evaluated once before the iterator loop and reused for each invocation. String-producing descriptor-backed `array_map()` and `preg_replace_callback()` calls detach string results from boxed Mixed values before releasing the invoker result. `call_user_func()` and `call_user_func_array()` can dispatch through callable descriptors directly: closure and first-class-callable values with hidden context allocate runtime descriptor copies whose static header is followed by 16-byte capture slots. Dynamic callback dispatch uses `codegen::callable_dispatch` cases carrying descriptor labels with PHP-visible names, signature metadata, defaults, by-reference flags, variadic metadata, and hidden captures. Descriptor-selected callbacks compare entry slots with user functions and emitted closure/FCC wrappers; when the matched case exposes an invoker, the callsite switches back to the actual descriptor and calls the uniform wrapper. Runtime string-name callbacks compare case-insensitively against user-function, extern-wrapper, builtin-wrapper, and static-method names, materialize the matched descriptor, then call the descriptor's uniform invoker wrapper. Compile-time static-method callable arrays also use static-method descriptors for direct variable and literal calls, `call_user_func()` calls, and `call_user_func_array()` calls, including associative variadic tails and positional prefixes before indexed spreads. Direct instance-method callable-array variable calls read the receiver from the stored callable array slot, while direct literal calls evaluate the receiver before visible call arguments; both then build the receiver-prefixed descriptor argument container. Direct invokable-object variable calls build the same receiver-prefixed descriptor argument container from the local object and use the object-invoke invocation shape. Receiver-bound `call_user_func()` calls with one spread source pass that source container through the receiver-prefix normalizer and descriptor invoker instead of falling back to direct method emission. Receiver-bound calls with positional prefixes followed by indexed spreads build a raw Mixed-slot indexed argument array, prepend the receiver as descriptor slot zero, append the prefix values, merge cloned indexed spread tails, and then let the descriptor normalizer clone and box that container for invocation. Direct branch-selected expression calls that combine spread prefixes with named arguments build a temporary Mixed hash from the source-order positional prefix plus named suffix entries, while direct calls whose only argument segment is one spread source pass that source container to the same descriptor invoker. Direct descriptor calls with positional prefixes followed by indexed spreads use the same raw Mixed-slot indexed container builder, without adding a receiver slot. Indirect calls on callable variables or array elements fall back to the descriptor invoker whenever the local callsite no longer has a trustworthy static signature or capture list; variable arguments in indexed containers, positional prefixes before indexed spreads, and associative named-argument hashes are encoded as ref-cell markers so the generated invoker can apply runtime by-reference flags from the descriptor or dereference the same marker for by-value parameters. The invoker receives `(descriptor, boxed argument container)`, loads the entry slot itself, reloads hidden captures from the descriptor when present, branches on the boxed container tag for indexed-array or associative-hash materialization, unboxes boxed array/hash payloads for declared array parameters, applies the matched signature, and returns a boxed `mixed`. `call_user_func_array()` descriptor invokers receive a cloned temporary argument container widened to boxed `Mixed`, so wrappers are shared by callable signature instead of by the caller's static element type or argument-array shape. When `call_user_func_array()` targets a by-reference callback and receives a literal argument array, codegen passes frame-slot addresses for variable elements in by-reference positions instead of loading array payload values.
-
-Extern `callable` parameters that receive descriptor values use a separate C-ABI trampoline path. Codegen emits one mutable descriptor slot and one trampoline symbol per callsite; the extern call stores the retained descriptor in that slot, passes the trampoline address to C, and the trampoline boxes incoming scalar/pointer callback arguments before calling the descriptor invoker. This lets C APIs with only a raw function-pointer slot retain closure captures, first-class method receivers, and branch-selected descriptor state without changing the C signature.
-
-First-class callable wrappers reuse this hidden argument path when the callable target carries context. `$obj->method(...)` records the receiver as a hidden capture; non-local receiver expressions are evaluated once into a hidden temporary before wrapper creation. `static::method(...)` records the forwarded called-class id, or `$this` in an instance method, so late static binding is preserved for direct callable calls and for callback paths that forward an environment.
-
-## Generator codegen
-
-**Files:** `src/codegen/functions/generator/`, `src/codegen/runtime/generators/`, `src/codegen/expr/objects/dispatch/vtable.rs`
-
-A function or closure body that contains `yield` does not emit as an ordinary function body. Codegen emits two symbols:
-
-1. `_fn_` — a wrapper that allocates a heap `GeneratorFrame`, stamps it as the built-in `Generator` object, copies supported scalar parameters/captures into frame slots, zeroes local slots, and returns the frame pointer.
-2. `_fn___resume` — a state-machine entry point. State `0` enters the body; each yield gets a numbered resume label. At a yield, the resume function boxes the key/value into Mixed cells, replaces the frame's last key/value slots, stores the next state index, and returns to the caller.
-
-Generator closures reuse the same path as ordinary deferred closures, but their hidden `use (...)` captures are copied into the generator frame alongside visible parameters. `yield from` stores the active inner generator in the frame's `delegated_iter` slot and resumes it through the same `__rt_gen_*` runtime helpers used by user-visible `Generator` methods.
-
-The generated `Generator` object has a custom payload layout rather than ordinary PHP properties. Method dispatch for `current`, `key`, `valid`, `next`, `rewind`, `send`, `throw`, and `getReturn` is intercepted before vtable lookup and routed directly to `__rt_gen_*`. Both AArch64 and Linux `x86_64` follow the same high-level state-machine model; the wrapper, resume dispatcher, and runtime helper emitters select target-specific instruction sequences internally.
-
-## Fiber codegen
-
-**Files:** `src/codegen/expr/objects/allocation.rs`, `src/codegen/expr/objects/dispatch/`, `src/codegen/expr/objects/fiber_callable.rs`, `src/codegen/expr/objects/fiber_wrapper.rs`, `src/codegen/functions/fiber_wrapper.rs`, `src/codegen/runtime/fibers/`
-
-`Fiber` is a built-in class, but codegen does not lower it through the ordinary object constructor and method-dispatch path. `new Fiber($callable)` is intercepted, materializes raw string callbacks, callable-array, and invokable-object shapes into callable descriptors, then delegates to `__rt_fiber_construct`, which allocates the larger runtime-managed Fiber object, creates its guarded native stack, stores the callable descriptor pointer, and records the generated wrapper label that adapts Fiber start values to the callback ABI. Stored instance-method callable arrays bind the receiver from `$callback[0]` into a descriptor capture so later writes to the source callable variable cannot change the Fiber body. Inline receiver expressions, invokable-object expressions, and runtime-selected callable-array variables or literals are evaluated once at construction time before the receiver is stored in the descriptor.
-
-Each accepted Fiber callback gets a deferred entry wrapper emitted next to deferred closure bodies. The wrapper runs on the Fiber stack, reloads boxed `start()` values from `start_args[0..6]`, unboxes them to the callback's declared parameter types, reloads hidden captures or method receivers from the stored callable descriptor's runtime capture slots, loads the original entry from that descriptor, calls it with normal ABI materialization, and boxes the terminal return value back to `mixed`.
-
-Instance and static Fiber methods are also intercepted:
-
-- `$fiber->start(...)` spills up to seven boxed `mixed` start arguments into the Fiber object before calling `__rt_fiber_start`; callable captures and receivers are already stored in the descriptor and are not overwritten by start arguments.
-- `$fiber->resume($value)`, `$fiber->throw($exception)`, `$fiber->getReturn()`, and the state predicates branch directly to their `__rt_fiber_*` runtime helpers.
-- `Fiber::suspend($value)` and `Fiber::getCurrent()` lower to runtime helper calls instead of ordinary static method dispatch.
-
-Both AArch64 and Linux `x86_64` use the same high-level lowering. The final register moves, temporary-stack layout, direct/indirect calls, and frame setup go through the ABI module so the Fiber wrapper follows each target's calling convention rather than hardcoding ARM64 register names in shared code.
-
-## Associative array codegen
-
-Associative arrays use a hash table stored on the heap. The codegen differs from indexed arrays at every level:
-
-### Literal creation
-
-```php
-$m = ["name" => "Alice", "age" => "30"];
-```
-
-1. Call `__rt_hash_new` with initial capacity and value type tag → `x0` = hash table pointer
-2. For each key-value pair: evaluate key (string → `x1`/`x2`), evaluate value, call `__rt_hash_set`
-
-### Access
-
-```php
-$m["name"]
-```
-
-1. Save hash table pointer on stack
-2. Evaluate key expression → `x1`/`x2` (string)
-3. Call `__rt_hash_get` → `x0` = found (0/1), `x1` = value_lo, `x2` = value_hi, `x3` = per-entry value tag
-4. Move result to standard registers based on value type; if the static result is `Mixed`, box the payload into a heap cell first
-
-### Functions on associative arrays
-
-Builtin functions like `array_key_exists`, `in_array`, `array_keys`, `array_values` dispatch on the array type at compile time:
-- `PhpType::Array` → use indexed runtime routines (e.g., bounds check, linear scan)
-- `PhpType::AssocArray` → use hash table routines (e.g., `__rt_hash_get`, `__rt_hash_iter_next`)
-
-### `foreach` over associative arrays
-
-When `foreach` iterates a `PhpType::AssocArray`, the lowering differs from indexed arrays:
-
-1. Save the hash pointer and an iteration cursor on the stack (`0` means "start from header.head")
-2. Call `__rt_hash_iter_next`
-3. If `x0 == -1`, exit the loop
-4. Otherwise save the returned cursor, store `x1`/`x2` into the optional key variable, and store `x3`/`x4`/`x5` into the value variable according to the inferred element type; `Mixed` loop variables reuse or allocate boxed mixed cells as needed
-5. Emit the loop body, then branch back to the iterator call
-
-This preserves PHP-style insertion order because `__rt_hash_iter_next` walks the hash table's linked insertion-order chain rather than scanning physical buckets.
-
-See [The Runtime](the-runtime.md) for details on hash table routines and [Memory Model](memory-model.md) for the hash table memory layout.
-
-## String indexing codegen
-
-The same `ArrayAccess` AST node also covers string indexing such as `$str[1]` or `$str[-1]`. In `src/codegen/expr/arrays.rs`, `emit_array_access()` checks for `PhpType::Str` and lowers the operation inline:
-
-1. Save the string pointer/length while evaluating the index expression
-2. Adjust negative indices relative to the end of the string
-3. Clamp offsets below `-len` to the start and offsets past the end to the end
-4. Advance the string pointer to the selected byte
-5. Return either a one-character string (`x1` + `x2 = 1`) or an empty string when the offset is out of bounds
-
-So the behavior is slice-like, but it does not call `substr()` or a dedicated runtime helper.
-
-## Statement codegen
-
-**Files:** `src/codegen/stmt.rs`, `src/codegen/stmt/`
-
-`emit_stmt()` is similarly split across focused helpers under `stmt/`: assignment/storage logic, array statements, include-once guards, and control-flow lowering (`branching`, `foreach`, `loops`) now live outside the thin top-level dispatcher. `stmt/includes.rs` emits the `.comm` flag and branch sequence used by resolver-generated `IncludeOnceMark` and `IncludeOnceGuard` nodes, plus the active-variant store used when an include point loads a hidden function implementation. Small shared statement-side policies such as borrowed-result retention, local-slot ownership updates, static-init guards, and indexed-array metadata stamping now sit in `stmt/helpers.rs` instead of bloating `stmt.rs` itself. Storage lowering is now split too: `stmt/storage.rs` is just a boundary, with `storage/locals.rs` handling ordinary global/static symbol access and `storage/extern_globals.rs` owning extern-global load/store conventions. Assignment lowering is also split one level deeper: `stmt/assignments/locals.rs` handles plain local/global/ref writes, while `stmt/assignments/properties.rs` now orchestrates property writes across `properties/target.rs`, `magic_set.rs`, and `storage.rs`. Array-index writes follow the same pattern now: `stmt/arrays/assign.rs` is just a dispatcher, while `stmt/arrays/assign/buffer.rs` and `assoc.rs` isolate the non-indexed-container paths, and `stmt/arrays/assign/indexed.rs` now orchestrates the indexed-array write across `indexed/prepare.rs`, `normalize.rs`, `store.rs`, and `extend.rs`. Branching lowering now follows that same shape too: `stmt/control_flow/branching.rs` is just a boundary, while `branching/if_stmt.rs` and `branching/switch_stmt.rs` own the distinct lowering paths. Exception lowering follows the same structure: `stmt/control_flow/exceptions.rs` orchestrates the high-level try/catch/finally flow, while `exceptions/handlers.rs`, `catches.rs`, and `finally.rs` own the lower-level handler stack, catch matching, and pending-action/finally dispatch mechanics. Loop lowering is split too: `stmt/control_flow/loops.rs` is now just a boundary, with `loops/iterative.rs` handling `for`/`while`/`do...while` and `loops/exits.rs` owning `break`/`continue`/`return`. `foreach` lowering now follows the same pattern: `stmt/control_flow/foreach.rs` dispatches between `foreach/indexed.rs`, `foreach/assoc.rs`, and `foreach/iterator.rs` for arrays, hashes, `Iterator`, `IteratorAggregate`, and object-backed `iterable` values.
-
-### Statement AST dispatch coverage
-
-The statement dispatcher maps `StmtKind` variants to storage, control-flow,
-declaration, include, or extension paths:
-
-| Variants | Lowering path |
-|---|---|
-| `Echo`, `ExprStmt`, `Throw`, `Synthetic` | Direct statement helpers, expression dispatch, exception throw, or already-lowered statement sequences |
-| `Assign`, `RefAssign`, `TypedAssign`, `ArrayAssign`, `NestedArrayAssign`, `ArrayPush`, `ListUnpack`, `PropertyAssign`, `StaticPropertyAssign`, `PropertyArrayPush`, `PropertyArrayAssign`, `StaticPropertyArrayPush`, `StaticPropertyArrayAssign` | Local/global/static storage, reference aliasing, array storage, destructuring, and property storage helpers |
-| `If`, `IfDef`, `While`, `DoWhile`, `For`, `Foreach`, `Switch`, `Try`, `Break`, `Continue`, `Return` | Branching, compile-time conditional lowering, loops, foreach dispatch, switch lowering, exception/finally control flow, loop exits, and return epilogues |
-| `Include`, `IncludeOnceMark`, `IncludeOnceGuard`, `FunctionVariantGroup`, `FunctionVariantMark` | Resolver-produced include guards and include-loaded function variant activation |
-| `NamespaceDecl`, `NamespaceBlock`, `UseDecl`, `ConstDecl` | Mostly frontend/name-resolution artifacts; constants remain available through the codegen context |
-| `FunctionDecl`, `ClassDecl`, `EnumDecl`, `InterfaceDecl`, `TraitDecl`, `PackedClassDecl` | Deferred function/method emission and metadata-driven class, enum, interface, trait, and packed-record setup |
-| `Global`, `StaticVar` | Symbol-backed local aliases and per-function static storage |
-| `ExternFunctionDecl`, `ExternClassDecl`, `ExternGlobalDecl` | Registration-only at statement emission; expression/call lowering uses the collected FFI metadata |
-
-### Echo and print
-
-```php
-echo $x;
-echo "a", "b";
-$status = print $x;
-```
-
-1. Evaluate each `echo` expression in source order → result in registers
-2. Check for null/false (skip printing if so — matches PHP behavior where `echo false` prints nothing)
-3. Call `emit_write_stdout()` from the [ABI module](#the-abi-module)
-
-`print` expressions reuse the same stdout helper, then write integer `1` into
-the expression result register so the value can be assigned, concatenated, or
-passed into another expression.
-
-### Assignment
-
-```php
-$x = expr;
-```
-
-1. Evaluate expression
-2. If the result is a borrowed heap value, retain it before the local slot becomes a new owner
-3. Release the previous owned heap value from `$x` when overwriting a heap-backed slot
-4. `emit_store()` — write result to `$x`'s stack slot and classify the local slot as `Owned` for heap-backed types
-
-Typed local declarations such as `int $x = 42;` or `buffer $xs = buffer_new(8);` share the same storage path after the checker has resolved `StmtKind::TypedAssign` into a concrete `PhpType`.
-
-### Constant declaration
-
-```php
-const MAX = 100;
-```
-
-`ConstDecl` registers a compile-time constant. The value is stored in the codegen context and substituted directly wherever the constant is referenced via `ConstRef`. No runtime storage or stack allocation is needed.
-
-### Global variables
-
-```php
-$x = 10;
-function inc() {
- global $x;
- $x++;
-}
-```
-
-The `global` statement inside a function declares that a variable refers to global storage rather than a local stack slot. The codegen uses BSS-allocated storage (`_gvar_NAME`, 16 bytes each) for global variables:
-
-1. At `global $x;`: the variable is marked as global in the context. The current value is loaded from `_gvar_x` into the local stack slot.
- The local view is tracked as a borrowed alias of the BSS-backed owner.
-2. On assignment to a global variable: the codegen writes to the BSS storage (`_gvar_x`) via `adrp`/`add`/`str` instead of (or in addition to) the local stack slot.
-3. In `_main`: when the main scope assigns to a variable that any function declares as `global`, the value is also written to `_gvar_NAME` so that functions can read it.
-
-### Extern declarations
-
-`ExternFunctionDecl`, `ExternClassDecl`, and `ExternGlobalDecl` are registration-only statements during codegen. Their metadata has already been collected by the type checker and copied into `Context`, so `emit_stmt()` treats the declarations themselves as no-ops while later expression codegen uses the recorded FFI data.
-
-Extern globals are loaded through GOT-relative addressing (`adrp ...@GOTPAGE` / `ldr ...@GOTPAGEOFF`) instead of ordinary stack or BSS slots.
-
-### Static variables
-
-```php
-function counter() {
- static $count = 0;
- $count++;
- echo $count;
-}
-```
-
-Static variables persist their value across function calls. Each static variable gets two BSS slots:
-
-- `_static_FUNC_VAR` (16 bytes) — stores the persisted value
-- `_static_FUNC_VAR_init` (8 bytes) — initialization flag (0 = not yet initialized)
-
-The codegen for `static $count = 0;`:
-
-1. Check the init flag — if already initialized, skip to loading the persisted value
-2. If not initialized: evaluate the init expression, store to the BSS slot, set the init flag to 1
-3. Load the persisted value into the local stack slot
-
-That per-call local slot is tracked as `Borrowed`; the persisted static storage remains the long-lived owner.
-
-At function epilogue, variables marked as static are written back to their BSS storage.
-
-### Static properties
-
-Static properties use one global 16-byte storage slot per effective declaring class property:
-
-- `_static_prop_CLASS_PROP` stores the current value payload
-- inherited static properties point back to the declaring class slot until a subclass redeclares the property
-- redeclared static properties get a separate subclass slot
-
-At program startup, `_main` evaluates static property defaults and stores them into these slots before user statements run. Reads such as `ClassName::$count` load directly from the resolved symbol, and assignments store the new result back to the same symbol after type coercion and previous-value release for heap-backed values. `static::$count` uses the forwarded called-class id (or `$this` in instance methods) to select a redeclared descendant slot at runtime; if that late-bound slot is private and inaccessible from the current method scope, generated code emits a fatal private-static-property diagnostic.
-
-### List unpacking
-
-```php
-[$a, $b, $c] = [10, 20, 30];
-```
-
-Simple local positional destructuring remains a `ListUnpack` statement. The codegen:
-
-1. Evaluates the right-hand side expression (an array)
-2. Saves the array pointer on the stack
-3. For each variable in the list: loads the element at the corresponding index from the array, stores it into the variable's stack slot, and marks heap-backed elements as borrowed aliases of the source container
-
-Richer PHP destructuring patterns are lowered by the parser into ordinary synthetic assignments before checking and codegen. Skipped entries simply emit no assignment, keyed entries become array reads with the given key, nested patterns bind a hidden temporary for the nested source array, and non-local targets reuse the same assignment emitters as `$arr[$i] = ...`, `$arr[] = ...`, `$obj->prop = ...`, and static-property writes.
-
-### If / Elseif / Else
-
-```php
-if ($cond1) { body1 } elseif ($cond2) { body2 } else { body3 }
-```
-
-```asm
-; evaluate $cond1
-cmp x0, #0
-b.eq _elseif_1 ; skip to next branch if falsy
-
-; body1
-b _end_if_1 ; done — skip all remaining branches
-
-_elseif_1:
-; evaluate $cond2
-cmp x0, #0
-b.eq _else_1
-
-; body2
-b _end_if_1
-
-_else_1:
-; body3
-
-_end_if_1:
-```
-
-### While loop
-
-```php
-while ($cond) { body }
-```
-
-```asm
-_while_1: ; ← continue jumps here
-; evaluate $cond
-cmp x0, #0
-b.eq _end_while_1 ; exit if falsy ← break jumps here
-
-; body
-b _while_1 ; loop back
-
-_end_while_1:
-```
-
-### For loop
-
-```php
-for ($i = 0; $i < 10; $i++) { body }
-```
-
-```asm
-; emit init ($i = 0)
-
-_for_1:
-; evaluate condition ($i < 10)
-cmp x0, #0
-b.eq _end_for_1
-
-; body
-
-_for_cont_1: ; ← continue jumps here
-; emit update ($i++)
-b _for_1
-
-_end_for_1: ; ← break jumps here
-```
-
-### Foreach
-
-```php
-foreach ($arr as $v) { body }
-```
-
-For indexed arrays:
-
-1. Save array pointer, length, and index counter on the stack (3 × 16-byte slots)
-2. Loop: load element at current index, unbox through the runtime `value_type` tag when the static element type is `Mixed`, store to `$v`, and classify heap-backed loop variables as borrowed aliases of the iterated container
-3. Branch back to condition check
-4. Cleanup: deallocate the 48 bytes
-
-For associative arrays, see [Associative array codegen](#associative-array-codegen): the loop stores a hash pointer plus cursor, then advances with `__rt_hash_iter_next`.
-
-For `Iterator` objects, codegen parks the receiver in a 16-byte stack slot, dispatches `rewind()`, then drives the loop through `valid()`, `key()`, `current()`, and `next()`. Keys and values are boxed into `Mixed` because the concrete runtime payload can vary per iterator implementation. `IteratorAggregate` values dispatch `getIterator()` first, then reuse the same iterator loop path. Values typed as `iterable` branch through runtime heap-kind and interface metadata so arrays, direct `Iterator` objects, and aggregate-backed objects select the correct lowering.
-
-Before the first `valid()` call, foreach target slots are normalized to boxed `Mixed`. That keeps empty iterators compatible with PHP: existing target variables keep a valid mixed cell, fresh loop variables remain null-like, and receiver aliases stay live until loop cleanup.
-
-### Break / Continue
-
-`break` emits a `b` (unconditional jump) to the selected loop/switch end label.
-`continue` emits a `b` to the selected continue label (the condition check for
-`while`, the update for `for`, or the switch end label for PHP-style
-`continue` inside `switch`).
-
-The `loop_stack` in the Context tracks labels for nested loops and switches.
-Multi-level forms such as `break 2;` and `continue 2;` index back through that
-stack. Each `LoopLabels` entry also carries an `sp_adjust` field so multi-level
-exits and returns can undo any skipped switch-subject temporary stack slots
-before jumping to the selected target or shared function epilogue. If the exit
-crosses a `finally`, codegen records the selected target and runs the active
-`finally` chain before resuming the branch.
-
-The type checker rejects `break` / `continue` that would jump out of a
-`finally` body, so codegen only has to route legal exits from protected `try` or
-`catch` bodies through `finally_stack`.
-
-### Exceptions and `finally`
-
-Exception lowering lives in `src/codegen/stmt/control_flow/exceptions.rs`. The basic strategy is:
-
-1. Evaluate the thrown object and publish it to `_exc_value`
-2. Call `__rt_throw_current`, which unwinds activation records and `longjmp`s into the nearest handler
-3. For `try`, emit a `_setjmp` resume point plus a linked handler record in `_exc_handler_top`
-4. Test each catch target by class id or interface id through `__rt_exception_matches`
-5. Route `return`, `break`, `continue`, and rethrow through `finally_stack` so every enclosing `finally` runs before control leaves the protected region. The checker rejects `break` / `continue` that would originate inside a `finally` and target an outer loop/switch.
-
-This means `finally` is part of ordinary control-flow lowering, not a separate runtime pass. The runtime only unwinds frames and chooses the landing pad; the compiler-generated labels still decide whether execution resumes in a matching `catch`, in a `finally`, or in an outer handler.
-
-### Switch
-
-```php
-switch ($x) {
- case 1: echo "one"; break;
- case 2: echo "two"; break;
- default: echo "other"; break;
-}
-```
-
-1. Evaluate the subject expression once and push the result onto the stack
-2. For each case: pop subject, evaluate case value, compare (`cmp` + `b.ne` for integers, `bl __rt_str_eq` for strings)
-3. If match: emit case body, which may contain `break` (jump to end label) or fall through to next case
-4. Default case: emit body unconditionally
-5. End label after all cases
-
-The switch uses the loop stack so that `break` inside a case body jumps to the switch end label rather than an enclosing loop.
-
-### Match expression
-
-Match is an expression (returns a value), not a statement. It uses strict comparison (`===`) and has no fall-through:
-
-```php
-$result = match($x) {
- 1 => "one",
- 2 => "two",
- default => "other",
-};
-```
-
-1. Evaluate subject, push onto stack
-2. For each arm: compare subject with each pattern in the arm's pattern list
-3. If any pattern matches: evaluate the arm's result expression, jump to end
-4. Default arm: evaluate result unconditionally
-5. Result is left in standard registers (`x0`, `d0`, or `x1`/`x2`)
-
-## Class codegen
-
-### Object allocation (`new ClassName(...)`)
-
-When the codegen encounters a `NewObject` expression:
-
-1. **Calculate object size**: `8 + (num_properties × 16) + dyn_props_slot` — 8 bytes for the class ID, 16 bytes per property across the full inherited layout, plus one optional 8-byte slot for the dynamic-property hash pointer when the class carries `#[\AllowDynamicProperties]`
-2. **Allocate heap memory**: call `__rt_heap_alloc` with the calculated size
-3. **Zero-initialize**: clear all property slots to zero
-4. **Store class ID**: write the class identifier at offset 0
-5. **Apply defaults**: for properties with default values, evaluate and store them at their fixed offsets
-6. **Call constructor**: if the class exposes `__construct`, pass the new object pointer as `x0` (`$this`) followed by the constructor arguments, then branch to the implementation label recorded in class metadata (which may come from an inherited constructor)
-
-Classes declared with the PHP 8.2 `#[\AllowDynamicProperties]` attribute reserve a trailing per-object hash slot so undeclared property writes/reads can be routed through a runtime side table instead of failing at compile time.
-
-The result is the object pointer in `x0`.
-
-### Attribute reflection objects
-
-`new ReflectionClass(...)`, `new ReflectionMethod(...)`, and `new ReflectionProperty(...)` are intercepted by `src/codegen/expr/objects/reflection.rs` instead of relying on ordinary user-defined constructor bodies. The type checker has already forced their class/member arguments to compile-time strings after normal call-argument planning, so codegen can look up the target `ClassInfo` directly and populate the private `__attrs` slot with a freshly built `array`.
-
-`src/codegen/reflection.rs` owns the shared materialization path. It allocates each synthetic `ReflectionAttribute`, writes the resolved `__name`, builds the `array` `__args` payload from supported literal attribute arguments, and stores a deterministic `__factory` id. `ReflectionAttribute::newInstance()` is then generated in `src/codegen/class_methods.rs` as a branch table over those factory ids; each branch constructs the real attribute class with the captured literal args, and the fallback returns `null` when no defined attribute class can be materialized.
-
-The `_class_attribute_*` runtime data tables still emit class-level attribute metadata from the same `ClassInfo` fields, but the supported Reflection owner constructors are compile-time materialized and do not perform runtime name lookups for classes, methods, or properties.
-
-### Type checks (`$obj instanceof ClassName`)
-
-`ExprKind::InstanceOf` evaluates the left-hand side exactly once, materializes the target class or interface id from emitted metadata, and returns a boolean in `x0`. Direct object values call `__rt_exception_matches`, the same metadata matcher used by exception catch lowering, so inherited classes and implemented interfaces are handled through the same parent-id and class-interface tables.
-
-For named targets, when the left-hand side is lowered as `Mixed` or `Union`, codegen calls `__rt_mixed_instanceof` instead. That helper unwraps nested mixed boxes, returns `false` for scalar, array, null, and unknown payload tags, and only forwards object payloads into `__rt_exception_matches`. This keeps nullable and union object checks PHP-compatible without treating the boxed mixed cell itself as an object pointer.
-
-Named targets are resolved before codegen. Named classes/interfaces become concrete metadata ids, `self` and `parent` resolve in the current lexical class context, and `static` uses the forwarded called-class id for late static binding. Dynamic targets are evaluated and validated after the left-hand side is evaluated; string targets are resolved through emitted case-insensitive class/interface name metadata, object targets load the target object's runtime class id, invalid target payloads branch to a fatal runtime diagnostic, and non-object left-hand payloads become `false` after that validation step.
-
-### Property access (`$obj->prop`)
-
-Property access usually uses fixed offsets computed at compile time from `ClassInfo.property_offsets`:
-
-```asm
-; $obj->prop where prop resolved to offset 24
-ldur x0, [x29, #-offset] ; load object pointer
-ldur x0, [x0, #24] ; load property at resolved inherited offset
-```
-
-If the property does not exist but the class exposes `__get($name)`, codegen materializes the property name as a string literal, pushes it as an argument, and dispatches the instance method through the normal object-call path. The returned value then flows back through the ordinary result registers based on the inferred return type.
-
-For property assignment (`$obj->prop = value`), the value is evaluated first, then stored at the resolved inherited offset. If the property is missing but the class exposes `__set($name, $value)`, codegen boxes the value as `Mixed`, materializes the property name, and dispatches `__set` instead of emitting a direct store.
-
-Property-array writes use the same fixed-offset property resolution first, then delegate to the ordinary array storage paths for the nested container. `$obj->items[] = $value` lowers through `PropertyArrayPush`, and `$obj->items[$key] = $value` lowers through `PropertyArrayAssign`; both require a concrete array/assoc-array property rather than a magic `__set` fallback.
-
-### Method call (`$obj->method(args)`)
-
-1. Evaluate the object expression to get the pointer in `x0`
-2. Push the object pointer onto the stack
-3. Evaluate and push all arguments
-4. Pop arguments into ABI registers, with the object pointer as the first argument (`x0`)
-5. Load the object's `class_id`, fetch the class vtable pointer from `_class_vtable_ptrs`, load the method slot, and `blr` to the resolved implementation
-6. Result is in the standard registers based on return type
-
-Inside the method body, `$this` is the first parameter and lives in the function's first stack slot.
-
-Private instance methods are the exception: they do not get vtable slots, so calls resolved to a private method of the current lexical class use a direct `_method_Class_method` branch instead of virtual dispatch.
-
-### Static method call (`ClassName::method(args)`)
-
-Static methods are called like regular functions, but with the label `_static_ClassName_methodName`. No object pointer is passed:
-
-```asm
-bl _static_Point_origin ; call static method
-; result in x0 (object pointer)
-```
-
-`self::method()` is handled as a direct call against the current lexical class. If it resolves to an instance method, codegen loads the implicit `$this` receiver and branches directly to the resolved `_method_Class_method` label. `parent::method()` works the same way against the immediate parent class. For static targets, codegen now also threads a hidden "called class id" argument through static method bodies: named `ClassName::method()` calls pin that id to the named class, while `self::` and `parent::` forward the current called class. `static::method()` then uses that forwarded class id to load the target from a per-class static-method table at runtime.
-
-## The ABI module
-
-**Files:** `src/codegen/abi/mod.rs`, `src/codegen/abi/`
-
-Centralizes register conventions so they're consistent everywhere:
-
-### Large offset addressing
-
-ARM64's `stur`/`ldur` instructions only support 9-bit signed immediates (offsets up to 255). Functions with many local variables can exceed this limit. The ABI module handles this transparently via `store_at_offset()` and `load_at_offset()`:
-
-- **Offsets <= 255**: single `stur`/`ldur` instruction (fast path)
-- **Offsets 256-4095**: two-instruction sequence — `sub x9, x29, #offset` to compute the address in a scratch register, then `str`/`ldr` through that register
-
-This means all codegen that accesses stack variables goes through the ABI helpers rather than emitting `stur`/`ldur` directly, so large stack frames work automatically. The same boundary now also owns indirect `[*ptr]` loads/stores used by by-reference params and mutation-heavy expression paths, so x86_64-specific memory syntax does not leak back into `expr.rs`.
-
-`emit_frame_slot_address()` complements those helpers when codegen needs the address of a local slot itself rather than the value stored there. By-reference calls, `ptr($var)`, and exception-frame bookkeeping now all reuse that helper instead of open-coding frame-slot address math.
-
-### Frame and return-value helpers
-
-The `abi/` module now centralizes the frame-management primitives used by both `_main` and ordinary functions:
-
-- `emit_frame_prologue()` / `emit_frame_restore()` — shared stack-frame setup and teardown
-- `emit_cleanup_callback_prologue()` / `emit_cleanup_callback_epilogue()` — tiny helper frames used by exception cleanup callbacks
-- `emit_preserve_return_value()` / `emit_restore_return_value()` — spill/reload of scalar, float, and string returns across epilogue side effects or `finally` dispatch
-
-That moves prologue/epilogue mechanics out of the higher-level walkers and makes the ABI layer responsible for more than just local-slot addressing.
-
-### Incoming argument lowering
-
-Incoming parameter decoding now goes through `IncomingArgCursor` plus `emit_store_incoming_param()`.
-
-The cursor tracks:
-
-- current integer argument register index
-- current floating-point argument register index
-- when argument passing has overflowed to the caller stack
-- the caller-stack byte offset for subsequent spilled parameters
-
-Those helpers now understand both the AArch64 calling convention and the Linux `x86_64` SysV AMD64 target. Function codegen delegates incoming-parameter lowering to the ABI layer instead of open-coding register names or caller-stack offsets inline.
-
-### Outgoing call argument lowering
-
-Outgoing calls now use ABI-owned helpers as well:
-
-- `build_outgoing_arg_assignments_for_target()` decides whether each argument lands in an integer register, a floating-point register, or overflows onto the caller-visible stack area for the selected target
-- `materialize_outgoing_args()` rewrites the temporary pushed-argument stack into the final ABI layout expected at the call site
-
-That logic is shared by ordinary function calls, indirect/callable dispatch, object/method calls, constructor/static dispatch, and helpers such as `call_user_func_array()`. The assignment/materialization rules now cover both AArch64 and Linux `x86_64` SysV layout, so the call ABI policy lives in one place instead of being duplicated across several dispatch paths.
-
-The same module now also owns a thin layer of call-site and temporary-stack primitives used by higher-level walkers:
-
-- `emit_call_label()` / `emit_call_reg()` emit direct and indirect calls for the current target
-- `emit_push_reg()`, `emit_pop_reg()`, `emit_push_float_reg()`, `emit_pop_float_reg()`, `emit_push_reg_pair()`, `emit_pop_reg_pair()`, and `emit_push_result_value()` manage the temporary argument stack without hardcoding ARM64 push/pop forms in each call path
-- `emit_reserve_temporary_stack()`, `emit_temporary_stack_address()`, and `emit_load_temporary_stack_slot()` now also back the FFI extern-call path, where borrowed C-string temporaries are tracked and released after the foreign call returns
-- `emit_release_temporary_stack()` and `emit_store_zero_to_local_slot()` centralize target-specific stack cleanup and zero-initialization details
-- `emit_store_process_args_to_globals()`, `emit_enable_heap_debug_flag()`, `emit_copy_frame_pointer()`, and `emit_exit()` cover the `_main` bootstrap/teardown path without hardcoding process-entry registers or exit sequences in the higher-level driver
-
-That keeps target-specific ABI work focused inside `abi/` instead of scattering `call`, `blr`, `add sp`, `rsp`, or zero-register assumptions across function, closure, callable, and method dispatch code.
-
-The same `abi/` layer now also owns symbol-slot plumbing for compiler-managed globals such as `_gvar_*`, `_static_*`, `_exc_*`, `_global_*`, and the high-frequency runtime symbols used by string builders, heap bookkeeping, and GC state such as `_concat_off`, `_heap_*`, and `_gc_*`: computing symbol addresses, moving result registers into symbol storage, loading symbol storage back into result registers, and copying local frame slots into symbol-backed storage during epilogues. Extern globals now use the same boundary too, so GOT/GOTPCREL address materialization lives in `abi/` instead of being open-coded separately in expression and statement lowering.
-
-### `emit_store(emitter, type, offset)`
-
-Stores the current result to a stack variable. Uses `store_at_offset()` internally to handle large offsets:
-
-| Type | What it stores |
-|---|---|
-| `Int` / `Bool` / `Resource` | `stur x0, [x29, #-offset]` (or 2-insn sequence for large offsets) |
-| `Float` | `stur d0, [x29, #-offset]` |
-| `Str` | `bl __rt_str_persist`, then `stur x1, [x29, #-offset]` + `stur x2, [x29, #-(offset-8)]` |
-| `Array` / `AssocArray` / `Iterable` | `stur x0, [x29, #-offset]` |
-| `Mixed` | `stur x0, [x29, #-offset]` |
-| `Object` | `stur x0, [x29, #-offset]` |
-| `Callable` / `Pointer` | `stur x0, [x29, #-offset]` |
-| `Buffer` / `Packed` / `Union` | `stur x0, [x29, #-offset]` |
-
-### `emit_load(emitter, type, offset)`
-
-Loads a stack variable into result registers (inverse of store). Uses `load_at_offset()` internally.
-
-### `emit_write_stdout(emitter, type)`
-
-Emits code to print a value to stdout:
-
-| Type | How it prints |
-|---|---|
-| `Str` | move the string pointer/length into `__rt_stdout_write`'s convention, then `bl __rt_stdout_write` |
-| `Int` | `bl __rt_itoa` → then write |
-| `Float` | `bl __rt_ftoa` → then write |
-| `Bool` | `true` prints "1", `false` prints nothing |
-| `Pointer` | `bl __rt_ptoa` → then write |
-| `Mixed` | `bl __rt_mixed_write_stdout` → inspect boxed runtime tag, then write |
-| `Void`/`Array`/`AssocArray`/`Callable`/`Object` | Prints nothing |
-
-The terminal write itself goes through one shared runtime indirection, `__rt_stdout_write(ptr, len)` (byte pointer in `x0`/`rdi`, length in `x1`/`rsi`). It performs the platform `write(1, ptr, len)` syscall directly. In `--web` builds it first checks the `_elephc_web_capture` flag and, when capture is enabled, hands the bytes to `elephc_web_write` instead so per-request response bodies can be captured; non-web binaries never reference the web symbols. (The `Mixed` / `Resource` / `Iterable` writers still issue their own syscalls and bypass this indirection.)
-
-For Linux `x86_64`, the same write path now follows the SysV ABI and a broad native runtime slice rather than AArch64-specific helper sequences. String results use the Linux syscall register layout, integer and float echo go through x86_64 `__rt_itoa` / `__rt_ftoa`, `_main` initializes `$argc` / `$argv` only when needed, and the bootstrap runtime now covers a wide set of array, string, math, filesystem, FFI, enum, exception, GC, and mixed-value helpers without leaking AArch64-only assumptions back into the higher-level walkers.
-
-That same bootstrap system slice now also includes x86_64-native `time()` / `microtime(true)` through libc `gettimeofday()`, target-aware `php_uname()` through libc `uname()`, plus package-version lowering for `phpversion()` and constant-string lowering for `sys_get_temp_dir()` via the shared symbol-address ABI helpers instead of ARM64-only `adrp` / `add_lo12` sequences.
-
-The x86_64 math surface is broader now too: the libc-backed float builtin family (`sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `exp`, `log`, `log2`, `log10`, `atan2`, `hypot`, `pow`) and the pure float helpers (`sqrt`, `pi`, `deg2rad`, `rad2deg`, `min`, `max`) all use SysV floating-point registers plus the shared temporary-stack ABI helpers instead of raw AArch64 `d0` / `scvtf` / `str d0` lowering. The same applies to the `**` operator in expression codegen, which now routes through the x86_64 `pow()` libc call path with the right floating argument order. The scalar random helpers (`rand()`, `mt_rand()`, `random_int()`) also live on that target-aware ABI path now, so their `[min, max]` range materialization no longer emits raw AArch64 stack spills on Linux x86_64. Comparator-driven indexed-array sorting is on that same path too: `usort()`, `uasort()`, and `uksort()` now resolve callback addresses through the shared symbol/stack ABI helpers and dispatch through an x86_64 `__rt_usort` bubble-sort runtime instead of hard-coded ARM64 `adrp` / `blr` sequences.
-
-## Function codegen
-
-**Files:** `src/codegen/functions/mod.rs`, `src/codegen/functions/`
-
-### `emit_function()`
-
-Compiles a user-defined function:
-
-1. **Collect local variables** — scan the function body to find all variables and their types
-2. **Calculate stack frame size** — 16-byte aligned, includes space for all locals
-3. **Emit prologue** — call the shared ABI frame helper
-4. **Store parameters** — lower incoming arguments through the ABI helpers into stack slots, marking by-value heap params as `Owned` and by-reference params as borrowed aliases of the caller's storage
-5. **Emit body** — all statements
-6. **Emit epilogue** — preserve return registers, save static locals back to BSS through the shared ABI storage helpers, clean up only `Owned` + `epilogue_cleanup_safe` heap locals, then call the shared ABI frame-restore helper and `ret`
-
-### Pass by reference
-
-```php
-function increment(&$val) {
- $val++;
-}
-```
-
-When a parameter is declared with `&`, the codegen passes the **stack address** of the argument instead of its value:
-
-1. At the call site: the address of the argument's stack slot is computed (`sub x_n, x29, #offset`) and passed in the argument register.
-2. In the function prologue: the address is stored in the parameter's stack slot (it holds a pointer, not a value).
-3. On reads: the codegen dereferences the pointer (`ldr x0, [x0]`) to get the actual value.
-4. On writes: the codegen stores through the pointer (`str x0, [addr]`), modifying the caller's variable directly.
-
-The context tracks which parameters are pass-by-reference via `ctx.ref_params`.
-
-### Variadic parameters and spread operator
-
-```php
-function sum(...$nums) { /* $nums is an array */ }
-sum(1, 2, 3);
-sum(...$arr); // spread
-```
-
-**Variadic functions**: The last parameter can be prefixed with `...` to collect all remaining arguments into an array. At the call site, the codegen:
-
-1. Passes regular (non-variadic) arguments normally via registers
-2. Uses the shared helpers in `src/codegen/expr/calls/args.rs` to prepare normalized/defaulted argument lists, lower pass-by-reference slots, handle spread-into-named parameters, and build the trailing variadic array when needed
-3. Passes the array pointer as the last argument register
-
-**Spread operator** (`...$arr`): When calling a function with `...$arr`, the array is unpacked into positional parameters. For `function f($a, ...$rest)`, `f(...[1, 2, 3])` passes `1` to `$a` and collects `[2, 3]` into `$rest`. Associative-array spreads map string keys to named arguments, keep numeric keys positional, and collapse duplicate static string keys to the last value before planning. Variable `AssocArray` spreads before named arguments can satisfy later parameters by string key at runtime, so codegen skips fixed prefix length checks for that dynamic provider and emits per-parameter lookup/default handling instead. In array literals, the spread operator uses `__rt_array_merge_into` to append all elements from the spread array into the target array.
-
-### Default parameter values
-
-Functions and closures support default parameter values:
-
-```php
-function greet($name, $greeting = "Hello") { ... }
-```
-
-When a call site omits an argument that has a default value, the codegen fills in the default. At the call site, the compiler checks how many arguments were actually passed and, for each missing parameter with a default, evaluates the default expression and places it in the appropriate argument register. This is handled at compile time — no runtime checks are needed.
-
-### `collect_local_vars()`
-
-Pre-scans the function body AST to find every variable that will be used. This is necessary because stack space must be allocated in the prologue, before any code runs.
-
-It walks the statement tree before code emission and handles the major local-binding forms recursively (`Assign`, control-flow blocks, `For`/`Foreach`, `ListUnpack`, `Global`, `StaticVar`, and related cases). The exact match is implementation-driven in the `functions/` module, so this list is illustrative rather than exhaustive.
-
-## Main program codegen
-
-**File:** `src/codegen/mod.rs`
-
-The `generate()` function orchestrates everything:
-
-1. **Emit user functions** — scan AST for `FunctionDecl`, emit each one
-2. **Emit class methods** — constructor, instance methods, and static methods use their own labels
-3. **Emit `_main`**:
- - Prologue (stack frame for global variables)
- - Save `argc` and `argv` from OS (they arrive in `x0` and `x1`)
- - Build `$argv` array via `__rt_build_argv` runtime call
- - Register the main activation record so exceptions can unwind through top-level code too
- - Emit all non-function statements
- - Epilogue: clean up owned locals, unregister the activation record, then `exit(0)`
-4. **Emit deferred closures** — closure bodies recorded during earlier expression codegen
-5. **Emit runtime routines** — all `__rt_*` helper functions
-6. **Emit data section** — string and float literals
-7. **Emit runtime data / BSS** — global buffers, globals, statics, and lookup tables
-
-Linux x86_64 uses the same shared runtime emission surface as the AArch64 targets. Array transforms, sorting helpers, copy-on-write paths, GC accounting, heap debug helpers, string search/formatting helpers, inline array/string accessors, and list unpacking all route through target-aware emitters and the ABI module rather than a separate reduced runtime slice.
-
-When an operation needs architecture-specific assembly, the leaf runtime module selects the native sequence internally. For example, x86_64 helpers use SysV registers, RIP-relative addressing, and x86_64 heap markers where needed, while AArch64 helpers use their own register and relocation conventions. Higher-level lowering should continue to call the shared runtime labels and ABI helpers instead of branching on raw ARM64 or x86_64 details.
+| `src/codegen/mod.rs` | Public codegen facade, EIR backend entry points, runtime metadata finalization |
+| `src/codegen/block_emit.rs` | Function/block traversal, prologues, top-level entry and deferred EIR wrappers |
+| `src/codegen/lower_inst.rs`, `src/codegen/lower_inst/` | Instruction lowering and builtin-specific EIR emission |
+| `src/codegen/lower_term.rs` | Terminator lowering for returns, branches, switches, and unreachable paths |
+| `src/codegen/context.rs` | EIR function emission state and value materialization helpers |
+| `src/codegen/frame.rs` | Stack-frame sizing, local slots, register allocation integration |
+| `src/codegen/value_placement.rs` | Stack/register placement for EIR values |
+| `src/codegen_support/abi/` | Target ABI helpers for registers, calls, stack slots, symbols, and frame mechanics |
+| `src/codegen_support/arrays.rs` | Shared array metadata helpers used by EIR and runtime support |
+| `src/codegen_support/callable_invoker_args.rs` | Shared descriptor-invoker argument cloning and boxing helpers |
+| `src/codegen_support/value_boxing.rs` | Shared scalar/string/array/object/iterable boxing into runtime `Mixed` cells |
+| `src/codegen_support/wrappers/` | Shared callback and fiber wrapper emitters used by deferred EIR wrapper emission |
+| `src/codegen_support/runtime/` | Shared `__rt_*` routines and runtime data emission |
+| `src/codegen_support/platform/` | Target descriptions and assembler/linker naming conventions |
+
+The active backend must remain target-aware. New lowering paths should use the
+ABI helpers instead of hardcoding AArch64 or x86_64 register and stack details.
+
+## Runtime Split
+
+Codegen produces two linked artifacts:
+
+1. **User assembly** from `src/codegen/`, containing lowered PHP functions,
+ methods, top-level entry code, user metadata, and literal data.
+2. **Runtime object** from `src/codegen_support/runtime/`, cached by compiler
+ version, target, heap size, runtime features, and PIC mode.
+
+Runtime feature selection is derived from the EIR module plus CLI-owned modes
+such as `--web`. This keeps ordinary binaries from carrying unused helper
+families while preserving deterministic linking.
+
+## Emit Modes
+
+`--emit executable` is the default and emits a process entry point. `--emit
+cdylib` emits a PIC user object with `#[Export]` trampolines and lifecycle
+symbols for embedding hosts. On Linux cdylib output also hides internal runtime
+symbols so separate loaded elephc modules do not preempt each other's state.
+
+## Backend Contract
+
+- PHP-visible behavior belongs in `src/ir_lower/` and `src/codegen/`.
+- Shared runtime, ABI, platform, and metadata helpers belong in
+ `src/codegen_support/`.
diff --git a/docs/internals/the-ir.md b/docs/internals/the-ir.md
index 5412720106..28a40ed833 100644
--- a/docs/internals/the-ir.md
+++ b/docs/internals/the-ir.md
@@ -5,10 +5,10 @@ sidebar:
order: 13
---
-**Status:** EIR is the default user-facing backend. The diagnostic `--emit-ir`
-path lowers the checked and optimized AST into validated textual EIR, and the
-normal executable/cdylib path lowers that same EIR into assembly. The legacy
-AST backend remains available only as the temporary `--ast-backend` fallback.
+**Status:** EIR is the canonical compiler IR and backend contract for v1.0.
+The diagnostic `--emit-ir` path lowers the checked and optimized AST into
+validated textual EIR, and normal executable/cdylib builds lower that same EIR
+into assembly.
**Implementation phases:** `.plans/eir-*.md`
@@ -23,13 +23,9 @@ AST backend remains available only as the temporary `--ast-backend` fallback.
- `src/parser/ast/ffi.rs`
- `src/ir/`
- `src/ir_lower/`
-- `src/codegen_ir/`
-- `src/codegen/expr.rs` and `src/codegen/expr/`
-- `src/codegen/stmt.rs` and `src/codegen/stmt/`
-- `src/codegen/functions/locals.rs`
-- `src/codegen/context.rs`
-- `src/codegen/builtins/`
-- `src/codegen/runtime/emitters.rs`
+- `src/codegen/`
+- `src/codegen_support/abi/`
+- `src/codegen_support/runtime/emitters.rs`
- `src/optimize/effects.rs` and `src/optimize/effects/`
EIR is elephc's intermediate representation. It sits between the AST-level
@@ -42,27 +38,6 @@ descriptors, copy-on-write checks, fatal paths, exception paths, runtime calls,
and exact source evaluation order are first-class compiler concepts. EIR is not
a generic LLVM- or Cranelift-style IR.
-## Historical Design Boundary
-
-The first roadmap item originally covered this document only:
-
-```text
-EIR design specification (`docs/internals/the-ir.md`) - types, instructions,
-terminators, effects, ownership, textual format
-```
-
-That first item did **not** include:
-
-- EIR -> assembly backend
-- `--ir-backend`
-- register allocation
-- IR optimization passes
-
-That design-only milestone is complete. The current implementation has since
-added `src/ir/`, `src/ir_lower/`, `--emit-ir`, and the default EIR assembly
-backend under `src/codegen_ir/`. Register allocation and IR optimization passes
-remain follow-up work.
-
## Pipeline Position
Current default production path:
@@ -88,45 +63,20 @@ PHP source
-> binary
```
-Temporary legacy fallback path (`--ast-backend`):
-
-```text
-PHP source
- -> Lexer
- -> Parser
- -> Magic constants
- -> Conditional compilation
- -> Resolver
- -> NameResolver
- -> Autoload insertion
- -> AST constant folding
- -> Type checker / warnings
- -> AST optimizer passes
- -> AST -> assembly codegen
- -> runtime cache
- -> assembler / linker
- -> binary
-```
-
The AST optimizer remains in front of EIR. It handles PHP-preserving rewrites
that are naturally expressed over syntax: constant folding, local scalar
propagation, control-flow pruning, control-flow normalization, and DCE. EIR
adds what the AST cannot express well: value identity, basic blocks, block
parameters, liveness, dominance, register placement, CSE, LICM, and inlining.
-## Backend Selection
-
-The compiler currently supports two assembly backends:
+## Backend Contract
-- EIR backend, selected by default and also by explicit `--ir-backend`
-- legacy AST backend, selected only by `--ast-backend`
+The compiler lowers EIR through the target-aware assembly emitter in
+`src/codegen/`.
-`--ast-backend` is a deprecated escape hatch while the legacy emitter remains
-in-tree. It emits a warning and will be removed after the default EIR path has
-completed its validation window. EIR preserves the existing hand-written assembly
-style and adds linear-scan register allocation plus a fixed-point IR optimization
-pass driver (see [Optimization Passes](#optimization-passes)); further IR passes
-are incremental follow-up work.
+EIR preserves the hand-written assembly style while adding linear-scan register
+allocation plus a fixed-point IR optimization pass driver (see
+[Optimization Passes](#optimization-passes)).
## Design Invariants
@@ -138,7 +88,7 @@ are incremental follow-up work.
generic "call".
- EIR is target-aware through metadata and the selected `Target`, but it does
not hardcode ARM64/x86_64 register names or stack layouts.
-- `src/codegen/abi/` remains authoritative for physical ABI lowering.
+- `src/codegen_support/abi/` remains authoritative for physical ABI lowering.
- Runtime helpers remain outside the EIR `Module`; EIR references them through
`RuntimeCall` or specialized opcodes.
- Source spans must survive lowering for diagnostics, source maps, and `--emit-ir`
@@ -1456,7 +1406,7 @@ The pass has four stages:
active set; a free register from the matching pool is assigned, otherwise the
use-weighted spill heuristic evicts the cheapest interval. Integer and float
values draw from separate pools.
-4. **Frame integration** (`codegen_ir/frame.rs`): the allocation is stored in
+4. **Frame integration** (`codegen/frame.rs`): the allocation is stored in
the frame layout, each used callee-saved register gets a save slot, and the
value-access chokepoints (`load_value_to_result`, `load_value_to_reg`,
`store_result_value`) read and write registers instead of slots.
diff --git a/docs/internals/the-optimizer.md b/docs/internals/the-optimizer.md
index 623cf55e20..9059b85ab5 100644
--- a/docs/internals/the-optimizer.md
+++ b/docs/internals/the-optimizer.md
@@ -416,7 +416,6 @@ The current optimizer is still intentionally local. It does not yet implement:
- broader guard reasoning for range facts and multi-variable relationships beyond the current boolean, scalar, loose-comparison, and safe relational-complement facts
- broader control-flow normalization beyond the current local AST shell rewrites
- backend-specific peephole cleanup
-- runtime dead stripping
- elimination of the `adrp/add/stur` instruction triple at the FCC assignment site when the wrapper is stubbed (the stub address still gets loaded and stored even though both are dead)
Those remain roadmap items for later optimization work.
diff --git a/docs/internals/the-parser.md b/docs/internals/the-parser.md
index 412eefed49..eb5da2f4c0 100644
--- a/docs/internals/the-parser.md
+++ b/docs/internals/the-parser.md
@@ -73,7 +73,7 @@ Things that have a value:
| `ShortTernary { value, default }` | `$a ?: $fallback` | PHP short ternary / Elvis form. Codegen evaluates `value` once, returns it if truthy, otherwise returns `default`. |
| `ErrorSuppress(Expr)` | `@file_get_contents("missing.txt")` | PHP error-control prefix expression. Codegen wraps the operand in a runtime warning-suppression scope. |
| `Cast { target, expr }` | `(int)$x` | |
-| `Closure { params, variadic, variadic_type, return_type, body, is_arrow, is_static, captures, capture_refs }` | `function(int $x = 1) use ($y, &$z): string { ... }`, `fn(int $x): int => $x * 2`, or `static function(): int { ... }` | Anonymous function / arrow function. Params is `Vec<(String, Option, Option, bool)>` - name, declared type, default, is_ref. `variadic` is an optional parameter name and `variadic_type` its optional declared element type (`int ...$xs`). `return_type` stores the optional declared closure / arrow return `TypeExpr`. `captures` stores by-value captures and `capture_refs` stores `use (&$var)` captures. Arrow functions are still represented as `Closure`, parse with `is_arrow = true`, and do not carry explicit `use (...)` captures in the AST. `is_static` is set when the closure is prefixed with the `static` keyword (PHP `static function () {}` / `static fn () => ...`); the type checker rejects any reference to `$this` inside a static closure. |
+| `Closure { params, variadic, variadic_type, return_type, body, is_arrow, is_static, by_ref_return, captures, capture_refs }` | `function(int $x = 1) use ($y, &$z): string { ... }`, `fn(int $x): int => $x * 2`, or `static function(): int { ... }` | Anonymous function / arrow function. Params is `Vec<(String, Option, Option, bool)>` - name, declared type, default, is_ref. `variadic` is an optional parameter name and `variadic_type` its optional declared element type (`int ...$xs`). `return_type` stores the optional declared closure / arrow return `TypeExpr`. `captures` stores by-value captures and `capture_refs` stores `use (&$var)` captures. Arrow functions are still represented as `Closure`, parse with `is_arrow = true`, and do not carry explicit `use (...)` captures in the AST. `is_static` is set when the closure is prefixed with the `static` keyword (PHP `static function () {}` / `static fn () => ...`); the type checker rejects any reference to `$this` inside a static closure. `by_ref_return` is set when the closure is declared `fn &()` / `function &()`, so it returns a reference (alias) to the returned lvalue rather than a copy. |
| `NamedArg { name, value }` | `foo(name: "Alice")` | Named call argument. The parser preserves source order; later phases validate names against the declared parameter list and normalize known-signature calls for ABI lowering. |
| `ClosureCall { var, args }` | `$fn(1, 2)` | Calling a closure stored in a variable |
| `ExprCall { callee, args }` | `$arr[0](1, 2)` | Calling the result of an expression (e.g., array access returning a callable) |
@@ -110,7 +110,7 @@ Each `Stmt` also carries a source `span` and an `attributes` list. The list is p
|---|---|
| `Echo(Expr)` | `echo $x;`; multi-argument `echo $a, $b;` lowers to a `Synthetic` sequence of `Echo` statements |
| `Assign { name, value }` | `$x = 42;` |
-| `RefAssign { target, source }` | `$y =& $x;` — reference aliasing where both target and source are plain variables |
+| `RefAssign { target, source }` | `$y =& $x;`, `$y =& $obj->prop;`, `$y =& $arr[$k];` — reference aliasing where `target` is a plain variable name and `source` is the aliased lvalue: a plain variable, a property access, an array element, or a call to a by-reference-returning callee |
| `If { condition, then_body, elseif_clauses, else_body }` | `if (...) { } elseif (...) { } else { }` |
| `While { condition, body }` | `while (...) { }` |
| `DoWhile { body, condition }` | `do { } while (...);` |
@@ -121,7 +121,7 @@ Each `Stmt` also carries a source `span` and an `attributes` list. The list is p
| `NestedArrayAssign { target, value }` | `$arr[0][1] = 5;`, `$obj->items[0] = 5;` |
| `ArrayPush { array, value }` | `$arr[] = 5;` |
| `TypedAssign { type_expr, name, value }` | `int $x = 42;`, `buffer $xs = buffer_new(8);` |
-| `FunctionDecl { name, params, variadic, return_type, body }` | `function foo(int $a, &$b, string $c = "x"): string { }` — params is `Vec<(String, Option, Option, bool)>` where the tuple stores name, declared type, default value, and `is_ref` (pass by reference). `variadic` is `Option` for variadic parameters (`...$args`) and `return_type` is an optional declared `TypeExpr` |
+| `FunctionDecl { name, params, variadic, variadic_type, return_type, by_ref_return, body }` | `function foo(int $a, &$b, string $c = "x"): string { }`, `function &ref(): int { ... }` — params is `Vec<(String, Option, Option, bool)>` where the tuple stores name, declared type, default value, and `is_ref` (pass by reference). `variadic` is `Option` for variadic parameters (`...$args`), `variadic_type` is the optional declared element type on that variadic (`int ...$xs`), `return_type` is an optional declared `TypeExpr`, and `by_ref_return` is `true` when declared `function &f()` so calls return a reference (alias) to the returned lvalue rather than a copy |
| `FunctionVariantGroup { name, variants }` | Internal resolver metadata for include-loaded hidden function implementations behind one public name |
| `FunctionVariantMark { name, variant }` | Internal include-body marker that activates the hidden function variant loaded at that runtime include point |
| `Return(Option)` | `return $x;` or `return;` |
diff --git a/docs/internals/the-runtime.md b/docs/internals/the-runtime.md
index d19c441b08..51e3143d00 100644
--- a/docs/internals/the-runtime.md
+++ b/docs/internals/the-runtime.md
@@ -137,7 +137,8 @@ Each routine follows the same pattern — inputs in registers, output in standar
|---|---|---|---|
| `__rt_strcopy` | Copy string into concat buffer | `x1`/`x2` | `x1`/`x2` |
| `__rt_str_to_number` | Parse a PHP numeric string for loose comparison and numeric-string casts | `x1`/`x2` | numeric payload + success flag |
-| `__rt_str_to_int` | Parse a PHP numeric-string prefix (via `__rt_str_to_number`) and truncate toward zero like PHP `(int)` casts | `x1`/`x2` | `x0` (integer) |
+| `__rt_str_looks_like_int_for_coercion` | Validate PHP coercive int-parameter numeric strings while rejecting libc-only `strtod` forms such as `0x`, `INF`, and `NAN` | `x1`/`x2` | `x0` (0 or 1) |
+| `__rt_str_to_int` | Parse a PHP numeric-string prefix with integer/float forms and truncate toward zero like PHP `(int)` casts | `x1`/`x2` | `x0` (integer) |
| `__rt_str_loose_eq` | Compare two strings using PHP loose-comparison numeric-string rules before falling back to bytes | two strings | `x0` (0 or 1) |
| `__rt_strtolower` | Lowercase conversion | `x1`/`x2` | `x1`/`x2` |
| `__rt_strtoupper` | Uppercase conversion | `x1`/`x2` | `x1`/`x2` |
@@ -184,6 +185,7 @@ Each routine follows the same pattern — inputs in registers, output in standar
| `__rt_hash` | Hash with algorithm | algo + data | `x1`/`x2` |
| `__rt_hash_init` / `__rt_hash_update` / `__rt_hash_final` | Incremental hash-context API backing `hash_init()` and friends | context + data | context / `x1`/`x2` |
| `__rt_hash_copy` | Clone an incremental hash context | context | context |
+| `__rt_hash_ctx_free` | Free a HashContext via `elephc_crypto_free`; the sole destructor, called by `__rt_mixed_free_deep` when a Mixed(tag=9, kind=2) cell is released at scope exit (`hash_final` no longer frees) | context | — |
| `__rt_hash_hmac` | Keyed HMAC over a message | algo + key + data | `x1`/`x2` |
| `__rt_hash_equals` | Constant-time string comparison | two strings | `x0` (0 or 1) |
| `__rt_hash_algos_list` | Build the `hash_algos()` array of supported algorithm names | — | `x0` (array ptr) |
@@ -196,9 +198,9 @@ Each routine follows the same pattern — inputs in registers, output in standar
## Callable routines
-**Source:** `src/codegen/runtime/callables/` (3 files including `mod.rs`)
+**Source:** `src/codegen/runtime/callables/` (4 files including `mod.rs`)
-These routines implement the runtime fallback path for `is_callable()` when the argument is not a compile-time literal or statically known callable value. They consult generated metadata for builtins, user functions, public methods, public static methods, and `__invoke` objects.
+These routines implement the runtime fallback path for `is_callable()` when the argument is not a compile-time literal or statically known callable value, plus the `Closure::bind` family helper. They consult generated metadata for builtins, user functions, public methods, public static methods, and `__invoke` objects.
Dynamic invocation builtins use generated callable descriptors rather than these boolean helpers. A descriptor is an eight-word record: callable kind, native entry pointer, PHP-visible name pointer, name length, signature-record pointer, environment-record pointer, invocation-record pointer, and optional uniform invoker pointer. Indirect calls keep the one-word callable ABI by loading the native entry from the descriptor, while descriptor-invoker paths call the generated `(descriptor, boxed argument container) -> mixed` adapter.
@@ -219,6 +221,7 @@ Extern callback trampolines use the same descriptor invoker from a C-facing entr
| `__rt_is_callable_mixed` | Unbox a Mixed value and dispatch string, array, hash, or object callable checks | mixed pointer | `x0` = bool |
| `__rt_is_callable_heap` | Dispatch callable checks from a raw heap pointer by inspecting its heap-kind tag | heap pointer | `x0` = bool |
| `__rt_callable_descriptor_release` | Free a heap-backed callable descriptor copy plus the by-value capture slots appended after its static header; static `.data` descriptors are ignored | `x0` = descriptor pointer | — |
+| `__rt_closure_bind` | Bind a `$this`-only closure to a new receiver for `Closure::bind` / `Closure::bindTo` / `Closure::call`: copy the runtime descriptor, overwrite the captured object, and incref it. Closures with any other capture shape abort with a fatal diagnostic | `x0` = source closure descriptor, `x1` = new `$this` object | `x0` = bound descriptor copy |
## Array routines
@@ -348,7 +351,7 @@ See [Memory Model](memory-model.md) for the hash table memory layout.
| `__rt_gc_note_child_ref` | Add one transient incoming edge to a heap child during cycle counting | `x0` = child pointer | — |
| `__rt_gc_mark_reachable` | Recursively mark array/hash/object blocks reachable from external roots | `x0` = pointer | — |
| `__rt_gc_collect_cycles` | Run the targeted cycle collector over heap-backed arrays/hashes/objects | — | — |
-| `__rt_mixed_free_deep` | Free a mixed cell and release any nested heap-backed payload | `x0` = mixed pointer | — |
+| `__rt_mixed_free_deep` | Free a mixed cell and release any nested heap-backed payload; for tag-9 resources, dispatch the kind-specific destructor (kind 1 `close`, kind 2 `__rt_hash_ctx_free`, kind 3 `__rt_pclose`, kind 4 `__rt_closedir`) | `x0` = mixed pointer | — |
| `__rt_object_free_deep` | Free an object and release heap-backed properties using runtime/class metadata | `x0` = object pointer | — |
Refcounts are stored as a 32-bit value in the uniform 16-byte heap header, at `[user_ptr - 12]`. Each heap allocation starts with refcount 1. When a reference is shared (e.g., assigned to another variable or passed to a function), `__rt_incref` bumps it. When the reference goes away, `__rt_decref_any` can dispatch through the uniform heap-kind tag to the concrete string/array/hash/object/mixed release path. Arrays, hashes, objects, and boxed mixed cells still use ordinary reference counting first, but when a decref sees a container/object graph that can contain nested heap-backed values, the runtime can invoke `__rt_gc_collect_cycles` to clear transient metadata, count heap-only incoming edges, mark externally reachable blocks, and deep-free the remaining unreachable array/hash/object/mixed island.
@@ -443,6 +446,20 @@ The `json_encode` implementation uses **type-aware dispatch** — the codegen ca
| `__rt_json_last_error_msg` | Return the message string corresponding to `_json_last_error` through the `_json_err_msg_table` data table, including decode location suffixes when active | global JSON state | `x1`/`x2` = message |
| `__rt_json_pretty_push` / `__rt_json_pretty_pop` / `__rt_json_pretty_line` / `__rt_json_pretty_colon_space` | Maintain `_json_indent_depth` and append PHP-style pretty-print whitespace while each container encoder emits bytes. These helpers are no-ops unless `JSON_PRETTY_PRINT` is active, avoiding a second buffer walk. | current JSON state, `x11` write pointer for line/space helpers | updated formatting state / `x11` write pointer |
+### Serialization routines
+
+**Files:** `system/serialize.rs`, `system/unserialize.rs`
+
+These helpers back PHP's `serialize()` / `unserialize()`. The serializer writes PHP's exact wire format (`N;`, `b:0;`/`b:1;`, `i:;`, `d:;`, `s::"";`, `a::{...}`, and `O::""::{...}`) directly into the [concat buffer](memory-model.md#the-string-buffer-scratch-pad), reusing `__rt_json_ftoa` for shortest-round-trip float digits. Object serialization honors `__sleep()` / `Serializable` and reuses an object back-reference table so repeated instances emit `r:`/`R:` references.
+
+| Routine | What it does | Input | Output |
+|---|---|---|---|
+| `__rt_serialize_value` | Tag-dispatching serializer for a raw runtime value, appending its wire form to the concat buffer | value tag + payload | `x1`/`x2` = string slice |
+| `__rt_serialize_mixed` | Unbox a boxed Mixed cell (null pointer → `N;`), then serialize it | `x0` = mixed cell | `x1`/`x2` = string slice |
+| `__rt_serialize_indexed_array` / `__rt_serialize_hash` | Serialize indexed arrays and hashes as `a::{...}` | array/hash pointer | `x1`/`x2` = string slice |
+| `__rt_serialize_object` / `__rt_serialize_named_prop` / `__rt_serialize_obj_ref` | Serialize objects (`O:`/`C:`), emit one named property entry, and resolve back-references | object pointer | `x1`/`x2` = string slice |
+| `__rt_unserialize_begin` / `__rt_unserialize_mixed` / `__rt_unserialize_object` | Parse a serialized string back into boxed Mixed cells, including nested arrays/hashes and objects | `x1`/`x2` = serialized string | `x0` = Mixed* (0 on malformed input) |
+
### Regex routines
**Files:** `system/preg_strip.rs`, `system/pcre_to_posix.rs`, `system/preg_match.rs`, `system/preg_match_all.rs`, `system/preg_replace.rs`, `system/preg_replace_callback.rs`, `system/preg_split.rs`
@@ -661,26 +678,26 @@ These helpers back SPL container classes whose PHP surface needs custom runtime
## Generator routines
-**Source:** `src/codegen/runtime/generators/` (2 files)
+**Source:** `src/codegen/runtime/generators/` (3 files: `mod.rs`, `coro.rs`, `frame.rs`)
+
+These helpers back the built-in `Generator` class. Generators are **stackful coroutines** that reuse the [Fiber runtime](#fiber-routines): a `Generator` object reuses the Fiber 232-byte layout (so it can drive itself through `__rt_fiber_switch` / `suspend` / `resume` / `throw`) plus a small block of generator-specific fields (`last_key`, `last_value`, `return_value`, `auto_key`, `delegated_iter`) at offsets 184..224 inside the otherwise-unused Fiber reserved region. The generated generator body runs on its own coroutine stack and calls `__rt_gen_suspend` at each `yield`; the accessor helpers below drive the coroutine for the public Iterator surface, `send()`/`throw()`, and `getReturn()`.
-These helpers back the built-in `Generator` class. Generator functions emit a heap-allocated frame and a generated resume function; the runtime helpers read/write that frame for the public Iterator surface and coroutine operations.
+Because the fiber suspend boundary re-raises a scheduled exception *inside* the coroutine's own stack, `Generator::throw()` lands in an in-generator `try/catch` (issue #329) rather than unwinding the caller.
| Routine | What it does | Input | Output |
|---|---|---|---|
-| `__rt_gen_current` | Return an owned ref to the boxed Mixed value from the most recent yield | `GeneratorFrame*` | boxed `mixed` payload |
-| `__rt_gen_key` | Return an owned ref to the boxed Mixed key from the most recent yield | `GeneratorFrame*` | boxed `mixed` key |
-| `__rt_gen_valid` | Report whether the generator is not terminated | `GeneratorFrame*` | bool |
-| `__rt_gen_next` | Resume the state machine past the current yield unless terminated | `GeneratorFrame*` | — |
-| `__rt_gen_next_done` | Shared global return label used after `next()` skips or completes a resume | `GeneratorFrame*` | — |
-| `__rt_gen_send` | Store a boxed Mixed sent value, then resume the state machine | `GeneratorFrame*`, boxed `mixed` value | boxed `mixed` payload |
-| `__rt_gen_send_done` | Shared global return label used after `send()` skips or completes a resume | `GeneratorFrame*` | boxed `mixed` payload |
-| `__rt_gen_send_epilogue` | Shared epilogue that boxes and returns the yield produced by a resumed `send()` | `GeneratorFrame*` | boxed `mixed` payload |
-| `__rt_gen_rewind` | Run the generator to its first yield once | `GeneratorFrame*` | — |
-| `__rt_gen_rewind_done` | Shared global return label used when `rewind()` has already run or just finished | `GeneratorFrame*` | — |
-| `__rt_gen_throw` | Mark the generator terminated and throw through the normal exception runtime | `GeneratorFrame*`, throwable object | does not return |
-| `__rt_gen_get_return` | Return an owned ref to the boxed terminal return value | `GeneratorFrame*` | boxed `mixed` payload |
-
-Generator frames are stamped as object heap blocks because `Generator` is a built-in class implementing `Iterator`. `__rt_object_free_deep` detects the built-in Generator class id and releases the frame's custom Mixed slots plus any active `yield from` delegate instead of treating the payload as ordinary class properties.
+| `__rt_gen_suspend` | `yield` suspension primitive: record the yielded key/value into the generator's persistent slots (NULL key → auto-increment integer key), then suspend via `__rt_fiber_suspend` | boxed key cell, boxed value cell | boxed `mixed` delivered by the next `send()`/`next()` |
+| `__rt_gen_current` | Return an owned ref to the boxed Mixed value from the most recent yield | `Generator*` | boxed `mixed` payload |
+| `__rt_gen_key` | Return an owned ref to the boxed Mixed key from the most recent yield | `Generator*` | boxed `mixed` key |
+| `__rt_gen_valid` | Report whether the generator is not terminated | `Generator*` | bool |
+| `__rt_gen_next` | Resume the coroutine past the current yield unless terminated | `Generator*` | — |
+| `__rt_gen_send` | Store a boxed Mixed sent value, then resume the coroutine | `Generator*`, boxed `mixed` value | boxed `mixed` payload |
+| `__rt_gen_throw` | Schedule a pending throw and resume so the exception is re-raised inside the coroutine | `Generator*`, throwable object | boxed `mixed` payload or rethrown exception |
+| `__rt_gen_rewind` | Run the generator to its first yield once | `Generator*` | — |
+| `__rt_gen_get_return` | Return an owned ref to the boxed terminal return value | `Generator*` | boxed `mixed` payload |
+| `__rt_gen_delegate` | Drive a `yield from` delegate stored in `delegated_iter`, forwarding inner yields to the outer caller until the inner iterator is exhausted | `Generator*` | — |
+
+Generators are stamped as object heap blocks (heap kind `4`) because `Generator` is a built-in class implementing `Iterator`. `__rt_object_free_deep` detects the built-in Generator class id and releases the coroutine's custom Mixed slots plus any active `yield from` delegate instead of treating the payload as ordinary class properties.
## Fiber routines
@@ -715,10 +732,10 @@ pub fn emit_runtime(emitter: &mut Emitter) {
// diagnostics: runtime warning emission and @ suppression state
// strings: itoa, resource display/stdout, ftoa, concat, atoi, equality, formatting, trim/mask,
// search/replace, explode/implode, hashing, encoding, sscanf, ...
- // callables: dynamic is_callable() fallback plus callable-descriptor release
- // system: argv, time, getenv, shell, date/mktime/strtotime, JSON, regex
+ // callables: dynamic is_callable() fallback, callable-descriptor release, Closure::bind
+ // system: argv, time, getenv, shell, date/mktime/strtotime, JSON, serialize/unserialize, regex
// exceptions: cleanup walk, catch matching, class-implements, throw/rethrow helpers
- // generators: Generator current/key/valid/next/send/rewind/throw/getReturn helpers
+ // generators: fiber-backed Generator suspend/current/key/valid/next/send/rewind/throw/getReturn/yield-from
// arrays: heap alloc/free, array/hash helpers, sort, callbacks, refcount
// spl: SplDoublyLinkedList/SplStack/SplQueue and SplFixedArray storage helpers
// objects: stdClass dynamic properties and boxed Mixed property/index dispatch
@@ -729,9 +746,9 @@ pub fn emit_runtime(emitter: &mut Emitter) {
}
```
-Notable runtime-only helpers emitted here include `__rt_diag_push_suppression`, `__rt_diag_pop_suppression`, `__rt_diag_warning`, `__rt_exception_cleanup_frames`, `__rt_exception_matches`, `__rt_instanceof_lookup`, `__rt_instanceof_invalid_target`, `__rt_throw_current`, `__rt_heap_debug_fail`, `__rt_heap_kind`, `__rt_hash_insert_owned`, `__rt_hash_free_deep`, `__rt_array_column_ref`, `__rt_mixed_instanceof`, `__rt_iterable_write_stdout`, `__rt_iterable_unsupported_kind`, `__rt_class_implements_interface`, `__rt_callable_descriptor_release`, `__rt_spl_dll_new`, `__rt_spl_fixed_new`, `__rt_gen_current`, `__rt_gen_send`, `__rt_preg_strip`, `__rt_pcre_to_posix`, `__rt_str_to_cstr`, `__rt_cstr_to_str`, `__rt_fiber_switch`, and `__rt_fiber_entry` in addition to the more user-visible helpers.
+Notable runtime-only helpers emitted here include `__rt_diag_push_suppression`, `__rt_diag_pop_suppression`, `__rt_diag_warning`, `__rt_exception_cleanup_frames`, `__rt_exception_matches`, `__rt_instanceof_lookup`, `__rt_instanceof_invalid_target`, `__rt_throw_current`, `__rt_heap_debug_fail`, `__rt_heap_kind`, `__rt_hash_insert_owned`, `__rt_hash_free_deep`, `__rt_array_column_ref`, `__rt_mixed_instanceof`, `__rt_iterable_write_stdout`, `__rt_iterable_unsupported_kind`, `__rt_class_implements_interface`, `__rt_callable_descriptor_release`, `__rt_closure_bind`, `__rt_serialize_value`, `__rt_unserialize_begin`, `__rt_spl_dll_new`, `__rt_spl_fixed_new`, `__rt_gen_suspend`, `__rt_gen_current`, `__rt_gen_send`, `__rt_preg_strip`, `__rt_pcre_to_posix`, `__rt_str_to_cstr`, `__rt_cstr_to_str`, `__rt_fiber_switch`, and `__rt_fiber_entry` in addition to the more user-visible helpers.
-Every routine in the selected target runtime slice is linked into the binary, even if unused by the current program. elephc already does AST-side control-flow pruning and dead-code elimination before codegen, but runtime-specific dead stripping is still future work.
+Compiled **executables** dead-strip unreachable runtime helpers at link time. On Linux each `__rt_*` helper is emitted in its own `.text.` section and collected with `--gc-sections`; on macOS the runtime object carries a `.subsections_via_symbols` footer so each helper is a separately collectable atom dropped by `-dead_strip` (internal cross-helper labels stay assembler-local `L`-locals, with the few helpers reached by a `b`/`bl` from another atom marked `.alt_entry` so they remain live symbols). Combined with the AST-side control-flow pruning and dead-code elimination elephc already does before codegen, only the helpers a program actually reaches are linked. Shared libraries (`--emit cdylib`) keep the full runtime so every exported entry stays callable.
The runtime can also be emitted in **position-independent mode** for `--emit cdylib` builds: the emitter's `pic_data_refs` flag makes the `abi::symbols` helpers route every global data reference through the GOT (`@GOTPCREL` on x86_64, `:got:`/`:got_lo12:` on AArch64) instead of direct PC-relative addressing, and on ELF targets every internal global gets a `.hidden` visibility directive. The PIC and non-PIC variants produce different assembly text, so they cache as separate runtime objects. See [The Codegen](the-codegen.md) and [Shared Libraries](../beyond-php/cdylib.md).
diff --git a/docs/php/arrays.md b/docs/php/arrays.md
index c23a3135fb..2585651ee2 100644
--- a/docs/php/arrays.md
+++ b/docs/php/arrays.md
@@ -160,6 +160,26 @@ $matrix = [[1, 2], [3, 4]];
echo $matrix[0][1]; // 2
```
+## Spread in array literals
+
+The `...` spread operator flattens an array's elements into a new array literal, matching PHP semantics.
+
+```php
+ '0.0.0.0', 'timeout' => 30];
+$config = ['host' => 'localhost', 'port' => 8080];
+$merged = [...$defaults, ...$config];
+// ['host' => 'localhost', 'timeout' => 30, 'port' => 8080]
+```
+
## Array destructuring
Array destructuring assigns array elements to writable targets. Both short syntax and `list(...)` are supported.
@@ -203,6 +223,9 @@ PHP does not allow keyed and unkeyed entries in the same destructuring pattern,
| `array_splice()` | `array_splice($arr, $offset [, $length]): array` | Remove a slice in place and return the removed elements |
| `array_chunk()` | `array_chunk($arr, $size): array` | Split into chunks |
| `array_merge()` | `array_merge($arr1, $arr2): array` | Merge two arrays |
+| `array_merge_recursive()` | `array_merge_recursive($arr1, $arr2): array` | Recursively merge two arrays: integer keys append (renumbered), string keys that collide recurse when both values are arrays and otherwise combine into a list. Accepts associative arrays or **indexed arrays of scalars** (int/float/bool); nested indexed-array values are treated as opaque. |
+| `array_replace()` | `array_replace($arr, $replacements): array` | Overwrite matching keys in `$arr` (in place, keeping position) and append new keys from `$replacements`; later values win. Accepts associative arrays or **indexed arrays of scalars** (int/float/bool). |
+| `array_replace_recursive()` | `array_replace_recursive($arr, $replacements): array` | Like `array_replace()`, but when both values at a key are associative arrays they are merged recursively instead of overwritten. Accepts associative arrays or **indexed arrays of scalars** (int/float/bool); nested indexed arrays are overwritten, not merged. |
| `array_combine()` | `array_combine($keys, $values): array` | Create array from keys/values |
| `array_fill()` | `array_fill($start, $num, $value): array` | Fill with values |
| `array_fill_keys()` | `array_fill_keys($keys, $value): array` | Fill with values using keys |
@@ -212,6 +235,10 @@ PHP does not allow keyed and unkeyed entries in the same destructuring pattern,
| `array_intersect()` | `array_intersect($arr1, $arr2): array` | Values in both |
| `array_diff_key()` | `array_diff_key($arr1, $arr2): array` | Keys in $arr1 not in $arr2 |
| `array_intersect_key()` | `array_intersect_key($arr1, $arr2): array` | Keys in both |
+| `array_diff_assoc()` | `array_diff_assoc($arr1, $arr2): array` | Entries of $arr1 whose `(key, value)` pair is absent from $arr2 (values compared as `(string)$a === (string)$b`). Accepts associative arrays or **indexed arrays of scalars** (int/float/bool). |
+| `array_intersect_assoc()` | `array_intersect_assoc($arr1, $arr2): array` | Entries of $arr1 whose `(key, value)` pair is present in $arr2 (values compared as strings). Accepts associative arrays or **indexed arrays of scalars** (int/float/bool). |
+| `array_udiff()` | `array_udiff($arr1, $arr2, $cmp): array` | Values in $arr1 not in $arr2, equality decided by the two-argument comparator (`$cmp($a, $b) === 0`). Supports string / function / non-capturing closure comparators. |
+| `array_uintersect()` | `array_uintersect($arr1, $arr2, $cmp): array` | Values in both arrays, equality decided by the comparator (`$cmp($a, $b) === 0`). |
| `array_unique()` | `array_unique($arr): array` | Remove duplicates |
| `array_reverse()` | `array_reverse($arr): array` | Reverse order |
| `array_flip()` | `array_flip($arr): array` | Exchange keys and values, normalizing integer and numeric-string result keys |
@@ -220,6 +247,9 @@ PHP does not allow keyed and unkeyed entries in the same destructuring pattern,
| `array_sum()` | `array_sum($arr): int\|float` | Sum of values |
| `array_product()` | `array_product($arr): int\|float` | Product of values |
| `array_column()` | `array_column($arr, $column_key): array` | Extract column from array of assoc rows |
+| `array_is_list()` | `array_is_list($arr): bool` | `true` if the keys are exactly `0..count-1` in order (the empty array is a list) |
+| `array_key_first()` | `array_key_first($arr): int\|string\|null` | First key in insertion order, or `null` if the array is empty |
+| `array_key_last()` | `array_key_last($arr): int\|string\|null` | Last key in insertion order, or `null` if the array is empty |
| `sort()` | `sort($arr): void` | Sort ascending (in-place) |
| `rsort()` | `rsort($arr): void` | Sort descending |
| `asort()` | `asort($arr): void` | Sort by value, maintain keys |
@@ -229,11 +259,16 @@ PHP does not allow keyed and unkeyed entries in the same destructuring pattern,
| `natsort()` | `natsort($arr): void` | Natural order sort |
| `natcasesort()` | `natcasesort($arr): void` | Case-insensitive natural sort |
| `shuffle()` | `shuffle($arr): void` | Randomly shuffle (in-place) |
+| `array_multisort()` | `array_multisort($arr1, $arr2): bool` | Sort `$arr1` ascending (stable) and reorder `$arr2` in tandem; both are sorted in place (by reference). **Two indexed arrays of scalar elements**; sort flags, descending order, and >2 arrays are follow-ups. |
| `array_rand()` | `array_rand($arr): int` | Pick one random key |
| `array_map()` | `array_map($callback, $arr): array` | Apply callback to each element |
| `array_filter()` | `array_filter($arr, $callback, $mode = ARRAY_FILTER_USE_VALUE): array` | Filter where callback is truthy; mode selects value, key, or both callback args |
| `array_reduce()` | `array_reduce($arr, $callback, $init): int` | Reduce to single value |
| `array_walk()` | `array_walk($arr, $callback): void` | Call callback on each element |
+| `array_walk_recursive()` | `array_walk_recursive($arr, $callback): void` | Apply `$callback` to each non-array leaf value, recursing into nested indexed/associative arrays. Leaf values must share a scalar type (consistent with `array_walk`: leaf passed by value, no key argument). |
+| `array_find()` | `array_find($arr, $callback): mixed` | (PHP 8.4) Returns the first element for which `$callback($value)` is truthy, or `null` if none match. |
+| `array_any()` | `array_any($arr, $callback): bool` | (PHP 8.4) `true` if `$callback($value)` is truthy for at least one element. |
+| `array_all()` | `array_all($arr, $callback): bool` | (PHP 8.4) `true` if `$callback($value)` is truthy for every element. |
| `usort()` | `usort($arr, $callback): void` | Sort with user comparison |
| `uksort()` | `uksort($arr, $callback): void` | Sort by key with user comparison |
| `uasort()` | `uasort($arr, $callback): void` | Sort with user comparison, maintain keys |
diff --git a/docs/php/builtins.md b/docs/php/builtins.md
index b73b60e7c9..3b6a671303 100644
--- a/docs/php/builtins.md
+++ b/docs/php/builtins.md
@@ -9,94 +9,111 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`array_chunk()`](./builtins/array/array_chunk.md) | `(array $array, int $length, bool $preserve_keys): array` | `array` |
-| [`array_column()`](./builtins/array/array_column.md) | `(array $array, string $column_key, string $index_key): array` | `array` |
+| [`array_all()`](./builtins/array/array_all.md) | `(mixed $array, mixed $callback): bool` | `bool` |
+| [`array_any()`](./builtins/array/array_any.md) | `(mixed $array, mixed $callback): bool` | `bool` |
+| [`array_chunk()`](./builtins/array/array_chunk.md) | `(array $array, int $length): array` | `array` |
+| [`array_column()`](./builtins/array/array_column.md) | `(array $array, string $column_key): array` | `array` |
| [`array_combine()`](./builtins/array/array_combine.md) | `(array $keys, array $values): array` | `array` |
| [`array_diff()`](./builtins/array/array_diff.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_diff_assoc()`](./builtins/array/array_diff_assoc.md) | `(array $array, ...$arrays): mixed` | `mixed` |
| [`array_diff_key()`](./builtins/array/array_diff_key.md) | `(array $array, ...$arrays): array` | `array` |
| [`array_fill()`](./builtins/array/array_fill.md) | `(int $start_index, int $count, mixed $value): array` | `array` |
| [`array_fill_keys()`](./builtins/array/array_fill_keys.md) | `(array $keys, mixed $value): array` | `array` |
-| [`array_filter()`](./builtins/array/array_filter.md) | `(array $array, callable $callback, int $mode): array` | `array` |
-| [`array_flip()`](./builtins/array/array_flip.md) | `(array $array): float` | `float` |
+| [`array_filter()`](./builtins/array/array_filter.md) | `(array $array, callable $callback = null, int $mode = 0): array` | `array` |
+| [`array_find()`](./builtins/array/array_find.md) | `(mixed $array, mixed $callback): mixed` | `mixed` |
+| [`array_flip()`](./builtins/array/array_flip.md) | `(array $array): array` | `array` |
| [`array_intersect()`](./builtins/array/array_intersect.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_intersect_assoc()`](./builtins/array/array_intersect_assoc.md) | `(array $array, ...$arrays): mixed` | `mixed` |
| [`array_intersect_key()`](./builtins/array/array_intersect_key.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_is_list()`](./builtins/array/array_is_list.md) | `(mixed $array): bool` | `bool` |
| [`array_key_exists()`](./builtins/array/array_key_exists.md) | `(string $key, array $array): bool` | `bool` |
-| [`array_keys()`](./builtins/array/array_keys.md) | `(array $array, string $filter_value, bool $strict): array` | `array` |
+| [`array_key_first()`](./builtins/array/array_key_first.md) | `(array $array): mixed` | `mixed` |
+| [`array_key_last()`](./builtins/array/array_key_last.md) | `(array $array): mixed` | `mixed` |
+| [`array_keys()`](./builtins/array/array_keys.md) | `(array $array): array` | `array` |
| [`array_map()`](./builtins/array/array_map.md) | `(callable $callback, array $array, ...$arrays): array` | `array` |
| [`array_merge()`](./builtins/array/array_merge.md) | `(...$arrays): array` | `array` |
+| [`array_merge_recursive()`](./builtins/array/array_merge_recursive.md) | `(...$arrays): array` | `array` |
+| [`array_multisort()`](./builtins/array/array_multisort.md) | `(array $array1, int $array2): bool` | `bool` |
| [`array_pad()`](./builtins/array/array_pad.md) | `(array $array, int $length, mixed $value): array` | `array` |
| [`array_pop()`](./builtins/array/array_pop.md) | `(array $array): mixed` | `mixed` |
-| [`array_product()`](./builtins/array/array_product.md) | `(array $array): float` | `float` |
+| [`array_product()`](./builtins/array/array_product.md) | `(array $array): int` | `int` |
| [`array_push()`](./builtins/array/array_push.md) | `(array $array, ...$values): void` | `void` |
-| [`array_rand()`](./builtins/array/array_rand.md) | `(array $array, int $num): int` | `int` |
-| [`array_reduce()`](./builtins/array/array_reduce.md) | `(array $array, callable $callback, mixed $initial): int` | `int` |
-| [`array_reverse()`](./builtins/array/array_reverse.md) | `(array $array, bool $preserve_keys): array` | `array` |
-| [`array_search()`](./builtins/array/array_search.md) | `(mixed $needle, array $haystack, bool $strict): mixed` | `mixed` |
+| [`array_rand()`](./builtins/array/array_rand.md) | `(array $array): int` | `int` |
+| [`array_reduce()`](./builtins/array/array_reduce.md) | `(array $array, callable $callback, mixed $initial = null): int` | `int` |
+| [`array_replace()`](./builtins/array/array_replace.md) | `(array $array, array $replacements): mixed` | `mixed` |
+| [`array_replace_recursive()`](./builtins/array/array_replace_recursive.md) | `(array $array, array $replacements): mixed` | `mixed` |
+| [`array_reverse()`](./builtins/array/array_reverse.md) | `(array $array): array` | `array` |
+| [`array_search()`](./builtins/array/array_search.md) | `(mixed $needle, array $haystack, bool $strict = false): mixed` | `mixed` |
| [`array_shift()`](./builtins/array/array_shift.md) | `(array $array): mixed` | `mixed` |
-| [`array_slice()`](./builtins/array/array_slice.md) | `(array $array, int $offset, int $length, bool $preserve_keys): array` | `array` |
-| [`array_splice()`](./builtins/array/array_splice.md) | `(array $array, int $offset, int $length, array $replacement): array` | `array` |
-| [`array_sum()`](./builtins/array/array_sum.md) | `(array $array): float` | `float` |
-| [`array_unique()`](./builtins/array/array_unique.md) | `(array $array, int $flags): array` | `array` |
+| [`array_slice()`](./builtins/array/array_slice.md) | `(array $array, int $offset, int $length = null): array` | `array` |
+| [`array_splice()`](./builtins/array/array_splice.md) | `(array $array, int $offset, int $length = null): array` | `array` |
+| [`array_sum()`](./builtins/array/array_sum.md) | `(array $array): int` | `int` |
+| [`array_udiff()`](./builtins/array/array_udiff.md) | `(array $array1, array $array2, callable $callback): array` | `array` |
+| [`array_uintersect()`](./builtins/array/array_uintersect.md) | `(array $array1, array $array2, callable $callback): array` | `array` |
+| [`array_unique()`](./builtins/array/array_unique.md) | `(array $array): array` | `array` |
| [`array_unshift()`](./builtins/array/array_unshift.md) | `(array $array, ...$values): int` | `int` |
| [`array_values()`](./builtins/array/array_values.md) | `(array $array): array` | `array` |
-| [`array_walk()`](./builtins/array/array_walk.md) | `(array $array, callable $callback, mixed $arg): void` | `void` |
-| [`arsort()`](./builtins/array/arsort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`asort()`](./builtins/array/asort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`count()`](./builtins/array/count.md) | `(array $value, int $mode): int` | `int` |
-| [`in_array()`](./builtins/array/in_array.md) | `(mixed $needle, array $haystack, bool $strict): mixed` | `mixed` |
-| [`krsort()`](./builtins/array/krsort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`ksort()`](./builtins/array/ksort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`array_walk()`](./builtins/array/array_walk.md) | `(array $array, callable $callback): void` | `void` |
+| [`array_walk_recursive()`](./builtins/array/array_walk_recursive.md) | `(array $array, callable $callback): void` | `void` |
+| [`arsort()`](./builtins/array/arsort.md) | `(array $array): bool` | `bool` |
+| [`asort()`](./builtins/array/asort.md) | `(array $array): bool` | `bool` |
+| [`call_user_func()`](./builtins/array/call_user_func.md) | `(callable $callback, ...$args): mixed` | `mixed` |
+| [`call_user_func_array()`](./builtins/array/call_user_func_array.md) | `(callable $callback, array $args): mixed` | `mixed` |
+| [`count()`](./builtins/array/count.md) | `(array $value, int $mode = 0): int` | `int` |
+| [`in_array()`](./builtins/array/in_array.md) | `(mixed $needle, array $haystack, bool $strict = false): bool` | `bool` |
+| [`krsort()`](./builtins/array/krsort.md) | `(array $array): bool` | `bool` |
+| [`ksort()`](./builtins/array/ksort.md) | `(array $array): bool` | `bool` |
| [`natcasesort()`](./builtins/array/natcasesort.md) | `(array $array): bool` | `bool` |
| [`natsort()`](./builtins/array/natsort.md) | `(array $array): bool` | `bool` |
-| [`range()`](./builtins/array/range.md) | `(mixed $start, mixed $end, int $step): array` | `array` |
-| [`rsort()`](./builtins/array/rsort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`range()`](./builtins/array/range.md) | `(mixed $start, mixed $end): array` | `array` |
+| [`rsort()`](./builtins/array/rsort.md) | `(array $array): bool` | `bool` |
| [`shuffle()`](./builtins/array/shuffle.md) | `(array $array): bool` | `bool` |
-| [`sort()`](./builtins/array/sort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`sort()`](./builtins/array/sort.md) | `(array $array): bool` | `bool` |
| [`uasort()`](./builtins/array/uasort.md) | `(array $array, callable $callback): bool` | `bool` |
| [`uksort()`](./builtins/array/uksort.md) | `(array $array, callable $callback): bool` | `bool` |
| [`usort()`](./builtins/array/usort.md) | `(array $array, callable $callback): bool` | `bool` |
| [`buffer_free()`](./builtins/buffer/buffer_free.md) | `(buffer $buffer): mixed` | `mixed` |
| [`buffer_len()`](./builtins/buffer/buffer_len.md) | `(buffer $buffer): int` | `int` |
-| [`class_alias()`](./builtins/class/class_alias.md) | `(string $class, string $alias, bool $autoload): bool` | `bool` |
+| [`class_alias()`](./builtins/class/class_alias.md) | `(string $class, string $alias, bool $autoload = true): bool` | `bool` |
| [`class_attribute_args()`](./builtins/class/class_attribute_args.md) | `(string $class_name, string $attribute_name): array` | `array` |
| [`class_attribute_names()`](./builtins/class/class_attribute_names.md) | `(string $class_name): array` | `array` |
-| [`class_exists()`](./builtins/class/class_exists.md) | `(string $class, bool $autoload): bool` | `bool` |
-| [`class_get_attributes()`](./builtins/class/class_get_attributes.md) | `(string $class_name): mixed` | `mixed` |
-| [`class_implements()`](./builtins/class/class_implements.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`class_parents()`](./builtins/class/class_parents.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`class_uses()`](./builtins/class/class_uses.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`enum_exists()`](./builtins/class/enum_exists.md) | `(string $enum, bool $autoload): bool` | `bool` |
+| [`class_exists()`](./builtins/class/class_exists.md) | `(string $class, bool $autoload = true): bool` | `bool` |
+| [`class_get_attributes()`](./builtins/class/class_get_attributes.md) | `(string $class_name): array` | `array` |
+| [`class_implements()`](./builtins/class/class_implements.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`class_parents()`](./builtins/class/class_parents.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`class_uses()`](./builtins/class/class_uses.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`enum_exists()`](./builtins/class/enum_exists.md) | `(string $enum, bool $autoload = true): bool` | `bool` |
| [`function_exists()`](./builtins/class/function_exists.md) | `(string $function): bool` | `bool` |
-| [`get_class()`](./builtins/class/get_class.md) | `(object $object): string` | `string` |
+| [`get_class()`](./builtins/class/get_class.md) | `(object $object = null): string` | `string` |
| [`get_declared_classes()`](./builtins/class/get_declared_classes.md) | `(): array` | `array` |
| [`get_declared_interfaces()`](./builtins/class/get_declared_interfaces.md) | `(): array` | `array` |
| [`get_declared_traits()`](./builtins/class/get_declared_traits.md) | `(): array` | `array` |
-| [`get_parent_class()`](./builtins/class/get_parent_class.md) | `(mixed $object_or_class): string` | `string` |
-| [`interface_exists()`](./builtins/class/interface_exists.md) | `(string $interface, bool $autoload): bool` | `bool` |
-| [`is_a()`](./builtins/class/is_a.md) | `(object $object_or_class, string $class, bool $allow_string): bool` | `bool` |
-| [`is_subclass_of()`](./builtins/class/is_subclass_of.md) | `(mixed $object_or_class, string $class, bool $allow_string): bool` | `bool` |
-| [`trait_exists()`](./builtins/class/trait_exists.md) | `(string $trait, bool $autoload): bool` | `bool` |
+| [`get_parent_class()`](./builtins/class/get_parent_class.md) | `(mixed $object_or_class = null): string` | `string` |
+| [`interface_exists()`](./builtins/class/interface_exists.md) | `(string $interface, bool $autoload = true): bool` | `bool` |
+| [`is_a()`](./builtins/class/is_a.md) | `(object $object_or_class, string $class, bool $allow_string = false): bool` | `bool` |
+| [`is_subclass_of()`](./builtins/class/is_subclass_of.md) | `(mixed $object_or_class, string $class, bool $allow_string = true): bool` | `bool` |
+| [`trait_exists()`](./builtins/class/trait_exists.md) | `(string $trait, bool $autoload = true): bool` | `bool` |
| [`checkdate()`](./builtins/date/checkdate.md) | `(int $month, int $day, int $year): bool` | `bool` |
-| [`date()`](./builtins/date/date.md) | `(string $format, int $timestamp): string` | `string` |
+| [`date()`](./builtins/date/date.md) | `(string $format, int $timestamp = null): string` | `string` |
| [`date_default_timezone_get()`](./builtins/date/date_default_timezone_get.md) | `(): string` | `string` |
| [`date_default_timezone_set()`](./builtins/date/date_default_timezone_set.md) | `(string $timezoneId): bool` | `bool` |
-| [`getdate()`](./builtins/date/getdate.md) | `(int $timestamp): array` | `array` |
-| [`gmdate()`](./builtins/date/gmdate.md) | `(string $format, int $timestamp): string` | `string` |
+| [`getdate()`](./builtins/date/getdate.md) | `(int $timestamp = null): array` | `array` |
+| [`gmdate()`](./builtins/date/gmdate.md) | `(string $format, int $timestamp = null): string` | `string` |
| [`gmmktime()`](./builtins/date/gmmktime.md) | `(int $hour, int $minute, int $second, int $month, int $day, int $year): int` | `int` |
-| [`hrtime()`](./builtins/date/hrtime.md) | `(bool $as_number): mixed` | `mixed` |
-| [`localtime()`](./builtins/date/localtime.md) | `(int $timestamp, bool $associative): array` | `array` |
-| [`microtime()`](./builtins/date/microtime.md) | `(bool $as_float): int` | `int` |
+| [`hrtime()`](./builtins/date/hrtime.md) | `(bool $as_number = false): mixed` | `mixed` |
+| [`localtime()`](./builtins/date/localtime.md) | `(int $timestamp = -1, bool $associative = false): array` | `array` |
+| [`microtime()`](./builtins/date/microtime.md) | `(bool $as_float = false): mixed` | `mixed` |
| [`mktime()`](./builtins/date/mktime.md) | `(int $hour, int $minute, int $second, int $month, int $day, int $year): int` | `int` |
-| [`strtotime()`](./builtins/date/strtotime.md) | `(string $datetime, int $baseTimestamp): mixed` | `mixed` |
+| [`strtotime()`](./builtins/date/strtotime.md) | `(string $datetime, int $baseTimestamp = null): mixed` | `mixed` |
| [`time()`](./builtins/date/time.md) | `(): int` | `int` |
-| [`basename()`](./builtins/filesystem/basename.md) | `(string $path, string $suffix): string` | `string` |
+| [`basename()`](./builtins/filesystem/basename.md) | `(string $path, string $suffix = ''): string` | `string` |
| [`chdir()`](./builtins/filesystem/chdir.md) | `(string $directory): bool` | `bool` |
-| [`chgrp()`](./builtins/filesystem/chgrp.md) | `(string $filename, int $group): bool` | `bool` |
+| [`chgrp()`](./builtins/filesystem/chgrp.md) | `(string $filename, string $group): bool` | `bool` |
| [`chmod()`](./builtins/filesystem/chmod.md) | `(string $filename, int $permissions): bool` | `bool` |
-| [`chown()`](./builtins/filesystem/chown.md) | `(string $filename, int $user): bool` | `bool` |
-| [`clearstatcache()`](./builtins/filesystem/clearstatcache.md) | `(bool $clear_realpath_cache, string $filename): void` | `void` |
-| [`copy()`](./builtins/filesystem/copy.md) | `(string $from, string $to, mixed $context): bool` | `bool` |
-| [`dirname()`](./builtins/filesystem/dirname.md) | `(string $path, int $levels): string` | `string` |
+| [`chown()`](./builtins/filesystem/chown.md) | `(string $filename, string $user): bool` | `bool` |
+| [`clearstatcache()`](./builtins/filesystem/clearstatcache.md) | `(bool $clear_realpath_cache = false, string $filename = ''): void` | `void` |
+| [`copy()`](./builtins/filesystem/copy.md) | `(string $from, string $to): bool` | `bool` |
+| [`dirname()`](./builtins/filesystem/dirname.md) | `(string $path, int $levels = 1): string` | `string` |
| [`disk_free_space()`](./builtins/filesystem/disk_free_space.md) | `(string $directory): float` | `float` |
| [`disk_total_space()`](./builtins/filesystem/disk_total_space.md) | `(string $directory): float` | `float` |
| [`file_exists()`](./builtins/filesystem/file_exists.md) | `(string $filename): bool` | `bool` |
@@ -109,10 +126,10 @@ sidebar:
| [`fileperms()`](./builtins/filesystem/fileperms.md) | `(string $filename): mixed` | `mixed` |
| [`filesize()`](./builtins/filesystem/filesize.md) | `(string $filename): int` | `int` |
| [`filetype()`](./builtins/filesystem/filetype.md) | `(string $filename): mixed` | `mixed` |
-| [`fnmatch()`](./builtins/filesystem/fnmatch.md) | `(string $pattern, string $filename, int $flags): bool` | `bool` |
+| [`fnmatch()`](./builtins/filesystem/fnmatch.md) | `(string $pattern, string $filename, int $flags = 0): bool` | `bool` |
| [`getcwd()`](./builtins/filesystem/getcwd.md) | `(): string` | `string` |
-| [`getenv()`](./builtins/filesystem/getenv.md) | `(string $name, bool $local_only): mixed` | `mixed` |
-| [`glob()`](./builtins/filesystem/glob.md) | `(string $pattern, int $flags): array` | `array` |
+| [`getenv()`](./builtins/filesystem/getenv.md) | `(string $name): mixed` | `mixed` |
+| [`glob()`](./builtins/filesystem/glob.md) | `(string $pattern): array` | `array` |
| [`is_dir()`](./builtins/filesystem/is_dir.md) | `(string $filename): bool` | `bool` |
| [`is_executable()`](./builtins/filesystem/is_executable.md) | `(string $filename): bool` | `bool` |
| [`is_file()`](./builtins/filesystem/is_file.md) | `(string $filename): bool` | `bool` |
@@ -120,29 +137,29 @@ sidebar:
| [`is_readable()`](./builtins/filesystem/is_readable.md) | `(string $filename): bool` | `bool` |
| [`is_writable()`](./builtins/filesystem/is_writable.md) | `(string $filename): bool` | `bool` |
| [`is_writeable()`](./builtins/filesystem/is_writeable.md) | `(string $filename): bool` | `bool` |
-| [`lchgrp()`](./builtins/filesystem/lchgrp.md) | `(string $filename, int $group): bool` | `bool` |
-| [`lchown()`](./builtins/filesystem/lchown.md) | `(string $filename, int $user): bool` | `bool` |
+| [`lchgrp()`](./builtins/filesystem/lchgrp.md) | `(string $filename, string $group): bool` | `bool` |
+| [`lchown()`](./builtins/filesystem/lchown.md) | `(string $filename, string $user): bool` | `bool` |
| [`link()`](./builtins/filesystem/link.md) | `(string $target, string $link): bool` | `bool` |
| [`linkinfo()`](./builtins/filesystem/linkinfo.md) | `(string $path): int` | `int` |
| [`lstat()`](./builtins/filesystem/lstat.md) | `(string $filename): mixed` | `mixed` |
-| [`mkdir()`](./builtins/filesystem/mkdir.md) | `(string $directory, int $permissions, bool $recursive, bool $context): bool` | `bool` |
-| [`pathinfo()`](./builtins/filesystem/pathinfo.md) | `(string $path, int $flags): mixed` | `mixed` |
+| [`mkdir()`](./builtins/filesystem/mkdir.md) | `(string $directory): bool` | `bool` |
+| [`pathinfo()`](./builtins/filesystem/pathinfo.md) | `(string $path, int $flags = 15): array` | `array` |
| [`putenv()`](./builtins/filesystem/putenv.md) | `(string $assignment): bool` | `bool` |
-| [`readfile()`](./builtins/filesystem/readfile.md) | `(string $filename, bool $use_include_path, mixed $context): mixed` | `mixed` |
+| [`readfile()`](./builtins/filesystem/readfile.md) | `(string $filename): mixed` | `mixed` |
| [`readlink()`](./builtins/filesystem/readlink.md) | `(string $path): mixed` | `mixed` |
| [`realpath()`](./builtins/filesystem/realpath.md) | `(string $path): mixed` | `mixed` |
| [`realpath_cache_get()`](./builtins/filesystem/realpath_cache_get.md) | `(): array` | `array` |
| [`realpath_cache_size()`](./builtins/filesystem/realpath_cache_size.md) | `(): int` | `int` |
-| [`rename()`](./builtins/filesystem/rename.md) | `(string $from, string $to, mixed $context): bool` | `bool` |
-| [`rmdir()`](./builtins/filesystem/rmdir.md) | `(string $directory, mixed $context = null): bool` | `bool` |
-| [`scandir()`](./builtins/filesystem/scandir.md) | `(string $directory, int $sorting_order, mixed $context): array` | `array` |
+| [`rename()`](./builtins/filesystem/rename.md) | `(string $from, string $to): bool` | `bool` |
+| [`rmdir()`](./builtins/filesystem/rmdir.md) | `(string $directory): bool` | `bool` |
+| [`scandir()`](./builtins/filesystem/scandir.md) | `(string $directory): array` | `array` |
| [`stat()`](./builtins/filesystem/stat.md) | `(string $filename): mixed` | `mixed` |
| [`symlink()`](./builtins/filesystem/symlink.md) | `(string $target, string $link): bool` | `bool` |
| [`sys_get_temp_dir()`](./builtins/filesystem/sys_get_temp_dir.md) | `(): string` | `string` |
| [`tempnam()`](./builtins/filesystem/tempnam.md) | `(string $directory, string $prefix): string` | `string` |
| [`tmpfile()`](./builtins/filesystem/tmpfile.md) | `(): mixed` | `mixed` |
-| [`touch()`](./builtins/filesystem/touch.md) | `(string $filename, int $mtime, int $atime): bool` | `bool` |
-| [`umask()`](./builtins/filesystem/umask.md) | `(int $mask): int` | `int` |
+| [`touch()`](./builtins/filesystem/touch.md) | `(string $filename, int $mtime = null, int $atime = null): bool` | `bool` |
+| [`umask()`](./builtins/filesystem/umask.md) | `(int $mask = null): int` | `int` |
| [`unlink()`](./builtins/filesystem/unlink.md) | `(string $filename): bool` | `bool` |
| [`closedir()`](./builtins/io/closedir.md) | `(resource $dir_handle): void` | `void` |
| [`fclose()`](./builtins/io/fclose.md) | `(resource $stream): bool` | `bool` |
@@ -150,24 +167,24 @@ sidebar:
| [`feof()`](./builtins/io/feof.md) | `(resource $stream): bool` | `bool` |
| [`fflush()`](./builtins/io/fflush.md) | `(resource $stream): bool` | `bool` |
| [`fgetc()`](./builtins/io/fgetc.md) | `(resource $stream): mixed` | `mixed` |
-| [`fgetcsv()`](./builtins/io/fgetcsv.md) | `(resource $stream, int $length, string $separator, string $enclosure, string $escape): array` | `array` |
-| [`fgets()`](./builtins/io/fgets.md) | `(resource $stream, int $length): mixed` | `mixed` |
-| [`file()`](./builtins/io/file.md) | `(string $filename, int $flags, mixed $context): array` | `array` |
-| [`file_get_contents()`](./builtins/io/file_get_contents.md) | `(string $filename, bool $use_include_path, mixed $context, int $offset, int $length): mixed` | `mixed` |
-| [`file_put_contents()`](./builtins/io/file_put_contents.md) | `(string $filename, mixed $data, int $flags = 0, mixed $context = null): int` | `int` |
-| [`flock()`](./builtins/io/flock.md) | `(resource $stream, int $operation, bool $would_block): bool` | `bool` |
-| [`fopen()`](./builtins/io/fopen.md) | `(string $filename, string $mode, bool $use_include_path, mixed $context): mixed` | `mixed` |
+| [`fgetcsv()`](./builtins/io/fgetcsv.md) | `(resource $stream, int $length = null, string $separator = ','): array` | `array` |
+| [`fgets()`](./builtins/io/fgets.md) | `(resource $stream): mixed` | `mixed` |
+| [`file()`](./builtins/io/file.md) | `(string $filename): array` | `array` |
+| [`file_get_contents()`](./builtins/io/file_get_contents.md) | `(string $filename): mixed` | `mixed` |
+| [`file_put_contents()`](./builtins/io/file_put_contents.md) | `(string $filename, string $data): int` | `int` |
+| [`flock()`](./builtins/io/flock.md) | `(resource $stream, int $operation, bool $would_block = null): bool` | `bool` |
+| [`fopen()`](./builtins/io/fopen.md) | `(string $filename, string $mode, bool $use_include_path = false, mixed $context = null): mixed` | `mixed` |
| [`fpassthru()`](./builtins/io/fpassthru.md) | `(resource $stream): int` | `int` |
| [`fprintf()`](./builtins/io/fprintf.md) | `(resource $stream, string $format, ...$values): int` | `int` |
-| [`fputcsv()`](./builtins/io/fputcsv.md) | `(resource $stream, array $fields, string $separator = ',', string $enclosure = '"', string $escape = '\\', string $eol = '\n'): int` | `int` |
+| [`fputcsv()`](./builtins/io/fputcsv.md) | `(resource $stream, array $fields, string $separator = ',', string $enclosure = '"'): int` | `int` |
| [`fread()`](./builtins/io/fread.md) | `(resource $stream, int $length): string` | `string` |
| [`fscanf()`](./builtins/io/fscanf.md) | `(resource $stream, string $format, ...$vars): array` | `array` |
-| [`fseek()`](./builtins/io/fseek.md) | `(resource $stream, int $offset, int $whence): int` | `int` |
+| [`fseek()`](./builtins/io/fseek.md) | `(resource $stream, int $offset, int $whence = 0): int` | `int` |
| [`fstat()`](./builtins/io/fstat.md) | `(resource $stream): mixed` | `mixed` |
| [`fsync()`](./builtins/io/fsync.md) | `(resource $stream): bool` | `bool` |
| [`ftell()`](./builtins/io/ftell.md) | `(resource $stream): int` | `int` |
| [`ftruncate()`](./builtins/io/ftruncate.md) | `(resource $stream, int $size): bool` | `bool` |
-| [`fwrite()`](./builtins/io/fwrite.md) | `(resource $stream, string $data, int $length): int` | `int` |
+| [`fwrite()`](./builtins/io/fwrite.md) | `(resource $stream, string $data): int` | `int` |
| [`gethostbyaddr()`](./builtins/io/gethostbyaddr.md) | `(string $ip): mixed` | `mixed` |
| [`gethostbyname()`](./builtins/io/gethostbyname.md) | `(string $hostname): string` | `string` |
| [`gethostname()`](./builtins/io/gethostname.md) | `(): string` | `string` |
@@ -175,64 +192,64 @@ sidebar:
| [`getprotobynumber()`](./builtins/io/getprotobynumber.md) | `(int $protocol): mixed` | `mixed` |
| [`getservbyname()`](./builtins/io/getservbyname.md) | `(string $service, string $protocol): mixed` | `mixed` |
| [`getservbyport()`](./builtins/io/getservbyport.md) | `(int $port, string $protocol): mixed` | `mixed` |
-| [`hash_file()`](./builtins/io/hash_file.md) | `(string $algo, string $filename, bool $binary = false, array $options = []): mixed` | `mixed` |
+| [`hash_file()`](./builtins/io/hash_file.md) | `(string $algo, string $filename, bool $binary = false): mixed` | `mixed` |
| [`opendir()`](./builtins/io/opendir.md) | `(string $directory): mixed` | `mixed` |
| [`readdir()`](./builtins/io/readdir.md) | `(resource $dir_handle): mixed` | `mixed` |
| [`rewind()`](./builtins/io/rewind.md) | `(resource $stream): bool` | `bool` |
| [`rewinddir()`](./builtins/io/rewinddir.md) | `(resource $dir_handle): void` | `void` |
| [`stream_bucket_make_writeable()`](./builtins/io/stream_bucket_make_writeable.md) | `(mixed $brigade): mixed` | `mixed` |
| [`stream_bucket_new()`](./builtins/io/stream_bucket_new.md) | `(resource $stream, string $buffer): mixed` | `mixed` |
-| [`stream_context_create()`](./builtins/io/stream_context_create.md) | `(array $options, array $params): mixed` | `mixed` |
-| [`stream_context_get_default()`](./builtins/io/stream_context_get_default.md) | `(array $options): mixed` | `mixed` |
-| [`stream_context_get_options()`](./builtins/io/stream_context_get_options.md) | `(resource $stream_or_context): array` | `array` |
+| [`stream_context_create()`](./builtins/io/stream_context_create.md) | `(array $options = null, array $params = null): mixed` | `mixed` |
+| [`stream_context_get_default()`](./builtins/io/stream_context_get_default.md) | `(array $options = null): mixed` | `mixed` |
+| [`stream_context_get_options()`](./builtins/io/stream_context_get_options.md) | `(resource $context): array` | `array` |
| [`stream_context_get_params()`](./builtins/io/stream_context_get_params.md) | `(resource $context): array` | `array` |
| [`stream_context_set_default()`](./builtins/io/stream_context_set_default.md) | `(array $options): mixed` | `mixed` |
-| [`stream_context_set_option()`](./builtins/io/stream_context_set_option.md) | `(resource $context, string $wrapper_or_options, string $option_name, mixed $value): bool` | `bool` |
+| [`stream_context_set_option()`](./builtins/io/stream_context_set_option.md) | `(resource $context, string $wrapper_or_options, string $option_name = null, mixed $value = null): bool` | `bool` |
| [`stream_context_set_params()`](./builtins/io/stream_context_set_params.md) | `(resource $context, array $params): bool` | `bool` |
-| [`stream_copy_to_stream()`](./builtins/io/stream_copy_to_stream.md) | `(resource $from, resource $to, int $length, int $offset): mixed` | `mixed` |
+| [`stream_copy_to_stream()`](./builtins/io/stream_copy_to_stream.md) | `(resource $from, resource $to, int $length = null, int $offset = -1): mixed` | `mixed` |
| [`stream_filter_register()`](./builtins/io/stream_filter_register.md) | `(string $filter_name, string $class): bool` | `bool` |
| [`stream_filter_remove()`](./builtins/io/stream_filter_remove.md) | `(resource $stream_filter): bool` | `bool` |
-| [`stream_get_contents()`](./builtins/io/stream_get_contents.md) | `(resource $stream, int $length, int $offset): mixed` | `mixed` |
+| [`stream_get_contents()`](./builtins/io/stream_get_contents.md) | `(resource $stream, int $length = null, int $offset = -1): mixed` | `mixed` |
| [`stream_get_filters()`](./builtins/io/stream_get_filters.md) | `(): array` | `array` |
-| [`stream_get_line()`](./builtins/io/stream_get_line.md) | `(resource $stream, int $length, string $ending): string` | `string` |
+| [`stream_get_line()`](./builtins/io/stream_get_line.md) | `(resource $stream, int $length, string $ending = ''): string` | `string` |
| [`stream_get_meta_data()`](./builtins/io/stream_get_meta_data.md) | `(resource $stream): array` | `array` |
| [`stream_get_transports()`](./builtins/io/stream_get_transports.md) | `(): array` | `array` |
| [`stream_get_wrappers()`](./builtins/io/stream_get_wrappers.md) | `(): array` | `array` |
| [`stream_is_local()`](./builtins/io/stream_is_local.md) | `(resource $stream): bool` | `bool` |
| [`stream_isatty()`](./builtins/io/stream_isatty.md) | `(resource $stream): bool` | `bool` |
| [`stream_resolve_include_path()`](./builtins/io/stream_resolve_include_path.md) | `(string $filename): mixed` | `mixed` |
-| [`stream_select()`](./builtins/io/stream_select.md) | `(array $read, array $write, array $except, int $seconds, int $microseconds): int` | `int` |
+| [`stream_select()`](./builtins/io/stream_select.md) | `(array $read, array $write, array $except, int $seconds, int $microseconds = 0): int` | `int` |
| [`stream_set_blocking()`](./builtins/io/stream_set_blocking.md) | `(resource $stream, bool $enable): bool` | `bool` |
| [`stream_set_chunk_size()`](./builtins/io/stream_set_chunk_size.md) | `(resource $stream, int $size): int` | `int` |
| [`stream_set_read_buffer()`](./builtins/io/stream_set_read_buffer.md) | `(resource $stream, int $size): int` | `int` |
-| [`stream_set_timeout()`](./builtins/io/stream_set_timeout.md) | `(resource $stream, int $seconds, int $microseconds): bool` | `bool` |
+| [`stream_set_timeout()`](./builtins/io/stream_set_timeout.md) | `(resource $stream, int $seconds, int $microseconds = 0): bool` | `bool` |
| [`stream_set_write_buffer()`](./builtins/io/stream_set_write_buffer.md) | `(resource $stream, int $size): int` | `int` |
-| [`stream_socket_accept()`](./builtins/io/stream_socket_accept.md) | `(resource $socket, float $timeout, string $peer_name): mixed` | `mixed` |
-| [`stream_socket_client()`](./builtins/io/stream_socket_client.md) | `(string $address, int $error_code, int $error_message, string $timeout, float $flags): mixed` | `mixed` |
-| [`stream_socket_enable_crypto()`](./builtins/io/stream_socket_enable_crypto.md) | `(resource $stream, bool $enable, int $crypto_method, resource $session_stream): bool` | `bool` |
+| [`stream_socket_accept()`](./builtins/io/stream_socket_accept.md) | `(resource $socket, float $timeout = null, string $peer_name = null): mixed` | `mixed` |
+| [`stream_socket_client()`](./builtins/io/stream_socket_client.md) | `(string $address): mixed` | `mixed` |
+| [`stream_socket_enable_crypto()`](./builtins/io/stream_socket_enable_crypto.md) | `(resource $stream, bool $enable, int $crypto_method = null, resource $session_stream = null): bool` | `bool` |
| [`stream_socket_get_name()`](./builtins/io/stream_socket_get_name.md) | `(resource $socket, bool $remote): mixed` | `mixed` |
| [`stream_socket_pair()`](./builtins/io/stream_socket_pair.md) | `(int $domain, int $type, int $protocol): mixed` | `mixed` |
-| [`stream_socket_recvfrom()`](./builtins/io/stream_socket_recvfrom.md) | `(resource $socket, int $length, int $flags, string $address): mixed` | `mixed` |
-| [`stream_socket_sendto()`](./builtins/io/stream_socket_sendto.md) | `(resource $socket, string $data, int $flags, string $address): mixed` | `mixed` |
-| [`stream_socket_server()`](./builtins/io/stream_socket_server.md) | `(string $address, int $error_code, int $error_message): mixed` | `mixed` |
+| [`stream_socket_recvfrom()`](./builtins/io/stream_socket_recvfrom.md) | `(resource $socket, int $length, int $flags = 0, string $address = ''): mixed` | `mixed` |
+| [`stream_socket_sendto()`](./builtins/io/stream_socket_sendto.md) | `(resource $socket, string $data, int $flags = 0, string $address = ''): mixed` | `mixed` |
+| [`stream_socket_server()`](./builtins/io/stream_socket_server.md) | `(string $address): mixed` | `mixed` |
| [`stream_socket_shutdown()`](./builtins/io/stream_socket_shutdown.md) | `(resource $stream, int $mode): bool` | `bool` |
| [`stream_supports_lock()`](./builtins/io/stream_supports_lock.md) | `(resource $stream): bool` | `bool` |
-| [`stream_wrapper_register()`](./builtins/io/stream_wrapper_register.md) | `(string $protocol, string $class, int $flags): bool` | `bool` |
+| [`stream_wrapper_register()`](./builtins/io/stream_wrapper_register.md) | `(string $protocol, string $class, int $flags = 0): bool` | `bool` |
| [`stream_wrapper_restore()`](./builtins/io/stream_wrapper_restore.md) | `(string $protocol): bool` | `bool` |
| [`stream_wrapper_unregister()`](./builtins/io/stream_wrapper_unregister.md) | `(string $protocol): bool` | `bool` |
| [`vfprintf()`](./builtins/io/vfprintf.md) | `(resource $stream, string $format, array $values): int` | `int` |
-| [`json_decode()`](./builtins/json/json_decode.md) | `(string $json, bool $associative, int $depth, int $flags): mixed` | `mixed` |
-| [`json_encode()`](./builtins/json/json_encode.md) | `(mixed $value, int $flags, int $depth): string` | `string` |
+| [`json_decode()`](./builtins/json/json_decode.md) | `(string $json, bool $associative = null, int $depth = 512, int $flags = 0): mixed` | `mixed` |
+| [`json_encode()`](./builtins/json/json_encode.md) | `(mixed $value, int $flags = 0, int $depth = 512): string` | `string` |
| [`json_last_error()`](./builtins/json/json_last_error.md) | `(): int` | `int` |
| [`json_last_error_msg()`](./builtins/json/json_last_error_msg.md) | `(): string` | `string` |
-| [`json_validate()`](./builtins/json/json_validate.md) | `(string $json, int $depth, int $flags): bool` | `bool` |
+| [`json_validate()`](./builtins/json/json_validate.md) | `(string $json, int $depth = 512, int $flags = 0): bool` | `bool` |
| [`abs()`](./builtins/math/abs.md) | `(int $num): mixed` | `mixed` |
| [`acos()`](./builtins/math/acos.md) | `(float $num): float` | `float` |
| [`asin()`](./builtins/math/asin.md) | `(float $num): float` | `float` |
| [`atan()`](./builtins/math/atan.md) | `(float $num): float` | `float` |
| [`atan2()`](./builtins/math/atan2.md) | `(float $y, float $x): float` | `float` |
| [`ceil()`](./builtins/math/ceil.md) | `(float $num): float` | `float` |
-| [`clamp()`](./builtins/math/clamp.md) | `(int $value, int $min, int $max): string` | `string` |
+| [`clamp()`](./builtins/math/clamp.md) | `(int $value, int $min, int $max): mixed` | `mixed` |
| [`cos()`](./builtins/math/cos.md) | `(float $num): float` | `float` |
| [`cosh()`](./builtins/math/cosh.md) | `(float $num): float` | `float` |
| [`deg2rad()`](./builtins/math/deg2rad.md) | `(float $num): float` | `float` |
@@ -245,37 +262,38 @@ sidebar:
| [`is_finite()`](./builtins/math/is_finite.md) | `(float $num): bool` | `bool` |
| [`is_infinite()`](./builtins/math/is_infinite.md) | `(float $num): bool` | `bool` |
| [`is_nan()`](./builtins/math/is_nan.md) | `(float $num): bool` | `bool` |
-| [`log()`](./builtins/math/log.md) | `(float $num, float $base): float` | `float` |
+| [`log()`](./builtins/math/log.md) | `(float $num, float $base = 2.718281828459045): float` | `float` |
| [`log10()`](./builtins/math/log10.md) | `(float $num): float` | `float` |
| [`log2()`](./builtins/math/log2.md) | `(float $num): float` | `float` |
-| [`max()`](./builtins/math/max.md) | `(mixed $value, ...$values): float` | `float` |
-| [`min()`](./builtins/math/min.md) | `(mixed $value, ...$values): float` | `float` |
+| [`max()`](./builtins/math/max.md) | `(mixed $value, ...$values): mixed` | `mixed` |
+| [`min()`](./builtins/math/min.md) | `(mixed $value, ...$values): mixed` | `mixed` |
| [`mt_rand()`](./builtins/math/mt_rand.md) | `(int $min, int $max): int` | `int` |
| [`pi()`](./builtins/math/pi.md) | `(): float` | `float` |
| [`pow()`](./builtins/math/pow.md) | `(float $num, float $exponent): float` | `float` |
| [`rad2deg()`](./builtins/math/rad2deg.md) | `(float $num): float` | `float` |
| [`rand()`](./builtins/math/rand.md) | `(int $min, int $max): int` | `int` |
+| [`random_bytes()`](./builtins/math/random_bytes.md) | `(int $length): string` | `string` |
| [`random_int()`](./builtins/math/random_int.md) | `(int $min, int $max): int` | `int` |
-| [`round()`](./builtins/math/round.md) | `(float $num, int $precision): float` | `float` |
+| [`round()`](./builtins/math/round.md) | `(float $num, int $precision = 0): float` | `float` |
| [`sin()`](./builtins/math/sin.md) | `(float $num): float` | `float` |
| [`sinh()`](./builtins/math/sinh.md) | `(float $num): float` | `float` |
| [`sqrt()`](./builtins/math/sqrt.md) | `(float $num): float` | `float` |
| [`tan()`](./builtins/math/tan.md) | `(float $num): float` | `float` |
| [`tanh()`](./builtins/math/tanh.md) | `(float $num): float` | `float` |
| [`buffer_new()`](./builtins/misc/buffer_new.md) | `(int $length): mixed` | `mixed` |
-| [`call_user_func()`](./builtins/misc/call_user_func.md) | `(callable $callback, ...$args): mixed` | `mixed` |
-| [`call_user_func_array()`](./builtins/misc/call_user_func_array.md) | `(callable $callback, array $args): mixed` | `mixed` |
-| [`define()`](./builtins/misc/define.md) | `(string $constant_name, mixed $value, bool $case_insensitive): bool` | `bool` |
+| [`define()`](./builtins/misc/define.md) | `(string $constant_name, mixed $value): bool` | `bool` |
| [`defined()`](./builtins/misc/defined.md) | `(string $constant_name): bool` | `bool` |
| [`empty()`](./builtins/misc/empty.md) | `(mixed $value): bool` | `bool` |
-| [`header()`](./builtins/misc/header.md) | `(mixed $header, mixed $replace, mixed $response_code): void` | `void` |
-| [`http_response_code()`](./builtins/misc/http_response_code.md) | `(mixed $response_code): int` | `int` |
+| [`header()`](./builtins/misc/header.md) | `(string $header, bool $replace = true, int $response_code = 0): void` | `void` |
+| [`http_response_code()`](./builtins/misc/http_response_code.md) | `(int $response_code = 0): int` | `int` |
| [`isset()`](./builtins/misc/isset.md) | `(mixed $var, ...$vars): bool` | `bool` |
-| [`php_uname()`](./builtins/misc/php_uname.md) | `(string $mode): string` | `string` |
-| [`phpversion()`](./builtins/misc/phpversion.md) | `(string $extension = null): string` | `string` |
-| [`print_r()`](./builtins/misc/print_r.md) | `(...$values): void` | `void` |
+| [`php_uname()`](./builtins/misc/php_uname.md) | `(string $mode = 'a'): string` | `string` |
+| [`phpversion()`](./builtins/misc/phpversion.md) | `(): string` | `string` |
+| [`print_r()`](./builtins/misc/print_r.md) | `(mixed $value): void` | `void` |
+| [`serialize()`](./builtins/misc/serialize.md) | `(mixed $value): string` | `string` |
+| [`unserialize()`](./builtins/misc/unserialize.md) | `(string $data, mixed $options = []): mixed` | `mixed` |
| [`unset()`](./builtins/misc/unset.md) | `(mixed $var, ...$vars): void` | `void` |
-| [`var_dump()`](./builtins/misc/var_dump.md) | `(...$values): void` | `void` |
+| [`var_dump()`](./builtins/misc/var_dump.md) | `(mixed $value): void` | `void` |
| [`ptr()`](./builtins/pointer/ptr.md) | `(mixed $value): mixed` | `mixed` |
| [`ptr_get()`](./builtins/pointer/ptr_get.md) | `(pointer $pointer): int` | `int` |
| [`ptr_is_null()`](./builtins/pointer/ptr_is_null.md) | `(pointer $pointer): bool` | `bool` |
@@ -286,116 +304,116 @@ sidebar:
| [`ptr_read8()`](./builtins/pointer/ptr_read8.md) | `(pointer $pointer): int` | `int` |
| [`ptr_read_string()`](./builtins/pointer/ptr_read_string.md) | `(pointer $pointer, int $length): string` | `string` |
| [`ptr_set()`](./builtins/pointer/ptr_set.md) | `(pointer $pointer, mixed $value): void` | `void` |
-| [`ptr_sizeof()`](./builtins/pointer/ptr_sizeof.md) | `(string $type): mixed` | `mixed` |
+| [`ptr_sizeof()`](./builtins/pointer/ptr_sizeof.md) | `(string $type): int` | `int` |
| [`ptr_write16()`](./builtins/pointer/ptr_write16.md) | `(pointer $pointer, int $value): void` | `void` |
| [`ptr_write32()`](./builtins/pointer/ptr_write32.md) | `(pointer $pointer, int $value): void` | `void` |
| [`ptr_write8()`](./builtins/pointer/ptr_write8.md) | `(pointer $pointer, int $value): void` | `void` |
| [`ptr_write_string()`](./builtins/pointer/ptr_write_string.md) | `(pointer $pointer, string $string): int` | `int` |
| [`die()`](./builtins/process/die.md) | `(int $status): void` | `void` |
-| [`exec()`](./builtins/process/exec.md) | `(string $command, array $output, int $result_code): string` | `string` |
+| [`exec()`](./builtins/process/exec.md) | `(string $command): string` | `string` |
| [`exit()`](./builtins/process/exit.md) | `(int $status): void` | `void` |
-| [`passthru()`](./builtins/process/passthru.md) | `(string $command, int $result_code): void` | `void` |
+| [`passthru()`](./builtins/process/passthru.md) | `(string $command): void` | `void` |
| [`pclose()`](./builtins/process/pclose.md) | `(resource $handle): int` | `int` |
| [`popen()`](./builtins/process/popen.md) | `(string $command, string $mode): mixed` | `mixed` |
-| [`readline()`](./builtins/process/readline.md) | `(string $prompt): mixed` | `mixed` |
+| [`readline()`](./builtins/process/readline.md) | `(string $prompt = null): mixed` | `mixed` |
| [`shell_exec()`](./builtins/process/shell_exec.md) | `(string $command): string` | `string` |
| [`sleep()`](./builtins/process/sleep.md) | `(int $seconds): int` | `int` |
-| [`system()`](./builtins/process/system.md) | `(string $command, int $result_code): string` | `string` |
+| [`system()`](./builtins/process/system.md) | `(string $command): string` | `string` |
| [`usleep()`](./builtins/process/usleep.md) | `(int $microseconds): void` | `void` |
-| [`preg_match()`](./builtins/regex/preg_match.md) | `(string $pattern, string $subject, array $matches): int` | `int` |
-| [`preg_match_all()`](./builtins/regex/preg_match_all.md) | `(string $pattern, string $subject, array $matches): int` | `int` |
-| [`preg_replace()`](./builtins/regex/preg_replace.md) | `(string $pattern, string $replacement, string $subject, int $limit = -1, int $count = null): string` | `string` |
-| [`preg_replace_callback()`](./builtins/regex/preg_replace_callback.md) | `(string $pattern, callable $callback, string $subject, int $limit = -1, int $count = null, int $flags = 0): array` | `array` |
-| [`preg_split()`](./builtins/regex/preg_split.md) | `(string $pattern, string $subject, int $limit, int $flags): array` | `array` |
-| [`iterator_apply()`](./builtins/spl/iterator_apply.md) | `(traversable $iterator, callable $callback, array $args): int` | `int` |
+| [`preg_match()`](./builtins/regex/preg_match.md) | `(string $pattern, string $subject, array $matches = []): int` | `int` |
+| [`preg_match_all()`](./builtins/regex/preg_match_all.md) | `(string $pattern, string $subject): int` | `int` |
+| [`preg_replace()`](./builtins/regex/preg_replace.md) | `(string $pattern, string $replacement, string $subject): string` | `string` |
+| [`preg_replace_callback()`](./builtins/regex/preg_replace_callback.md) | `(string $pattern, callable $callback, string $subject): string` | `string` |
+| [`preg_split()`](./builtins/regex/preg_split.md) | `(string $pattern, string $subject, int $limit = -1, int $flags = 0): array` | `array` |
+| [`iterator_apply()`](./builtins/spl/iterator_apply.md) | `(traversable $iterator, callable $callback, array $args = null): int` | `int` |
| [`iterator_count()`](./builtins/spl/iterator_count.md) | `(traversable $iterator): int` | `int` |
-| [`iterator_to_array()`](./builtins/spl/iterator_to_array.md) | `(traversable $iterator, bool $preserve_keys): array` | `array` |
-| [`spl_autoload()`](./builtins/spl/spl_autoload.md) | `(string $class, string $file_extensions): void` | `void` |
+| [`iterator_to_array()`](./builtins/spl/iterator_to_array.md) | `(traversable $iterator, bool $preserve_keys = true): array` | `array` |
+| [`spl_autoload()`](./builtins/spl/spl_autoload.md) | `(string $class, string $file_extensions = null): void` | `void` |
| [`spl_autoload_call()`](./builtins/spl/spl_autoload_call.md) | `(string $class): void` | `void` |
-| [`spl_autoload_extensions()`](./builtins/spl/spl_autoload_extensions.md) | `(string $file_extensions): string` | `string` |
+| [`spl_autoload_extensions()`](./builtins/spl/spl_autoload_extensions.md) | `(string $file_extensions = null): string` | `string` |
| [`spl_autoload_functions()`](./builtins/spl/spl_autoload_functions.md) | `(): array` | `array` |
-| [`spl_autoload_register()`](./builtins/spl/spl_autoload_register.md) | `(callable $callback, bool $throw, bool $prepend): bool` | `bool` |
+| [`spl_autoload_register()`](./builtins/spl/spl_autoload_register.md) | `(callable $callback = null, bool $throw = true, bool $prepend = false): bool` | `bool` |
| [`spl_autoload_unregister()`](./builtins/spl/spl_autoload_unregister.md) | `(callable $callback): bool` | `bool` |
| [`spl_classes()`](./builtins/spl/spl_classes.md) | `(): array` | `array` |
| [`spl_object_hash()`](./builtins/spl/spl_object_hash.md) | `(object $object): string` | `string` |
| [`spl_object_id()`](./builtins/spl/spl_object_id.md) | `(object $object): int` | `int` |
-| [`fsockopen()`](./builtins/streams/fsockopen.md) | `(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed` | `mixed` |
-| [`pfsockopen()`](./builtins/streams/pfsockopen.md) | `(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed` | `mixed` |
+| [`fsockopen()`](./builtins/streams/fsockopen.md) | `(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed` | `mixed` |
+| [`pfsockopen()`](./builtins/streams/pfsockopen.md) | `(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed` | `mixed` |
| [`stream_bucket_append()`](./builtins/streams/stream_bucket_append.md) | `(mixed $brigade, mixed $bucket): void` | `void` |
| [`stream_bucket_prepend()`](./builtins/streams/stream_bucket_prepend.md) | `(mixed $brigade, mixed $bucket): void` | `void` |
-| [`stream_filter_append()`](./builtins/streams/stream_filter_append.md) | `(resource $stream, string $filter_name, int $mode, mixed $params): mixed` | `mixed` |
-| [`stream_filter_prepend()`](./builtins/streams/stream_filter_prepend.md) | `(resource $stream, string $filter_name, int $mode, mixed $params): mixed` | `mixed` |
+| [`stream_filter_append()`](./builtins/streams/stream_filter_append.md) | `(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed` | `mixed` |
+| [`stream_filter_prepend()`](./builtins/streams/stream_filter_prepend.md) | `(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed` | `mixed` |
| [`addslashes()`](./builtins/string/addslashes.md) | `(string $string): string` | `string` |
-| [`base64_decode()`](./builtins/string/base64_decode.md) | `(string $string, bool $strict): string` | `string` |
+| [`base64_decode()`](./builtins/string/base64_decode.md) | `(string $string): string` | `string` |
| [`base64_encode()`](./builtins/string/base64_encode.md) | `(string $string): string` | `string` |
| [`bin2hex()`](./builtins/string/bin2hex.md) | `(string $string): string` | `string` |
-| [`chop()`](./builtins/string/chop.md) | `(string $string, string $characters): string` | `string` |
+| [`chop()`](./builtins/string/chop.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
| [`chr()`](./builtins/string/chr.md) | `(int $codepoint): string` | `string` |
| [`crc32()`](./builtins/string/crc32.md) | `(string $string): int` | `int` |
-| [`explode()`](./builtins/string/explode.md) | `(string $separator, string $string, int $limit): array` | `array` |
+| [`explode()`](./builtins/string/explode.md) | `(string $separator, string $string, int $limit = PHP_INT_MAX): array` | `array` |
| [`grapheme_strrev()`](./builtins/string/grapheme_strrev.md) | `(string $string): mixed` | `mixed` |
-| [`gzcompress()`](./builtins/string/gzcompress.md) | `(string $data, int $level, int $encoding): string` | `string` |
-| [`gzdeflate()`](./builtins/string/gzdeflate.md) | `(string $data, int $level, int $encoding): string` | `string` |
-| [`gzinflate()`](./builtins/string/gzinflate.md) | `(string $data, int $max_length): string` | `string` |
-| [`gzuncompress()`](./builtins/string/gzuncompress.md) | `(string $data, int $max_length): string` | `string` |
-| [`hash()`](./builtins/string/hash.md) | `(string $algo, string $data, bool $binary = false, array $options = []): string` | `string` |
+| [`gzcompress()`](./builtins/string/gzcompress.md) | `(string $data, int $level = -1): string` | `string` |
+| [`gzdeflate()`](./builtins/string/gzdeflate.md) | `(string $data, int $level = -1): string` | `string` |
+| [`gzinflate()`](./builtins/string/gzinflate.md) | `(string $data, int $max_length = 0): mixed` | `mixed` |
+| [`gzuncompress()`](./builtins/string/gzuncompress.md) | `(string $data, int $max_length = 0): mixed` | `mixed` |
+| [`hash()`](./builtins/string/hash.md) | `(string $algo, string $data, bool $binary = false): string` | `string` |
| [`hash_algos()`](./builtins/string/hash_algos.md) | `(): array` | `array` |
| [`hash_copy()`](./builtins/string/hash_copy.md) | `(resource $context): mixed` | `mixed` |
| [`hash_equals()`](./builtins/string/hash_equals.md) | `(string $known_string, string $user_string): bool` | `bool` |
-| [`hash_final()`](./builtins/string/hash_final.md) | `(resource $context, bool $binary): string` | `string` |
-| [`hash_hmac()`](./builtins/string/hash_hmac.md) | `(string $algo, string $data, string $key, bool $binary): string` | `string` |
-| [`hash_init()`](./builtins/string/hash_init.md) | `(string $algo, int $flags = 0, string $key = '', array $options = []): mixed` | `mixed` |
+| [`hash_final()`](./builtins/string/hash_final.md) | `(resource $context, bool $binary = false): string` | `string` |
+| [`hash_hmac()`](./builtins/string/hash_hmac.md) | `(string $algo, string $data, string $key, bool $binary = false): string` | `string` |
+| [`hash_init()`](./builtins/string/hash_init.md) | `(string $algo, int $flags = 0, string $key = ''): mixed` | `mixed` |
| [`hash_update()`](./builtins/string/hash_update.md) | `(resource $context, string $data): bool` | `bool` |
| [`hex2bin()`](./builtins/string/hex2bin.md) | `(string $string): string` | `string` |
-| [`html_entity_decode()`](./builtins/string/html_entity_decode.md) | `(string $string, int $flags, string $encoding): string` | `string` |
-| [`htmlentities()`](./builtins/string/htmlentities.md) | `(string $string, int $flags, string $encoding, bool $double_encode): string` | `string` |
-| [`htmlspecialchars()`](./builtins/string/htmlspecialchars.md) | `(string $string, int $flags, string $encoding, bool $double_encode): string` | `string` |
-| [`implode()`](./builtins/string/implode.md) | `(string $separator, array $array): string` | `string` |
+| [`html_entity_decode()`](./builtins/string/html_entity_decode.md) | `(string $string): string` | `string` |
+| [`htmlentities()`](./builtins/string/htmlentities.md) | `(string $string): string` | `string` |
+| [`htmlspecialchars()`](./builtins/string/htmlspecialchars.md) | `(string $string): string` | `string` |
+| [`implode()`](./builtins/string/implode.md) | `(string $separator, array $array = null): string` | `string` |
| [`inet_ntop()`](./builtins/string/inet_ntop.md) | `(string $ip): mixed` | `mixed` |
| [`inet_pton()`](./builtins/string/inet_pton.md) | `(string $ip): mixed` | `mixed` |
| [`ip2long()`](./builtins/string/ip2long.md) | `(string $ip): mixed` | `mixed` |
| [`lcfirst()`](./builtins/string/lcfirst.md) | `(string $string): string` | `string` |
| [`long2ip()`](./builtins/string/long2ip.md) | `(int $ip): string` | `string` |
-| [`ltrim()`](./builtins/string/ltrim.md) | `(string $string, string $characters): string` | `string` |
-| [`md5()`](./builtins/string/md5.md) | `(string $string, bool $binary): string` | `string` |
-| [`nl2br()`](./builtins/string/nl2br.md) | `(string $string, bool $use_xhtml): string` | `string` |
-| [`number_format()`](./builtins/string/number_format.md) | `(float $num, int $decimals, string $decimal_separator, string $thousands_separator): string` | `string` |
+| [`ltrim()`](./builtins/string/ltrim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
+| [`md5()`](./builtins/string/md5.md) | `(string $string, bool $binary = false): string` | `string` |
+| [`nl2br()`](./builtins/string/nl2br.md) | `(string $string): string` | `string` |
+| [`number_format()`](./builtins/string/number_format.md) | `(float $num, int $decimals = 0, string $decimal_separator = '.', string $thousands_separator = ','): string` | `string` |
| [`ord()`](./builtins/string/ord.md) | `(string $character): int` | `int` |
| [`printf()`](./builtins/string/printf.md) | `(string $format, ...$values): int` | `int` |
| [`rawurldecode()`](./builtins/string/rawurldecode.md) | `(string $string): string` | `string` |
| [`rawurlencode()`](./builtins/string/rawurlencode.md) | `(string $string): string` | `string` |
-| [`rtrim()`](./builtins/string/rtrim.md) | `(string $string, string $characters): string` | `string` |
-| [`sha1()`](./builtins/string/sha1.md) | `(string $string, bool $binary): string` | `string` |
+| [`rtrim()`](./builtins/string/rtrim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
+| [`sha1()`](./builtins/string/sha1.md) | `(string $string, bool $binary = false): string` | `string` |
| [`sprintf()`](./builtins/string/sprintf.md) | `(string $format, ...$values): string` | `string` |
| [`sscanf()`](./builtins/string/sscanf.md) | `(string $string, string $format, ...$vars): array` | `array` |
| [`str_contains()`](./builtins/string/str_contains.md) | `(string $haystack, string $needle): bool` | `bool` |
| [`str_ends_with()`](./builtins/string/str_ends_with.md) | `(string $haystack, string $needle): bool` | `bool` |
-| [`str_ireplace()`](./builtins/string/str_ireplace.md) | `(mixed $search, mixed $replace, mixed $subject, int $count): mixed` | `mixed` |
-| [`str_pad()`](./builtins/string/str_pad.md) | `(string $string, int $length, string $pad_string, int $pad_type): string` | `string` |
+| [`str_ireplace()`](./builtins/string/str_ireplace.md) | `(string $search, string $replace, string $subject, int $count = null): string` | `string` |
+| [`str_pad()`](./builtins/string/str_pad.md) | `(string $string, int $length, string $pad_string = ' ', int $pad_type = 1): string` | `string` |
| [`str_repeat()`](./builtins/string/str_repeat.md) | `(string $string, int $times): string` | `string` |
-| [`str_replace()`](./builtins/string/str_replace.md) | `(string $search, string $replace, string $subject, int $count): mixed` | `mixed` |
-| [`str_split()`](./builtins/string/str_split.md) | `(string $string, int $length): array` | `array` |
+| [`str_replace()`](./builtins/string/str_replace.md) | `(string $search, string $replace, string $subject, int $count = null): string` | `string` |
+| [`str_split()`](./builtins/string/str_split.md) | `(string $string, int $length = 1): array` | `array` |
| [`str_starts_with()`](./builtins/string/str_starts_with.md) | `(string $haystack, string $needle): bool` | `bool` |
| [`strcasecmp()`](./builtins/string/strcasecmp.md) | `(string $string1, string $string2): int` | `int` |
| [`strcmp()`](./builtins/string/strcmp.md) | `(string $string1, string $string2): int` | `int` |
| [`stripslashes()`](./builtins/string/stripslashes.md) | `(string $string): string` | `string` |
| [`strlen()`](./builtins/string/strlen.md) | `(string $string): int` | `int` |
-| [`strpos()`](./builtins/string/strpos.md) | `(string $haystack, string $needle, int $offset): mixed` | `mixed` |
+| [`strpos()`](./builtins/string/strpos.md) | `(string $haystack, string $needle, int $offset = 0): mixed` | `mixed` |
| [`strrev()`](./builtins/string/strrev.md) | `(string $string): string` | `string` |
-| [`strrpos()`](./builtins/string/strrpos.md) | `(string $haystack, string $needle, int $offset): mixed` | `mixed` |
-| [`strstr()`](./builtins/string/strstr.md) | `(string $haystack, string $needle, bool $before_needle): string` | `string` |
+| [`strrpos()`](./builtins/string/strrpos.md) | `(string $haystack, string $needle, int $offset = 0): mixed` | `mixed` |
+| [`strstr()`](./builtins/string/strstr.md) | `(string $haystack, string $needle, bool $before_needle = false): string` | `string` |
| [`strtolower()`](./builtins/string/strtolower.md) | `(string $string): string` | `string` |
| [`strtoupper()`](./builtins/string/strtoupper.md) | `(string $string): string` | `string` |
-| [`substr()`](./builtins/string/substr.md) | `(string $string, int $offset, int $length): string` | `string` |
-| [`substr_replace()`](./builtins/string/substr_replace.md) | `(string $string, string $replace, int $offset, int $length): string` | `string` |
-| [`trim()`](./builtins/string/trim.md) | `(string $string, string $characters): string` | `string` |
+| [`substr()`](./builtins/string/substr.md) | `(string $string, int $offset, int $length = null): string` | `string` |
+| [`substr_replace()`](./builtins/string/substr_replace.md) | `(string $string, string $replace, int $offset, int $length = null): string` | `string` |
+| [`trim()`](./builtins/string/trim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
| [`ucfirst()`](./builtins/string/ucfirst.md) | `(string $string): string` | `string` |
-| [`ucwords()`](./builtins/string/ucwords.md) | `(string $string, string $separators): string` | `string` |
+| [`ucwords()`](./builtins/string/ucwords.md) | `(string $string, string $separators = ' \t\r\n\x0c\x0b'): string` | `string` |
| [`urldecode()`](./builtins/string/urldecode.md) | `(string $string): string` | `string` |
| [`urlencode()`](./builtins/string/urlencode.md) | `(string $string): string` | `string` |
| [`vprintf()`](./builtins/string/vprintf.md) | `(string $format, array $values): int` | `int` |
| [`vsprintf()`](./builtins/string/vsprintf.md) | `(string $format, array $values): string` | `string` |
-| [`wordwrap()`](./builtins/string/wordwrap.md) | `(string $string, int $width, string $break, bool $cut_long_words): string` | `string` |
+| [`wordwrap()`](./builtins/string/wordwrap.md) | `(string $string, int $width = 75, string $break = '\n', bool $cut_long_words = false): string` | `string` |
| [`boolval()`](./builtins/type/boolval.md) | `(mixed $value): bool` | `bool` |
| [`ctype_alnum()`](./builtins/type/ctype_alnum.md) | `(string $text): bool` | `bool` |
| [`ctype_alpha()`](./builtins/type/ctype_alpha.md) | `(string $text): bool` | `bool` |
@@ -405,10 +423,10 @@ sidebar:
| [`get_resource_id()`](./builtins/type/get_resource_id.md) | `(resource $resource): int` | `int` |
| [`get_resource_type()`](./builtins/type/get_resource_type.md) | `(resource $resource): string` | `string` |
| [`gettype()`](./builtins/type/gettype.md) | `(mixed $value): string` | `string` |
-| [`intval()`](./builtins/type/intval.md) | `(mixed $value, int $base): int` | `int` |
+| [`intval()`](./builtins/type/intval.md) | `(mixed $value): int` | `int` |
| [`is_array()`](./builtins/type/is_array.md) | `(mixed $value): bool` | `bool` |
| [`is_bool()`](./builtins/type/is_bool.md) | `(mixed $value): bool` | `bool` |
-| [`is_callable()`](./builtins/type/is_callable.md) | `(mixed $value, bool $syntax_only = false, string $callable_name = null): bool` | `bool` |
+| [`is_callable()`](./builtins/type/is_callable.md) | `(mixed $value): bool` | `bool` |
| [`is_float()`](./builtins/type/is_float.md) | `(mixed $value): bool` | `bool` |
| [`is_int()`](./builtins/type/is_int.md) | `(mixed $value): bool` | `bool` |
| [`is_iterable()`](./builtins/type/is_iterable.md) | `(mixed $value): bool` | `bool` |
diff --git a/docs/php/builtins/array.md b/docs/php/builtins/array.md
index d157f2c790..f2f4d44092 100644
--- a/docs/php/builtins/array.md
+++ b/docs/php/builtins/array.md
@@ -9,49 +9,66 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`array_chunk()`](./array/array_chunk.md) | `(array $array, int $length, bool $preserve_keys): array` | `array` |
-| [`array_column()`](./array/array_column.md) | `(array $array, string $column_key, string $index_key): array` | `array` |
+| [`array_all()`](./array/array_all.md) | `(mixed $array, mixed $callback): bool` | `bool` |
+| [`array_any()`](./array/array_any.md) | `(mixed $array, mixed $callback): bool` | `bool` |
+| [`array_chunk()`](./array/array_chunk.md) | `(array $array, int $length): array` | `array` |
+| [`array_column()`](./array/array_column.md) | `(array $array, string $column_key): array` | `array` |
| [`array_combine()`](./array/array_combine.md) | `(array $keys, array $values): array` | `array` |
| [`array_diff()`](./array/array_diff.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_diff_assoc()`](./array/array_diff_assoc.md) | `(array $array, ...$arrays): mixed` | `mixed` |
| [`array_diff_key()`](./array/array_diff_key.md) | `(array $array, ...$arrays): array` | `array` |
| [`array_fill()`](./array/array_fill.md) | `(int $start_index, int $count, mixed $value): array` | `array` |
| [`array_fill_keys()`](./array/array_fill_keys.md) | `(array $keys, mixed $value): array` | `array` |
-| [`array_filter()`](./array/array_filter.md) | `(array $array, callable $callback, int $mode): array` | `array` |
-| [`array_flip()`](./array/array_flip.md) | `(array $array): float` | `float` |
+| [`array_filter()`](./array/array_filter.md) | `(array $array, callable $callback = null, int $mode = 0): array` | `array` |
+| [`array_find()`](./array/array_find.md) | `(mixed $array, mixed $callback): mixed` | `mixed` |
+| [`array_flip()`](./array/array_flip.md) | `(array $array): array` | `array` |
| [`array_intersect()`](./array/array_intersect.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_intersect_assoc()`](./array/array_intersect_assoc.md) | `(array $array, ...$arrays): mixed` | `mixed` |
| [`array_intersect_key()`](./array/array_intersect_key.md) | `(array $array, ...$arrays): array` | `array` |
+| [`array_is_list()`](./array/array_is_list.md) | `(mixed $array): bool` | `bool` |
| [`array_key_exists()`](./array/array_key_exists.md) | `(string $key, array $array): bool` | `bool` |
-| [`array_keys()`](./array/array_keys.md) | `(array $array, string $filter_value, bool $strict): array` | `array` |
+| [`array_key_first()`](./array/array_key_first.md) | `(array $array): mixed` | `mixed` |
+| [`array_key_last()`](./array/array_key_last.md) | `(array $array): mixed` | `mixed` |
+| [`array_keys()`](./array/array_keys.md) | `(array $array): array` | `array` |
| [`array_map()`](./array/array_map.md) | `(callable $callback, array $array, ...$arrays): array` | `array` |
| [`array_merge()`](./array/array_merge.md) | `(...$arrays): array` | `array` |
+| [`array_merge_recursive()`](./array/array_merge_recursive.md) | `(...$arrays): array` | `array` |
+| [`array_multisort()`](./array/array_multisort.md) | `(array $array1, int $array2): bool` | `bool` |
| [`array_pad()`](./array/array_pad.md) | `(array $array, int $length, mixed $value): array` | `array` |
| [`array_pop()`](./array/array_pop.md) | `(array $array): mixed` | `mixed` |
-| [`array_product()`](./array/array_product.md) | `(array $array): float` | `float` |
+| [`array_product()`](./array/array_product.md) | `(array $array): int` | `int` |
| [`array_push()`](./array/array_push.md) | `(array $array, ...$values): void` | `void` |
-| [`array_rand()`](./array/array_rand.md) | `(array $array, int $num): int` | `int` |
-| [`array_reduce()`](./array/array_reduce.md) | `(array $array, callable $callback, mixed $initial): int` | `int` |
-| [`array_reverse()`](./array/array_reverse.md) | `(array $array, bool $preserve_keys): array` | `array` |
-| [`array_search()`](./array/array_search.md) | `(mixed $needle, array $haystack, bool $strict): mixed` | `mixed` |
+| [`array_rand()`](./array/array_rand.md) | `(array $array): int` | `int` |
+| [`array_reduce()`](./array/array_reduce.md) | `(array $array, callable $callback, mixed $initial = null): int` | `int` |
+| [`array_replace()`](./array/array_replace.md) | `(array $array, array $replacements): mixed` | `mixed` |
+| [`array_replace_recursive()`](./array/array_replace_recursive.md) | `(array $array, array $replacements): mixed` | `mixed` |
+| [`array_reverse()`](./array/array_reverse.md) | `(array $array): array` | `array` |
+| [`array_search()`](./array/array_search.md) | `(mixed $needle, array $haystack, bool $strict = false): mixed` | `mixed` |
| [`array_shift()`](./array/array_shift.md) | `(array $array): mixed` | `mixed` |
-| [`array_slice()`](./array/array_slice.md) | `(array $array, int $offset, int $length, bool $preserve_keys): array` | `array` |
-| [`array_splice()`](./array/array_splice.md) | `(array $array, int $offset, int $length, array $replacement): array` | `array` |
-| [`array_sum()`](./array/array_sum.md) | `(array $array): float` | `float` |
-| [`array_unique()`](./array/array_unique.md) | `(array $array, int $flags): array` | `array` |
+| [`array_slice()`](./array/array_slice.md) | `(array $array, int $offset, int $length = null): array` | `array` |
+| [`array_splice()`](./array/array_splice.md) | `(array $array, int $offset, int $length = null): array` | `array` |
+| [`array_sum()`](./array/array_sum.md) | `(array $array): int` | `int` |
+| [`array_udiff()`](./array/array_udiff.md) | `(array $array1, array $array2, callable $callback): array` | `array` |
+| [`array_uintersect()`](./array/array_uintersect.md) | `(array $array1, array $array2, callable $callback): array` | `array` |
+| [`array_unique()`](./array/array_unique.md) | `(array $array): array` | `array` |
| [`array_unshift()`](./array/array_unshift.md) | `(array $array, ...$values): int` | `int` |
| [`array_values()`](./array/array_values.md) | `(array $array): array` | `array` |
-| [`array_walk()`](./array/array_walk.md) | `(array $array, callable $callback, mixed $arg): void` | `void` |
-| [`arsort()`](./array/arsort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`asort()`](./array/asort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`count()`](./array/count.md) | `(array $value, int $mode): int` | `int` |
-| [`in_array()`](./array/in_array.md) | `(mixed $needle, array $haystack, bool $strict): mixed` | `mixed` |
-| [`krsort()`](./array/krsort.md) | `(array $array, int $flags): bool` | `bool` |
-| [`ksort()`](./array/ksort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`array_walk()`](./array/array_walk.md) | `(array $array, callable $callback): void` | `void` |
+| [`array_walk_recursive()`](./array/array_walk_recursive.md) | `(array $array, callable $callback): void` | `void` |
+| [`arsort()`](./array/arsort.md) | `(array $array): bool` | `bool` |
+| [`asort()`](./array/asort.md) | `(array $array): bool` | `bool` |
+| [`call_user_func()`](./array/call_user_func.md) | `(callable $callback, ...$args): mixed` | `mixed` |
+| [`call_user_func_array()`](./array/call_user_func_array.md) | `(callable $callback, array $args): mixed` | `mixed` |
+| [`count()`](./array/count.md) | `(array $value, int $mode = 0): int` | `int` |
+| [`in_array()`](./array/in_array.md) | `(mixed $needle, array $haystack, bool $strict = false): bool` | `bool` |
+| [`krsort()`](./array/krsort.md) | `(array $array): bool` | `bool` |
+| [`ksort()`](./array/ksort.md) | `(array $array): bool` | `bool` |
| [`natcasesort()`](./array/natcasesort.md) | `(array $array): bool` | `bool` |
| [`natsort()`](./array/natsort.md) | `(array $array): bool` | `bool` |
-| [`range()`](./array/range.md) | `(mixed $start, mixed $end, int $step): array` | `array` |
-| [`rsort()`](./array/rsort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`range()`](./array/range.md) | `(mixed $start, mixed $end): array` | `array` |
+| [`rsort()`](./array/rsort.md) | `(array $array): bool` | `bool` |
| [`shuffle()`](./array/shuffle.md) | `(array $array): bool` | `bool` |
-| [`sort()`](./array/sort.md) | `(array $array, int $flags): bool` | `bool` |
+| [`sort()`](./array/sort.md) | `(array $array): bool` | `bool` |
| [`uasort()`](./array/uasort.md) | `(array $array, callable $callback): bool` | `bool` |
| [`uksort()`](./array/uksort.md) | `(array $array, callable $callback): bool` | `bool` |
| [`usort()`](./array/usort.md) | `(array $array, callable $callback): bool` | `bool` |
diff --git a/docs/php/builtins/array/array_all.md b/docs/php/builtins/array/array_all.md
new file mode 100644
index 0000000000..20fa87b4bd
--- /dev/null
+++ b/docs/php/builtins/array/array_all.md
@@ -0,0 +1,33 @@
+---
+title: "array_all()"
+description: "Returns true when every array element satisfies the predicate callback."
+sidebar:
+ order: 1
+---
+
+## array_all()
+
+```php
+function array_all(mixed $array, mixed $callback): bool
+```
+
+Returns true when every array element satisfies the predicate callback.
+
+**Parameters**:
+- `$array` (`mixed`)
+- `$callback` (`mixed`)
+
+**Returns**: `bool`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_all` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_all.md).
+
diff --git a/docs/php/builtins/array/array_any.md b/docs/php/builtins/array/array_any.md
new file mode 100644
index 0000000000..c8c8658fad
--- /dev/null
+++ b/docs/php/builtins/array/array_any.md
@@ -0,0 +1,33 @@
+---
+title: "array_any()"
+description: "Returns true when at least one array element satisfies the predicate callback."
+sidebar:
+ order: 2
+---
+
+## array_any()
+
+```php
+function array_any(mixed $array, mixed $callback): bool
+```
+
+Returns true when at least one array element satisfies the predicate callback.
+
+**Parameters**:
+- `$array` (`mixed`)
+- `$callback` (`mixed`)
+
+**Returns**: `bool`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_any` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_any.md).
+
diff --git a/docs/php/builtins/array/array_chunk.md b/docs/php/builtins/array/array_chunk.md
index a1a2366bbe..3bf1f31b8c 100644
--- a/docs/php/builtins/array/array_chunk.md
+++ b/docs/php/builtins/array/array_chunk.md
@@ -1,22 +1,21 @@
---
title: "array_chunk()"
-description: "Lowers `array_chunk()` by splitting an indexed array into nested indexed arrays."
+description: "Splits an array into chunks of the given size."
sidebar:
- order: 1
+ order: 3
---
## array_chunk()
```php
-function array_chunk(array $array, int $length, bool $preserve_keys): array
+function array_chunk(array $array, int $length): array
```
-Lowers `array_chunk()` by splitting an indexed array into nested indexed arrays.
+Splits an array into chunks of the given size.
**Parameters**:
- `$array` (`array`)
- `$length` (`int`)
-- `$preserve_keys` (`bool`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_column.md b/docs/php/builtins/array/array_column.md
index 371306c602..c4f21c1c22 100644
--- a/docs/php/builtins/array/array_column.md
+++ b/docs/php/builtins/array/array_column.md
@@ -1,22 +1,21 @@
---
title: "array_column()"
-description: "Lowers `array_column()` by dispatching to the helper matching row value ownership."
+description: "Returns the values from a single column of an array of arrays."
sidebar:
- order: 2
+ order: 4
---
## array_column()
```php
-function array_column(array $array, string $column_key, string $index_key): array
+function array_column(array $array, string $column_key): array
```
-Lowers `array_column()` by dispatching to the helper matching row value ownership.
+Returns the values from a single column of an array of arrays.
**Parameters**:
- `$array` (`array`)
- `$column_key` (`string`)
-- `$index_key` (`string`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_combine.md b/docs/php/builtins/array/array_combine.md
index 8f8b288202..18b4617d4d 100644
--- a/docs/php/builtins/array/array_combine.md
+++ b/docs/php/builtins/array/array_combine.md
@@ -1,8 +1,8 @@
---
title: "array_combine()"
-description: "Lowers `array_combine()` through the legacy hash-building runtime helpers."
+description: "Creates an array by using one array for keys and another for values."
sidebar:
- order: 3
+ order: 5
---
## array_combine()
@@ -11,7 +11,7 @@ sidebar:
function array_combine(array $keys, array $values): array
```
-Lowers `array_combine()` through the legacy hash-building runtime helpers.
+Creates an array by using one array for keys and another for values.
**Parameters**:
- `$keys` (`array`)
diff --git a/docs/php/builtins/array/array_diff.md b/docs/php/builtins/array/array_diff.md
index 0bd02f2992..ea514aa384 100644
--- a/docs/php/builtins/array/array_diff.md
+++ b/docs/php/builtins/array/array_diff.md
@@ -1,8 +1,8 @@
---
title: "array_diff()"
-description: "Lowers `array_diff()` for two compatible indexed arrays with pointer-sized payload slots."
+description: "Computes the difference of arrays."
sidebar:
- order: 4
+ order: 6
---
## array_diff()
@@ -11,7 +11,7 @@ sidebar:
function array_diff(array $array, ...$arrays): array
```
-Lowers `array_diff()` for two compatible indexed arrays with pointer-sized payload slots.
+Computes the difference of arrays.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_diff_assoc.md b/docs/php/builtins/array/array_diff_assoc.md
new file mode 100644
index 0000000000..488a870134
--- /dev/null
+++ b/docs/php/builtins/array/array_diff_assoc.md
@@ -0,0 +1,33 @@
+---
+title: "array_diff_assoc()"
+description: "Computes the difference of arrays with additional index check."
+sidebar:
+ order: 7
+---
+
+## array_diff_assoc()
+
+```php
+function array_diff_assoc(array $array, ...$arrays): mixed
+```
+
+Computes the difference of arrays with additional index check.
+
+**Parameters**:
+- `$array` (`array`)
+- `...$arrays` — variadic: collects excess arguments into `$arrays`.
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_diff_assoc` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_diff_assoc.md).
+
diff --git a/docs/php/builtins/array/array_diff_key.md b/docs/php/builtins/array/array_diff_key.md
index 1863d34d62..2fd900fa8a 100644
--- a/docs/php/builtins/array/array_diff_key.md
+++ b/docs/php/builtins/array/array_diff_key.md
@@ -1,8 +1,8 @@
---
title: "array_diff_key()"
-description: "Lowers `array_diff_key()` for two associative arrays by filtering first-operand keys."
+description: "Computes the difference of arrays using keys for comparison."
sidebar:
- order: 5
+ order: 8
---
## array_diff_key()
@@ -11,7 +11,7 @@ sidebar:
function array_diff_key(array $array, ...$arrays): array
```
-Lowers `array_diff_key()` for two associative arrays by filtering first-operand keys.
+Computes the difference of arrays using keys for comparison.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_fill.md b/docs/php/builtins/array/array_fill.md
index 7d6a189a21..c6140e68f5 100644
--- a/docs/php/builtins/array/array_fill.md
+++ b/docs/php/builtins/array/array_fill.md
@@ -1,8 +1,8 @@
---
title: "array_fill()"
-description: "Lowers `array_fill()` for pointer-sized scalar and refcounted payloads."
+description: "Fill an array with values."
sidebar:
- order: 6
+ order: 9
---
## array_fill()
@@ -11,7 +11,7 @@ sidebar:
function array_fill(int $start_index, int $count, mixed $value): array
```
-Lowers `array_fill()` for pointer-sized scalar and refcounted payloads.
+Fill an array with values.
**Parameters**:
- `$start_index` (`int`)
diff --git a/docs/php/builtins/array/array_fill_keys.md b/docs/php/builtins/array/array_fill_keys.md
index 023a9f4abb..d8c4197c76 100644
--- a/docs/php/builtins/array/array_fill_keys.md
+++ b/docs/php/builtins/array/array_fill_keys.md
@@ -1,8 +1,8 @@
---
title: "array_fill_keys()"
-description: "Lowers `array_fill_keys()` through the legacy hash-building runtime helpers."
+description: "Fill an array with values, specifying keys."
sidebar:
- order: 7
+ order: 10
---
## array_fill_keys()
@@ -11,7 +11,7 @@ sidebar:
function array_fill_keys(array $keys, mixed $value): array
```
-Lowers `array_fill_keys()` through the legacy hash-building runtime helpers.
+Fill an array with values, specifying keys.
**Parameters**:
- `$keys` (`array`)
diff --git a/docs/php/builtins/array/array_filter.md b/docs/php/builtins/array/array_filter.md
index cf0b7430c0..0aca1d8604 100644
--- a/docs/php/builtins/array/array_filter.md
+++ b/docs/php/builtins/array/array_filter.md
@@ -1,22 +1,22 @@
---
title: "array_filter()"
-description: "Lowers `array_filter()` for static and first-class callbacks through the runtime helper."
+description: "Filters elements of an array using a callback function."
sidebar:
- order: 8
+ order: 11
---
## array_filter()
```php
-function array_filter(array $array, callable $callback, int $mode): array
+function array_filter(array $array, callable $callback = null, int $mode = 0): array
```
-Lowers `array_filter()` for static and first-class callbacks through the runtime helper.
+Filters elements of an array using a callback function.
**Parameters**:
- `$array` (`array`)
-- `$callback` (`callable`), optional
-- `$mode` (`int`), optional
+- `$callback` (`callable`), default `null`, optional
+- `$mode` (`int`), default `0`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_find.md b/docs/php/builtins/array/array_find.md
new file mode 100644
index 0000000000..e21471ea11
--- /dev/null
+++ b/docs/php/builtins/array/array_find.md
@@ -0,0 +1,33 @@
+---
+title: "array_find()"
+description: "Returns the first element satisfying a predicate callback, or null."
+sidebar:
+ order: 12
+---
+
+## array_find()
+
+```php
+function array_find(mixed $array, mixed $callback): mixed
+```
+
+Returns the first element satisfying a predicate callback, or null.
+
+**Parameters**:
+- `$array` (`mixed`)
+- `$callback` (`mixed`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_find` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_find.md).
+
diff --git a/docs/php/builtins/array/array_flip.md b/docs/php/builtins/array/array_flip.md
index be2392afb5..d4a791bde8 100644
--- a/docs/php/builtins/array/array_flip.md
+++ b/docs/php/builtins/array/array_flip.md
@@ -1,22 +1,22 @@
---
title: "array_flip()"
-description: "Lowers `array_flip()` through the legacy hash-building runtime helpers."
+description: "Exchanges all keys with their associated values in an array."
sidebar:
- order: 9
+ order: 13
---
## array_flip()
```php
-function array_flip(array $array): float
+function array_flip(array $array): array
```
-Lowers `array_flip()` through the legacy hash-building runtime helpers.
+Exchanges all keys with their associated values in an array.
**Parameters**:
- `$array` (`array`)
-**Returns**: `float`
+**Returns**: `array`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/array/array_intersect.md b/docs/php/builtins/array/array_intersect.md
index bff2c4ba62..55c5ce3ab5 100644
--- a/docs/php/builtins/array/array_intersect.md
+++ b/docs/php/builtins/array/array_intersect.md
@@ -1,8 +1,8 @@
---
title: "array_intersect()"
-description: "Lowers `array_intersect()` for two compatible indexed arrays with pointer-sized payload slots."
+description: "Computes the intersection of arrays."
sidebar:
- order: 10
+ order: 14
---
## array_intersect()
@@ -11,7 +11,7 @@ sidebar:
function array_intersect(array $array, ...$arrays): array
```
-Lowers `array_intersect()` for two compatible indexed arrays with pointer-sized payload slots.
+Computes the intersection of arrays.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_intersect_assoc.md b/docs/php/builtins/array/array_intersect_assoc.md
new file mode 100644
index 0000000000..889e440ea1
--- /dev/null
+++ b/docs/php/builtins/array/array_intersect_assoc.md
@@ -0,0 +1,33 @@
+---
+title: "array_intersect_assoc()"
+description: "Computes the intersection of arrays with additional index check."
+sidebar:
+ order: 15
+---
+
+## array_intersect_assoc()
+
+```php
+function array_intersect_assoc(array $array, ...$arrays): mixed
+```
+
+Computes the intersection of arrays with additional index check.
+
+**Parameters**:
+- `$array` (`array`)
+- `...$arrays` — variadic: collects excess arguments into `$arrays`.
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_intersect_assoc` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_intersect_assoc.md).
+
diff --git a/docs/php/builtins/array/array_intersect_key.md b/docs/php/builtins/array/array_intersect_key.md
index 1cdcf85c28..d9fb6a57c0 100644
--- a/docs/php/builtins/array/array_intersect_key.md
+++ b/docs/php/builtins/array/array_intersect_key.md
@@ -1,8 +1,8 @@
---
title: "array_intersect_key()"
-description: "Lowers `array_intersect_key()` for two associative arrays by keeping shared first-operand keys."
+description: "Computes the intersection of arrays using keys for comparison."
sidebar:
- order: 11
+ order: 16
---
## array_intersect_key()
@@ -11,7 +11,7 @@ sidebar:
function array_intersect_key(array $array, ...$arrays): array
```
-Lowers `array_intersect_key()` for two associative arrays by keeping shared first-operand keys.
+Computes the intersection of arrays using keys for comparison.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_is_list.md b/docs/php/builtins/array/array_is_list.md
new file mode 100644
index 0000000000..c5264801c8
--- /dev/null
+++ b/docs/php/builtins/array/array_is_list.md
@@ -0,0 +1,32 @@
+---
+title: "array_is_list()"
+description: "Checks whether an array is a list (sequential 0-based integer keys)."
+sidebar:
+ order: 17
+---
+
+## array_is_list()
+
+```php
+function array_is_list(mixed $array): bool
+```
+
+Checks whether an array is a list (sequential 0-based integer keys).
+
+**Parameters**:
+- `$array` (`mixed`)
+
+**Returns**: `bool`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_is_list` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_is_list.md).
+
diff --git a/docs/php/builtins/array/array_key_exists.md b/docs/php/builtins/array/array_key_exists.md
index 08e8fb5ae0..bee1b864a3 100644
--- a/docs/php/builtins/array/array_key_exists.md
+++ b/docs/php/builtins/array/array_key_exists.md
@@ -1,8 +1,8 @@
---
title: "array_key_exists()"
-description: "Lowers `array_key_exists()` for indexed arrays and associative arrays."
+description: "Checks if the given key or index exists in the array."
sidebar:
- order: 12
+ order: 18
---
## array_key_exists()
@@ -11,7 +11,7 @@ sidebar:
function array_key_exists(string $key, array $array): bool
```
-Lowers `array_key_exists()` for indexed arrays and associative arrays.
+Checks if the given key or index exists in the array.
**Parameters**:
- `$key` (`string`)
diff --git a/docs/php/builtins/array/array_key_first.md b/docs/php/builtins/array/array_key_first.md
new file mode 100644
index 0000000000..64e4ab6b28
--- /dev/null
+++ b/docs/php/builtins/array/array_key_first.md
@@ -0,0 +1,32 @@
+---
+title: "array_key_first()"
+description: "Gets the first key of an array."
+sidebar:
+ order: 19
+---
+
+## array_key_first()
+
+```php
+function array_key_first(array $array): mixed
+```
+
+Gets the first key of an array.
+
+**Parameters**:
+- `$array` (`array`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_key_first` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_key_first.md).
+
diff --git a/docs/php/builtins/array/array_key_last.md b/docs/php/builtins/array/array_key_last.md
new file mode 100644
index 0000000000..50da0ddb35
--- /dev/null
+++ b/docs/php/builtins/array/array_key_last.md
@@ -0,0 +1,32 @@
+---
+title: "array_key_last()"
+description: "Gets the last key of an array."
+sidebar:
+ order: 20
+---
+
+## array_key_last()
+
+```php
+function array_key_last(array $array): mixed
+```
+
+Gets the last key of an array.
+
+**Parameters**:
+- `$array` (`array`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_key_last` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_key_last.md).
+
diff --git a/docs/php/builtins/array/array_keys.md b/docs/php/builtins/array/array_keys.md
index 26c3b9baf2..dd9eab185a 100644
--- a/docs/php/builtins/array/array_keys.md
+++ b/docs/php/builtins/array/array_keys.md
@@ -1,22 +1,20 @@
---
title: "array_keys()"
-description: "Lowers `array_keys()` for indexed arrays and associative arrays."
+description: "Returns all the keys of an array."
sidebar:
- order: 13
+ order: 21
---
## array_keys()
```php
-function array_keys(array $array, string $filter_value, bool $strict): array
+function array_keys(array $array): array
```
-Lowers `array_keys()` for indexed arrays and associative arrays.
+Returns all the keys of an array.
**Parameters**:
- `$array` (`array`)
-- `$filter_value` (`string`)
-- `$strict` (`bool`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_map.md b/docs/php/builtins/array/array_map.md
index 945cc70a41..c1dcfac6ca 100644
--- a/docs/php/builtins/array/array_map.md
+++ b/docs/php/builtins/array/array_map.md
@@ -1,8 +1,8 @@
---
title: "array_map()"
-description: "Lowers `array_map()` through the callback runtime helper matching the callback result type."
+description: "Applies a callback to the elements of an array."
sidebar:
- order: 14
+ order: 22
---
## array_map()
@@ -11,7 +11,7 @@ sidebar:
function array_map(callable $callback, array $array, ...$arrays): array
```
-Lowers `array_map()` through the callback runtime helper matching the callback result type.
+Applies a callback to the elements of an array.
**Parameters**:
- `$callback` (`callable`)
diff --git a/docs/php/builtins/array/array_merge.md b/docs/php/builtins/array/array_merge.md
index dd26a53e25..944a0185fb 100644
--- a/docs/php/builtins/array/array_merge.md
+++ b/docs/php/builtins/array/array_merge.md
@@ -1,8 +1,8 @@
---
title: "array_merge()"
-description: "Lowers `array_merge()` for two compatible indexed arrays with 8-byte payload slots."
+description: "Merges the elements of two arrays."
sidebar:
- order: 15
+ order: 23
---
## array_merge()
@@ -11,7 +11,7 @@ sidebar:
function array_merge(...$arrays): array
```
-Lowers `array_merge()` for two compatible indexed arrays with 8-byte payload slots.
+Merges the elements of two arrays.
**Parameters**:
- `...$arrays` — variadic: collects excess arguments into `$arrays`.
diff --git a/docs/php/builtins/array/array_merge_recursive.md b/docs/php/builtins/array/array_merge_recursive.md
new file mode 100644
index 0000000000..4cccbd6f2a
--- /dev/null
+++ b/docs/php/builtins/array/array_merge_recursive.md
@@ -0,0 +1,32 @@
+---
+title: "array_merge_recursive()"
+description: "Recursively merges two arrays, combining scalar collisions into lists."
+sidebar:
+ order: 24
+---
+
+## array_merge_recursive()
+
+```php
+function array_merge_recursive(...$arrays): array
+```
+
+Recursively merges two arrays, combining scalar collisions into lists.
+
+**Parameters**:
+- `...$arrays` — variadic: collects excess arguments into `$arrays`.
+
+**Returns**: `array`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_merge_recursive` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_merge_recursive.md).
+
diff --git a/docs/php/builtins/array/array_multisort.md b/docs/php/builtins/array/array_multisort.md
new file mode 100644
index 0000000000..0d03600acb
--- /dev/null
+++ b/docs/php/builtins/array/array_multisort.md
@@ -0,0 +1,33 @@
+---
+title: "array_multisort()"
+description: "Sorts multiple arrays or multi-dimensional arrays."
+sidebar:
+ order: 25
+---
+
+## array_multisort()
+
+```php
+function array_multisort(array $array1, int $array2): bool
+```
+
+Sorts multiple arrays or multi-dimensional arrays.
+
+**Parameters**:
+- `$array1` (`array`), passed by reference
+- `$array2` (`int`), passed by reference
+
+**Returns**: `bool`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_multisort` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_multisort.md).
+
diff --git a/docs/php/builtins/array/array_pad.md b/docs/php/builtins/array/array_pad.md
index 435fe7bf58..57e9cdf9fc 100644
--- a/docs/php/builtins/array/array_pad.md
+++ b/docs/php/builtins/array/array_pad.md
@@ -1,8 +1,8 @@
---
title: "array_pad()"
-description: "Lowers `array_pad()` by copying an indexed array and filling missing slots."
+description: "Pads an array to the specified length with a value."
sidebar:
- order: 16
+ order: 26
---
## array_pad()
@@ -11,7 +11,7 @@ sidebar:
function array_pad(array $array, int $length, mixed $value): array
```
-Lowers `array_pad()` by copying an indexed array and filling missing slots.
+Pads an array to the specified length with a value.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_pop.md b/docs/php/builtins/array/array_pop.md
index 51238b0406..640e0c5c4d 100644
--- a/docs/php/builtins/array/array_pop.md
+++ b/docs/php/builtins/array/array_pop.md
@@ -1,8 +1,8 @@
---
title: "array_pop()"
-description: "Lowers `array_pop()` for indexed arrays by mutating length and boxing `T|null` as Mixed."
+description: "Pops the element off the end of array."
sidebar:
- order: 17
+ order: 27
---
## array_pop()
@@ -11,7 +11,7 @@ sidebar:
function array_pop(array $array): mixed
```
-Lowers `array_pop()` for indexed arrays by mutating length and boxing `T|null` as Mixed.
+Pops the element off the end of array.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/array_product.md b/docs/php/builtins/array/array_product.md
index a9da7b382c..bcf9ca0e28 100644
--- a/docs/php/builtins/array/array_product.md
+++ b/docs/php/builtins/array/array_product.md
@@ -1,22 +1,22 @@
---
title: "array_product()"
-description: "Lowers `array_product()` over supported indexed-array payloads."
+description: "Calculate the product of values in an array."
sidebar:
- order: 18
+ order: 28
---
## array_product()
```php
-function array_product(array $array): float
+function array_product(array $array): int
```
-Lowers `array_product()` over supported indexed-array payloads.
+Calculate the product of values in an array.
**Parameters**:
- `$array` (`array`)
-**Returns**: `float`
+**Returns**: `int`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/array/array_push.md b/docs/php/builtins/array/array_push.md
index 8661acb87f..1024eb9e30 100644
--- a/docs/php/builtins/array/array_push.md
+++ b/docs/php/builtins/array/array_push.md
@@ -1,8 +1,8 @@
---
title: "array_push()"
-description: "Lowers `array_push()` by appending one value and publishing the mutated array."
+description: "Pushes one or more elements onto the end of array."
sidebar:
- order: 19
+ order: 29
---
## array_push()
@@ -11,7 +11,7 @@ sidebar:
function array_push(array $array, ...$values): void
```
-Lowers `array_push()` by appending one value and publishing the mutated array.
+Pushes one or more elements onto the end of array.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/array_rand.md b/docs/php/builtins/array/array_rand.md
index 7df0941362..6194bc5301 100644
--- a/docs/php/builtins/array/array_rand.md
+++ b/docs/php/builtins/array/array_rand.md
@@ -1,21 +1,20 @@
---
title: "array_rand()"
-description: "Lowers `array_rand()` for indexed arrays."
+description: "Pick one or more random keys out of an array."
sidebar:
- order: 20
+ order: 30
---
## array_rand()
```php
-function array_rand(array $array, int $num): int
+function array_rand(array $array): int
```
-Lowers `array_rand()` for indexed arrays.
+Pick one or more random keys out of an array.
**Parameters**:
- `$array` (`array`)
-- `$num` (`int`)
**Returns**: `int`
diff --git a/docs/php/builtins/array/array_reduce.md b/docs/php/builtins/array/array_reduce.md
index 2c39521d83..63782c9e33 100644
--- a/docs/php/builtins/array/array_reduce.md
+++ b/docs/php/builtins/array/array_reduce.md
@@ -1,22 +1,22 @@
---
title: "array_reduce()"
-description: "Lowers `array_reduce()` through the callback-driven runtime helper."
+description: "Iteratively reduces an array to a single value using a callback function."
sidebar:
- order: 21
+ order: 31
---
## array_reduce()
```php
-function array_reduce(array $array, callable $callback, mixed $initial): int
+function array_reduce(array $array, callable $callback, mixed $initial = null): int
```
-Lowers `array_reduce()` through the callback-driven runtime helper.
+Iteratively reduces an array to a single value using a callback function.
**Parameters**:
- `$array` (`array`)
- `$callback` (`callable`)
-- `$initial` (`mixed`), optional
+- `$initial` (`mixed`), default `null`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/array/array_replace.md b/docs/php/builtins/array/array_replace.md
new file mode 100644
index 0000000000..f5964ad250
--- /dev/null
+++ b/docs/php/builtins/array/array_replace.md
@@ -0,0 +1,33 @@
+---
+title: "array_replace()"
+description: "Replaces elements from passed arrays into the first array."
+sidebar:
+ order: 32
+---
+
+## array_replace()
+
+```php
+function array_replace(array $array, array $replacements): mixed
+```
+
+Replaces elements from passed arrays into the first array.
+
+**Parameters**:
+- `$array` (`array`)
+- `$replacements` (`array`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_replace` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_replace.md).
+
diff --git a/docs/php/builtins/array/array_replace_recursive.md b/docs/php/builtins/array/array_replace_recursive.md
new file mode 100644
index 0000000000..379edc8897
--- /dev/null
+++ b/docs/php/builtins/array/array_replace_recursive.md
@@ -0,0 +1,33 @@
+---
+title: "array_replace_recursive()"
+description: "Replaces elements from passed arrays into the first array recursively."
+sidebar:
+ order: 33
+---
+
+## array_replace_recursive()
+
+```php
+function array_replace_recursive(array $array, array $replacements): mixed
+```
+
+Replaces elements from passed arrays into the first array recursively.
+
+**Parameters**:
+- `$array` (`array`)
+- `$replacements` (`array`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_replace_recursive` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_replace_recursive.md).
+
diff --git a/docs/php/builtins/array/array_reverse.md b/docs/php/builtins/array/array_reverse.md
index 0e2b408150..6bd856cbd5 100644
--- a/docs/php/builtins/array/array_reverse.md
+++ b/docs/php/builtins/array/array_reverse.md
@@ -1,21 +1,20 @@
---
title: "array_reverse()"
-description: "Lowers `array_reverse()` for indexed arrays with 8-byte payload slots."
+description: "Returns an array with the elements in reverse order."
sidebar:
- order: 22
+ order: 34
---
## array_reverse()
```php
-function array_reverse(array $array, bool $preserve_keys): array
+function array_reverse(array $array): array
```
-Lowers `array_reverse()` for indexed arrays with 8-byte payload slots.
+Returns an array with the elements in reverse order.
**Parameters**:
- `$array` (`array`)
-- `$preserve_keys` (`bool`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_search.md b/docs/php/builtins/array/array_search.md
index 709bce75e6..f2e1a42180 100644
--- a/docs/php/builtins/array/array_search.md
+++ b/docs/php/builtins/array/array_search.md
@@ -1,22 +1,22 @@
---
title: "array_search()"
-description: "Lowers `array_search()` for indexed arrays with integer-like payloads."
+description: "Searches the array for a given value and returns the first corresponding key if successful."
sidebar:
- order: 23
+ order: 35
---
## array_search()
```php
-function array_search(mixed $needle, array $haystack, bool $strict): mixed
+function array_search(mixed $needle, array $haystack, bool $strict = false): mixed
```
-Lowers `array_search()` for indexed arrays with integer-like payloads.
+Searches the array for a given value and returns the first corresponding key if successful.
**Parameters**:
- `$needle` (`mixed`)
- `$haystack` (`array`)
-- `$strict` (`bool`), optional
+- `$strict` (`bool`), default `false`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/array/array_shift.md b/docs/php/builtins/array/array_shift.md
index b3f2d07cbb..bd4d8dc9b5 100644
--- a/docs/php/builtins/array/array_shift.md
+++ b/docs/php/builtins/array/array_shift.md
@@ -1,8 +1,8 @@
---
title: "array_shift()"
-description: "Lowers `array_shift()` for indexed arrays by compacting slots and boxing `T|null` as Mixed."
+description: "Shifts an element off the beginning of array."
sidebar:
- order: 24
+ order: 36
---
## array_shift()
@@ -11,7 +11,7 @@ sidebar:
function array_shift(array $array): mixed
```
-Lowers `array_shift()` for indexed arrays by compacting slots and boxing `T|null` as Mixed.
+Shifts an element off the beginning of array.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/array_slice.md b/docs/php/builtins/array/array_slice.md
index 506326205f..8abb0326ca 100644
--- a/docs/php/builtins/array/array_slice.md
+++ b/docs/php/builtins/array/array_slice.md
@@ -1,23 +1,22 @@
---
title: "array_slice()"
-description: "Lowers `array_slice()` for indexed arrays with pointer-sized payload slots."
+description: "Extracts a slice of an array."
sidebar:
- order: 25
+ order: 37
---
## array_slice()
```php
-function array_slice(array $array, int $offset, int $length, bool $preserve_keys): array
+function array_slice(array $array, int $offset, int $length = null): array
```
-Lowers `array_slice()` for indexed arrays with pointer-sized payload slots.
+Extracts a slice of an array.
**Parameters**:
- `$array` (`array`)
- `$offset` (`int`)
-- `$length` (`int`), optional
-- `$preserve_keys` (`bool`)
+- `$length` (`int`), default `null`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_splice.md b/docs/php/builtins/array/array_splice.md
index 8a27973ab7..eba2b7a107 100644
--- a/docs/php/builtins/array/array_splice.md
+++ b/docs/php/builtins/array/array_splice.md
@@ -1,23 +1,22 @@
---
title: "array_splice()"
-description: "Lowers `array_splice()` by mutating an indexed source array and returning removed elements."
+description: "Removes a portion of the array and replaces it with something else."
sidebar:
- order: 26
+ order: 38
---
## array_splice()
```php
-function array_splice(array $array, int $offset, int $length, array $replacement): array
+function array_splice(array $array, int $offset, int $length = null): array
```
-Lowers `array_splice()` by mutating an indexed source array and returning removed elements.
+Removes a portion of the array and replaces it with something else.
**Parameters**:
- `$array` (`array`), passed by reference
- `$offset` (`int`)
-- `$length` (`int`), optional
-- `$replacement` (`array`)
+- `$length` (`int`), default `null`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_sum.md b/docs/php/builtins/array/array_sum.md
index 047ccd207c..775aceb2fc 100644
--- a/docs/php/builtins/array/array_sum.md
+++ b/docs/php/builtins/array/array_sum.md
@@ -1,22 +1,22 @@
---
title: "array_sum()"
-description: "Lowers `array_sum()` over supported indexed-array payloads."
+description: "Calculate the sum of values in an array."
sidebar:
- order: 27
+ order: 39
---
## array_sum()
```php
-function array_sum(array $array): float
+function array_sum(array $array): int
```
-Lowers `array_sum()` over supported indexed-array payloads.
+Calculate the sum of values in an array.
**Parameters**:
- `$array` (`array`)
-**Returns**: `float`
+**Returns**: `int`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/array/array_udiff.md b/docs/php/builtins/array/array_udiff.md
new file mode 100644
index 0000000000..29fed10285
--- /dev/null
+++ b/docs/php/builtins/array/array_udiff.md
@@ -0,0 +1,34 @@
+---
+title: "array_udiff()"
+description: "Computes the difference of arrays using a callback comparator."
+sidebar:
+ order: 40
+---
+
+## array_udiff()
+
+```php
+function array_udiff(array $array1, array $array2, callable $callback): array
+```
+
+Computes the difference of arrays using a callback comparator.
+
+**Parameters**:
+- `$array1` (`array`)
+- `$array2` (`array`)
+- `$callback` (`callable`)
+
+**Returns**: `array`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_udiff` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_udiff.md).
+
diff --git a/docs/php/builtins/array/array_uintersect.md b/docs/php/builtins/array/array_uintersect.md
new file mode 100644
index 0000000000..9ae4631678
--- /dev/null
+++ b/docs/php/builtins/array/array_uintersect.md
@@ -0,0 +1,34 @@
+---
+title: "array_uintersect()"
+description: "Computes the intersection of arrays using a callback comparator."
+sidebar:
+ order: 41
+---
+
+## array_uintersect()
+
+```php
+function array_uintersect(array $array1, array $array2, callable $callback): array
+```
+
+Computes the intersection of arrays using a callback comparator.
+
+**Parameters**:
+- `$array1` (`array`)
+- `$array2` (`array`)
+- `$callback` (`callable`)
+
+**Returns**: `array`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_uintersect` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_uintersect.md).
+
diff --git a/docs/php/builtins/array/array_unique.md b/docs/php/builtins/array/array_unique.md
index 0d126d80df..f69c66df25 100644
--- a/docs/php/builtins/array/array_unique.md
+++ b/docs/php/builtins/array/array_unique.md
@@ -1,21 +1,20 @@
---
title: "array_unique()"
-description: "Lowers `array_unique()` for indexed arrays with 8-byte payload slots."
+description: "Removes duplicate values from an array."
sidebar:
- order: 28
+ order: 42
---
## array_unique()
```php
-function array_unique(array $array, int $flags): array
+function array_unique(array $array): array
```
-Lowers `array_unique()` for indexed arrays with 8-byte payload slots.
+Removes duplicate values from an array.
**Parameters**:
- `$array` (`array`)
-- `$flags` (`int`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/array_unshift.md b/docs/php/builtins/array/array_unshift.md
index 2a35a8840b..cd8fa5e2ea 100644
--- a/docs/php/builtins/array/array_unshift.md
+++ b/docs/php/builtins/array/array_unshift.md
@@ -1,8 +1,8 @@
---
title: "array_unshift()"
-description: "Lowers `array_unshift()` by ensuring uniqueness, prepending one scalar value, and returning count."
+description: "Prepends one or more elements to the beginning of an array."
sidebar:
- order: 29
+ order: 43
---
## array_unshift()
@@ -11,7 +11,7 @@ sidebar:
function array_unshift(array $array, ...$values): int
```
-Lowers `array_unshift()` by ensuring uniqueness, prepending one scalar value, and returning count.
+Prepends one or more elements to the beginning of an array.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/array_values.md b/docs/php/builtins/array/array_values.md
index bcbec8977b..4e3a852a99 100644
--- a/docs/php/builtins/array/array_values.md
+++ b/docs/php/builtins/array/array_values.md
@@ -1,8 +1,8 @@
---
title: "array_values()"
-description: "Lowers `array_values()` for indexed arrays as an alias or associative arrays as a new values array."
+description: "Returns all the values of an array, re-indexed numerically."
sidebar:
- order: 30
+ order: 44
---
## array_values()
@@ -11,7 +11,7 @@ sidebar:
function array_values(array $array): array
```
-Lowers `array_values()` for indexed arrays as an alias or associative arrays as a new values array.
+Returns all the values of an array, re-indexed numerically.
**Parameters**:
- `$array` (`array`)
diff --git a/docs/php/builtins/array/array_walk.md b/docs/php/builtins/array/array_walk.md
index 6e0ffd6787..0b6a72bec4 100644
--- a/docs/php/builtins/array/array_walk.md
+++ b/docs/php/builtins/array/array_walk.md
@@ -1,22 +1,21 @@
---
title: "array_walk()"
-description: "Lowers `array_walk()` through the callback-driven runtime helper."
+description: "Applies a user function to every member of an array."
sidebar:
- order: 31
+ order: 45
---
## array_walk()
```php
-function array_walk(array $array, callable $callback, mixed $arg): void
+function array_walk(array $array, callable $callback): void
```
-Lowers `array_walk()` through the callback-driven runtime helper.
+Applies a user function to every member of an array.
**Parameters**:
- `$array` (`array`), passed by reference
- `$callback` (`callable`)
-- `$arg` (`mixed`)
**Returns**: `void`
diff --git a/docs/php/builtins/array/array_walk_recursive.md b/docs/php/builtins/array/array_walk_recursive.md
new file mode 100644
index 0000000000..7de26032cf
--- /dev/null
+++ b/docs/php/builtins/array/array_walk_recursive.md
@@ -0,0 +1,33 @@
+---
+title: "array_walk_recursive()"
+description: "Applies a user function recursively to every member of an array."
+sidebar:
+ order: 46
+---
+
+## array_walk_recursive()
+
+```php
+function array_walk_recursive(array $array, callable $callback): void
+```
+
+Applies a user function recursively to every member of an array.
+
+**Parameters**:
+- `$array` (`array`), passed by reference
+- `$callback` (`callable`)
+
+**Returns**: `void`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `array_walk_recursive` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/array_walk_recursive.md).
+
diff --git a/docs/php/builtins/array/arsort.md b/docs/php/builtins/array/arsort.md
index c6c3d21df6..b13789bfc9 100644
--- a/docs/php/builtins/array/arsort.md
+++ b/docs/php/builtins/array/arsort.md
@@ -1,21 +1,20 @@
---
title: "arsort()"
-description: "Lowers `arsort()` for indexed integer arrays through the descending value-sort wrapper."
+description: "Sorts an array in descending order and maintains index association."
sidebar:
- order: 32
+ order: 47
---
## arsort()
```php
-function arsort(array $array, int $flags): bool
+function arsort(array $array): bool
```
-Lowers `arsort()` for indexed integer arrays through the descending value-sort wrapper.
+Sorts an array in descending order and maintains index association.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/asort.md b/docs/php/builtins/array/asort.md
index f85ef46729..cf3377b9de 100644
--- a/docs/php/builtins/array/asort.md
+++ b/docs/php/builtins/array/asort.md
@@ -1,21 +1,20 @@
---
title: "asort()"
-description: "Lowers `asort()` for indexed integer arrays through the value-sort runtime wrapper."
+description: "Sorts an array and maintains index association."
sidebar:
- order: 33
+ order: 48
---
## asort()
```php
-function asort(array $array, int $flags): bool
+function asort(array $array): bool
```
-Lowers `asort()` for indexed integer arrays through the value-sort runtime wrapper.
+Sorts an array and maintains index association.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/call_user_func.md b/docs/php/builtins/array/call_user_func.md
new file mode 100644
index 0000000000..937fc917e6
--- /dev/null
+++ b/docs/php/builtins/array/call_user_func.md
@@ -0,0 +1,33 @@
+---
+title: "call_user_func()"
+description: "Calls a callback with the given arguments."
+sidebar:
+ order: 49
+---
+
+## call_user_func()
+
+```php
+function call_user_func(callable $callback, ...$args): mixed
+```
+
+Calls a callback with the given arguments.
+
+**Parameters**:
+- `$callback` (`callable`)
+- `...$args` — variadic: collects excess arguments into `$args`.
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `call_user_func` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/call_user_func.md).
+
diff --git a/docs/php/builtins/array/call_user_func_array.md b/docs/php/builtins/array/call_user_func_array.md
new file mode 100644
index 0000000000..e6cd18b3a5
--- /dev/null
+++ b/docs/php/builtins/array/call_user_func_array.md
@@ -0,0 +1,33 @@
+---
+title: "call_user_func_array()"
+description: "Calls a callback with an array of parameters."
+sidebar:
+ order: 50
+---
+
+## call_user_func_array()
+
+```php
+function call_user_func_array(callable $callback, array $args): mixed
+```
+
+Calls a callback with an array of parameters.
+
+**Parameters**:
+- `$callback` (`callable`)
+- `$args` (`array`)
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `call_user_func_array` is implemented in the compiler, see [the internals page](../../../internals/builtins/array/call_user_func_array.md).
+
diff --git a/docs/php/builtins/array/count.md b/docs/php/builtins/array/count.md
index 7679563958..a865fb07d7 100644
--- a/docs/php/builtins/array/count.md
+++ b/docs/php/builtins/array/count.md
@@ -1,21 +1,21 @@
---
title: "count()"
-description: "Lowers `count(array)` for concrete array values by reading the runtime length header."
+description: "Counts all elements in an array or Countable object."
sidebar:
- order: 34
+ order: 51
---
## count()
```php
-function count(array $value, int $mode): int
+function count(array $value, int $mode = 0): int
```
-Lowers `count(array)` for concrete array values by reading the runtime length header.
+Counts all elements in an array or Countable object.
**Parameters**:
- `$value` (`array`)
-- `$mode` (`int`), optional
+- `$mode` (`int`), default `0`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/array/in_array.md b/docs/php/builtins/array/in_array.md
index 609dd9d30f..9064877063 100644
--- a/docs/php/builtins/array/in_array.md
+++ b/docs/php/builtins/array/in_array.md
@@ -1,24 +1,24 @@
---
title: "in_array()"
-description: "Lowers `in_array()` for indexed arrays with scalar or string payloads."
+description: "Checks if a value exists in an array."
sidebar:
- order: 35
+ order: 52
---
## in_array()
```php
-function in_array(mixed $needle, array $haystack, bool $strict): mixed
+function in_array(mixed $needle, array $haystack, bool $strict = false): bool
```
-Lowers `in_array()` for indexed arrays with scalar or string payloads.
+Checks if a value exists in an array.
**Parameters**:
- `$needle` (`mixed`)
- `$haystack` (`array`)
-- `$strict` (`bool`), optional
+- `$strict` (`bool`), default `false`, optional
-**Returns**: `mixed`
+**Returns**: `bool`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/array/krsort.md b/docs/php/builtins/array/krsort.md
index 89fcefce4d..62873b00b3 100644
--- a/docs/php/builtins/array/krsort.md
+++ b/docs/php/builtins/array/krsort.md
@@ -1,21 +1,20 @@
---
title: "krsort()"
-description: "Lowers `krsort()` through the legacy reverse key-sort helper surface."
+description: "Sorts an array by key in descending order."
sidebar:
- order: 36
+ order: 53
---
## krsort()
```php
-function krsort(array $array, int $flags): bool
+function krsort(array $array): bool
```
-Lowers `krsort()` through the legacy reverse key-sort helper surface.
+Sorts an array by key in descending order.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/ksort.md b/docs/php/builtins/array/ksort.md
index 9e7ba6669d..558cadc3c5 100644
--- a/docs/php/builtins/array/ksort.md
+++ b/docs/php/builtins/array/ksort.md
@@ -1,21 +1,20 @@
---
title: "ksort()"
-description: "Lowers `ksort()` through the legacy key-sort helper surface."
+description: "Sorts an array by key in ascending order."
sidebar:
- order: 37
+ order: 54
---
## ksort()
```php
-function ksort(array $array, int $flags): bool
+function ksort(array $array): bool
```
-Lowers `ksort()` through the legacy key-sort helper surface.
+Sorts an array by key in ascending order.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/natcasesort.md b/docs/php/builtins/array/natcasesort.md
index 3e0e1c49b4..b4f4825c81 100644
--- a/docs/php/builtins/array/natcasesort.md
+++ b/docs/php/builtins/array/natcasesort.md
@@ -1,8 +1,8 @@
---
title: "natcasesort()"
-description: "Lowers `natcasesort()` for indexed integer arrays through the case-insensitive wrapper."
+description: "Sorts an array using a case-insensitive natural order algorithm."
sidebar:
- order: 38
+ order: 55
---
## natcasesort()
@@ -11,7 +11,7 @@ sidebar:
function natcasesort(array $array): bool
```
-Lowers `natcasesort()` for indexed integer arrays through the case-insensitive wrapper.
+Sorts an array using a case-insensitive natural order algorithm.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/natsort.md b/docs/php/builtins/array/natsort.md
index 13ea00e623..81eb9f565f 100644
--- a/docs/php/builtins/array/natsort.md
+++ b/docs/php/builtins/array/natsort.md
@@ -1,8 +1,8 @@
---
title: "natsort()"
-description: "Lowers `natsort()` for indexed integer arrays through the natural-sort runtime wrapper."
+description: "Sorts an array using a natural order algorithm."
sidebar:
- order: 39
+ order: 56
---
## natsort()
@@ -11,7 +11,7 @@ sidebar:
function natsort(array $array): bool
```
-Lowers `natsort()` for indexed integer arrays through the natural-sort runtime wrapper.
+Sorts an array using a natural order algorithm.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/range.md b/docs/php/builtins/array/range.md
index a87df6b7da..306f693278 100644
--- a/docs/php/builtins/array/range.md
+++ b/docs/php/builtins/array/range.md
@@ -1,22 +1,21 @@
---
title: "range()"
-description: "Lowers `range()` for integer endpoints through the shared runtime constructor."
+description: "Create an array containing a range of elements."
sidebar:
- order: 40
+ order: 57
---
## range()
```php
-function range(mixed $start, mixed $end, int $step): array
+function range(mixed $start, mixed $end): array
```
-Lowers `range()` for integer endpoints through the shared runtime constructor.
+Create an array containing a range of elements.
**Parameters**:
- `$start` (`mixed`)
- `$end` (`mixed`)
-- `$step` (`int`)
**Returns**: `array`
diff --git a/docs/php/builtins/array/rsort.md b/docs/php/builtins/array/rsort.md
index b70306099a..9741571ad5 100644
--- a/docs/php/builtins/array/rsort.md
+++ b/docs/php/builtins/array/rsort.md
@@ -1,21 +1,20 @@
---
title: "rsort()"
-description: "Lowers `rsort()` for indexed integer arrays by mutating the source array in place."
+description: "Sorts an array in descending order."
sidebar:
- order: 41
+ order: 58
---
## rsort()
```php
-function rsort(array $array, int $flags): bool
+function rsort(array $array): bool
```
-Lowers `rsort()` for indexed integer arrays by mutating the source array in place.
+Sorts an array in descending order.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/shuffle.md b/docs/php/builtins/array/shuffle.md
index 9db0e1bb43..35eeb0a290 100644
--- a/docs/php/builtins/array/shuffle.md
+++ b/docs/php/builtins/array/shuffle.md
@@ -1,8 +1,8 @@
---
title: "shuffle()"
-description: "Lowers `shuffle()` for indexed arrays with 8-byte slots by mutating the source array in place."
+description: "Shuffles an array into random order."
sidebar:
- order: 42
+ order: 59
---
## shuffle()
@@ -11,7 +11,7 @@ sidebar:
function shuffle(array $array): bool
```
-Lowers `shuffle()` for indexed arrays with 8-byte slots by mutating the source array in place.
+Shuffles an array into random order.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/sort.md b/docs/php/builtins/array/sort.md
index 9d2aa25141..bd02d22310 100644
--- a/docs/php/builtins/array/sort.md
+++ b/docs/php/builtins/array/sort.md
@@ -1,21 +1,20 @@
---
title: "sort()"
-description: "Lowers `sort()` for indexed integer arrays by mutating the source array in place."
+description: "Sorts an array in ascending order."
sidebar:
- order: 43
+ order: 60
---
## sort()
```php
-function sort(array $array, int $flags): bool
+function sort(array $array): bool
```
-Lowers `sort()` for indexed integer arrays by mutating the source array in place.
+Sorts an array in ascending order.
**Parameters**:
- `$array` (`array`), passed by reference
-- `$flags` (`int`)
**Returns**: `bool`
diff --git a/docs/php/builtins/array/uasort.md b/docs/php/builtins/array/uasort.md
index e491619eb2..12cd0cbf14 100644
--- a/docs/php/builtins/array/uasort.md
+++ b/docs/php/builtins/array/uasort.md
@@ -1,8 +1,8 @@
---
title: "uasort()"
-description: "Lowers `uasort()` through the legacy user-sort helper for static comparators."
+description: "Sorts an array with a user-defined comparison function and maintains index association."
sidebar:
- order: 44
+ order: 61
---
## uasort()
@@ -11,7 +11,7 @@ sidebar:
function uasort(array $array, callable $callback): bool
```
-Lowers `uasort()` through the legacy user-sort helper for static comparators.
+Sorts an array with a user-defined comparison function and maintains index association.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/uksort.md b/docs/php/builtins/array/uksort.md
index 5cd86d79db..4968295de1 100644
--- a/docs/php/builtins/array/uksort.md
+++ b/docs/php/builtins/array/uksort.md
@@ -1,8 +1,8 @@
---
title: "uksort()"
-description: "Lowers `uksort()` through the legacy user-sort helper for static comparators."
+description: "Sorts an array by keys using a user-defined comparison function."
sidebar:
- order: 45
+ order: 62
---
## uksort()
@@ -11,7 +11,7 @@ sidebar:
function uksort(array $array, callable $callback): bool
```
-Lowers `uksort()` through the legacy user-sort helper for static comparators.
+Sorts an array by keys using a user-defined comparison function.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/array/usort.md b/docs/php/builtins/array/usort.md
index eed48b11fb..76afdff10f 100644
--- a/docs/php/builtins/array/usort.md
+++ b/docs/php/builtins/array/usort.md
@@ -1,8 +1,8 @@
---
title: "usort()"
-description: "Lowers `usort()` for indexed integer arrays with a static user comparator."
+description: "Sorts an array by values using a user-defined comparison function."
sidebar:
- order: 46
+ order: 63
---
## usort()
@@ -11,7 +11,7 @@ sidebar:
function usort(array $array, callable $callback): bool
```
-Lowers `usort()` for indexed integer arrays with a static user comparator.
+Sorts an array by values using a user-defined comparison function.
**Parameters**:
- `$array` (`array`), passed by reference
diff --git a/docs/php/builtins/buffer/buffer_free.md b/docs/php/builtins/buffer/buffer_free.md
index 8e0db97335..136dba1345 100644
--- a/docs/php/builtins/buffer/buffer_free.md
+++ b/docs/php/builtins/buffer/buffer_free.md
@@ -2,7 +2,7 @@
title: "buffer_free()"
description: "Lowers `buffer_free()` through the direct buffer opcode helper."
sidebar:
- order: 47
+ order: 64
---
## buffer_free()
diff --git a/docs/php/builtins/buffer/buffer_len.md b/docs/php/builtins/buffer/buffer_len.md
index af40f1dfe9..07ff13516b 100644
--- a/docs/php/builtins/buffer/buffer_len.md
+++ b/docs/php/builtins/buffer/buffer_len.md
@@ -2,7 +2,7 @@
title: "buffer_len()"
description: "Lowers `buffer_len()` through the direct buffer opcode helper."
sidebar:
- order: 48
+ order: 65
---
## buffer_len()
diff --git a/docs/php/builtins/class.md b/docs/php/builtins/class.md
index 1dcb36bb06..38b0b23bd1 100644
--- a/docs/php/builtins/class.md
+++ b/docs/php/builtins/class.md
@@ -9,22 +9,22 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`class_alias()`](./class/class_alias.md) | `(string $class, string $alias, bool $autoload): bool` | `bool` |
+| [`class_alias()`](./class/class_alias.md) | `(string $class, string $alias, bool $autoload = true): bool` | `bool` |
| [`class_attribute_args()`](./class/class_attribute_args.md) | `(string $class_name, string $attribute_name): array` | `array` |
| [`class_attribute_names()`](./class/class_attribute_names.md) | `(string $class_name): array` | `array` |
-| [`class_exists()`](./class/class_exists.md) | `(string $class, bool $autoload): bool` | `bool` |
-| [`class_get_attributes()`](./class/class_get_attributes.md) | `(string $class_name): mixed` | `mixed` |
-| [`class_implements()`](./class/class_implements.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`class_parents()`](./class/class_parents.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`class_uses()`](./class/class_uses.md) | `(mixed $object_or_class, bool $autoload): mixed` | `mixed` |
-| [`enum_exists()`](./class/enum_exists.md) | `(string $enum, bool $autoload): bool` | `bool` |
+| [`class_exists()`](./class/class_exists.md) | `(string $class, bool $autoload = true): bool` | `bool` |
+| [`class_get_attributes()`](./class/class_get_attributes.md) | `(string $class_name): array` | `array` |
+| [`class_implements()`](./class/class_implements.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`class_parents()`](./class/class_parents.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`class_uses()`](./class/class_uses.md) | `(mixed $object_or_class, bool $autoload = true): mixed` | `mixed` |
+| [`enum_exists()`](./class/enum_exists.md) | `(string $enum, bool $autoload = true): bool` | `bool` |
| [`function_exists()`](./class/function_exists.md) | `(string $function): bool` | `bool` |
-| [`get_class()`](./class/get_class.md) | `(object $object): string` | `string` |
+| [`get_class()`](./class/get_class.md) | `(object $object = null): string` | `string` |
| [`get_declared_classes()`](./class/get_declared_classes.md) | `(): array` | `array` |
| [`get_declared_interfaces()`](./class/get_declared_interfaces.md) | `(): array` | `array` |
| [`get_declared_traits()`](./class/get_declared_traits.md) | `(): array` | `array` |
-| [`get_parent_class()`](./class/get_parent_class.md) | `(mixed $object_or_class): string` | `string` |
-| [`interface_exists()`](./class/interface_exists.md) | `(string $interface, bool $autoload): bool` | `bool` |
-| [`is_a()`](./class/is_a.md) | `(object $object_or_class, string $class, bool $allow_string): bool` | `bool` |
-| [`is_subclass_of()`](./class/is_subclass_of.md) | `(mixed $object_or_class, string $class, bool $allow_string): bool` | `bool` |
-| [`trait_exists()`](./class/trait_exists.md) | `(string $trait, bool $autoload): bool` | `bool` |
+| [`get_parent_class()`](./class/get_parent_class.md) | `(mixed $object_or_class = null): string` | `string` |
+| [`interface_exists()`](./class/interface_exists.md) | `(string $interface, bool $autoload = true): bool` | `bool` |
+| [`is_a()`](./class/is_a.md) | `(object $object_or_class, string $class, bool $allow_string = false): bool` | `bool` |
+| [`is_subclass_of()`](./class/is_subclass_of.md) | `(mixed $object_or_class, string $class, bool $allow_string = true): bool` | `bool` |
+| [`trait_exists()`](./class/trait_exists.md) | `(string $trait, bool $autoload = true): bool` | `bool` |
diff --git a/docs/php/builtins/class/class_alias.md b/docs/php/builtins/class/class_alias.md
index 735d2ecb7e..3afbf9fbd2 100644
--- a/docs/php/builtins/class/class_alias.md
+++ b/docs/php/builtins/class/class_alias.md
@@ -1,22 +1,22 @@
---
title: "class_alias()"
-description: "Lowers the defensive `class_alias()` fallback that remains after AOT alias extraction."
+description: "Creates an alias for a class."
sidebar:
- order: 49
+ order: 66
---
## class_alias()
```php
-function class_alias(string $class, string $alias, bool $autoload): bool
+function class_alias(string $class, string $alias, bool $autoload = true): bool
```
-Lowers the defensive `class_alias()` fallback that remains after AOT alias extraction.
+Creates an alias for a class.
**Parameters**:
- `$class` (`string`)
- `$alias` (`string`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/class/class_attribute_args.md b/docs/php/builtins/class/class_attribute_args.md
index e13f613214..554d29d965 100644
--- a/docs/php/builtins/class/class_attribute_args.md
+++ b/docs/php/builtins/class/class_attribute_args.md
@@ -1,8 +1,8 @@
---
title: "class_attribute_args()"
-description: "Lowers `class_attribute_args(class, attr)` into an indexed Mixed array."
+description: "Returns the constructor arguments of a named attribute applied to a class."
sidebar:
- order: 50
+ order: 67
---
## class_attribute_args()
@@ -11,7 +11,7 @@ sidebar:
function class_attribute_args(string $class_name, string $attribute_name): array
```
-Lowers `class_attribute_args(class, attr)` into an indexed Mixed array.
+Returns the constructor arguments of a named attribute applied to a class.
**Parameters**:
- `$class_name` (`string`)
diff --git a/docs/php/builtins/class/class_attribute_names.md b/docs/php/builtins/class/class_attribute_names.md
index dca8e06324..78fb12b96c 100644
--- a/docs/php/builtins/class/class_attribute_names.md
+++ b/docs/php/builtins/class/class_attribute_names.md
@@ -1,8 +1,8 @@
---
title: "class_attribute_names()"
-description: "Lowers `class_attribute_names(class)` into an indexed string array."
+description: "Returns the list of attribute names applied to a class."
sidebar:
- order: 51
+ order: 68
---
## class_attribute_names()
@@ -11,7 +11,7 @@ sidebar:
function class_attribute_names(string $class_name): array
```
-Lowers `class_attribute_names(class)` into an indexed string array.
+Returns the list of attribute names applied to a class.
**Parameters**:
- `$class_name` (`string`)
diff --git a/docs/php/builtins/class/class_exists.md b/docs/php/builtins/class/class_exists.md
index 79a28325f8..2fad0f1dcd 100644
--- a/docs/php/builtins/class/class_exists.md
+++ b/docs/php/builtins/class/class_exists.md
@@ -2,20 +2,20 @@
title: "class_exists()"
description: "Checks whether the given class has been defined."
sidebar:
- order: 52
+ order: 69
---
## class_exists()
```php
-function class_exists(string $class, bool $autoload): bool
+function class_exists(string $class, bool $autoload = true): bool
```
Checks whether the given class has been defined.
**Parameters**:
- `$class` (`string`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `bool`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `class_exists` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/class_exists.md).
+
diff --git a/docs/php/builtins/class/class_get_attributes.md b/docs/php/builtins/class/class_get_attributes.md
index 6e83206c2c..1baa768aab 100644
--- a/docs/php/builtins/class/class_get_attributes.md
+++ b/docs/php/builtins/class/class_get_attributes.md
@@ -1,22 +1,22 @@
---
title: "class_get_attributes()"
-description: "Lowers `class_get_attributes(class)` into an array of `ReflectionAttribute` objects."
+description: "Returns an array of ReflectionAttribute objects for all attributes of a class."
sidebar:
- order: 53
+ order: 70
---
## class_get_attributes()
```php
-function class_get_attributes(string $class_name): mixed
+function class_get_attributes(string $class_name): array
```
-Lowers `class_get_attributes(class)` into an array of `ReflectionAttribute` objects.
+Returns an array of ReflectionAttribute objects for all attributes of a class.
**Parameters**:
- `$class_name` (`string`)
-**Returns**: `mixed`
+**Returns**: `array`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/class/class_implements.md b/docs/php/builtins/class/class_implements.md
index ce2048988a..138b873524 100644
--- a/docs/php/builtins/class/class_implements.md
+++ b/docs/php/builtins/class/class_implements.md
@@ -1,21 +1,21 @@
---
title: "class_implements()"
-description: "class_implements() — class builtin supported by Elephc."
+description: "Returns the interfaces which are implemented by the given class or its parents."
sidebar:
- order: 54
+ order: 71
---
## class_implements()
```php
-function class_implements(mixed $object_or_class, bool $autoload): mixed
+function class_implements(mixed $object_or_class, bool $autoload = true): mixed
```
-`class_implements()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the interfaces which are implemented by the given class or its parents.
**Parameters**:
- `$object_or_class` (`mixed`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `mixed`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `class_implements` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/class_implements.md).
+
diff --git a/docs/php/builtins/class/class_parents.md b/docs/php/builtins/class/class_parents.md
index 75f5b9d93f..8bf00fa244 100644
--- a/docs/php/builtins/class/class_parents.md
+++ b/docs/php/builtins/class/class_parents.md
@@ -1,21 +1,21 @@
---
title: "class_parents()"
-description: "class_parents() — class builtin supported by Elephc."
+description: "Returns the parent classes of the given class."
sidebar:
- order: 55
+ order: 72
---
## class_parents()
```php
-function class_parents(mixed $object_or_class, bool $autoload): mixed
+function class_parents(mixed $object_or_class, bool $autoload = true): mixed
```
-`class_parents()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the parent classes of the given class.
**Parameters**:
- `$object_or_class` (`mixed`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `mixed`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `class_parents` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/class_parents.md).
+
diff --git a/docs/php/builtins/class/class_uses.md b/docs/php/builtins/class/class_uses.md
index 6bd354052b..b2da4b4185 100644
--- a/docs/php/builtins/class/class_uses.md
+++ b/docs/php/builtins/class/class_uses.md
@@ -1,21 +1,21 @@
---
title: "class_uses()"
-description: "class_uses() — class builtin supported by Elephc."
+description: "Returns the traits used by the given class."
sidebar:
- order: 56
+ order: 73
---
## class_uses()
```php
-function class_uses(mixed $object_or_class, bool $autoload): mixed
+function class_uses(mixed $object_or_class, bool $autoload = true): mixed
```
-`class_uses()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the traits used by the given class.
**Parameters**:
- `$object_or_class` (`mixed`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `mixed`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `class_uses` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/class_uses.md).
+
diff --git a/docs/php/builtins/class/enum_exists.md b/docs/php/builtins/class/enum_exists.md
index f0537c4404..a1389d8667 100644
--- a/docs/php/builtins/class/enum_exists.md
+++ b/docs/php/builtins/class/enum_exists.md
@@ -1,21 +1,21 @@
---
title: "enum_exists()"
-description: "enum_exists() — class builtin supported by Elephc."
+description: "Checks if the enum has been defined."
sidebar:
- order: 57
+ order: 74
---
## enum_exists()
```php
-function enum_exists(string $enum, bool $autoload): bool
+function enum_exists(string $enum, bool $autoload = true): bool
```
-`enum_exists()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks if the enum has been defined.
**Parameters**:
- `$enum` (`string`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `bool`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `enum_exists` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/enum_exists.md).
+
diff --git a/docs/php/builtins/class/function_exists.md b/docs/php/builtins/class/function_exists.md
index 97dd99bba0..e358d8e6d0 100644
--- a/docs/php/builtins/class/function_exists.md
+++ b/docs/php/builtins/class/function_exists.md
@@ -1,8 +1,8 @@
---
title: "function_exists()"
-description: "Lowers `function_exists(\"name\")` for compile-time string names."
+description: "Returns true if the given function has been defined."
sidebar:
- order: 58
+ order: 75
---
## function_exists()
@@ -11,7 +11,7 @@ sidebar:
function function_exists(string $function): bool
```
-Lowers `function_exists("name")` for compile-time string names.
+Returns true if the given function has been defined.
**Parameters**:
- `$function` (`string`)
diff --git a/docs/php/builtins/class/get_class.md b/docs/php/builtins/class/get_class.md
index 6934374b10..082cda5c3e 100644
--- a/docs/php/builtins/class/get_class.md
+++ b/docs/php/builtins/class/get_class.md
@@ -1,20 +1,20 @@
---
title: "get_class()"
-description: "get_class() — class builtin supported by Elephc."
+description: "Returns the name of the class of an object."
sidebar:
- order: 59
+ order: 76
---
## get_class()
```php
-function get_class(object $object): string
+function get_class(object $object = null): string
```
-`get_class()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the name of the class of an object.
**Parameters**:
-- `$object` (`object`), optional
+- `$object` (`object`), default `null`, optional
**Returns**: `string`
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `get_class` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/get_class.md).
+
diff --git a/docs/php/builtins/class/get_declared_classes.md b/docs/php/builtins/class/get_declared_classes.md
index 9f8a8da8d3..150f682d7e 100644
--- a/docs/php/builtins/class/get_declared_classes.md
+++ b/docs/php/builtins/class/get_declared_classes.md
@@ -1,8 +1,8 @@
---
title: "get_declared_classes()"
-description: "get_declared_classes() — class builtin supported by Elephc."
+description: "Returns an array of the names of the defined classes."
sidebar:
- order: 60
+ order: 77
---
## get_declared_classes()
@@ -11,7 +11,7 @@ sidebar:
function get_declared_classes(): array
```
-`get_declared_classes()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns an array of the names of the defined classes.
**Parameters**: none.
@@ -25,3 +25,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `get_declared_classes` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/get_declared_classes.md).
+
diff --git a/docs/php/builtins/class/get_declared_interfaces.md b/docs/php/builtins/class/get_declared_interfaces.md
index 8c466956fa..0994cbbb8c 100644
--- a/docs/php/builtins/class/get_declared_interfaces.md
+++ b/docs/php/builtins/class/get_declared_interfaces.md
@@ -1,8 +1,8 @@
---
title: "get_declared_interfaces()"
-description: "get_declared_interfaces() — class builtin supported by Elephc."
+description: "Returns an array of all declared interfaces."
sidebar:
- order: 61
+ order: 78
---
## get_declared_interfaces()
@@ -11,7 +11,7 @@ sidebar:
function get_declared_interfaces(): array
```
-`get_declared_interfaces()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns an array of all declared interfaces.
**Parameters**: none.
@@ -25,3 +25,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `get_declared_interfaces` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/get_declared_interfaces.md).
+
diff --git a/docs/php/builtins/class/get_declared_traits.md b/docs/php/builtins/class/get_declared_traits.md
index 35c17ad588..0c4cc105b9 100644
--- a/docs/php/builtins/class/get_declared_traits.md
+++ b/docs/php/builtins/class/get_declared_traits.md
@@ -1,8 +1,8 @@
---
title: "get_declared_traits()"
-description: "get_declared_traits() — class builtin supported by Elephc."
+description: "Returns an array of all declared traits."
sidebar:
- order: 62
+ order: 79
---
## get_declared_traits()
@@ -11,7 +11,7 @@ sidebar:
function get_declared_traits(): array
```
-`get_declared_traits()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns an array of all declared traits.
**Parameters**: none.
@@ -25,3 +25,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `get_declared_traits` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/get_declared_traits.md).
+
diff --git a/docs/php/builtins/class/get_parent_class.md b/docs/php/builtins/class/get_parent_class.md
index 3012dc737f..35ae146525 100644
--- a/docs/php/builtins/class/get_parent_class.md
+++ b/docs/php/builtins/class/get_parent_class.md
@@ -1,20 +1,20 @@
---
title: "get_parent_class()"
-description: "get_parent_class() — class builtin supported by Elephc."
+description: "Returns the name of the parent class of an object or class."
sidebar:
- order: 63
+ order: 80
---
## get_parent_class()
```php
-function get_parent_class(mixed $object_or_class): string
+function get_parent_class(mixed $object_or_class = null): string
```
-`get_parent_class()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the name of the parent class of an object or class.
**Parameters**:
-- `$object_or_class` (`mixed`), optional
+- `$object_or_class` (`mixed`), default `null`, optional
**Returns**: `string`
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `get_parent_class` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/get_parent_class.md).
+
diff --git a/docs/php/builtins/class/interface_exists.md b/docs/php/builtins/class/interface_exists.md
index 961c3dc4fa..3e39f7d827 100644
--- a/docs/php/builtins/class/interface_exists.md
+++ b/docs/php/builtins/class/interface_exists.md
@@ -1,21 +1,21 @@
---
title: "interface_exists()"
-description: "interface_exists() — class builtin supported by Elephc."
+description: "Checks if the interface has been defined."
sidebar:
- order: 64
+ order: 81
---
## interface_exists()
```php
-function interface_exists(string $interface, bool $autoload): bool
+function interface_exists(string $interface, bool $autoload = true): bool
```
-`interface_exists()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks if the interface has been defined.
**Parameters**:
- `$interface` (`string`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `bool`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `interface_exists` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/interface_exists.md).
+
diff --git a/docs/php/builtins/class/is_a.md b/docs/php/builtins/class/is_a.md
index ff0eb87d10..6eda7d1fac 100644
--- a/docs/php/builtins/class/is_a.md
+++ b/docs/php/builtins/class/is_a.md
@@ -1,22 +1,22 @@
---
title: "is_a()"
-description: "is_a() — class builtin supported by Elephc."
+description: "Checks whether an object is of a given type or has it as one of its parents."
sidebar:
- order: 65
+ order: 82
---
## is_a()
```php
-function is_a(object $object_or_class, string $class, bool $allow_string): bool
+function is_a(object $object_or_class, string $class, bool $allow_string = false): bool
```
-`is_a()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether an object is of a given type or has it as one of its parents.
**Parameters**:
- `$object_or_class` (`object`)
- `$class` (`string`)
-- `$allow_string` (`bool`), optional
+- `$allow_string` (`bool`), default `false`, optional
**Returns**: `bool`
@@ -28,3 +28,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_a` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/is_a.md).
+
diff --git a/docs/php/builtins/class/is_subclass_of.md b/docs/php/builtins/class/is_subclass_of.md
index a106efd3e9..72822ee334 100644
--- a/docs/php/builtins/class/is_subclass_of.md
+++ b/docs/php/builtins/class/is_subclass_of.md
@@ -1,22 +1,22 @@
---
title: "is_subclass_of()"
-description: "is_subclass_of() — class builtin supported by Elephc."
+description: "Checks if the object has a given class as one of its parents or implements it."
sidebar:
- order: 66
+ order: 83
---
## is_subclass_of()
```php
-function is_subclass_of(mixed $object_or_class, string $class, bool $allow_string): bool
+function is_subclass_of(mixed $object_or_class, string $class, bool $allow_string = true): bool
```
-`is_subclass_of()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks if the object has a given class as one of its parents or implements it.
**Parameters**:
- `$object_or_class` (`mixed`)
- `$class` (`string`)
-- `$allow_string` (`bool`), optional
+- `$allow_string` (`bool`), default `true`, optional
**Returns**: `bool`
@@ -28,3 +28,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_subclass_of` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/is_subclass_of.md).
+
diff --git a/docs/php/builtins/class/trait_exists.md b/docs/php/builtins/class/trait_exists.md
index 5353b1e9dc..de92f3a1c8 100644
--- a/docs/php/builtins/class/trait_exists.md
+++ b/docs/php/builtins/class/trait_exists.md
@@ -1,21 +1,21 @@
---
title: "trait_exists()"
-description: "trait_exists() — class builtin supported by Elephc."
+description: "Checks whether the trait exists."
sidebar:
- order: 67
+ order: 84
---
## trait_exists()
```php
-function trait_exists(string $trait, bool $autoload): bool
+function trait_exists(string $trait, bool $autoload = true): bool
```
-`trait_exists()` is a class builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether the trait exists.
**Parameters**:
- `$trait` (`string`)
-- `$autoload` (`bool`), optional
+- `$autoload` (`bool`), default `true`, optional
**Returns**: `bool`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `trait_exists` is implemented in the compiler, see [the internals page](../../../internals/builtins/class/trait_exists.md).
+
diff --git a/docs/php/builtins/date.md b/docs/php/builtins/date.md
index f90f6a8893..d5c0b90298 100644
--- a/docs/php/builtins/date.md
+++ b/docs/php/builtins/date.md
@@ -10,15 +10,15 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
| [`checkdate()`](./date/checkdate.md) | `(int $month, int $day, int $year): bool` | `bool` |
-| [`date()`](./date/date.md) | `(string $format, int $timestamp): string` | `string` |
+| [`date()`](./date/date.md) | `(string $format, int $timestamp = null): string` | `string` |
| [`date_default_timezone_get()`](./date/date_default_timezone_get.md) | `(): string` | `string` |
| [`date_default_timezone_set()`](./date/date_default_timezone_set.md) | `(string $timezoneId): bool` | `bool` |
-| [`getdate()`](./date/getdate.md) | `(int $timestamp): array` | `array` |
-| [`gmdate()`](./date/gmdate.md) | `(string $format, int $timestamp): string` | `string` |
+| [`getdate()`](./date/getdate.md) | `(int $timestamp = null): array` | `array` |
+| [`gmdate()`](./date/gmdate.md) | `(string $format, int $timestamp = null): string` | `string` |
| [`gmmktime()`](./date/gmmktime.md) | `(int $hour, int $minute, int $second, int $month, int $day, int $year): int` | `int` |
-| [`hrtime()`](./date/hrtime.md) | `(bool $as_number): mixed` | `mixed` |
-| [`localtime()`](./date/localtime.md) | `(int $timestamp, bool $associative): array` | `array` |
-| [`microtime()`](./date/microtime.md) | `(bool $as_float): int` | `int` |
+| [`hrtime()`](./date/hrtime.md) | `(bool $as_number = false): mixed` | `mixed` |
+| [`localtime()`](./date/localtime.md) | `(int $timestamp = -1, bool $associative = false): array` | `array` |
+| [`microtime()`](./date/microtime.md) | `(bool $as_float = false): mixed` | `mixed` |
| [`mktime()`](./date/mktime.md) | `(int $hour, int $minute, int $second, int $month, int $day, int $year): int` | `int` |
-| [`strtotime()`](./date/strtotime.md) | `(string $datetime, int $baseTimestamp): mixed` | `mixed` |
+| [`strtotime()`](./date/strtotime.md) | `(string $datetime, int $baseTimestamp = null): mixed` | `mixed` |
| [`time()`](./date/time.md) | `(): int` | `int` |
diff --git a/docs/php/builtins/date/checkdate.md b/docs/php/builtins/date/checkdate.md
index 6c8c220b05..3f9f33d68f 100644
--- a/docs/php/builtins/date/checkdate.md
+++ b/docs/php/builtins/date/checkdate.md
@@ -1,8 +1,8 @@
---
title: "checkdate()"
-description: "Lowers `checkdate(month, day, year)` through the shared Gregorian-validation runtime helper."
+description: "Validates a Gregorian date."
sidebar:
- order: 68
+ order: 85
---
## checkdate()
@@ -11,7 +11,7 @@ sidebar:
function checkdate(int $month, int $day, int $year): bool
```
-Lowers `checkdate(month, day, year)` through the shared Gregorian-validation runtime helper.
+Validates a Gregorian date.
**Parameters**:
- `$month` (`int`)
diff --git a/docs/php/builtins/date/date.md b/docs/php/builtins/date/date.md
index ee8553ff59..8251d76f38 100644
--- a/docs/php/builtins/date/date.md
+++ b/docs/php/builtins/date/date.md
@@ -1,21 +1,21 @@
---
title: "date()"
-description: "Lowers `date(format, timestamp?)` through the shared formatter runtime helper."
+description: "Formats a local time/date."
sidebar:
- order: 69
+ order: 86
---
## date()
```php
-function date(string $format, int $timestamp): string
+function date(string $format, int $timestamp = null): string
```
-Lowers `date(format, timestamp?)` through the shared formatter runtime helper.
+Formats a local time/date.
**Parameters**:
- `$format` (`string`)
-- `$timestamp` (`int`), optional
+- `$timestamp` (`int`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/date/date_default_timezone_get.md b/docs/php/builtins/date/date_default_timezone_get.md
index 656acb7332..52e9b0f470 100644
--- a/docs/php/builtins/date/date_default_timezone_get.md
+++ b/docs/php/builtins/date/date_default_timezone_get.md
@@ -1,8 +1,8 @@
---
title: "date_default_timezone_get()"
-description: "Lowers `date_default_timezone_get()` through the shared runtime helper."
+description: "Gets the default timezone."
sidebar:
- order: 70
+ order: 87
---
## date_default_timezone_get()
@@ -11,7 +11,7 @@ sidebar:
function date_default_timezone_get(): string
```
-Lowers `date_default_timezone_get()` through the shared runtime helper.
+Gets the default timezone.
**Parameters**: none.
diff --git a/docs/php/builtins/date/date_default_timezone_set.md b/docs/php/builtins/date/date_default_timezone_set.md
index 897253d238..8f83807597 100644
--- a/docs/php/builtins/date/date_default_timezone_set.md
+++ b/docs/php/builtins/date/date_default_timezone_set.md
@@ -1,8 +1,8 @@
---
title: "date_default_timezone_set()"
-description: "Lowers `date_default_timezone_set(timezoneId)` through the shared runtime helper."
+description: "Sets the default timezone."
sidebar:
- order: 71
+ order: 88
---
## date_default_timezone_set()
@@ -11,7 +11,7 @@ sidebar:
function date_default_timezone_set(string $timezoneId): bool
```
-Lowers `date_default_timezone_set(timezoneId)` through the shared runtime helper.
+Sets the default timezone.
**Parameters**:
- `$timezoneId` (`string`)
diff --git a/docs/php/builtins/date/getdate.md b/docs/php/builtins/date/getdate.md
index 7b1abee859..ac3b17ed08 100644
--- a/docs/php/builtins/date/getdate.md
+++ b/docs/php/builtins/date/getdate.md
@@ -1,20 +1,20 @@
---
title: "getdate()"
-description: "Lowers `getdate([$timestamp])` through the shared decomposition runtime helper."
+description: "Returns date/time information."
sidebar:
- order: 72
+ order: 89
---
## getdate()
```php
-function getdate(int $timestamp): array
+function getdate(int $timestamp = null): array
```
-Lowers `getdate([$timestamp])` through the shared decomposition runtime helper.
+Returns date/time information.
**Parameters**:
-- `$timestamp` (`int`), optional
+- `$timestamp` (`int`), default `null`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/date/gmdate.md b/docs/php/builtins/date/gmdate.md
index 4c0e02afcb..58cc570ae0 100644
--- a/docs/php/builtins/date/gmdate.md
+++ b/docs/php/builtins/date/gmdate.md
@@ -1,21 +1,21 @@
---
title: "gmdate()"
-description: "Lowers `gmdate(format[, timestamp])`: the UTC counterpart of `date()`."
+description: "Formats a GMT/UTC date and time."
sidebar:
- order: 73
+ order: 90
---
## gmdate()
```php
-function gmdate(string $format, int $timestamp): string
+function gmdate(string $format, int $timestamp = null): string
```
-Lowers `gmdate(format[, timestamp])`: the UTC counterpart of `date()`.
+Formats a GMT/UTC date and time.
**Parameters**:
- `$format` (`string`)
-- `$timestamp` (`int`), optional
+- `$timestamp` (`int`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/date/gmmktime.md b/docs/php/builtins/date/gmmktime.md
index 7243f47661..69b8f37f89 100644
--- a/docs/php/builtins/date/gmmktime.md
+++ b/docs/php/builtins/date/gmmktime.md
@@ -1,8 +1,8 @@
---
title: "gmmktime()"
-description: "Lowers `gmmktime(...)`: the UTC counterpart of `mktime()`."
+description: "Returns the Unix timestamp for a GMT date."
sidebar:
- order: 74
+ order: 91
---
## gmmktime()
@@ -11,7 +11,7 @@ sidebar:
function gmmktime(int $hour, int $minute, int $second, int $month, int $day, int $year): int
```
-Lowers `gmmktime(...)`: the UTC counterpart of `mktime()`.
+Returns the Unix timestamp for a GMT date.
**Parameters**:
- `$hour` (`int`)
diff --git a/docs/php/builtins/date/hrtime.md b/docs/php/builtins/date/hrtime.md
index 74af0c3f4e..764f21dffc 100644
--- a/docs/php/builtins/date/hrtime.md
+++ b/docs/php/builtins/date/hrtime.md
@@ -1,20 +1,20 @@
---
title: "hrtime()"
-description: "Lowers `hrtime([$as_number])` through the monotonic-clock runtime helper."
+description: "Returns the current high-resolution time."
sidebar:
- order: 75
+ order: 92
---
## hrtime()
```php
-function hrtime(bool $as_number): mixed
+function hrtime(bool $as_number = false): mixed
```
-Lowers `hrtime([$as_number])` through the monotonic-clock runtime helper.
+Returns the current high-resolution time.
**Parameters**:
-- `$as_number` (`bool`), optional
+- `$as_number` (`bool`), default `false`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/date/localtime.md b/docs/php/builtins/date/localtime.md
index 9e75f9dff6..7afafc1967 100644
--- a/docs/php/builtins/date/localtime.md
+++ b/docs/php/builtins/date/localtime.md
@@ -1,21 +1,21 @@
---
title: "localtime()"
-description: "Lowers `localtime([$timestamp[, $associative]])` through the shared decomposition runtime helper."
+description: "Returns the local time."
sidebar:
- order: 76
+ order: 93
---
## localtime()
```php
-function localtime(int $timestamp, bool $associative): array
+function localtime(int $timestamp = -1, bool $associative = false): array
```
-Lowers `localtime([$timestamp[, $associative]])` through the shared decomposition runtime helper.
+Returns the local time.
**Parameters**:
-- `$timestamp` (`int`), optional
-- `$associative` (`bool`), optional
+- `$timestamp` (`int`), default `-1`, optional
+- `$associative` (`bool`), default `false`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/date/microtime.md b/docs/php/builtins/date/microtime.md
index 4594c5c060..22abe5ed1f 100644
--- a/docs/php/builtins/date/microtime.md
+++ b/docs/php/builtins/date/microtime.md
@@ -1,22 +1,22 @@
---
title: "microtime()"
-description: "Lowers `microtime()` / `microtime(true)` / `microtime(false)` / `microtime($flag)`."
+description: "Returns the current Unix timestamp with microseconds."
sidebar:
- order: 77
+ order: 94
---
## microtime()
```php
-function microtime(bool $as_float): int
+function microtime(bool $as_float = false): mixed
```
-Lowers `microtime()` / `microtime(true)` / `microtime(false)` / `microtime($flag)`.
+Returns the current Unix timestamp with microseconds.
**Parameters**:
-- `$as_float` (`bool`), optional
+- `$as_float` (`bool`), default `false`, optional
-**Returns**: `int`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/date/mktime.md b/docs/php/builtins/date/mktime.md
index a7966d9acd..109320d678 100644
--- a/docs/php/builtins/date/mktime.md
+++ b/docs/php/builtins/date/mktime.md
@@ -1,8 +1,8 @@
---
title: "mktime()"
-description: "Lowers `mktime(hour, minute, second, month, day, year)` through the runtime helper."
+description: "Returns the Unix timestamp for a date."
sidebar:
- order: 78
+ order: 95
---
## mktime()
@@ -11,7 +11,7 @@ sidebar:
function mktime(int $hour, int $minute, int $second, int $month, int $day, int $year): int
```
-Lowers `mktime(hour, minute, second, month, day, year)` through the runtime helper.
+Returns the Unix timestamp for a date.
**Parameters**:
- `$hour` (`int`)
diff --git a/docs/php/builtins/date/strtotime.md b/docs/php/builtins/date/strtotime.md
index 6144ee199c..b367f34f5c 100644
--- a/docs/php/builtins/date/strtotime.md
+++ b/docs/php/builtins/date/strtotime.md
@@ -1,21 +1,21 @@
---
title: "strtotime()"
-description: "Lowers `strtotime(datetime[, baseTimestamp])` through the shared parser runtime helper."
+description: "Parses an English textual datetime description into a Unix timestamp."
sidebar:
- order: 79
+ order: 96
---
## strtotime()
```php
-function strtotime(string $datetime, int $baseTimestamp): mixed
+function strtotime(string $datetime, int $baseTimestamp = null): mixed
```
-Lowers `strtotime(datetime[, baseTimestamp])` through the shared parser runtime helper.
+Parses an English textual datetime description into a Unix timestamp.
**Parameters**:
- `$datetime` (`string`)
-- `$baseTimestamp` (`int`), optional
+- `$baseTimestamp` (`int`), default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/date/time.md b/docs/php/builtins/date/time.md
index d9a2656171..a100865cf0 100644
--- a/docs/php/builtins/date/time.md
+++ b/docs/php/builtins/date/time.md
@@ -1,8 +1,8 @@
---
title: "time()"
-description: "Lowers `time()` through the shared wall-clock runtime helper."
+description: "Returns the current Unix timestamp."
sidebar:
- order: 80
+ order: 97
---
## time()
@@ -11,7 +11,7 @@ sidebar:
function time(): int
```
-Lowers `time()` through the shared wall-clock runtime helper.
+Returns the current Unix timestamp.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem.md b/docs/php/builtins/filesystem.md
index 9689190f34..1edc01cb60 100644
--- a/docs/php/builtins/filesystem.md
+++ b/docs/php/builtins/filesystem.md
@@ -9,14 +9,14 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`basename()`](./filesystem/basename.md) | `(string $path, string $suffix): string` | `string` |
+| [`basename()`](./filesystem/basename.md) | `(string $path, string $suffix = ''): string` | `string` |
| [`chdir()`](./filesystem/chdir.md) | `(string $directory): bool` | `bool` |
-| [`chgrp()`](./filesystem/chgrp.md) | `(string $filename, int $group): bool` | `bool` |
+| [`chgrp()`](./filesystem/chgrp.md) | `(string $filename, string $group): bool` | `bool` |
| [`chmod()`](./filesystem/chmod.md) | `(string $filename, int $permissions): bool` | `bool` |
-| [`chown()`](./filesystem/chown.md) | `(string $filename, int $user): bool` | `bool` |
-| [`clearstatcache()`](./filesystem/clearstatcache.md) | `(bool $clear_realpath_cache, string $filename): void` | `void` |
-| [`copy()`](./filesystem/copy.md) | `(string $from, string $to, mixed $context): bool` | `bool` |
-| [`dirname()`](./filesystem/dirname.md) | `(string $path, int $levels): string` | `string` |
+| [`chown()`](./filesystem/chown.md) | `(string $filename, string $user): bool` | `bool` |
+| [`clearstatcache()`](./filesystem/clearstatcache.md) | `(bool $clear_realpath_cache = false, string $filename = ''): void` | `void` |
+| [`copy()`](./filesystem/copy.md) | `(string $from, string $to): bool` | `bool` |
+| [`dirname()`](./filesystem/dirname.md) | `(string $path, int $levels = 1): string` | `string` |
| [`disk_free_space()`](./filesystem/disk_free_space.md) | `(string $directory): float` | `float` |
| [`disk_total_space()`](./filesystem/disk_total_space.md) | `(string $directory): float` | `float` |
| [`file_exists()`](./filesystem/file_exists.md) | `(string $filename): bool` | `bool` |
@@ -29,10 +29,10 @@ sidebar:
| [`fileperms()`](./filesystem/fileperms.md) | `(string $filename): mixed` | `mixed` |
| [`filesize()`](./filesystem/filesize.md) | `(string $filename): int` | `int` |
| [`filetype()`](./filesystem/filetype.md) | `(string $filename): mixed` | `mixed` |
-| [`fnmatch()`](./filesystem/fnmatch.md) | `(string $pattern, string $filename, int $flags): bool` | `bool` |
+| [`fnmatch()`](./filesystem/fnmatch.md) | `(string $pattern, string $filename, int $flags = 0): bool` | `bool` |
| [`getcwd()`](./filesystem/getcwd.md) | `(): string` | `string` |
-| [`getenv()`](./filesystem/getenv.md) | `(string $name, bool $local_only): mixed` | `mixed` |
-| [`glob()`](./filesystem/glob.md) | `(string $pattern, int $flags): array` | `array` |
+| [`getenv()`](./filesystem/getenv.md) | `(string $name): mixed` | `mixed` |
+| [`glob()`](./filesystem/glob.md) | `(string $pattern): array` | `array` |
| [`is_dir()`](./filesystem/is_dir.md) | `(string $filename): bool` | `bool` |
| [`is_executable()`](./filesystem/is_executable.md) | `(string $filename): bool` | `bool` |
| [`is_file()`](./filesystem/is_file.md) | `(string $filename): bool` | `bool` |
@@ -40,27 +40,27 @@ sidebar:
| [`is_readable()`](./filesystem/is_readable.md) | `(string $filename): bool` | `bool` |
| [`is_writable()`](./filesystem/is_writable.md) | `(string $filename): bool` | `bool` |
| [`is_writeable()`](./filesystem/is_writeable.md) | `(string $filename): bool` | `bool` |
-| [`lchgrp()`](./filesystem/lchgrp.md) | `(string $filename, int $group): bool` | `bool` |
-| [`lchown()`](./filesystem/lchown.md) | `(string $filename, int $user): bool` | `bool` |
+| [`lchgrp()`](./filesystem/lchgrp.md) | `(string $filename, string $group): bool` | `bool` |
+| [`lchown()`](./filesystem/lchown.md) | `(string $filename, string $user): bool` | `bool` |
| [`link()`](./filesystem/link.md) | `(string $target, string $link): bool` | `bool` |
| [`linkinfo()`](./filesystem/linkinfo.md) | `(string $path): int` | `int` |
| [`lstat()`](./filesystem/lstat.md) | `(string $filename): mixed` | `mixed` |
-| [`mkdir()`](./filesystem/mkdir.md) | `(string $directory, int $permissions, bool $recursive, bool $context): bool` | `bool` |
-| [`pathinfo()`](./filesystem/pathinfo.md) | `(string $path, int $flags): mixed` | `mixed` |
+| [`mkdir()`](./filesystem/mkdir.md) | `(string $directory): bool` | `bool` |
+| [`pathinfo()`](./filesystem/pathinfo.md) | `(string $path, int $flags = 15): array` | `array` |
| [`putenv()`](./filesystem/putenv.md) | `(string $assignment): bool` | `bool` |
-| [`readfile()`](./filesystem/readfile.md) | `(string $filename, bool $use_include_path, mixed $context): mixed` | `mixed` |
+| [`readfile()`](./filesystem/readfile.md) | `(string $filename): mixed` | `mixed` |
| [`readlink()`](./filesystem/readlink.md) | `(string $path): mixed` | `mixed` |
| [`realpath()`](./filesystem/realpath.md) | `(string $path): mixed` | `mixed` |
| [`realpath_cache_get()`](./filesystem/realpath_cache_get.md) | `(): array` | `array` |
| [`realpath_cache_size()`](./filesystem/realpath_cache_size.md) | `(): int` | `int` |
-| [`rename()`](./filesystem/rename.md) | `(string $from, string $to, mixed $context): bool` | `bool` |
-| [`rmdir()`](./filesystem/rmdir.md) | `(string $directory, mixed $context = null): bool` | `bool` |
-| [`scandir()`](./filesystem/scandir.md) | `(string $directory, int $sorting_order, mixed $context): array` | `array` |
+| [`rename()`](./filesystem/rename.md) | `(string $from, string $to): bool` | `bool` |
+| [`rmdir()`](./filesystem/rmdir.md) | `(string $directory): bool` | `bool` |
+| [`scandir()`](./filesystem/scandir.md) | `(string $directory): array` | `array` |
| [`stat()`](./filesystem/stat.md) | `(string $filename): mixed` | `mixed` |
| [`symlink()`](./filesystem/symlink.md) | `(string $target, string $link): bool` | `bool` |
| [`sys_get_temp_dir()`](./filesystem/sys_get_temp_dir.md) | `(): string` | `string` |
| [`tempnam()`](./filesystem/tempnam.md) | `(string $directory, string $prefix): string` | `string` |
| [`tmpfile()`](./filesystem/tmpfile.md) | `(): mixed` | `mixed` |
-| [`touch()`](./filesystem/touch.md) | `(string $filename, int $mtime, int $atime): bool` | `bool` |
-| [`umask()`](./filesystem/umask.md) | `(int $mask): int` | `int` |
+| [`touch()`](./filesystem/touch.md) | `(string $filename, int $mtime = null, int $atime = null): bool` | `bool` |
+| [`umask()`](./filesystem/umask.md) | `(int $mask = null): int` | `int` |
| [`unlink()`](./filesystem/unlink.md) | `(string $filename): bool` | `bool` |
diff --git a/docs/php/builtins/filesystem/basename.md b/docs/php/builtins/filesystem/basename.md
index 2e236dabd6..b7fcae60c7 100644
--- a/docs/php/builtins/filesystem/basename.md
+++ b/docs/php/builtins/filesystem/basename.md
@@ -1,21 +1,21 @@
---
title: "basename()"
-description: "Lowers `basename(path, suffix?)` through the target-aware runtime helper."
+description: "Returns the trailing name component of a path."
sidebar:
- order: 81
+ order: 98
---
## basename()
```php
-function basename(string $path, string $suffix): string
+function basename(string $path, string $suffix = ''): string
```
-Lowers `basename(path, suffix?)` through the target-aware runtime helper.
+Returns the trailing name component of a path.
**Parameters**:
- `$path` (`string`)
-- `$suffix` (`string`), optional
+- `$suffix` (`string`), default `''`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/filesystem/chdir.md b/docs/php/builtins/filesystem/chdir.md
index 7c6ad31368..79eb4bd9bb 100644
--- a/docs/php/builtins/filesystem/chdir.md
+++ b/docs/php/builtins/filesystem/chdir.md
@@ -1,8 +1,8 @@
---
title: "chdir()"
-description: "Lowers `chdir(path)` through the target-aware runtime helper."
+description: "Changes the current directory."
sidebar:
- order: 82
+ order: 99
---
## chdir()
@@ -11,7 +11,7 @@ sidebar:
function chdir(string $directory): bool
```
-Lowers `chdir(path)` through the target-aware runtime helper.
+Changes the current directory.
**Parameters**:
- `$directory` (`string`)
diff --git a/docs/php/builtins/filesystem/chgrp.md b/docs/php/builtins/filesystem/chgrp.md
index f85124f9c1..a5f7d23632 100644
--- a/docs/php/builtins/filesystem/chgrp.md
+++ b/docs/php/builtins/filesystem/chgrp.md
@@ -1,21 +1,21 @@
---
title: "chgrp()"
-description: "Lowers `chgrp(path, group)` for integer GIDs and string group names."
+description: "Changes file group."
sidebar:
- order: 83
+ order: 100
---
## chgrp()
```php
-function chgrp(string $filename, int $group): bool
+function chgrp(string $filename, string $group): bool
```
-Lowers `chgrp(path, group)` for integer GIDs and string group names.
+Changes file group.
**Parameters**:
- `$filename` (`string`)
-- `$group` (`int`)
+- `$group` (`string`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/chmod.md b/docs/php/builtins/filesystem/chmod.md
index bf66bc2fdf..23a5012e9e 100644
--- a/docs/php/builtins/filesystem/chmod.md
+++ b/docs/php/builtins/filesystem/chmod.md
@@ -1,8 +1,8 @@
---
title: "chmod()"
-description: "Lowers `chmod(path, mode)` through the target-aware runtime helper."
+description: "Changes file mode."
sidebar:
- order: 84
+ order: 101
---
## chmod()
@@ -11,7 +11,7 @@ sidebar:
function chmod(string $filename, int $permissions): bool
```
-Lowers `chmod(path, mode)` through the target-aware runtime helper.
+Changes file mode.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/chown.md b/docs/php/builtins/filesystem/chown.md
index ce1e097850..26d53c1398 100644
--- a/docs/php/builtins/filesystem/chown.md
+++ b/docs/php/builtins/filesystem/chown.md
@@ -1,21 +1,21 @@
---
title: "chown()"
-description: "Lowers `chown(path, owner)` for integer UIDs and string user names."
+description: "Changes file owner."
sidebar:
- order: 85
+ order: 102
---
## chown()
```php
-function chown(string $filename, int $user): bool
+function chown(string $filename, string $user): bool
```
-Lowers `chown(path, owner)` for integer UIDs and string user names.
+Changes file owner.
**Parameters**:
- `$filename` (`string`)
-- `$user` (`int`)
+- `$user` (`string`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/clearstatcache.md b/docs/php/builtins/filesystem/clearstatcache.md
index 9ab178bb77..47f99f901c 100644
--- a/docs/php/builtins/filesystem/clearstatcache.md
+++ b/docs/php/builtins/filesystem/clearstatcache.md
@@ -1,21 +1,21 @@
---
title: "clearstatcache()"
-description: "Lowers `clearstatcache(...)` as an ordered no-op after EIR operand evaluation."
+description: "Clears file status cache."
sidebar:
- order: 86
+ order: 103
---
## clearstatcache()
```php
-function clearstatcache(bool $clear_realpath_cache, string $filename): void
+function clearstatcache(bool $clear_realpath_cache = false, string $filename = ''): void
```
-Lowers `clearstatcache(...)` as an ordered no-op after EIR operand evaluation.
+Clears file status cache.
**Parameters**:
-- `$clear_realpath_cache` (`bool`), optional
-- `$filename` (`string`), optional
+- `$clear_realpath_cache` (`bool`), default `false`, optional
+- `$filename` (`string`), default `''`, optional
**Returns**: `void`
diff --git a/docs/php/builtins/filesystem/copy.md b/docs/php/builtins/filesystem/copy.md
index ba7916871c..3d08aa2588 100644
--- a/docs/php/builtins/filesystem/copy.md
+++ b/docs/php/builtins/filesystem/copy.md
@@ -1,22 +1,21 @@
---
title: "copy()"
-description: "Lowers `copy(source, dest)` through the target-aware runtime helper."
+description: "Copies a file."
sidebar:
- order: 87
+ order: 104
---
## copy()
```php
-function copy(string $from, string $to, mixed $context): bool
+function copy(string $from, string $to): bool
```
-Lowers `copy(source, dest)` through the target-aware runtime helper.
+Copies a file.
**Parameters**:
- `$from` (`string`)
- `$to` (`string`)
-- `$context` (`mixed`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/dirname.md b/docs/php/builtins/filesystem/dirname.md
index 29130f25cb..4fe76dc5f5 100644
--- a/docs/php/builtins/filesystem/dirname.md
+++ b/docs/php/builtins/filesystem/dirname.md
@@ -1,21 +1,21 @@
---
title: "dirname()"
-description: "Lowers `dirname(path, levels?)` through the target-aware runtime helper."
+description: "Returns a parent directory's path."
sidebar:
- order: 88
+ order: 105
---
## dirname()
```php
-function dirname(string $path, int $levels): string
+function dirname(string $path, int $levels = 1): string
```
-Lowers `dirname(path, levels?)` through the target-aware runtime helper.
+Returns a parent directory's path.
**Parameters**:
- `$path` (`string`)
-- `$levels` (`int`), optional
+- `$levels` (`int`), default `1`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/filesystem/disk_free_space.md b/docs/php/builtins/filesystem/disk_free_space.md
index c61e5d35af..a13b08e44d 100644
--- a/docs/php/builtins/filesystem/disk_free_space.md
+++ b/docs/php/builtins/filesystem/disk_free_space.md
@@ -1,8 +1,8 @@
---
title: "disk_free_space()"
-description: "Lowers `disk_free_space(path)` through the shared disk-space runtime helper."
+description: "Returns available space on filesystem or disk partition."
sidebar:
- order: 89
+ order: 106
---
## disk_free_space()
@@ -11,7 +11,7 @@ sidebar:
function disk_free_space(string $directory): float
```
-Lowers `disk_free_space(path)` through the shared disk-space runtime helper.
+Returns available space on filesystem or disk partition.
**Parameters**:
- `$directory` (`string`)
diff --git a/docs/php/builtins/filesystem/disk_total_space.md b/docs/php/builtins/filesystem/disk_total_space.md
index 704c9054cd..b774d51383 100644
--- a/docs/php/builtins/filesystem/disk_total_space.md
+++ b/docs/php/builtins/filesystem/disk_total_space.md
@@ -1,8 +1,8 @@
---
title: "disk_total_space()"
-description: "Lowers `disk_total_space(path)` through the shared disk-space runtime helper."
+description: "Returns the total size of a filesystem or disk partition."
sidebar:
- order: 90
+ order: 107
---
## disk_total_space()
@@ -11,7 +11,7 @@ sidebar:
function disk_total_space(string $directory): float
```
-Lowers `disk_total_space(path)` through the shared disk-space runtime helper.
+Returns the total size of a filesystem or disk partition.
**Parameters**:
- `$directory` (`string`)
diff --git a/docs/php/builtins/filesystem/file_exists.md b/docs/php/builtins/filesystem/file_exists.md
index 66c40286ae..526d596428 100644
--- a/docs/php/builtins/filesystem/file_exists.md
+++ b/docs/php/builtins/filesystem/file_exists.md
@@ -1,8 +1,8 @@
---
title: "file_exists()"
-description: "Lowers `file_exists(path)` through the target-aware runtime stat helper."
+description: "Checks whether a file or directory exists."
sidebar:
- order: 91
+ order: 108
---
## file_exists()
@@ -11,7 +11,7 @@ sidebar:
function file_exists(string $filename): bool
```
-Lowers `file_exists(path)` through the target-aware runtime stat helper.
+Checks whether a file or directory exists.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/fileatime.md b/docs/php/builtins/filesystem/fileatime.md
index f65f31a763..61b1f09e2d 100644
--- a/docs/php/builtins/filesystem/fileatime.md
+++ b/docs/php/builtins/filesystem/fileatime.md
@@ -1,8 +1,8 @@
---
title: "fileatime()"
-description: "Lowers `fileatime(path)` and boxes the runtime integer-or-false result."
+description: "Gets last access time of file."
sidebar:
- order: 92
+ order: 109
---
## fileatime()
@@ -11,7 +11,7 @@ sidebar:
function fileatime(string $filename): mixed
```
-Lowers `fileatime(path)` and boxes the runtime integer-or-false result.
+Gets last access time of file.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/filectime.md b/docs/php/builtins/filesystem/filectime.md
index d11c5e8a2b..ea697c09f0 100644
--- a/docs/php/builtins/filesystem/filectime.md
+++ b/docs/php/builtins/filesystem/filectime.md
@@ -1,8 +1,8 @@
---
title: "filectime()"
-description: "Lowers `filectime(path)` and boxes the runtime integer-or-false result."
+description: "Gets inode change time of file."
sidebar:
- order: 93
+ order: 110
---
## filectime()
@@ -11,7 +11,7 @@ sidebar:
function filectime(string $filename): mixed
```
-Lowers `filectime(path)` and boxes the runtime integer-or-false result.
+Gets inode change time of file.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/filegroup.md b/docs/php/builtins/filesystem/filegroup.md
index 20b3af7a3a..18bfc38b86 100644
--- a/docs/php/builtins/filesystem/filegroup.md
+++ b/docs/php/builtins/filesystem/filegroup.md
@@ -1,8 +1,8 @@
---
title: "filegroup()"
-description: "Lowers `filegroup(path)` and boxes the runtime integer-or-false result."
+description: "Gets file group."
sidebar:
- order: 94
+ order: 111
---
## filegroup()
@@ -11,7 +11,7 @@ sidebar:
function filegroup(string $filename): mixed
```
-Lowers `filegroup(path)` and boxes the runtime integer-or-false result.
+Gets file group.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/fileinode.md b/docs/php/builtins/filesystem/fileinode.md
index 8586ef112e..2110a291ef 100644
--- a/docs/php/builtins/filesystem/fileinode.md
+++ b/docs/php/builtins/filesystem/fileinode.md
@@ -1,8 +1,8 @@
---
title: "fileinode()"
-description: "Lowers `fileinode(path)` and boxes the runtime integer-or-false result."
+description: "Gets file inode."
sidebar:
- order: 95
+ order: 112
---
## fileinode()
@@ -11,7 +11,7 @@ sidebar:
function fileinode(string $filename): mixed
```
-Lowers `fileinode(path)` and boxes the runtime integer-or-false result.
+Gets file inode.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/filemtime.md b/docs/php/builtins/filesystem/filemtime.md
index 6f9996350b..dfd9aba90e 100644
--- a/docs/php/builtins/filesystem/filemtime.md
+++ b/docs/php/builtins/filesystem/filemtime.md
@@ -1,8 +1,8 @@
---
title: "filemtime()"
-description: "Lowers `filemtime(path)` through the target-aware runtime stat helper."
+description: "Gets file modification time."
sidebar:
- order: 96
+ order: 113
---
## filemtime()
@@ -11,7 +11,7 @@ sidebar:
function filemtime(string $filename): int
```
-Lowers `filemtime(path)` through the target-aware runtime stat helper.
+Gets file modification time.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/fileowner.md b/docs/php/builtins/filesystem/fileowner.md
index a68514d8e4..b49e713523 100644
--- a/docs/php/builtins/filesystem/fileowner.md
+++ b/docs/php/builtins/filesystem/fileowner.md
@@ -1,8 +1,8 @@
---
title: "fileowner()"
-description: "Lowers `fileowner(path)` and boxes the runtime integer-or-false result."
+description: "Gets file owner."
sidebar:
- order: 97
+ order: 114
---
## fileowner()
@@ -11,7 +11,7 @@ sidebar:
function fileowner(string $filename): mixed
```
-Lowers `fileowner(path)` and boxes the runtime integer-or-false result.
+Gets file owner.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/fileperms.md b/docs/php/builtins/filesystem/fileperms.md
index c8dad103d6..ebb7453ba1 100644
--- a/docs/php/builtins/filesystem/fileperms.md
+++ b/docs/php/builtins/filesystem/fileperms.md
@@ -1,8 +1,8 @@
---
title: "fileperms()"
-description: "Lowers `fileperms(path)` and boxes the runtime integer-or-false result."
+description: "Gets file permissions."
sidebar:
- order: 98
+ order: 115
---
## fileperms()
@@ -11,7 +11,7 @@ sidebar:
function fileperms(string $filename): mixed
```
-Lowers `fileperms(path)` and boxes the runtime integer-or-false result.
+Gets file permissions.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/filesize.md b/docs/php/builtins/filesystem/filesize.md
index 5952cf7dab..c655357e10 100644
--- a/docs/php/builtins/filesystem/filesize.md
+++ b/docs/php/builtins/filesystem/filesize.md
@@ -1,8 +1,8 @@
---
title: "filesize()"
-description: "Lowers `filesize(path)` through the target-aware runtime stat helper."
+description: "Gets file size."
sidebar:
- order: 99
+ order: 116
---
## filesize()
@@ -11,7 +11,7 @@ sidebar:
function filesize(string $filename): int
```
-Lowers `filesize(path)` through the target-aware runtime stat helper.
+Gets file size.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/filetype.md b/docs/php/builtins/filesystem/filetype.md
index 66cd54fbde..61027757ed 100644
--- a/docs/php/builtins/filesystem/filetype.md
+++ b/docs/php/builtins/filesystem/filetype.md
@@ -1,8 +1,8 @@
---
title: "filetype()"
-description: "Lowers `filetype(path)` and boxes the runtime string-or-false result."
+description: "Gets file type."
sidebar:
- order: 100
+ order: 117
---
## filetype()
@@ -11,7 +11,7 @@ sidebar:
function filetype(string $filename): mixed
```
-Lowers `filetype(path)` and boxes the runtime string-or-false result.
+Gets file type.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/fnmatch.md b/docs/php/builtins/filesystem/fnmatch.md
index 5446f70c83..0f8791cc5f 100644
--- a/docs/php/builtins/filesystem/fnmatch.md
+++ b/docs/php/builtins/filesystem/fnmatch.md
@@ -1,22 +1,22 @@
---
title: "fnmatch()"
-description: "Lowers `fnmatch(pattern, filename, flags?)` through the target-aware runtime helper."
+description: "Matches a filename against a pattern."
sidebar:
- order: 101
+ order: 118
---
## fnmatch()
```php
-function fnmatch(string $pattern, string $filename, int $flags): bool
+function fnmatch(string $pattern, string $filename, int $flags = 0): bool
```
-Lowers `fnmatch(pattern, filename, flags?)` through the target-aware runtime helper.
+Matches a filename against a pattern.
**Parameters**:
- `$pattern` (`string`)
- `$filename` (`string`)
-- `$flags` (`int`), optional
+- `$flags` (`int`), default `0`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/getcwd.md b/docs/php/builtins/filesystem/getcwd.md
index 22e344daa1..330eabaefa 100644
--- a/docs/php/builtins/filesystem/getcwd.md
+++ b/docs/php/builtins/filesystem/getcwd.md
@@ -1,8 +1,8 @@
---
title: "getcwd()"
-description: "Lowers `getcwd()` through the target-aware runtime helper."
+description: "Gets the current working directory."
sidebar:
- order: 102
+ order: 119
---
## getcwd()
@@ -11,7 +11,7 @@ sidebar:
function getcwd(): string
```
-Lowers `getcwd()` through the target-aware runtime helper.
+Gets the current working directory.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem/getenv.md b/docs/php/builtins/filesystem/getenv.md
index 22d21a071a..4d5a9b30e5 100644
--- a/docs/php/builtins/filesystem/getenv.md
+++ b/docs/php/builtins/filesystem/getenv.md
@@ -1,21 +1,20 @@
---
title: "getenv()"
-description: "Lowers `getenv(name)` through the target-aware environment lookup helper."
+description: "Gets the value of an environment variable."
sidebar:
- order: 103
+ order: 120
---
## getenv()
```php
-function getenv(string $name, bool $local_only): mixed
+function getenv(string $name): mixed
```
-Lowers `getenv(name)` through the target-aware environment lookup helper.
+Gets the value of an environment variable.
**Parameters**:
- `$name` (`string`)
-- `$local_only` (`bool`)
**Returns**: `mixed`
diff --git a/docs/php/builtins/filesystem/glob.md b/docs/php/builtins/filesystem/glob.md
index 69de45fa57..af23fa2cf9 100644
--- a/docs/php/builtins/filesystem/glob.md
+++ b/docs/php/builtins/filesystem/glob.md
@@ -1,21 +1,20 @@
---
title: "glob()"
-description: "Lowers `glob(pattern)` through the target-aware runtime glob expansion helper."
+description: "Finds pathnames matching a pattern."
sidebar:
- order: 104
+ order: 121
---
## glob()
```php
-function glob(string $pattern, int $flags): array
+function glob(string $pattern): array
```
-Lowers `glob(pattern)` through the target-aware runtime glob expansion helper.
+Finds pathnames matching a pattern.
**Parameters**:
- `$pattern` (`string`)
-- `$flags` (`int`)
**Returns**: `array`
diff --git a/docs/php/builtins/filesystem/is_dir.md b/docs/php/builtins/filesystem/is_dir.md
index 9cc80b3398..967dd2819a 100644
--- a/docs/php/builtins/filesystem/is_dir.md
+++ b/docs/php/builtins/filesystem/is_dir.md
@@ -1,8 +1,8 @@
---
title: "is_dir()"
-description: "Lowers `is_dir(path)` through the target-aware runtime stat helper."
+description: "Tells whether the filename is a directory."
sidebar:
- order: 105
+ order: 122
---
## is_dir()
@@ -11,7 +11,7 @@ sidebar:
function is_dir(string $filename): bool
```
-Lowers `is_dir(path)` through the target-aware runtime stat helper.
+Tells whether the filename is a directory.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_executable.md b/docs/php/builtins/filesystem/is_executable.md
index f980150dcb..08b532dac1 100644
--- a/docs/php/builtins/filesystem/is_executable.md
+++ b/docs/php/builtins/filesystem/is_executable.md
@@ -1,8 +1,8 @@
---
title: "is_executable()"
-description: "Lowers `is_executable(path)` through the target-aware runtime access helper."
+description: "Tells whether the filename is executable."
sidebar:
- order: 106
+ order: 123
---
## is_executable()
@@ -11,7 +11,7 @@ sidebar:
function is_executable(string $filename): bool
```
-Lowers `is_executable(path)` through the target-aware runtime access helper.
+Tells whether the filename is executable.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_file.md b/docs/php/builtins/filesystem/is_file.md
index 4ba4c31771..b54871fded 100644
--- a/docs/php/builtins/filesystem/is_file.md
+++ b/docs/php/builtins/filesystem/is_file.md
@@ -1,8 +1,8 @@
---
title: "is_file()"
-description: "Lowers `is_file(path)` through the target-aware runtime stat helper."
+description: "Tells whether the filename is a regular file."
sidebar:
- order: 107
+ order: 124
---
## is_file()
@@ -11,7 +11,7 @@ sidebar:
function is_file(string $filename): bool
```
-Lowers `is_file(path)` through the target-aware runtime stat helper.
+Tells whether the filename is a regular file.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_link.md b/docs/php/builtins/filesystem/is_link.md
index 79d6b1c84d..96a1ee53ae 100644
--- a/docs/php/builtins/filesystem/is_link.md
+++ b/docs/php/builtins/filesystem/is_link.md
@@ -1,8 +1,8 @@
---
title: "is_link()"
-description: "Lowers `is_link(path)` through the target-aware runtime lstat helper."
+description: "Tells whether the filename is a symbolic link."
sidebar:
- order: 108
+ order: 125
---
## is_link()
@@ -11,7 +11,7 @@ sidebar:
function is_link(string $filename): bool
```
-Lowers `is_link(path)` through the target-aware runtime lstat helper.
+Tells whether the filename is a symbolic link.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_readable.md b/docs/php/builtins/filesystem/is_readable.md
index c326ec4d1b..c024a11243 100644
--- a/docs/php/builtins/filesystem/is_readable.md
+++ b/docs/php/builtins/filesystem/is_readable.md
@@ -1,8 +1,8 @@
---
title: "is_readable()"
-description: "Lowers `is_readable(path)` through the target-aware runtime access helper."
+description: "Tells whether the filename is readable."
sidebar:
- order: 109
+ order: 126
---
## is_readable()
@@ -11,7 +11,7 @@ sidebar:
function is_readable(string $filename): bool
```
-Lowers `is_readable(path)` through the target-aware runtime access helper.
+Tells whether the filename is readable.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_writable.md b/docs/php/builtins/filesystem/is_writable.md
index c456f9adea..2d121f3082 100644
--- a/docs/php/builtins/filesystem/is_writable.md
+++ b/docs/php/builtins/filesystem/is_writable.md
@@ -1,8 +1,8 @@
---
title: "is_writable()"
-description: "Lowers `is_writable(path)` through the target-aware runtime access helper."
+description: "Tells whether the filename is writable."
sidebar:
- order: 110
+ order: 127
---
## is_writable()
@@ -11,7 +11,7 @@ sidebar:
function is_writable(string $filename): bool
```
-Lowers `is_writable(path)` through the target-aware runtime access helper.
+Tells whether the filename is writable.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/is_writeable.md b/docs/php/builtins/filesystem/is_writeable.md
index 16e799a06d..d5c732bdcb 100644
--- a/docs/php/builtins/filesystem/is_writeable.md
+++ b/docs/php/builtins/filesystem/is_writeable.md
@@ -1,8 +1,8 @@
---
title: "is_writeable()"
-description: "Lowers `is_writeable(path)`, PHP's alias of `is_writable(path)`."
+description: "Tells whether the filename is writable (alias of is_writable)."
sidebar:
- order: 111
+ order: 128
---
## is_writeable()
@@ -11,7 +11,7 @@ sidebar:
function is_writeable(string $filename): bool
```
-Lowers `is_writeable(path)`, PHP's alias of `is_writable(path)`.
+Tells whether the filename is writable (alias of is_writable).
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/lchgrp.md b/docs/php/builtins/filesystem/lchgrp.md
index ae444a6d1d..60157747b1 100644
--- a/docs/php/builtins/filesystem/lchgrp.md
+++ b/docs/php/builtins/filesystem/lchgrp.md
@@ -1,21 +1,21 @@
---
title: "lchgrp()"
-description: "Lowers `lchgrp(path, group)` for integer GIDs and string group names without following symlinks."
+description: "Changes group ownership of a symlink."
sidebar:
- order: 112
+ order: 129
---
## lchgrp()
```php
-function lchgrp(string $filename, int $group): bool
+function lchgrp(string $filename, string $group): bool
```
-Lowers `lchgrp(path, group)` for integer GIDs and string group names without following symlinks.
+Changes group ownership of a symlink.
**Parameters**:
- `$filename` (`string`)
-- `$group` (`int`)
+- `$group` (`string`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/lchown.md b/docs/php/builtins/filesystem/lchown.md
index 0cf64a7488..7e1b51edc2 100644
--- a/docs/php/builtins/filesystem/lchown.md
+++ b/docs/php/builtins/filesystem/lchown.md
@@ -1,21 +1,21 @@
---
title: "lchown()"
-description: "Lowers `lchown(path, owner)` for integer UIDs and string user names without following symlinks."
+description: "Changes user ownership of a symlink."
sidebar:
- order: 113
+ order: 130
---
## lchown()
```php
-function lchown(string $filename, int $user): bool
+function lchown(string $filename, string $user): bool
```
-Lowers `lchown(path, owner)` for integer UIDs and string user names without following symlinks.
+Changes user ownership of a symlink.
**Parameters**:
- `$filename` (`string`)
-- `$user` (`int`)
+- `$user` (`string`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/link.md b/docs/php/builtins/filesystem/link.md
index c42a390ef7..a91aa299a4 100644
--- a/docs/php/builtins/filesystem/link.md
+++ b/docs/php/builtins/filesystem/link.md
@@ -1,8 +1,8 @@
---
title: "link()"
-description: "Lowers `link(oldpath, newpath)` through the target-aware libc wrapper."
+description: "Creates a hard link."
sidebar:
- order: 114
+ order: 131
---
## link()
@@ -11,7 +11,7 @@ sidebar:
function link(string $target, string $link): bool
```
-Lowers `link(oldpath, newpath)` through the target-aware libc wrapper.
+Creates a hard link.
**Parameters**:
- `$target` (`string`)
diff --git a/docs/php/builtins/filesystem/linkinfo.md b/docs/php/builtins/filesystem/linkinfo.md
index 1ec551ad0d..dbf0e821e3 100644
--- a/docs/php/builtins/filesystem/linkinfo.md
+++ b/docs/php/builtins/filesystem/linkinfo.md
@@ -1,8 +1,8 @@
---
title: "linkinfo()"
-description: "Lowers `linkinfo(path)` through the target-aware runtime lstat helper."
+description: "Gets information about a link."
sidebar:
- order: 115
+ order: 132
---
## linkinfo()
@@ -11,7 +11,7 @@ sidebar:
function linkinfo(string $path): int
```
-Lowers `linkinfo(path)` through the target-aware runtime lstat helper.
+Gets information about a link.
**Parameters**:
- `$path` (`string`)
diff --git a/docs/php/builtins/filesystem/lstat.md b/docs/php/builtins/filesystem/lstat.md
index 355c7c73cb..9872189521 100644
--- a/docs/php/builtins/filesystem/lstat.md
+++ b/docs/php/builtins/filesystem/lstat.md
@@ -1,8 +1,8 @@
---
title: "lstat()"
-description: "Lowers `lstat(path)` and boxes the runtime lstat array or PHP false result."
+description: "Gives information about a file or symbolic link."
sidebar:
- order: 116
+ order: 133
---
## lstat()
@@ -11,7 +11,7 @@ sidebar:
function lstat(string $filename): mixed
```
-Lowers `lstat(path)` and boxes the runtime lstat array or PHP false result.
+Gives information about a file or symbolic link.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/mkdir.md b/docs/php/builtins/filesystem/mkdir.md
index 1a33b7e555..b10b78219f 100644
--- a/docs/php/builtins/filesystem/mkdir.md
+++ b/docs/php/builtins/filesystem/mkdir.md
@@ -1,23 +1,20 @@
---
title: "mkdir()"
-description: "Lowers `mkdir(path)` through the target-aware runtime helper."
+description: "Makes a directory."
sidebar:
- order: 117
+ order: 134
---
## mkdir()
```php
-function mkdir(string $directory, int $permissions, bool $recursive, bool $context): bool
+function mkdir(string $directory): bool
```
-Lowers `mkdir(path)` through the target-aware runtime helper.
+Makes a directory.
**Parameters**:
- `$directory` (`string`)
-- `$permissions` (`int`)
-- `$recursive` (`bool`)
-- `$context` (`bool`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/pathinfo.md b/docs/php/builtins/filesystem/pathinfo.md
index 90abcea3b2..6f048c5f92 100644
--- a/docs/php/builtins/filesystem/pathinfo.md
+++ b/docs/php/builtins/filesystem/pathinfo.md
@@ -1,23 +1,23 @@
---
title: "pathinfo()"
-description: "Lowers `pathinfo(path, flags?)` through string, array, or boxed dynamic helpers."
+description: "Returns information about a file path."
sidebar:
- order: 118
+ order: 135
---
## pathinfo()
```php
-function pathinfo(string $path, int $flags): mixed
+function pathinfo(string $path, int $flags = 15): array
```
-Lowers `pathinfo(path, flags?)` through string, array, or boxed dynamic helpers.
+Returns information about a file path.
**Parameters**:
- `$path` (`string`)
-- `$flags` (`int`), optional
+- `$flags` (`int`), default `15`, optional
-**Returns**: `mixed`
+**Returns**: `array`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/filesystem/putenv.md b/docs/php/builtins/filesystem/putenv.md
index 7e8c5ea429..3be34a079a 100644
--- a/docs/php/builtins/filesystem/putenv.md
+++ b/docs/php/builtins/filesystem/putenv.md
@@ -1,8 +1,8 @@
---
title: "putenv()"
-description: "Lowers `putenv(assignment)` by copying the environment string into persistent heap storage."
+description: "Sets an environment variable."
sidebar:
- order: 119
+ order: 136
---
## putenv()
@@ -11,7 +11,7 @@ sidebar:
function putenv(string $assignment): bool
```
-Lowers `putenv(assignment)` by copying the environment string into persistent heap storage.
+Sets an environment variable.
**Parameters**:
- `$assignment` (`string`)
diff --git a/docs/php/builtins/filesystem/readfile.md b/docs/php/builtins/filesystem/readfile.md
index 3323edef6b..4ae8854ae2 100644
--- a/docs/php/builtins/filesystem/readfile.md
+++ b/docs/php/builtins/filesystem/readfile.md
@@ -1,22 +1,20 @@
---
title: "readfile()"
-description: "Lowers `readfile(path)` and boxes the runtime byte-count-or-false result."
+description: "Outputs a file."
sidebar:
- order: 120
+ order: 137
---
## readfile()
```php
-function readfile(string $filename, bool $use_include_path, mixed $context): mixed
+function readfile(string $filename): mixed
```
-Lowers `readfile(path)` and boxes the runtime byte-count-or-false result.
+Outputs a file.
**Parameters**:
- `$filename` (`string`)
-- `$use_include_path` (`bool`)
-- `$context` (`mixed`)
**Returns**: `mixed`
diff --git a/docs/php/builtins/filesystem/readlink.md b/docs/php/builtins/filesystem/readlink.md
index df2603debf..3225ce379b 100644
--- a/docs/php/builtins/filesystem/readlink.md
+++ b/docs/php/builtins/filesystem/readlink.md
@@ -1,8 +1,8 @@
---
title: "readlink()"
-description: "Lowers `readlink(path)` and boxes the owned runtime string-or-false result."
+description: "Returns the target of a symbolic link."
sidebar:
- order: 121
+ order: 138
---
## readlink()
@@ -11,7 +11,7 @@ sidebar:
function readlink(string $path): mixed
```
-Lowers `readlink(path)` and boxes the owned runtime string-or-false result.
+Returns the target of a symbolic link.
**Parameters**:
- `$path` (`string`)
diff --git a/docs/php/builtins/filesystem/realpath.md b/docs/php/builtins/filesystem/realpath.md
index 0c81b14823..ad0d795625 100644
--- a/docs/php/builtins/filesystem/realpath.md
+++ b/docs/php/builtins/filesystem/realpath.md
@@ -1,8 +1,8 @@
---
title: "realpath()"
-description: "Lowers `realpath(path)` and boxes the owned runtime string-or-false result."
+description: "Returns canonicalized absolute pathname."
sidebar:
- order: 122
+ order: 139
---
## realpath()
@@ -11,7 +11,7 @@ sidebar:
function realpath(string $path): mixed
```
-Lowers `realpath(path)` and boxes the owned runtime string-or-false result.
+Returns canonicalized absolute pathname.
**Parameters**:
- `$path` (`string`)
diff --git a/docs/php/builtins/filesystem/realpath_cache_get.md b/docs/php/builtins/filesystem/realpath_cache_get.md
index 5ab44c4e61..0780ccfffa 100644
--- a/docs/php/builtins/filesystem/realpath_cache_get.md
+++ b/docs/php/builtins/filesystem/realpath_cache_get.md
@@ -1,8 +1,8 @@
---
title: "realpath_cache_get()"
-description: "Lowers `realpath_cache_get()` to elephc's empty realpath-cache view."
+description: "Returns realpath cache entries."
sidebar:
- order: 123
+ order: 140
---
## realpath_cache_get()
@@ -11,7 +11,7 @@ sidebar:
function realpath_cache_get(): array
```
-Lowers `realpath_cache_get()` to elephc's empty realpath-cache view.
+Returns realpath cache entries.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem/realpath_cache_size.md b/docs/php/builtins/filesystem/realpath_cache_size.md
index f9b5534d52..7c7b7796e6 100644
--- a/docs/php/builtins/filesystem/realpath_cache_size.md
+++ b/docs/php/builtins/filesystem/realpath_cache_size.md
@@ -1,8 +1,8 @@
---
title: "realpath_cache_size()"
-description: "Lowers `realpath_cache_size()` to zero because elephc has no realpath cache."
+description: "Returns the amount of memory used by the realpath cache."
sidebar:
- order: 124
+ order: 141
---
## realpath_cache_size()
@@ -11,7 +11,7 @@ sidebar:
function realpath_cache_size(): int
```
-Lowers `realpath_cache_size()` to zero because elephc has no realpath cache.
+Returns the amount of memory used by the realpath cache.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem/rename.md b/docs/php/builtins/filesystem/rename.md
index 2b6ab3a6e5..0075032d5d 100644
--- a/docs/php/builtins/filesystem/rename.md
+++ b/docs/php/builtins/filesystem/rename.md
@@ -1,22 +1,21 @@
---
title: "rename()"
-description: "Lowers `rename(from, to)` through the target-aware runtime helper."
+description: "Renames a file or directory."
sidebar:
- order: 125
+ order: 142
---
## rename()
```php
-function rename(string $from, string $to, mixed $context): bool
+function rename(string $from, string $to): bool
```
-Lowers `rename(from, to)` through the target-aware runtime helper.
+Renames a file or directory.
**Parameters**:
- `$from` (`string`)
- `$to` (`string`)
-- `$context` (`mixed`)
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/rmdir.md b/docs/php/builtins/filesystem/rmdir.md
index 97480b75ed..ac6c8e1a93 100644
--- a/docs/php/builtins/filesystem/rmdir.md
+++ b/docs/php/builtins/filesystem/rmdir.md
@@ -1,21 +1,20 @@
---
title: "rmdir()"
-description: "Lowers `rmdir(path)` through the target-aware runtime helper."
+description: "Removes a directory."
sidebar:
- order: 126
+ order: 143
---
## rmdir()
```php
-function rmdir(string $directory, mixed $context = null): bool
+function rmdir(string $directory): bool
```
-Lowers `rmdir(path)` through the target-aware runtime helper.
+Removes a directory.
**Parameters**:
- `$directory` (`string`)
-- `$context` (`mixed`), default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/scandir.md b/docs/php/builtins/filesystem/scandir.md
index d020359c53..8540afa3a0 100644
--- a/docs/php/builtins/filesystem/scandir.md
+++ b/docs/php/builtins/filesystem/scandir.md
@@ -1,22 +1,20 @@
---
title: "scandir()"
-description: "Lowers `scandir(path)` through the target-aware runtime directory listing helper."
+description: "Lists files and directories inside the specified path."
sidebar:
- order: 127
+ order: 144
---
## scandir()
```php
-function scandir(string $directory, int $sorting_order, mixed $context): array
+function scandir(string $directory): array
```
-Lowers `scandir(path)` through the target-aware runtime directory listing helper.
+Lists files and directories inside the specified path.
**Parameters**:
- `$directory` (`string`)
-- `$sorting_order` (`int`)
-- `$context` (`mixed`)
**Returns**: `array`
diff --git a/docs/php/builtins/filesystem/stat.md b/docs/php/builtins/filesystem/stat.md
index 38855cb57d..3179e14368 100644
--- a/docs/php/builtins/filesystem/stat.md
+++ b/docs/php/builtins/filesystem/stat.md
@@ -1,8 +1,8 @@
---
title: "stat()"
-description: "Lowers `stat(path)` and boxes the runtime stat array or PHP false result."
+description: "Gives information about a file."
sidebar:
- order: 128
+ order: 145
---
## stat()
@@ -11,7 +11,7 @@ sidebar:
function stat(string $filename): mixed
```
-Lowers `stat(path)` and boxes the runtime stat array or PHP false result.
+Gives information about a file.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/filesystem/symlink.md b/docs/php/builtins/filesystem/symlink.md
index da6d795f31..7bd1e3a55c 100644
--- a/docs/php/builtins/filesystem/symlink.md
+++ b/docs/php/builtins/filesystem/symlink.md
@@ -1,8 +1,8 @@
---
title: "symlink()"
-description: "Lowers `symlink(target, link)` through the target-aware libc wrapper."
+description: "Creates a symbolic link."
sidebar:
- order: 129
+ order: 146
---
## symlink()
@@ -11,7 +11,7 @@ sidebar:
function symlink(string $target, string $link): bool
```
-Lowers `symlink(target, link)` through the target-aware libc wrapper.
+Creates a symbolic link.
**Parameters**:
- `$target` (`string`)
diff --git a/docs/php/builtins/filesystem/sys_get_temp_dir.md b/docs/php/builtins/filesystem/sys_get_temp_dir.md
index 2d58033529..e910e45349 100644
--- a/docs/php/builtins/filesystem/sys_get_temp_dir.md
+++ b/docs/php/builtins/filesystem/sys_get_temp_dir.md
@@ -1,8 +1,8 @@
---
title: "sys_get_temp_dir()"
-description: "Lowers `sys_get_temp_dir()` as the project's hardcoded `/tmp` string."
+description: "Returns the directory path used for temporary files."
sidebar:
- order: 130
+ order: 147
---
## sys_get_temp_dir()
@@ -11,7 +11,7 @@ sidebar:
function sys_get_temp_dir(): string
```
-Lowers `sys_get_temp_dir()` as the project's hardcoded `/tmp` string.
+Returns the directory path used for temporary files.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem/tempnam.md b/docs/php/builtins/filesystem/tempnam.md
index 30d316c868..a8ac18a230 100644
--- a/docs/php/builtins/filesystem/tempnam.md
+++ b/docs/php/builtins/filesystem/tempnam.md
@@ -1,8 +1,8 @@
---
title: "tempnam()"
-description: "Lowers `tempnam(directory, prefix)` through the target-aware runtime helper."
+description: "Creates a file with a unique filename."
sidebar:
- order: 131
+ order: 148
---
## tempnam()
@@ -11,7 +11,7 @@ sidebar:
function tempnam(string $directory, string $prefix): string
```
-Lowers `tempnam(directory, prefix)` through the target-aware runtime helper.
+Creates a file with a unique filename.
**Parameters**:
- `$directory` (`string`)
diff --git a/docs/php/builtins/filesystem/tmpfile.md b/docs/php/builtins/filesystem/tmpfile.md
index e7178ace0f..9919570752 100644
--- a/docs/php/builtins/filesystem/tmpfile.md
+++ b/docs/php/builtins/filesystem/tmpfile.md
@@ -1,8 +1,8 @@
---
title: "tmpfile()"
-description: "Lowers `tmpfile()` and boxes the anonymous stream descriptor or PHP false."
+description: "Creates a temporary file."
sidebar:
- order: 132
+ order: 149
---
## tmpfile()
@@ -11,7 +11,7 @@ sidebar:
function tmpfile(): mixed
```
-Lowers `tmpfile()` and boxes the anonymous stream descriptor or PHP false.
+Creates a temporary file.
**Parameters**: none.
diff --git a/docs/php/builtins/filesystem/touch.md b/docs/php/builtins/filesystem/touch.md
index 2d78d049d8..7bd652ac00 100644
--- a/docs/php/builtins/filesystem/touch.md
+++ b/docs/php/builtins/filesystem/touch.md
@@ -1,22 +1,22 @@
---
title: "touch()"
-description: "Lowers `touch(path, mtime?, atime?)` through the target-aware runtime helper."
+description: "Sets access and modification time of a file."
sidebar:
- order: 133
+ order: 150
---
## touch()
```php
-function touch(string $filename, int $mtime, int $atime): bool
+function touch(string $filename, int $mtime = null, int $atime = null): bool
```
-Lowers `touch(path, mtime?, atime?)` through the target-aware runtime helper.
+Sets access and modification time of a file.
**Parameters**:
- `$filename` (`string`)
-- `$mtime` (`int`), optional
-- `$atime` (`int`), optional
+- `$mtime` (`int`), default `null`, optional
+- `$atime` (`int`), default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/filesystem/umask.md b/docs/php/builtins/filesystem/umask.md
index 17b4d79e2b..c11ca2d250 100644
--- a/docs/php/builtins/filesystem/umask.md
+++ b/docs/php/builtins/filesystem/umask.md
@@ -1,20 +1,20 @@
---
title: "umask()"
-description: "Lowers `umask(mask?)` through the target-aware runtime helper."
+description: "Changes the current umask."
sidebar:
- order: 134
+ order: 151
---
## umask()
```php
-function umask(int $mask): int
+function umask(int $mask = null): int
```
-Lowers `umask(mask?)` through the target-aware runtime helper.
+Changes the current umask.
**Parameters**:
-- `$mask` (`int`), optional
+- `$mask` (`int`), default `null`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/filesystem/unlink.md b/docs/php/builtins/filesystem/unlink.md
index 668a0aa0bb..1910b9daa3 100644
--- a/docs/php/builtins/filesystem/unlink.md
+++ b/docs/php/builtins/filesystem/unlink.md
@@ -1,8 +1,8 @@
---
title: "unlink()"
-description: "Lowers `unlink(path)` through the target-aware runtime helper."
+description: "Deletes a file."
sidebar:
- order: 135
+ order: 152
---
## unlink()
@@ -11,7 +11,7 @@ sidebar:
function unlink(string $filename): bool
```
-Lowers `unlink(path)` through the target-aware runtime helper.
+Deletes a file.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/io.md b/docs/php/builtins/io.md
index a088a88551..135caf50f7 100644
--- a/docs/php/builtins/io.md
+++ b/docs/php/builtins/io.md
@@ -15,24 +15,24 @@ sidebar:
| [`feof()`](./io/feof.md) | `(resource $stream): bool` | `bool` |
| [`fflush()`](./io/fflush.md) | `(resource $stream): bool` | `bool` |
| [`fgetc()`](./io/fgetc.md) | `(resource $stream): mixed` | `mixed` |
-| [`fgetcsv()`](./io/fgetcsv.md) | `(resource $stream, int $length, string $separator, string $enclosure, string $escape): array` | `array` |
-| [`fgets()`](./io/fgets.md) | `(resource $stream, int $length): mixed` | `mixed` |
-| [`file()`](./io/file.md) | `(string $filename, int $flags, mixed $context): array` | `array` |
-| [`file_get_contents()`](./io/file_get_contents.md) | `(string $filename, bool $use_include_path, mixed $context, int $offset, int $length): mixed` | `mixed` |
-| [`file_put_contents()`](./io/file_put_contents.md) | `(string $filename, mixed $data, int $flags = 0, mixed $context = null): int` | `int` |
-| [`flock()`](./io/flock.md) | `(resource $stream, int $operation, bool $would_block): bool` | `bool` |
-| [`fopen()`](./io/fopen.md) | `(string $filename, string $mode, bool $use_include_path, mixed $context): mixed` | `mixed` |
+| [`fgetcsv()`](./io/fgetcsv.md) | `(resource $stream, int $length = null, string $separator = ','): array` | `array` |
+| [`fgets()`](./io/fgets.md) | `(resource $stream): mixed` | `mixed` |
+| [`file()`](./io/file.md) | `(string $filename): array` | `array` |
+| [`file_get_contents()`](./io/file_get_contents.md) | `(string $filename): mixed` | `mixed` |
+| [`file_put_contents()`](./io/file_put_contents.md) | `(string $filename, string $data): int` | `int` |
+| [`flock()`](./io/flock.md) | `(resource $stream, int $operation, bool $would_block = null): bool` | `bool` |
+| [`fopen()`](./io/fopen.md) | `(string $filename, string $mode, bool $use_include_path = false, mixed $context = null): mixed` | `mixed` |
| [`fpassthru()`](./io/fpassthru.md) | `(resource $stream): int` | `int` |
| [`fprintf()`](./io/fprintf.md) | `(resource $stream, string $format, ...$values): int` | `int` |
-| [`fputcsv()`](./io/fputcsv.md) | `(resource $stream, array $fields, string $separator = ',', string $enclosure = '"', string $escape = '\\', string $eol = '\n'): int` | `int` |
+| [`fputcsv()`](./io/fputcsv.md) | `(resource $stream, array $fields, string $separator = ',', string $enclosure = '"'): int` | `int` |
| [`fread()`](./io/fread.md) | `(resource $stream, int $length): string` | `string` |
| [`fscanf()`](./io/fscanf.md) | `(resource $stream, string $format, ...$vars): array` | `array` |
-| [`fseek()`](./io/fseek.md) | `(resource $stream, int $offset, int $whence): int` | `int` |
+| [`fseek()`](./io/fseek.md) | `(resource $stream, int $offset, int $whence = 0): int` | `int` |
| [`fstat()`](./io/fstat.md) | `(resource $stream): mixed` | `mixed` |
| [`fsync()`](./io/fsync.md) | `(resource $stream): bool` | `bool` |
| [`ftell()`](./io/ftell.md) | `(resource $stream): int` | `int` |
| [`ftruncate()`](./io/ftruncate.md) | `(resource $stream, int $size): bool` | `bool` |
-| [`fwrite()`](./io/fwrite.md) | `(resource $stream, string $data, int $length): int` | `int` |
+| [`fwrite()`](./io/fwrite.md) | `(resource $stream, string $data): int` | `int` |
| [`gethostbyaddr()`](./io/gethostbyaddr.md) | `(string $ip): mixed` | `mixed` |
| [`gethostbyname()`](./io/gethostbyname.md) | `(string $hostname): string` | `string` |
| [`gethostname()`](./io/gethostname.md) | `(): string` | `string` |
@@ -40,49 +40,49 @@ sidebar:
| [`getprotobynumber()`](./io/getprotobynumber.md) | `(int $protocol): mixed` | `mixed` |
| [`getservbyname()`](./io/getservbyname.md) | `(string $service, string $protocol): mixed` | `mixed` |
| [`getservbyport()`](./io/getservbyport.md) | `(int $port, string $protocol): mixed` | `mixed` |
-| [`hash_file()`](./io/hash_file.md) | `(string $algo, string $filename, bool $binary = false, array $options = []): mixed` | `mixed` |
+| [`hash_file()`](./io/hash_file.md) | `(string $algo, string $filename, bool $binary = false): mixed` | `mixed` |
| [`opendir()`](./io/opendir.md) | `(string $directory): mixed` | `mixed` |
| [`readdir()`](./io/readdir.md) | `(resource $dir_handle): mixed` | `mixed` |
| [`rewind()`](./io/rewind.md) | `(resource $stream): bool` | `bool` |
| [`rewinddir()`](./io/rewinddir.md) | `(resource $dir_handle): void` | `void` |
| [`stream_bucket_make_writeable()`](./io/stream_bucket_make_writeable.md) | `(mixed $brigade): mixed` | `mixed` |
| [`stream_bucket_new()`](./io/stream_bucket_new.md) | `(resource $stream, string $buffer): mixed` | `mixed` |
-| [`stream_context_create()`](./io/stream_context_create.md) | `(array $options, array $params): mixed` | `mixed` |
-| [`stream_context_get_default()`](./io/stream_context_get_default.md) | `(array $options): mixed` | `mixed` |
-| [`stream_context_get_options()`](./io/stream_context_get_options.md) | `(resource $stream_or_context): array` | `array` |
+| [`stream_context_create()`](./io/stream_context_create.md) | `(array $options = null, array $params = null): mixed` | `mixed` |
+| [`stream_context_get_default()`](./io/stream_context_get_default.md) | `(array $options = null): mixed` | `mixed` |
+| [`stream_context_get_options()`](./io/stream_context_get_options.md) | `(resource $context): array` | `array` |
| [`stream_context_get_params()`](./io/stream_context_get_params.md) | `(resource $context): array` | `array` |
| [`stream_context_set_default()`](./io/stream_context_set_default.md) | `(array $options): mixed` | `mixed` |
-| [`stream_context_set_option()`](./io/stream_context_set_option.md) | `(resource $context, string $wrapper_or_options, string $option_name, mixed $value): bool` | `bool` |
+| [`stream_context_set_option()`](./io/stream_context_set_option.md) | `(resource $context, string $wrapper_or_options, string $option_name = null, mixed $value = null): bool` | `bool` |
| [`stream_context_set_params()`](./io/stream_context_set_params.md) | `(resource $context, array $params): bool` | `bool` |
-| [`stream_copy_to_stream()`](./io/stream_copy_to_stream.md) | `(resource $from, resource $to, int $length, int $offset): mixed` | `mixed` |
+| [`stream_copy_to_stream()`](./io/stream_copy_to_stream.md) | `(resource $from, resource $to, int $length = null, int $offset = -1): mixed` | `mixed` |
| [`stream_filter_register()`](./io/stream_filter_register.md) | `(string $filter_name, string $class): bool` | `bool` |
| [`stream_filter_remove()`](./io/stream_filter_remove.md) | `(resource $stream_filter): bool` | `bool` |
-| [`stream_get_contents()`](./io/stream_get_contents.md) | `(resource $stream, int $length, int $offset): mixed` | `mixed` |
+| [`stream_get_contents()`](./io/stream_get_contents.md) | `(resource $stream, int $length = null, int $offset = -1): mixed` | `mixed` |
| [`stream_get_filters()`](./io/stream_get_filters.md) | `(): array` | `array` |
-| [`stream_get_line()`](./io/stream_get_line.md) | `(resource $stream, int $length, string $ending): string` | `string` |
+| [`stream_get_line()`](./io/stream_get_line.md) | `(resource $stream, int $length, string $ending = ''): string` | `string` |
| [`stream_get_meta_data()`](./io/stream_get_meta_data.md) | `(resource $stream): array` | `array` |
| [`stream_get_transports()`](./io/stream_get_transports.md) | `(): array` | `array` |
| [`stream_get_wrappers()`](./io/stream_get_wrappers.md) | `(): array` | `array` |
| [`stream_is_local()`](./io/stream_is_local.md) | `(resource $stream): bool` | `bool` |
| [`stream_isatty()`](./io/stream_isatty.md) | `(resource $stream): bool` | `bool` |
| [`stream_resolve_include_path()`](./io/stream_resolve_include_path.md) | `(string $filename): mixed` | `mixed` |
-| [`stream_select()`](./io/stream_select.md) | `(array $read, array $write, array $except, int $seconds, int $microseconds): int` | `int` |
+| [`stream_select()`](./io/stream_select.md) | `(array $read, array $write, array $except, int $seconds, int $microseconds = 0): int` | `int` |
| [`stream_set_blocking()`](./io/stream_set_blocking.md) | `(resource $stream, bool $enable): bool` | `bool` |
| [`stream_set_chunk_size()`](./io/stream_set_chunk_size.md) | `(resource $stream, int $size): int` | `int` |
| [`stream_set_read_buffer()`](./io/stream_set_read_buffer.md) | `(resource $stream, int $size): int` | `int` |
-| [`stream_set_timeout()`](./io/stream_set_timeout.md) | `(resource $stream, int $seconds, int $microseconds): bool` | `bool` |
+| [`stream_set_timeout()`](./io/stream_set_timeout.md) | `(resource $stream, int $seconds, int $microseconds = 0): bool` | `bool` |
| [`stream_set_write_buffer()`](./io/stream_set_write_buffer.md) | `(resource $stream, int $size): int` | `int` |
-| [`stream_socket_accept()`](./io/stream_socket_accept.md) | `(resource $socket, float $timeout, string $peer_name): mixed` | `mixed` |
-| [`stream_socket_client()`](./io/stream_socket_client.md) | `(string $address, int $error_code, int $error_message, string $timeout, float $flags): mixed` | `mixed` |
-| [`stream_socket_enable_crypto()`](./io/stream_socket_enable_crypto.md) | `(resource $stream, bool $enable, int $crypto_method, resource $session_stream): bool` | `bool` |
+| [`stream_socket_accept()`](./io/stream_socket_accept.md) | `(resource $socket, float $timeout = null, string $peer_name = null): mixed` | `mixed` |
+| [`stream_socket_client()`](./io/stream_socket_client.md) | `(string $address): mixed` | `mixed` |
+| [`stream_socket_enable_crypto()`](./io/stream_socket_enable_crypto.md) | `(resource $stream, bool $enable, int $crypto_method = null, resource $session_stream = null): bool` | `bool` |
| [`stream_socket_get_name()`](./io/stream_socket_get_name.md) | `(resource $socket, bool $remote): mixed` | `mixed` |
| [`stream_socket_pair()`](./io/stream_socket_pair.md) | `(int $domain, int $type, int $protocol): mixed` | `mixed` |
-| [`stream_socket_recvfrom()`](./io/stream_socket_recvfrom.md) | `(resource $socket, int $length, int $flags, string $address): mixed` | `mixed` |
-| [`stream_socket_sendto()`](./io/stream_socket_sendto.md) | `(resource $socket, string $data, int $flags, string $address): mixed` | `mixed` |
-| [`stream_socket_server()`](./io/stream_socket_server.md) | `(string $address, int $error_code, int $error_message): mixed` | `mixed` |
+| [`stream_socket_recvfrom()`](./io/stream_socket_recvfrom.md) | `(resource $socket, int $length, int $flags = 0, string $address = ''): mixed` | `mixed` |
+| [`stream_socket_sendto()`](./io/stream_socket_sendto.md) | `(resource $socket, string $data, int $flags = 0, string $address = ''): mixed` | `mixed` |
+| [`stream_socket_server()`](./io/stream_socket_server.md) | `(string $address): mixed` | `mixed` |
| [`stream_socket_shutdown()`](./io/stream_socket_shutdown.md) | `(resource $stream, int $mode): bool` | `bool` |
| [`stream_supports_lock()`](./io/stream_supports_lock.md) | `(resource $stream): bool` | `bool` |
-| [`stream_wrapper_register()`](./io/stream_wrapper_register.md) | `(string $protocol, string $class, int $flags): bool` | `bool` |
+| [`stream_wrapper_register()`](./io/stream_wrapper_register.md) | `(string $protocol, string $class, int $flags = 0): bool` | `bool` |
| [`stream_wrapper_restore()`](./io/stream_wrapper_restore.md) | `(string $protocol): bool` | `bool` |
| [`stream_wrapper_unregister()`](./io/stream_wrapper_unregister.md) | `(string $protocol): bool` | `bool` |
| [`vfprintf()`](./io/vfprintf.md) | `(resource $stream, string $format, array $values): int` | `int` |
diff --git a/docs/php/builtins/io/closedir.md b/docs/php/builtins/io/closedir.md
index ed8d4b26c4..d2f0c88f2a 100644
--- a/docs/php/builtins/io/closedir.md
+++ b/docs/php/builtins/io/closedir.md
@@ -1,8 +1,8 @@
---
title: "closedir()"
-description: "Lowers `closedir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+description: "Closes directory handle."
sidebar:
- order: 136
+ order: 153
---
## closedir()
@@ -11,7 +11,7 @@ sidebar:
function closedir(resource $dir_handle): void
```
-Lowers `closedir(dir_handle)` for libc, glob, and userspace-wrapper handles.
+Closes directory handle.
**Parameters**:
- `$dir_handle` (`resource`)
diff --git a/docs/php/builtins/io/fclose.md b/docs/php/builtins/io/fclose.md
index 1b598abadc..d9c8c44990 100644
--- a/docs/php/builtins/io/fclose.md
+++ b/docs/php/builtins/io/fclose.md
@@ -1,8 +1,8 @@
---
title: "fclose()"
-description: "Lowers `fclose(stream)` after validating and unboxing the stream handle."
+description: "Closes an open file pointer."
sidebar:
- order: 137
+ order: 154
---
## fclose()
@@ -11,7 +11,7 @@ sidebar:
function fclose(resource $stream): bool
```
-Lowers `fclose(stream)` after validating and unboxing the stream handle.
+Closes an open file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fdatasync.md b/docs/php/builtins/io/fdatasync.md
index 1f72778234..26b0b5562e 100644
--- a/docs/php/builtins/io/fdatasync.md
+++ b/docs/php/builtins/io/fdatasync.md
@@ -1,8 +1,8 @@
---
title: "fdatasync()"
-description: "Lowers `fdatasync(stream)` through the shared fd data-sync runtime helper."
+description: "Synchronizes data (but not meta-data) to file."
sidebar:
- order: 138
+ order: 155
---
## fdatasync()
@@ -11,7 +11,7 @@ sidebar:
function fdatasync(resource $stream): bool
```
-Lowers `fdatasync(stream)` through the shared fd data-sync runtime helper.
+Synchronizes data (but not meta-data) to file.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/feof.md b/docs/php/builtins/io/feof.md
index 6dd04bc93a..fcb60ca74e 100644
--- a/docs/php/builtins/io/feof.md
+++ b/docs/php/builtins/io/feof.md
@@ -1,8 +1,8 @@
---
title: "feof()"
-description: "Lowers `feof(stream)` through the runtime EOF-flag table helper."
+description: "Tests for end-of-file on a file pointer."
sidebar:
- order: 139
+ order: 156
---
## feof()
@@ -11,7 +11,7 @@ sidebar:
function feof(resource $stream): bool
```
-Lowers `feof(stream)` through the runtime EOF-flag table helper.
+Tests for end-of-file on a file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fflush.md b/docs/php/builtins/io/fflush.md
index 414bf77371..ba052577bc 100644
--- a/docs/php/builtins/io/fflush.md
+++ b/docs/php/builtins/io/fflush.md
@@ -1,8 +1,8 @@
---
title: "fflush()"
-description: "Lowers `fflush(stream)` through the shared fd flush runtime helper."
+description: "Flushes the output to a file."
sidebar:
- order: 140
+ order: 157
---
## fflush()
@@ -11,7 +11,7 @@ sidebar:
function fflush(resource $stream): bool
```
-Lowers `fflush(stream)` through the shared fd flush runtime helper.
+Flushes the output to a file.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fgetc.md b/docs/php/builtins/io/fgetc.md
index ac11449344..f69c10d508 100644
--- a/docs/php/builtins/io/fgetc.md
+++ b/docs/php/builtins/io/fgetc.md
@@ -1,8 +1,8 @@
---
title: "fgetc()"
-description: "Lowers `fgetc(stream)` and boxes the one-byte string or PHP false result."
+description: "Gets a character from the given file pointer."
sidebar:
- order: 141
+ order: 158
---
## fgetc()
@@ -11,7 +11,7 @@ sidebar:
function fgetc(resource $stream): mixed
```
-Lowers `fgetc(stream)` and boxes the one-byte string or PHP false result.
+Gets a character from the given file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fgetcsv.md b/docs/php/builtins/io/fgetcsv.md
index 84571aad5d..4baf3d3a40 100644
--- a/docs/php/builtins/io/fgetcsv.md
+++ b/docs/php/builtins/io/fgetcsv.md
@@ -1,24 +1,22 @@
---
title: "fgetcsv()"
-description: "Lowers `fgetcsv(stream, separator?, enclosure?)` through the CSV row runtime helper."
+description: "Gets line from file pointer and parse for CSV fields."
sidebar:
- order: 142
+ order: 159
---
## fgetcsv()
```php
-function fgetcsv(resource $stream, int $length, string $separator, string $enclosure, string $escape): array
+function fgetcsv(resource $stream, int $length = null, string $separator = ','): array
```
-Lowers `fgetcsv(stream, separator?, enclosure?)` through the CSV row runtime helper.
+Gets line from file pointer and parse for CSV fields.
**Parameters**:
- `$stream` (`resource`)
-- `$length` (`int`), optional
-- `$separator` (`string`), optional
-- `$enclosure` (`string`)
-- `$escape` (`string`)
+- `$length` (`int`), default `null`, optional
+- `$separator` (`string`), default `','`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/io/fgets.md b/docs/php/builtins/io/fgets.md
index c1132dafea..2150096f36 100644
--- a/docs/php/builtins/io/fgets.md
+++ b/docs/php/builtins/io/fgets.md
@@ -1,21 +1,20 @@
---
title: "fgets()"
-description: "Lowers `fgets(stream)` through the shared line-read runtime helper."
+description: "Gets line from file pointer."
sidebar:
- order: 143
+ order: 160
---
## fgets()
```php
-function fgets(resource $stream, int $length): mixed
+function fgets(resource $stream): mixed
```
-Lowers `fgets(stream)` through the shared line-read runtime helper.
+Gets line from file pointer.
**Parameters**:
- `$stream` (`resource`)
-- `$length` (`int`)
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/file.md b/docs/php/builtins/io/file.md
index 6ccbb4e83e..b6176accd9 100644
--- a/docs/php/builtins/io/file.md
+++ b/docs/php/builtins/io/file.md
@@ -1,22 +1,20 @@
---
title: "file()"
-description: "Lowers `file(path)` through the target-aware runtime line-array helper."
+description: "Reads an entire file into an array."
sidebar:
- order: 144
+ order: 161
---
## file()
```php
-function file(string $filename, int $flags, mixed $context): array
+function file(string $filename): array
```
-Lowers `file(path)` through the target-aware runtime line-array helper.
+Reads an entire file into an array.
**Parameters**:
- `$filename` (`string`)
-- `$flags` (`int`)
-- `$context` (`mixed`)
**Returns**: `array`
diff --git a/docs/php/builtins/io/file_get_contents.md b/docs/php/builtins/io/file_get_contents.md
index d85f744797..e8d8a6fc69 100644
--- a/docs/php/builtins/io/file_get_contents.md
+++ b/docs/php/builtins/io/file_get_contents.md
@@ -1,24 +1,20 @@
---
title: "file_get_contents()"
-description: "Lowers `file_get_contents(path)` and boxes the runtime string-or-false result."
+description: "Reads an entire file into a string."
sidebar:
- order: 145
+ order: 162
---
## file_get_contents()
```php
-function file_get_contents(string $filename, bool $use_include_path, mixed $context, int $offset, int $length): mixed
+function file_get_contents(string $filename): mixed
```
-Lowers `file_get_contents(path)` and boxes the runtime string-or-false result.
+Reads an entire file into a string.
**Parameters**:
- `$filename` (`string`)
-- `$use_include_path` (`bool`)
-- `$context` (`mixed`)
-- `$offset` (`int`)
-- `$length` (`int`)
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/file_put_contents.md b/docs/php/builtins/io/file_put_contents.md
index 12517fdf10..0cebb841a2 100644
--- a/docs/php/builtins/io/file_put_contents.md
+++ b/docs/php/builtins/io/file_put_contents.md
@@ -1,23 +1,21 @@
---
title: "file_put_contents()"
-description: "Lowers `file_put_contents(path, data)` through the target-aware runtime writer."
+description: "Writes data to a file."
sidebar:
- order: 146
+ order: 163
---
## file_put_contents()
```php
-function file_put_contents(string $filename, mixed $data, int $flags = 0, mixed $context = null): int
+function file_put_contents(string $filename, string $data): int
```
-Lowers `file_put_contents(path, data)` through the target-aware runtime writer.
+Writes data to a file.
**Parameters**:
- `$filename` (`string`)
-- `$data` (`mixed`)
-- `$flags` (`int`), default `0`, optional
-- `$context` (`mixed`), default `null`, optional
+- `$data` (`string`)
**Returns**: `int`
diff --git a/docs/php/builtins/io/flock.md b/docs/php/builtins/io/flock.md
index 8c3d14e075..efd46e23e8 100644
--- a/docs/php/builtins/io/flock.md
+++ b/docs/php/builtins/io/flock.md
@@ -1,22 +1,22 @@
---
title: "flock()"
-description: "Lowers `flock(stream, operation, would_block?)` through the libc flock wrapper."
+description: "Portable advisory file locking."
sidebar:
- order: 147
+ order: 164
---
## flock()
```php
-function flock(resource $stream, int $operation, bool $would_block): bool
+function flock(resource $stream, int $operation, bool $would_block = null): bool
```
-Lowers `flock(stream, operation, would_block?)` through the libc flock wrapper.
+Portable advisory file locking.
**Parameters**:
- `$stream` (`resource`)
- `$operation` (`int`)
-- `$would_block` (`bool`), passed by reference, optional
+- `$would_block` (`bool`), passed by reference, default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/io/fopen.md b/docs/php/builtins/io/fopen.md
index b43c87cc94..706973d49d 100644
--- a/docs/php/builtins/io/fopen.md
+++ b/docs/php/builtins/io/fopen.md
@@ -1,23 +1,23 @@
---
title: "fopen()"
-description: "Lowers `fopen(filename, mode)` and boxes stream resources or PHP false."
+description: "Opens file or URL."
sidebar:
- order: 148
+ order: 165
---
## fopen()
```php
-function fopen(string $filename, string $mode, bool $use_include_path, mixed $context): mixed
+function fopen(string $filename, string $mode, bool $use_include_path = false, mixed $context = null): mixed
```
-Lowers `fopen(filename, mode)` and boxes stream resources or PHP false.
+Opens file or URL.
**Parameters**:
- `$filename` (`string`)
- `$mode` (`string`)
-- `$use_include_path` (`bool`), optional
-- `$context` (`mixed`), optional
+- `$use_include_path` (`bool`), default `false`, optional
+- `$context` (`mixed`), default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/fpassthru.md b/docs/php/builtins/io/fpassthru.md
index d67b3d3d16..676aa59290 100644
--- a/docs/php/builtins/io/fpassthru.md
+++ b/docs/php/builtins/io/fpassthru.md
@@ -1,8 +1,8 @@
---
title: "fpassthru()"
-description: "Lowers `fpassthru(stream)` through the remaining-bytes stream runtime helper."
+description: "Output all remaining data on a file pointer."
sidebar:
- order: 149
+ order: 166
---
## fpassthru()
@@ -11,7 +11,7 @@ sidebar:
function fpassthru(resource $stream): int
```
-Lowers `fpassthru(stream)` through the remaining-bytes stream runtime helper.
+Output all remaining data on a file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fprintf.md b/docs/php/builtins/io/fprintf.md
index 772fc6064a..f9ceafb81e 100644
--- a/docs/php/builtins/io/fprintf.md
+++ b/docs/php/builtins/io/fprintf.md
@@ -1,8 +1,8 @@
---
title: "fprintf()"
-description: "Lowers `fprintf(stream, format, values...)` as `sprintf()` plus stream write."
+description: "Write a formatted string to a stream."
sidebar:
- order: 150
+ order: 167
---
## fprintf()
@@ -11,7 +11,7 @@ sidebar:
function fprintf(resource $stream, string $format, ...$values): int
```
-Lowers `fprintf(stream, format, values...)` as `sprintf()` plus stream write.
+Write a formatted string to a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fputcsv.md b/docs/php/builtins/io/fputcsv.md
index 25f7b6d898..3f004533f4 100644
--- a/docs/php/builtins/io/fputcsv.md
+++ b/docs/php/builtins/io/fputcsv.md
@@ -1,25 +1,23 @@
---
title: "fputcsv()"
-description: "Lowers `fputcsv(stream, fields, separator?, enclosure?)` for string arrays."
+description: "Format line as CSV and write to file pointer."
sidebar:
- order: 151
+ order: 168
---
## fputcsv()
```php
-function fputcsv(resource $stream, array $fields, string $separator = ',', string $enclosure = '"', string $escape = '\\', string $eol = '\n'): int
+function fputcsv(resource $stream, array $fields, string $separator = ',', string $enclosure = '"'): int
```
-Lowers `fputcsv(stream, fields, separator?, enclosure?)` for string arrays.
+Format line as CSV and write to file pointer.
**Parameters**:
- `$stream` (`resource`)
- `$fields` (`array`)
- `$separator` (`string`), default `','`, optional
- `$enclosure` (`string`), default `'"'`, optional
-- `$escape` (`string`), default `'\\'`, optional
-- `$eol` (`string`), default `'\n'`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/io/fread.md b/docs/php/builtins/io/fread.md
index 088ef4668b..17dbb2afc9 100644
--- a/docs/php/builtins/io/fread.md
+++ b/docs/php/builtins/io/fread.md
@@ -1,8 +1,8 @@
---
title: "fread()"
-description: "Lowers `fread(stream, length)` using the shared runtime file-read helper."
+description: "Binary-safe file read."
sidebar:
- order: 152
+ order: 169
---
## fread()
@@ -11,7 +11,7 @@ sidebar:
function fread(resource $stream, int $length): string
```
-Lowers `fread(stream, length)` using the shared runtime file-read helper.
+Binary-safe file read.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fscanf.md b/docs/php/builtins/io/fscanf.md
index c3214190fb..67d2cd9551 100644
--- a/docs/php/builtins/io/fscanf.md
+++ b/docs/php/builtins/io/fscanf.md
@@ -1,8 +1,8 @@
---
title: "fscanf()"
-description: "Lowers `fscanf(stream, format)` through `__rt_fgets` and `__rt_sscanf`."
+description: "Parses input from a file according to a format."
sidebar:
- order: 153
+ order: 170
---
## fscanf()
@@ -11,7 +11,7 @@ sidebar:
function fscanf(resource $stream, string $format, ...$vars): array
```
-Lowers `fscanf(stream, format)` through `__rt_fgets` and `__rt_sscanf`.
+Parses input from a file according to a format.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fseek.md b/docs/php/builtins/io/fseek.md
index 4780f460c0..66fea389be 100644
--- a/docs/php/builtins/io/fseek.md
+++ b/docs/php/builtins/io/fseek.md
@@ -1,22 +1,22 @@
---
title: "fseek()"
-description: "Lowers `fseek(stream, offset, whence?)` and clears EOF state on success."
+description: "Seeks on a file pointer."
sidebar:
- order: 154
+ order: 171
---
## fseek()
```php
-function fseek(resource $stream, int $offset, int $whence): int
+function fseek(resource $stream, int $offset, int $whence = 0): int
```
-Lowers `fseek(stream, offset, whence?)` and clears EOF state on success.
+Seeks on a file pointer.
**Parameters**:
- `$stream` (`resource`)
- `$offset` (`int`)
-- `$whence` (`int`), optional
+- `$whence` (`int`), default `0`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/io/fstat.md b/docs/php/builtins/io/fstat.md
index fc5f96b10d..673d0c0302 100644
--- a/docs/php/builtins/io/fstat.md
+++ b/docs/php/builtins/io/fstat.md
@@ -1,8 +1,8 @@
---
title: "fstat()"
-description: "Lowers `fstat(stream)` and boxes the runtime stat array or PHP false result."
+description: "Gets information about a file using an open file pointer."
sidebar:
- order: 155
+ order: 172
---
## fstat()
@@ -11,7 +11,7 @@ sidebar:
function fstat(resource $stream): mixed
```
-Lowers `fstat(stream)` and boxes the runtime stat array or PHP false result.
+Gets information about a file using an open file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fsync.md b/docs/php/builtins/io/fsync.md
index 2c9a709eb4..58a001a4b6 100644
--- a/docs/php/builtins/io/fsync.md
+++ b/docs/php/builtins/io/fsync.md
@@ -1,8 +1,8 @@
---
title: "fsync()"
-description: "Lowers `fsync(stream)` through the shared fd sync runtime helper."
+description: "Synchronizes changes to the file (including meta-data)."
sidebar:
- order: 156
+ order: 173
---
## fsync()
@@ -11,7 +11,7 @@ sidebar:
function fsync(resource $stream): bool
```
-Lowers `fsync(stream)` through the shared fd sync runtime helper.
+Synchronizes changes to the file (including meta-data).
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/ftell.md b/docs/php/builtins/io/ftell.md
index 8849fb75ff..5fc9fee511 100644
--- a/docs/php/builtins/io/ftell.md
+++ b/docs/php/builtins/io/ftell.md
@@ -1,8 +1,8 @@
---
title: "ftell()"
-description: "Lowers `ftell(stream)` as `lseek(fd, 0, SEEK_CUR)`."
+description: "Returns the current position of the file read/write pointer."
sidebar:
- order: 157
+ order: 174
---
## ftell()
@@ -11,7 +11,7 @@ sidebar:
function ftell(resource $stream): int
```
-Lowers `ftell(stream)` as `lseek(fd, 0, SEEK_CUR)`.
+Returns the current position of the file read/write pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/ftruncate.md b/docs/php/builtins/io/ftruncate.md
index 23e3799e0d..cfa0d10725 100644
--- a/docs/php/builtins/io/ftruncate.md
+++ b/docs/php/builtins/io/ftruncate.md
@@ -1,8 +1,8 @@
---
title: "ftruncate()"
-description: "Lowers `ftruncate(stream, size)` through the shared fd truncate runtime helper."
+description: "Truncates a file to a given length."
sidebar:
- order: 158
+ order: 175
---
## ftruncate()
@@ -11,7 +11,7 @@ sidebar:
function ftruncate(resource $stream, int $size): bool
```
-Lowers `ftruncate(stream, size)` through the shared fd truncate runtime helper.
+Truncates a file to a given length.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/fwrite.md b/docs/php/builtins/io/fwrite.md
index 94db0e707f..3edc50c485 100644
--- a/docs/php/builtins/io/fwrite.md
+++ b/docs/php/builtins/io/fwrite.md
@@ -1,22 +1,21 @@
---
title: "fwrite()"
-description: "Lowers `fwrite(stream, data)` and returns the number of bytes written."
+description: "Binary-safe file write."
sidebar:
- order: 159
+ order: 176
---
## fwrite()
```php
-function fwrite(resource $stream, string $data, int $length): int
+function fwrite(resource $stream, string $data): int
```
-Lowers `fwrite(stream, data)` and returns the number of bytes written.
+Binary-safe file write.
**Parameters**:
- `$stream` (`resource`)
- `$data` (`string`)
-- `$length` (`int`)
**Returns**: `int`
diff --git a/docs/php/builtins/io/gethostbyaddr.md b/docs/php/builtins/io/gethostbyaddr.md
index 3eeea88fcc..740bd536b8 100644
--- a/docs/php/builtins/io/gethostbyaddr.md
+++ b/docs/php/builtins/io/gethostbyaddr.md
@@ -1,8 +1,8 @@
---
title: "gethostbyaddr()"
-description: "Lowers `gethostbyaddr(address)` and boxes malformed addresses as PHP `false`."
+description: "Gets the Internet host name corresponding to a given IP address."
sidebar:
- order: 160
+ order: 177
---
## gethostbyaddr()
@@ -11,7 +11,7 @@ sidebar:
function gethostbyaddr(string $ip): mixed
```
-Lowers `gethostbyaddr(address)` and boxes malformed addresses as PHP `false`.
+Gets the Internet host name corresponding to a given IP address.
**Parameters**:
- `$ip` (`string`)
diff --git a/docs/php/builtins/io/gethostbyname.md b/docs/php/builtins/io/gethostbyname.md
index d457771454..669006764a 100644
--- a/docs/php/builtins/io/gethostbyname.md
+++ b/docs/php/builtins/io/gethostbyname.md
@@ -1,8 +1,8 @@
---
title: "gethostbyname()"
-description: "Lowers `gethostbyname(hostname)` through the shared runtime resolver."
+description: "Gets the IPv4 address corresponding to the given Internet host name."
sidebar:
- order: 161
+ order: 178
---
## gethostbyname()
@@ -11,7 +11,7 @@ sidebar:
function gethostbyname(string $hostname): string
```
-Lowers `gethostbyname(hostname)` through the shared runtime resolver.
+Gets the IPv4 address corresponding to the given Internet host name.
**Parameters**:
- `$hostname` (`string`)
diff --git a/docs/php/builtins/io/gethostname.md b/docs/php/builtins/io/gethostname.md
index dddd62481a..102c48204e 100644
--- a/docs/php/builtins/io/gethostname.md
+++ b/docs/php/builtins/io/gethostname.md
@@ -1,8 +1,8 @@
---
title: "gethostname()"
-description: "Lowers `gethostname()` through the shared runtime helper."
+description: "Gets the standard host name for the local machine."
sidebar:
- order: 162
+ order: 179
---
## gethostname()
@@ -11,7 +11,7 @@ sidebar:
function gethostname(): string
```
-Lowers `gethostname()` through the shared runtime helper.
+Gets the standard host name for the local machine.
**Parameters**: none.
diff --git a/docs/php/builtins/io/getprotobyname.md b/docs/php/builtins/io/getprotobyname.md
index 3efcd91b0a..bddcbae93a 100644
--- a/docs/php/builtins/io/getprotobyname.md
+++ b/docs/php/builtins/io/getprotobyname.md
@@ -1,8 +1,8 @@
---
title: "getprotobyname()"
-description: "Lowers `getprotobyname(protocol)` and boxes a missing entry as PHP `false`."
+description: "Gets the protocol number associated with the given protocol name."
sidebar:
- order: 163
+ order: 180
---
## getprotobyname()
@@ -11,7 +11,7 @@ sidebar:
function getprotobyname(string $protocol): mixed
```
-Lowers `getprotobyname(protocol)` and boxes a missing entry as PHP `false`.
+Gets the protocol number associated with the given protocol name.
**Parameters**:
- `$protocol` (`string`)
diff --git a/docs/php/builtins/io/getprotobynumber.md b/docs/php/builtins/io/getprotobynumber.md
index 0c5245c1e6..14de680e4a 100644
--- a/docs/php/builtins/io/getprotobynumber.md
+++ b/docs/php/builtins/io/getprotobynumber.md
@@ -1,8 +1,8 @@
---
title: "getprotobynumber()"
-description: "Lowers `getprotobynumber(number)` and boxes a missing entry as PHP `false`."
+description: "Gets the protocol name associated with the given protocol number."
sidebar:
- order: 164
+ order: 181
---
## getprotobynumber()
@@ -11,7 +11,7 @@ sidebar:
function getprotobynumber(int $protocol): mixed
```
-Lowers `getprotobynumber(number)` and boxes a missing entry as PHP `false`.
+Gets the protocol name associated with the given protocol number.
**Parameters**:
- `$protocol` (`int`)
diff --git a/docs/php/builtins/io/getservbyname.md b/docs/php/builtins/io/getservbyname.md
index 0a83def186..92114158d2 100644
--- a/docs/php/builtins/io/getservbyname.md
+++ b/docs/php/builtins/io/getservbyname.md
@@ -1,8 +1,8 @@
---
title: "getservbyname()"
-description: "Lowers `getservbyname(service, protocol)` and boxes a missing entry as PHP `false`."
+description: "Gets port number associated with an Internet service and protocol."
sidebar:
- order: 165
+ order: 182
---
## getservbyname()
@@ -11,7 +11,7 @@ sidebar:
function getservbyname(string $service, string $protocol): mixed
```
-Lowers `getservbyname(service, protocol)` and boxes a missing entry as PHP `false`.
+Gets port number associated with an Internet service and protocol.
**Parameters**:
- `$service` (`string`)
diff --git a/docs/php/builtins/io/getservbyport.md b/docs/php/builtins/io/getservbyport.md
index df13a6c953..ac10941245 100644
--- a/docs/php/builtins/io/getservbyport.md
+++ b/docs/php/builtins/io/getservbyport.md
@@ -1,8 +1,8 @@
---
title: "getservbyport()"
-description: "Lowers `getservbyport(port, protocol)` and boxes a missing entry as PHP `false`."
+description: "Gets the Internet service that corresponds to a port and protocol."
sidebar:
- order: 166
+ order: 183
---
## getservbyport()
@@ -11,7 +11,7 @@ sidebar:
function getservbyport(int $port, string $protocol): mixed
```
-Lowers `getservbyport(port, protocol)` and boxes a missing entry as PHP `false`.
+Gets the Internet service that corresponds to a port and protocol.
**Parameters**:
- `$port` (`int`)
diff --git a/docs/php/builtins/io/hash_file.md b/docs/php/builtins/io/hash_file.md
index 5bf645692f..7307465076 100644
--- a/docs/php/builtins/io/hash_file.md
+++ b/docs/php/builtins/io/hash_file.md
@@ -1,23 +1,22 @@
---
title: "hash_file()"
-description: "Lowers `hash_file(algo, filename, binary?)` by reading bytes then hashing them."
+description: "Generates a hash value using the contents of a given file."
sidebar:
- order: 167
+ order: 184
---
## hash_file()
```php
-function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): mixed
+function hash_file(string $algo, string $filename, bool $binary = false): mixed
```
-Lowers `hash_file(algo, filename, binary?)` by reading bytes then hashing them.
+Generates a hash value using the contents of a given file.
**Parameters**:
- `$algo` (`string`)
- `$filename` (`string`)
- `$binary` (`bool`), default `false`, optional
-- `$options` (`array`), default `[]`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/opendir.md b/docs/php/builtins/io/opendir.md
index 5526b7bc46..8666aaa8ec 100644
--- a/docs/php/builtins/io/opendir.md
+++ b/docs/php/builtins/io/opendir.md
@@ -1,8 +1,8 @@
---
title: "opendir()"
-description: "Lowers `opendir(path)` and boxes the directory stream as `resource|false`."
+description: "Open directory handle."
sidebar:
- order: 168
+ order: 185
---
## opendir()
@@ -11,7 +11,7 @@ sidebar:
function opendir(string $directory): mixed
```
-Lowers `opendir(path)` and boxes the directory stream as `resource|false`.
+Open directory handle.
**Parameters**:
- `$directory` (`string`)
diff --git a/docs/php/builtins/io/readdir.md b/docs/php/builtins/io/readdir.md
index de19712664..2626d69f9e 100644
--- a/docs/php/builtins/io/readdir.md
+++ b/docs/php/builtins/io/readdir.md
@@ -1,8 +1,8 @@
---
title: "readdir()"
-description: "Lowers `readdir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+description: "Read entry from directory handle."
sidebar:
- order: 169
+ order: 186
---
## readdir()
@@ -11,7 +11,7 @@ sidebar:
function readdir(resource $dir_handle): mixed
```
-Lowers `readdir(dir_handle)` for libc, glob, and userspace-wrapper handles.
+Read entry from directory handle.
**Parameters**:
- `$dir_handle` (`resource`)
diff --git a/docs/php/builtins/io/rewind.md b/docs/php/builtins/io/rewind.md
index 87d6688a8e..af56e86a73 100644
--- a/docs/php/builtins/io/rewind.md
+++ b/docs/php/builtins/io/rewind.md
@@ -1,8 +1,8 @@
---
title: "rewind()"
-description: "Lowers `rewind(stream)` as `lseek(fd, 0, SEEK_SET)` and clears EOF state on success."
+description: "Rewind the position of a file pointer."
sidebar:
- order: 170
+ order: 187
---
## rewind()
@@ -11,7 +11,7 @@ sidebar:
function rewind(resource $stream): bool
```
-Lowers `rewind(stream)` as `lseek(fd, 0, SEEK_SET)` and clears EOF state on success.
+Rewind the position of a file pointer.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/rewinddir.md b/docs/php/builtins/io/rewinddir.md
index 2c739dbeda..379f10be46 100644
--- a/docs/php/builtins/io/rewinddir.md
+++ b/docs/php/builtins/io/rewinddir.md
@@ -1,8 +1,8 @@
---
title: "rewinddir()"
-description: "Lowers `rewinddir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+description: "Rewind directory handle."
sidebar:
- order: 171
+ order: 188
---
## rewinddir()
@@ -11,7 +11,7 @@ sidebar:
function rewinddir(resource $dir_handle): void
```
-Lowers `rewinddir(dir_handle)` for libc, glob, and userspace-wrapper handles.
+Rewind directory handle.
**Parameters**:
- `$dir_handle` (`resource`)
diff --git a/docs/php/builtins/io/stream_bucket_make_writeable.md b/docs/php/builtins/io/stream_bucket_make_writeable.md
index e13379cb29..9fd73afea8 100644
--- a/docs/php/builtins/io/stream_bucket_make_writeable.md
+++ b/docs/php/builtins/io/stream_bucket_make_writeable.md
@@ -1,8 +1,8 @@
---
title: "stream_bucket_make_writeable()"
-description: "Lowers `stream_bucket_make_writeable(brigade)` by popping the brigade head."
+description: "Returns a bucket object from the brigade for use in a stream filter."
sidebar:
- order: 172
+ order: 189
---
## stream_bucket_make_writeable()
@@ -11,7 +11,7 @@ sidebar:
function stream_bucket_make_writeable(mixed $brigade): mixed
```
-Lowers `stream_bucket_make_writeable(brigade)` by popping the brigade head.
+Returns a bucket object from the brigade for use in a stream filter.
**Parameters**:
- `$brigade` (`mixed`)
diff --git a/docs/php/builtins/io/stream_bucket_new.md b/docs/php/builtins/io/stream_bucket_new.md
index dbd1a316fb..ebea411b29 100644
--- a/docs/php/builtins/io/stream_bucket_new.md
+++ b/docs/php/builtins/io/stream_bucket_new.md
@@ -1,8 +1,8 @@
---
title: "stream_bucket_new()"
-description: "Lowers `stream_bucket_new(stream, data)` into a stdClass-backed bucket object."
+description: "Creates a new bucket for use in a stream filter."
sidebar:
- order: 173
+ order: 190
---
## stream_bucket_new()
@@ -11,7 +11,7 @@ sidebar:
function stream_bucket_new(resource $stream, string $buffer): mixed
```
-Lowers `stream_bucket_new(stream, data)` into a stdClass-backed bucket object.
+Creates a new bucket for use in a stream filter.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_context_create.md b/docs/php/builtins/io/stream_context_create.md
index efcb84249b..7e556f6436 100644
--- a/docs/php/builtins/io/stream_context_create.md
+++ b/docs/php/builtins/io/stream_context_create.md
@@ -1,21 +1,21 @@
---
title: "stream_context_create()"
-description: "Lowers `stream_context_create(options?, params?)`."
+description: "Creates a stream context."
sidebar:
- order: 174
+ order: 191
---
## stream_context_create()
```php
-function stream_context_create(array $options, array $params): mixed
+function stream_context_create(array $options = null, array $params = null): mixed
```
-Lowers `stream_context_create(options?, params?)`.
+Creates a stream context.
**Parameters**:
-- `$options` (`array`), optional
-- `$params` (`array`), optional
+- `$options` (`array`), default `null`, optional
+- `$params` (`array`), default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_context_get_default.md b/docs/php/builtins/io/stream_context_get_default.md
index d1d8b4aec9..ad032c2764 100644
--- a/docs/php/builtins/io/stream_context_get_default.md
+++ b/docs/php/builtins/io/stream_context_get_default.md
@@ -1,20 +1,20 @@
---
title: "stream_context_get_default()"
-description: "Lowers `stream_context_get_default(options?)`."
+description: "Retrieves the default stream context."
sidebar:
- order: 175
+ order: 192
---
## stream_context_get_default()
```php
-function stream_context_get_default(array $options): mixed
+function stream_context_get_default(array $options = null): mixed
```
-Lowers `stream_context_get_default(options?)`.
+Retrieves the default stream context.
**Parameters**:
-- `$options` (`array`), optional
+- `$options` (`array`), default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_context_get_options.md b/docs/php/builtins/io/stream_context_get_options.md
index 6f29337abd..ffca97723d 100644
--- a/docs/php/builtins/io/stream_context_get_options.md
+++ b/docs/php/builtins/io/stream_context_get_options.md
@@ -1,20 +1,20 @@
---
title: "stream_context_get_options()"
-description: "Lowers `stream_context_get_options(context)`."
+description: "Retrieves options for the specified stream context."
sidebar:
- order: 176
+ order: 193
---
## stream_context_get_options()
```php
-function stream_context_get_options(resource $stream_or_context): array
+function stream_context_get_options(resource $context): array
```
-Lowers `stream_context_get_options(context)`.
+Retrieves options for the specified stream context.
**Parameters**:
-- `$stream_or_context` (`resource`)
+- `$context` (`resource`)
**Returns**: `array`
diff --git a/docs/php/builtins/io/stream_context_get_params.md b/docs/php/builtins/io/stream_context_get_params.md
index a5e110bdc9..836973a127 100644
--- a/docs/php/builtins/io/stream_context_get_params.md
+++ b/docs/php/builtins/io/stream_context_get_params.md
@@ -1,8 +1,8 @@
---
title: "stream_context_get_params()"
-description: "Lowers `stream_context_get_params(context)` to an empty associative hash."
+description: "Retrieves parameters from the specified stream context."
sidebar:
- order: 177
+ order: 194
---
## stream_context_get_params()
@@ -11,7 +11,7 @@ sidebar:
function stream_context_get_params(resource $context): array
```
-Lowers `stream_context_get_params(context)` to an empty associative hash.
+Retrieves parameters from the specified stream context.
**Parameters**:
- `$context` (`resource`)
diff --git a/docs/php/builtins/io/stream_context_set_default.md b/docs/php/builtins/io/stream_context_set_default.md
index cec4427094..afab538c14 100644
--- a/docs/php/builtins/io/stream_context_set_default.md
+++ b/docs/php/builtins/io/stream_context_set_default.md
@@ -1,8 +1,8 @@
---
title: "stream_context_set_default()"
-description: "Lowers `stream_context_set_default(options)`."
+description: "Sets the default stream context."
sidebar:
- order: 178
+ order: 195
---
## stream_context_set_default()
@@ -11,7 +11,7 @@ sidebar:
function stream_context_set_default(array $options): mixed
```
-Lowers `stream_context_set_default(options)`.
+Sets the default stream context.
**Parameters**:
- `$options` (`array`)
diff --git a/docs/php/builtins/io/stream_context_set_option.md b/docs/php/builtins/io/stream_context_set_option.md
index 9c1e26475c..2178ce7484 100644
--- a/docs/php/builtins/io/stream_context_set_option.md
+++ b/docs/php/builtins/io/stream_context_set_option.md
@@ -1,23 +1,23 @@
---
title: "stream_context_set_option()"
-description: "Lowers `stream_context_set_option(context, options)` and the four-argument form."
+description: "Sets an option on the specified context."
sidebar:
- order: 179
+ order: 196
---
## stream_context_set_option()
```php
-function stream_context_set_option(resource $context, string $wrapper_or_options, string $option_name, mixed $value): bool
+function stream_context_set_option(resource $context, string $wrapper_or_options, string $option_name = null, mixed $value = null): bool
```
-Lowers `stream_context_set_option(context, options)` and the four-argument form.
+Sets an option on the specified context.
**Parameters**:
- `$context` (`resource`)
- `$wrapper_or_options` (`string`)
-- `$option_name` (`string`), optional
-- `$value` (`mixed`), optional
+- `$option_name` (`string`), default `null`, optional
+- `$value` (`mixed`), default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/io/stream_context_set_params.md b/docs/php/builtins/io/stream_context_set_params.md
index 83c60a1acf..da7dcc12fa 100644
--- a/docs/php/builtins/io/stream_context_set_params.md
+++ b/docs/php/builtins/io/stream_context_set_params.md
@@ -1,8 +1,8 @@
---
title: "stream_context_set_params()"
-description: "Lowers `stream_context_set_params(context, params)` as an accepted parameter update."
+description: "Sets parameters on the specified context."
sidebar:
- order: 180
+ order: 197
---
## stream_context_set_params()
@@ -11,7 +11,7 @@ sidebar:
function stream_context_set_params(resource $context, array $params): bool
```
-Lowers `stream_context_set_params(context, params)` as an accepted parameter update.
+Sets parameters on the specified context.
**Parameters**:
- `$context` (`resource`)
diff --git a/docs/php/builtins/io/stream_copy_to_stream.md b/docs/php/builtins/io/stream_copy_to_stream.md
index f1002112db..1083006e1d 100644
--- a/docs/php/builtins/io/stream_copy_to_stream.md
+++ b/docs/php/builtins/io/stream_copy_to_stream.md
@@ -1,23 +1,23 @@
---
title: "stream_copy_to_stream()"
-description: "Lowers `stream_copy_to_stream(from, to, length?, offset?)` through wrapper-aware read/write loops."
+description: "Copies data from one stream to another."
sidebar:
- order: 181
+ order: 198
---
## stream_copy_to_stream()
```php
-function stream_copy_to_stream(resource $from, resource $to, int $length, int $offset): mixed
+function stream_copy_to_stream(resource $from, resource $to, int $length = null, int $offset = -1): mixed
```
-Lowers `stream_copy_to_stream(from, to, length?, offset?)` through wrapper-aware read/write loops.
+Copies data from one stream to another.
**Parameters**:
- `$from` (`resource`)
- `$to` (`resource`)
-- `$length` (`int`), optional
-- `$offset` (`int`), optional
+- `$length` (`int`), default `null`, optional
+- `$offset` (`int`), default `-1`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_filter_register.md b/docs/php/builtins/io/stream_filter_register.md
index 9b0cda282b..055527fdcc 100644
--- a/docs/php/builtins/io/stream_filter_register.md
+++ b/docs/php/builtins/io/stream_filter_register.md
@@ -1,8 +1,8 @@
---
title: "stream_filter_register()"
-description: "Lowers `stream_filter_register(filter_name, class)` into the user-filter registry helper."
+description: "Registers a user-defined stream filter."
sidebar:
- order: 182
+ order: 199
---
## stream_filter_register()
@@ -11,7 +11,7 @@ sidebar:
function stream_filter_register(string $filter_name, string $class): bool
```
-Lowers `stream_filter_register(filter_name, class)` into the user-filter registry helper.
+Registers a user-defined stream filter.
**Parameters**:
- `$filter_name` (`string`)
diff --git a/docs/php/builtins/io/stream_filter_remove.md b/docs/php/builtins/io/stream_filter_remove.md
index 308997b86e..b03bbe6b8a 100644
--- a/docs/php/builtins/io/stream_filter_remove.md
+++ b/docs/php/builtins/io/stream_filter_remove.md
@@ -1,8 +1,8 @@
---
title: "stream_filter_remove()"
-description: "Lowers `stream_filter_remove(filter)` and clears both direction tables for the fd."
+description: "Removes a filter from a stream."
sidebar:
- order: 183
+ order: 200
---
## stream_filter_remove()
@@ -11,7 +11,7 @@ sidebar:
function stream_filter_remove(resource $stream_filter): bool
```
-Lowers `stream_filter_remove(filter)` and clears both direction tables for the fd.
+Removes a filter from a stream.
**Parameters**:
- `$stream_filter` (`resource`)
diff --git a/docs/php/builtins/io/stream_get_contents.md b/docs/php/builtins/io/stream_get_contents.md
index 16ae4fe20a..6e41982c9a 100644
--- a/docs/php/builtins/io/stream_get_contents.md
+++ b/docs/php/builtins/io/stream_get_contents.md
@@ -1,22 +1,22 @@
---
title: "stream_get_contents()"
-description: "Lowers `stream_get_contents(stream, length?, offset?)` to `string|false`."
+description: "Reads remainder of a stream into a string."
sidebar:
- order: 184
+ order: 201
---
## stream_get_contents()
```php
-function stream_get_contents(resource $stream, int $length, int $offset): mixed
+function stream_get_contents(resource $stream, int $length = null, int $offset = -1): mixed
```
-Lowers `stream_get_contents(stream, length?, offset?)` to `string|false`.
+Reads remainder of a stream into a string.
**Parameters**:
- `$stream` (`resource`)
-- `$length` (`int`), optional
-- `$offset` (`int`), optional
+- `$length` (`int`), default `null`, optional
+- `$offset` (`int`), default `-1`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_get_filters.md b/docs/php/builtins/io/stream_get_filters.md
index fd5819f508..813204a4ec 100644
--- a/docs/php/builtins/io/stream_get_filters.md
+++ b/docs/php/builtins/io/stream_get_filters.md
@@ -1,8 +1,8 @@
---
title: "stream_get_filters()"
-description: "Lowers `stream_get_filters()` to the static built-in filter list."
+description: "Retrieves list of registered filters."
sidebar:
- order: 185
+ order: 202
---
## stream_get_filters()
@@ -11,7 +11,7 @@ sidebar:
function stream_get_filters(): array
```
-Lowers `stream_get_filters()` to the static built-in filter list.
+Retrieves list of registered filters.
**Parameters**: none.
diff --git a/docs/php/builtins/io/stream_get_line.md b/docs/php/builtins/io/stream_get_line.md
index 7e326c4399..ed332ef462 100644
--- a/docs/php/builtins/io/stream_get_line.md
+++ b/docs/php/builtins/io/stream_get_line.md
@@ -1,22 +1,22 @@
---
title: "stream_get_line()"
-description: "Lowers `stream_get_line(stream, length, ending?)`."
+description: "Gets line from stream resource up to a given delimiter."
sidebar:
- order: 186
+ order: 203
---
## stream_get_line()
```php
-function stream_get_line(resource $stream, int $length, string $ending): string
+function stream_get_line(resource $stream, int $length, string $ending = ''): string
```
-Lowers `stream_get_line(stream, length, ending?)`.
+Gets line from stream resource up to a given delimiter.
**Parameters**:
- `$stream` (`resource`)
- `$length` (`int`)
-- `$ending` (`string`), optional
+- `$ending` (`string`), default `''`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/io/stream_get_meta_data.md b/docs/php/builtins/io/stream_get_meta_data.md
index f1f5328891..4f8a0c5105 100644
--- a/docs/php/builtins/io/stream_get_meta_data.md
+++ b/docs/php/builtins/io/stream_get_meta_data.md
@@ -1,8 +1,8 @@
---
title: "stream_get_meta_data()"
-description: "Lowers `stream_get_meta_data(stream)` through the metadata runtime helper."
+description: "Retrieves metadata from streams/file pointers."
sidebar:
- order: 187
+ order: 204
---
## stream_get_meta_data()
@@ -11,7 +11,7 @@ sidebar:
function stream_get_meta_data(resource $stream): array
```
-Lowers `stream_get_meta_data(stream)` through the metadata runtime helper.
+Retrieves metadata from streams/file pointers.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_get_transports.md b/docs/php/builtins/io/stream_get_transports.md
index 5c1530b975..3e306c33b7 100644
--- a/docs/php/builtins/io/stream_get_transports.md
+++ b/docs/php/builtins/io/stream_get_transports.md
@@ -1,8 +1,8 @@
---
title: "stream_get_transports()"
-description: "Lowers `stream_get_transports()` to the static transport list."
+description: "Retrieves list of registered socket transports."
sidebar:
- order: 188
+ order: 205
---
## stream_get_transports()
@@ -11,7 +11,7 @@ sidebar:
function stream_get_transports(): array
```
-Lowers `stream_get_transports()` to the static transport list.
+Retrieves list of registered socket transports.
**Parameters**: none.
diff --git a/docs/php/builtins/io/stream_get_wrappers.md b/docs/php/builtins/io/stream_get_wrappers.md
index b70a0382c0..c87abb259c 100644
--- a/docs/php/builtins/io/stream_get_wrappers.md
+++ b/docs/php/builtins/io/stream_get_wrappers.md
@@ -1,8 +1,8 @@
---
title: "stream_get_wrappers()"
-description: "Lowers `stream_get_wrappers()` to the static built-in wrapper list."
+description: "Retrieves list of registered streams."
sidebar:
- order: 189
+ order: 206
---
## stream_get_wrappers()
@@ -11,7 +11,7 @@ sidebar:
function stream_get_wrappers(): array
```
-Lowers `stream_get_wrappers()` to the static built-in wrapper list.
+Retrieves list of registered streams.
**Parameters**: none.
diff --git a/docs/php/builtins/io/stream_is_local.md b/docs/php/builtins/io/stream_is_local.md
index 8a44541102..b30b1f8b69 100644
--- a/docs/php/builtins/io/stream_is_local.md
+++ b/docs/php/builtins/io/stream_is_local.md
@@ -1,8 +1,8 @@
---
title: "stream_is_local()"
-description: "Lowers `stream_is_local(stream)` as a true predicate after evaluating its argument."
+description: "Checks if a stream is a local stream."
sidebar:
- order: 190
+ order: 207
---
## stream_is_local()
@@ -11,7 +11,7 @@ sidebar:
function stream_is_local(resource $stream): bool
```
-Lowers `stream_is_local(stream)` as a true predicate after evaluating its argument.
+Checks if a stream is a local stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_isatty.md b/docs/php/builtins/io/stream_isatty.md
index 7db33f239a..59e169fb0a 100644
--- a/docs/php/builtins/io/stream_isatty.md
+++ b/docs/php/builtins/io/stream_isatty.md
@@ -1,8 +1,8 @@
---
title: "stream_isatty()"
-description: "Lowers `stream_isatty(stream)`."
+description: "Checks if a stream is a TTY."
sidebar:
- order: 191
+ order: 208
---
## stream_isatty()
@@ -11,7 +11,7 @@ sidebar:
function stream_isatty(resource $stream): bool
```
-Lowers `stream_isatty(stream)`.
+Checks if a stream is a TTY.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_resolve_include_path.md b/docs/php/builtins/io/stream_resolve_include_path.md
index 65e716d700..079a7c35b6 100644
--- a/docs/php/builtins/io/stream_resolve_include_path.md
+++ b/docs/php/builtins/io/stream_resolve_include_path.md
@@ -1,8 +1,8 @@
---
title: "stream_resolve_include_path()"
-description: "Lowers `stream_resolve_include_path(filename)` as realpath-backed `string|false`."
+description: "Resolves filename against the include path."
sidebar:
- order: 192
+ order: 209
---
## stream_resolve_include_path()
@@ -11,7 +11,7 @@ sidebar:
function stream_resolve_include_path(string $filename): mixed
```
-Lowers `stream_resolve_include_path(filename)` as realpath-backed `string|false`.
+Resolves filename against the include path.
**Parameters**:
- `$filename` (`string`)
diff --git a/docs/php/builtins/io/stream_select.md b/docs/php/builtins/io/stream_select.md
index 7663f34ee2..b5681c50ab 100644
--- a/docs/php/builtins/io/stream_select.md
+++ b/docs/php/builtins/io/stream_select.md
@@ -1,24 +1,24 @@
---
title: "stream_select()"
-description: "Lowers `stream_select(read, write, except, seconds, microseconds?)`."
+description: "Runs the equivalent of the select() system call on the given arrays of streams."
sidebar:
- order: 193
+ order: 210
---
## stream_select()
```php
-function stream_select(array $read, array $write, array $except, int $seconds, int $microseconds): int
+function stream_select(array $read, array $write, array $except, int $seconds, int $microseconds = 0): int
```
-Lowers `stream_select(read, write, except, seconds, microseconds?)`.
+Runs the equivalent of the select() system call on the given arrays of streams.
**Parameters**:
- `$read` (`array`), passed by reference
- `$write` (`array`), passed by reference
- `$except` (`array`), passed by reference
- `$seconds` (`int`)
-- `$microseconds` (`int`), optional
+- `$microseconds` (`int`), default `0`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/io/stream_set_blocking.md b/docs/php/builtins/io/stream_set_blocking.md
index 726641298a..37866fddaf 100644
--- a/docs/php/builtins/io/stream_set_blocking.md
+++ b/docs/php/builtins/io/stream_set_blocking.md
@@ -1,8 +1,8 @@
---
title: "stream_set_blocking()"
-description: "Lowers `stream_set_blocking(stream, enable)`."
+description: "Sets blocking/non-blocking mode on a stream."
sidebar:
- order: 194
+ order: 211
---
## stream_set_blocking()
@@ -11,7 +11,7 @@ sidebar:
function stream_set_blocking(resource $stream, bool $enable): bool
```
-Lowers `stream_set_blocking(stream, enable)`.
+Sets blocking/non-blocking mode on a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_set_chunk_size.md b/docs/php/builtins/io/stream_set_chunk_size.md
index 2d75b46495..1a8db790b5 100644
--- a/docs/php/builtins/io/stream_set_chunk_size.md
+++ b/docs/php/builtins/io/stream_set_chunk_size.md
@@ -1,8 +1,8 @@
---
title: "stream_set_chunk_size()"
-description: "Lowers `stream_set_chunk_size(stream, size)` and returns the previous size."
+description: "Sets the read chunk size on a stream."
sidebar:
- order: 195
+ order: 212
---
## stream_set_chunk_size()
@@ -11,7 +11,7 @@ sidebar:
function stream_set_chunk_size(resource $stream, int $size): int
```
-Lowers `stream_set_chunk_size(stream, size)` and returns the previous size.
+Sets the read chunk size on a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_set_read_buffer.md b/docs/php/builtins/io/stream_set_read_buffer.md
index ee518b9b45..fcbfcaa434 100644
--- a/docs/php/builtins/io/stream_set_read_buffer.md
+++ b/docs/php/builtins/io/stream_set_read_buffer.md
@@ -1,8 +1,8 @@
---
title: "stream_set_read_buffer()"
-description: "Lowers stream read/write buffer setters as successful no-ops."
+description: "Sets the read file buffering on a stream."
sidebar:
- order: 196
+ order: 213
---
## stream_set_read_buffer()
@@ -11,7 +11,7 @@ sidebar:
function stream_set_read_buffer(resource $stream, int $size): int
```
-Lowers stream read/write buffer setters as successful no-ops.
+Sets the read file buffering on a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_set_timeout.md b/docs/php/builtins/io/stream_set_timeout.md
index d508b651c0..990624b8c6 100644
--- a/docs/php/builtins/io/stream_set_timeout.md
+++ b/docs/php/builtins/io/stream_set_timeout.md
@@ -1,22 +1,22 @@
---
title: "stream_set_timeout()"
-description: "Lowers `stream_set_timeout(stream, seconds, microseconds?)`."
+description: "Sets timeout period on a stream."
sidebar:
- order: 197
+ order: 214
---
## stream_set_timeout()
```php
-function stream_set_timeout(resource $stream, int $seconds, int $microseconds): bool
+function stream_set_timeout(resource $stream, int $seconds, int $microseconds = 0): bool
```
-Lowers `stream_set_timeout(stream, seconds, microseconds?)`.
+Sets timeout period on a stream.
**Parameters**:
- `$stream` (`resource`)
- `$seconds` (`int`)
-- `$microseconds` (`int`), optional
+- `$microseconds` (`int`), default `0`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/io/stream_set_write_buffer.md b/docs/php/builtins/io/stream_set_write_buffer.md
index 4f1f556c3e..3ffbc69c95 100644
--- a/docs/php/builtins/io/stream_set_write_buffer.md
+++ b/docs/php/builtins/io/stream_set_write_buffer.md
@@ -1,8 +1,8 @@
---
title: "stream_set_write_buffer()"
-description: "Lowers stream read/write buffer setters as successful no-ops."
+description: "Sets the write file buffering on a stream."
sidebar:
- order: 198
+ order: 215
---
## stream_set_write_buffer()
@@ -11,7 +11,7 @@ sidebar:
function stream_set_write_buffer(resource $stream, int $size): int
```
-Lowers stream read/write buffer setters as successful no-ops.
+Sets the write file buffering on a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_socket_accept.md b/docs/php/builtins/io/stream_socket_accept.md
index d5f25d99b5..4f49662b6e 100644
--- a/docs/php/builtins/io/stream_socket_accept.md
+++ b/docs/php/builtins/io/stream_socket_accept.md
@@ -1,22 +1,22 @@
---
title: "stream_socket_accept()"
-description: "Lowers `stream_socket_accept(server, timeout?, peer_name?)`."
+description: "Accept a connection on a socket created by stream_socket_server()."
sidebar:
- order: 199
+ order: 216
---
## stream_socket_accept()
```php
-function stream_socket_accept(resource $socket, float $timeout, string $peer_name): mixed
+function stream_socket_accept(resource $socket, float $timeout = null, string $peer_name = null): mixed
```
-Lowers `stream_socket_accept(server, timeout?, peer_name?)`.
+Accept a connection on a socket created by stream_socket_server().
**Parameters**:
- `$socket` (`resource`)
-- `$timeout` (`float`), optional
-- `$peer_name` (`string`), passed by reference, optional
+- `$timeout` (`float`), default `null`, optional
+- `$peer_name` (`string`), passed by reference, default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_socket_client.md b/docs/php/builtins/io/stream_socket_client.md
index 5410d21fc0..d8133a2e64 100644
--- a/docs/php/builtins/io/stream_socket_client.md
+++ b/docs/php/builtins/io/stream_socket_client.md
@@ -1,24 +1,20 @@
---
title: "stream_socket_client()"
-description: "Lowers `stream_socket_client(address)` and records the connected host for TLS defaults."
+description: "Open Internet or Unix domain socket connection."
sidebar:
- order: 200
+ order: 217
---
## stream_socket_client()
```php
-function stream_socket_client(string $address, int $error_code, int $error_message, string $timeout, float $flags): mixed
+function stream_socket_client(string $address): mixed
```
-Lowers `stream_socket_client(address)` and records the connected host for TLS defaults.
+Open Internet or Unix domain socket connection.
**Parameters**:
- `$address` (`string`)
-- `$error_code` (`int`), passed by reference
-- `$error_message` (`int`), passed by reference
-- `$timeout` (`string`)
-- `$flags` (`float`)
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_socket_enable_crypto.md b/docs/php/builtins/io/stream_socket_enable_crypto.md
index a78f082e5c..c3a720b3b4 100644
--- a/docs/php/builtins/io/stream_socket_enable_crypto.md
+++ b/docs/php/builtins/io/stream_socket_enable_crypto.md
@@ -1,23 +1,23 @@
---
title: "stream_socket_enable_crypto()"
-description: "Lowers `stream_socket_enable_crypto(stream, enable, method?, session_stream?)`."
+description: "Turns encryption on/off on an already connected socket."
sidebar:
- order: 201
+ order: 218
---
## stream_socket_enable_crypto()
```php
-function stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto_method, resource $session_stream): bool
+function stream_socket_enable_crypto(resource $stream, bool $enable, int $crypto_method = null, resource $session_stream = null): bool
```
-Lowers `stream_socket_enable_crypto(stream, enable, method?, session_stream?)`.
+Turns encryption on/off on an already connected socket.
**Parameters**:
- `$stream` (`resource`)
- `$enable` (`bool`)
-- `$crypto_method` (`int`), optional
-- `$session_stream` (`resource`), optional
+- `$crypto_method` (`int`), default `null`, optional
+- `$session_stream` (`resource`), default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/io/stream_socket_get_name.md b/docs/php/builtins/io/stream_socket_get_name.md
index 2e47f8e0b5..66a9f9833d 100644
--- a/docs/php/builtins/io/stream_socket_get_name.md
+++ b/docs/php/builtins/io/stream_socket_get_name.md
@@ -1,8 +1,8 @@
---
title: "stream_socket_get_name()"
-description: "Lowers `stream_socket_get_name(socket, remote)` and boxes `string|false`."
+description: "Retrieve the name of the local or remote sockets."
sidebar:
- order: 202
+ order: 219
---
## stream_socket_get_name()
@@ -11,7 +11,7 @@ sidebar:
function stream_socket_get_name(resource $socket, bool $remote): mixed
```
-Lowers `stream_socket_get_name(socket, remote)` and boxes `string|false`.
+Retrieve the name of the local or remote sockets.
**Parameters**:
- `$socket` (`resource`)
diff --git a/docs/php/builtins/io/stream_socket_pair.md b/docs/php/builtins/io/stream_socket_pair.md
index 4d0ab9aef7..678755e505 100644
--- a/docs/php/builtins/io/stream_socket_pair.md
+++ b/docs/php/builtins/io/stream_socket_pair.md
@@ -1,8 +1,8 @@
---
title: "stream_socket_pair()"
-description: "Lowers `stream_socket_pair(domain, type, protocol)` and boxes `array|false`."
+description: "Creates a pair of connected, indistinguishable socket streams."
sidebar:
- order: 203
+ order: 220
---
## stream_socket_pair()
@@ -11,7 +11,7 @@ sidebar:
function stream_socket_pair(int $domain, int $type, int $protocol): mixed
```
-Lowers `stream_socket_pair(domain, type, protocol)` and boxes `array|false`.
+Creates a pair of connected, indistinguishable socket streams.
**Parameters**:
- `$domain` (`int`)
diff --git a/docs/php/builtins/io/stream_socket_recvfrom.md b/docs/php/builtins/io/stream_socket_recvfrom.md
index 89c8b5ce5e..3b549eae0e 100644
--- a/docs/php/builtins/io/stream_socket_recvfrom.md
+++ b/docs/php/builtins/io/stream_socket_recvfrom.md
@@ -1,23 +1,23 @@
---
title: "stream_socket_recvfrom()"
-description: "Lowers `stream_socket_recvfrom(socket, length, flags?, address?)`."
+description: "Receives data from a socket, connected or not."
sidebar:
- order: 204
+ order: 221
---
## stream_socket_recvfrom()
```php
-function stream_socket_recvfrom(resource $socket, int $length, int $flags, string $address): mixed
+function stream_socket_recvfrom(resource $socket, int $length, int $flags = 0, string $address = ''): mixed
```
-Lowers `stream_socket_recvfrom(socket, length, flags?, address?)`.
+Receives data from a socket, connected or not.
**Parameters**:
- `$socket` (`resource`)
- `$length` (`int`)
-- `$flags` (`int`), optional
-- `$address` (`string`), passed by reference, optional
+- `$flags` (`int`), default `0`, optional
+- `$address` (`string`), passed by reference, default `''`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_socket_sendto.md b/docs/php/builtins/io/stream_socket_sendto.md
index 03d3206d1f..2ae31eb084 100644
--- a/docs/php/builtins/io/stream_socket_sendto.md
+++ b/docs/php/builtins/io/stream_socket_sendto.md
@@ -1,23 +1,23 @@
---
title: "stream_socket_sendto()"
-description: "Lowers `stream_socket_sendto(socket, data, flags?, address?)` and boxes `int|false`."
+description: "Sends a message to a socket, whether it is connected or not."
sidebar:
- order: 205
+ order: 222
---
## stream_socket_sendto()
```php
-function stream_socket_sendto(resource $socket, string $data, int $flags, string $address): mixed
+function stream_socket_sendto(resource $socket, string $data, int $flags = 0, string $address = ''): mixed
```
-Lowers `stream_socket_sendto(socket, data, flags?, address?)` and boxes `int|false`.
+Sends a message to a socket, whether it is connected or not.
**Parameters**:
- `$socket` (`resource`)
- `$data` (`string`)
-- `$flags` (`int`), optional
-- `$address` (`string`), optional
+- `$flags` (`int`), default `0`, optional
+- `$address` (`string`), default `''`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_socket_server.md b/docs/php/builtins/io/stream_socket_server.md
index 6934314d72..658e6bb5c5 100644
--- a/docs/php/builtins/io/stream_socket_server.md
+++ b/docs/php/builtins/io/stream_socket_server.md
@@ -1,22 +1,20 @@
---
title: "stream_socket_server()"
-description: "Lowers `stream_socket_server(address)` and boxes `resource|false`."
+description: "Create an Internet or Unix domain server socket."
sidebar:
- order: 206
+ order: 223
---
## stream_socket_server()
```php
-function stream_socket_server(string $address, int $error_code, int $error_message): mixed
+function stream_socket_server(string $address): mixed
```
-Lowers `stream_socket_server(address)` and boxes `resource|false`.
+Create an Internet or Unix domain server socket.
**Parameters**:
- `$address` (`string`)
-- `$error_code` (`int`), passed by reference
-- `$error_message` (`int`), passed by reference
**Returns**: `mixed`
diff --git a/docs/php/builtins/io/stream_socket_shutdown.md b/docs/php/builtins/io/stream_socket_shutdown.md
index e76b0b7c36..e36aaa1fe5 100644
--- a/docs/php/builtins/io/stream_socket_shutdown.md
+++ b/docs/php/builtins/io/stream_socket_shutdown.md
@@ -1,8 +1,8 @@
---
title: "stream_socket_shutdown()"
-description: "Lowers `stream_socket_shutdown(stream, mode)`."
+description: "Shutdown a full-duplex connection."
sidebar:
- order: 207
+ order: 224
---
## stream_socket_shutdown()
@@ -11,7 +11,7 @@ sidebar:
function stream_socket_shutdown(resource $stream, int $mode): bool
```
-Lowers `stream_socket_shutdown(stream, mode)`.
+Shutdown a full-duplex connection.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_supports_lock.md b/docs/php/builtins/io/stream_supports_lock.md
index a42ba91025..0cde57ca47 100644
--- a/docs/php/builtins/io/stream_supports_lock.md
+++ b/docs/php/builtins/io/stream_supports_lock.md
@@ -1,8 +1,8 @@
---
title: "stream_supports_lock()"
-description: "Lowers `stream_supports_lock(stream)` as true after resource unboxing."
+description: "Tells whether the stream supports locking."
sidebar:
- order: 208
+ order: 225
---
## stream_supports_lock()
@@ -11,7 +11,7 @@ sidebar:
function stream_supports_lock(resource $stream): bool
```
-Lowers `stream_supports_lock(stream)` as true after resource unboxing.
+Tells whether the stream supports locking.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/io/stream_wrapper_register.md b/docs/php/builtins/io/stream_wrapper_register.md
index 571c5ba82b..b1eb6e6363 100644
--- a/docs/php/builtins/io/stream_wrapper_register.md
+++ b/docs/php/builtins/io/stream_wrapper_register.md
@@ -1,22 +1,22 @@
---
title: "stream_wrapper_register()"
-description: "Lowers `stream_wrapper_register(protocol, class, flags?)`."
+description: "Registers a URL wrapper implemented as a PHP class."
sidebar:
- order: 209
+ order: 226
---
## stream_wrapper_register()
```php
-function stream_wrapper_register(string $protocol, string $class, int $flags): bool
+function stream_wrapper_register(string $protocol, string $class, int $flags = 0): bool
```
-Lowers `stream_wrapper_register(protocol, class, flags?)`.
+Registers a URL wrapper implemented as a PHP class.
**Parameters**:
- `$protocol` (`string`)
- `$class` (`string`)
-- `$flags` (`int`), optional
+- `$flags` (`int`), default `0`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/io/stream_wrapper_restore.md b/docs/php/builtins/io/stream_wrapper_restore.md
index 800c5494e5..b3909c2564 100644
--- a/docs/php/builtins/io/stream_wrapper_restore.md
+++ b/docs/php/builtins/io/stream_wrapper_restore.md
@@ -1,8 +1,8 @@
---
title: "stream_wrapper_restore()"
-description: "Lowers `stream_wrapper_restore(protocol)` as a successful no-op."
+description: "Restores a previously unregistered built-in wrapper."
sidebar:
- order: 210
+ order: 227
---
## stream_wrapper_restore()
@@ -11,7 +11,7 @@ sidebar:
function stream_wrapper_restore(string $protocol): bool
```
-Lowers `stream_wrapper_restore(protocol)` as a successful no-op.
+Restores a previously unregistered built-in wrapper.
**Parameters**:
- `$protocol` (`string`)
diff --git a/docs/php/builtins/io/stream_wrapper_unregister.md b/docs/php/builtins/io/stream_wrapper_unregister.md
index 979b724015..0b96c1b0ea 100644
--- a/docs/php/builtins/io/stream_wrapper_unregister.md
+++ b/docs/php/builtins/io/stream_wrapper_unregister.md
@@ -1,8 +1,8 @@
---
title: "stream_wrapper_unregister()"
-description: "Lowers `stream_wrapper_unregister(protocol)`."
+description: "Unregisters a previously registered URL wrapper."
sidebar:
- order: 211
+ order: 228
---
## stream_wrapper_unregister()
@@ -11,7 +11,7 @@ sidebar:
function stream_wrapper_unregister(string $protocol): bool
```
-Lowers `stream_wrapper_unregister(protocol)`.
+Unregisters a previously registered URL wrapper.
**Parameters**:
- `$protocol` (`string`)
diff --git a/docs/php/builtins/io/vfprintf.md b/docs/php/builtins/io/vfprintf.md
index 74d7db24b0..faef5f65ab 100644
--- a/docs/php/builtins/io/vfprintf.md
+++ b/docs/php/builtins/io/vfprintf.md
@@ -1,8 +1,8 @@
---
title: "vfprintf()"
-description: "Lowers `vfprintf(stream, format, values)` through `__rt_vsprintf` then fwrite."
+description: "Write a formatted string to a stream."
sidebar:
- order: 212
+ order: 229
---
## vfprintf()
@@ -11,7 +11,7 @@ sidebar:
function vfprintf(resource $stream, string $format, array $values): int
```
-Lowers `vfprintf(stream, format, values)` through `__rt_vsprintf` then fwrite.
+Write a formatted string to a stream.
**Parameters**:
- `$stream` (`resource`)
diff --git a/docs/php/builtins/json.md b/docs/php/builtins/json.md
index f9499035dd..09a9e16654 100644
--- a/docs/php/builtins/json.md
+++ b/docs/php/builtins/json.md
@@ -9,8 +9,8 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`json_decode()`](./json/json_decode.md) | `(string $json, bool $associative, int $depth, int $flags): mixed` | `mixed` |
-| [`json_encode()`](./json/json_encode.md) | `(mixed $value, int $flags, int $depth): string` | `string` |
+| [`json_decode()`](./json/json_decode.md) | `(string $json, bool $associative = null, int $depth = 512, int $flags = 0): mixed` | `mixed` |
+| [`json_encode()`](./json/json_encode.md) | `(mixed $value, int $flags = 0, int $depth = 512): string` | `string` |
| [`json_last_error()`](./json/json_last_error.md) | `(): int` | `int` |
| [`json_last_error_msg()`](./json/json_last_error_msg.md) | `(): string` | `string` |
-| [`json_validate()`](./json/json_validate.md) | `(string $json, int $depth, int $flags): bool` | `bool` |
+| [`json_validate()`](./json/json_validate.md) | `(string $json, int $depth = 512, int $flags = 0): bool` | `bool` |
diff --git a/docs/php/builtins/json/json_decode.md b/docs/php/builtins/json/json_decode.md
index f045832ee3..d3ff351578 100644
--- a/docs/php/builtins/json/json_decode.md
+++ b/docs/php/builtins/json/json_decode.md
@@ -1,23 +1,23 @@
---
title: "json_decode()"
-description: "Lowers `json_decode(json, associative?, depth?, flags?)` through the shared JSON decoder runtime."
+description: "Decodes a JSON string."
sidebar:
- order: 213
+ order: 230
---
## json_decode()
```php
-function json_decode(string $json, bool $associative, int $depth, int $flags): mixed
+function json_decode(string $json, bool $associative = null, int $depth = 512, int $flags = 0): mixed
```
-Lowers `json_decode(json, associative?, depth?, flags?)` through the shared JSON decoder runtime.
+Decodes a JSON string.
**Parameters**:
- `$json` (`string`)
-- `$associative` (`bool`), optional
-- `$depth` (`int`), optional
-- `$flags` (`int`), optional
+- `$associative` (`bool`), default `null`, optional
+- `$depth` (`int`), default `512`, optional
+- `$flags` (`int`), default `0`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/json/json_encode.md b/docs/php/builtins/json/json_encode.md
index 070f2473b9..183c7517f9 100644
--- a/docs/php/builtins/json/json_encode.md
+++ b/docs/php/builtins/json/json_encode.md
@@ -1,22 +1,22 @@
---
title: "json_encode()"
-description: "Lowers `json_encode(value, flags?, depth?)` through the shared JSON encoder runtime."
+description: "Returns the JSON representation of a value."
sidebar:
- order: 214
+ order: 231
---
## json_encode()
```php
-function json_encode(mixed $value, int $flags, int $depth): string
+function json_encode(mixed $value, int $flags = 0, int $depth = 512): string
```
-Lowers `json_encode(value, flags?, depth?)` through the shared JSON encoder runtime.
+Returns the JSON representation of a value.
**Parameters**:
- `$value` (`mixed`)
-- `$flags` (`int`), optional
-- `$depth` (`int`), optional
+- `$flags` (`int`), default `0`, optional
+- `$depth` (`int`), default `512`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/json/json_last_error.md b/docs/php/builtins/json/json_last_error.md
index 0b8f0c9556..4873936518 100644
--- a/docs/php/builtins/json/json_last_error.md
+++ b/docs/php/builtins/json/json_last_error.md
@@ -1,8 +1,8 @@
---
title: "json_last_error()"
-description: "Lowers `json_last_error()` by reading the shared runtime error-code symbol."
+description: "Returns the last error (if any) occurred during the last JSON encoding/decoding."
sidebar:
- order: 215
+ order: 232
---
## json_last_error()
@@ -11,7 +11,7 @@ sidebar:
function json_last_error(): int
```
-Lowers `json_last_error()` by reading the shared runtime error-code symbol.
+Returns the last error (if any) occurred during the last JSON encoding/decoding.
**Parameters**: none.
diff --git a/docs/php/builtins/json/json_last_error_msg.md b/docs/php/builtins/json/json_last_error_msg.md
index da2d0c5d47..fc3d0d284e 100644
--- a/docs/php/builtins/json/json_last_error_msg.md
+++ b/docs/php/builtins/json/json_last_error_msg.md
@@ -1,8 +1,8 @@
---
title: "json_last_error_msg()"
-description: "Lowers `json_last_error_msg()` through the runtime message lookup table."
+description: "Returns the error string of the last json_encode() or json_decode() call."
sidebar:
- order: 216
+ order: 233
---
## json_last_error_msg()
@@ -11,7 +11,7 @@ sidebar:
function json_last_error_msg(): string
```
-Lowers `json_last_error_msg()` through the runtime message lookup table.
+Returns the error string of the last json_encode() or json_decode() call.
**Parameters**: none.
diff --git a/docs/php/builtins/json/json_validate.md b/docs/php/builtins/json/json_validate.md
index 113b111476..fbcd6e4743 100644
--- a/docs/php/builtins/json/json_validate.md
+++ b/docs/php/builtins/json/json_validate.md
@@ -1,22 +1,22 @@
---
title: "json_validate()"
-description: "Lowers `json_validate(json, depth?, flags?)` into the shared validator runtime."
+description: "Checks if a string contains valid JSON."
sidebar:
- order: 217
+ order: 234
---
## json_validate()
```php
-function json_validate(string $json, int $depth, int $flags): bool
+function json_validate(string $json, int $depth = 512, int $flags = 0): bool
```
-Lowers `json_validate(json, depth?, flags?)` into the shared validator runtime.
+Checks if a string contains valid JSON.
**Parameters**:
- `$json` (`string`)
-- `$depth` (`int`), optional
-- `$flags` (`int`), optional
+- `$depth` (`int`), default `512`, optional
+- `$flags` (`int`), default `0`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/math.md b/docs/php/builtins/math.md
index f7b95c244c..59fb40fa71 100644
--- a/docs/php/builtins/math.md
+++ b/docs/php/builtins/math.md
@@ -15,7 +15,7 @@ sidebar:
| [`atan()`](./math/atan.md) | `(float $num): float` | `float` |
| [`atan2()`](./math/atan2.md) | `(float $y, float $x): float` | `float` |
| [`ceil()`](./math/ceil.md) | `(float $num): float` | `float` |
-| [`clamp()`](./math/clamp.md) | `(int $value, int $min, int $max): string` | `string` |
+| [`clamp()`](./math/clamp.md) | `(int $value, int $min, int $max): mixed` | `mixed` |
| [`cos()`](./math/cos.md) | `(float $num): float` | `float` |
| [`cosh()`](./math/cosh.md) | `(float $num): float` | `float` |
| [`deg2rad()`](./math/deg2rad.md) | `(float $num): float` | `float` |
@@ -28,18 +28,19 @@ sidebar:
| [`is_finite()`](./math/is_finite.md) | `(float $num): bool` | `bool` |
| [`is_infinite()`](./math/is_infinite.md) | `(float $num): bool` | `bool` |
| [`is_nan()`](./math/is_nan.md) | `(float $num): bool` | `bool` |
-| [`log()`](./math/log.md) | `(float $num, float $base): float` | `float` |
+| [`log()`](./math/log.md) | `(float $num, float $base = 2.718281828459045): float` | `float` |
| [`log10()`](./math/log10.md) | `(float $num): float` | `float` |
| [`log2()`](./math/log2.md) | `(float $num): float` | `float` |
-| [`max()`](./math/max.md) | `(mixed $value, ...$values): float` | `float` |
-| [`min()`](./math/min.md) | `(mixed $value, ...$values): float` | `float` |
+| [`max()`](./math/max.md) | `(mixed $value, ...$values): mixed` | `mixed` |
+| [`min()`](./math/min.md) | `(mixed $value, ...$values): mixed` | `mixed` |
| [`mt_rand()`](./math/mt_rand.md) | `(int $min, int $max): int` | `int` |
| [`pi()`](./math/pi.md) | `(): float` | `float` |
| [`pow()`](./math/pow.md) | `(float $num, float $exponent): float` | `float` |
| [`rad2deg()`](./math/rad2deg.md) | `(float $num): float` | `float` |
| [`rand()`](./math/rand.md) | `(int $min, int $max): int` | `int` |
+| [`random_bytes()`](./math/random_bytes.md) | `(int $length): string` | `string` |
| [`random_int()`](./math/random_int.md) | `(int $min, int $max): int` | `int` |
-| [`round()`](./math/round.md) | `(float $num, int $precision): float` | `float` |
+| [`round()`](./math/round.md) | `(float $num, int $precision = 0): float` | `float` |
| [`sin()`](./math/sin.md) | `(float $num): float` | `float` |
| [`sinh()`](./math/sinh.md) | `(float $num): float` | `float` |
| [`sqrt()`](./math/sqrt.md) | `(float $num): float` | `float` |
diff --git a/docs/php/builtins/math/abs.md b/docs/php/builtins/math/abs.md
index 0214047182..a1bd48d104 100644
--- a/docs/php/builtins/math/abs.md
+++ b/docs/php/builtins/math/abs.md
@@ -1,8 +1,8 @@
---
title: "abs()"
-description: "Lowers `abs()` for concrete integer-like and floating operands."
+description: "Absolute value."
sidebar:
- order: 218
+ order: 235
---
## abs()
@@ -11,7 +11,7 @@ sidebar:
function abs(int $num): mixed
```
-Lowers `abs()` for concrete integer-like and floating operands.
+Absolute value.
**Parameters**:
- `$num` (`int`)
diff --git a/docs/php/builtins/math/acos.md b/docs/php/builtins/math/acos.md
index 3ff6cbafa5..acf751064a 100644
--- a/docs/php/builtins/math/acos.md
+++ b/docs/php/builtins/math/acos.md
@@ -1,8 +1,8 @@
---
title: "acos()"
-description: "acos() — math builtin supported by Elephc."
+description: "Returns the arccosine of a number in radians."
sidebar:
- order: 219
+ order: 236
---
## acos()
@@ -11,7 +11,7 @@ sidebar:
function acos(float $num): float
```
-`acos()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the arccosine of a number in radians.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `acos` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/acos.md).
+
diff --git a/docs/php/builtins/math/asin.md b/docs/php/builtins/math/asin.md
index c7d1967347..e8a5038ca1 100644
--- a/docs/php/builtins/math/asin.md
+++ b/docs/php/builtins/math/asin.md
@@ -1,8 +1,8 @@
---
title: "asin()"
-description: "asin() — math builtin supported by Elephc."
+description: "Returns the arcsine of a number in radians."
sidebar:
- order: 220
+ order: 237
---
## asin()
@@ -11,7 +11,7 @@ sidebar:
function asin(float $num): float
```
-`asin()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the arcsine of a number in radians.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `asin` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/asin.md).
+
diff --git a/docs/php/builtins/math/atan.md b/docs/php/builtins/math/atan.md
index c41d0b9d64..f93ba54f99 100644
--- a/docs/php/builtins/math/atan.md
+++ b/docs/php/builtins/math/atan.md
@@ -1,8 +1,8 @@
---
title: "atan()"
-description: "atan() — math builtin supported by Elephc."
+description: "Returns the arctangent of a number in radians."
sidebar:
- order: 221
+ order: 238
---
## atan()
@@ -11,7 +11,7 @@ sidebar:
function atan(float $num): float
```
-`atan()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the arctangent of a number in radians.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `atan` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/atan.md).
+
diff --git a/docs/php/builtins/math/atan2.md b/docs/php/builtins/math/atan2.md
index 190133618b..f52df945f5 100644
--- a/docs/php/builtins/math/atan2.md
+++ b/docs/php/builtins/math/atan2.md
@@ -1,8 +1,8 @@
---
title: "atan2()"
-description: "Lowers `atan2()` using the C ABI argument order `y, x`."
+description: "Returns the arc tangent of two variables."
sidebar:
- order: 222
+ order: 239
---
## atan2()
@@ -11,7 +11,7 @@ sidebar:
function atan2(float $y, float $x): float
```
-Lowers `atan2()` using the C ABI argument order `y, x`.
+Returns the arc tangent of two variables.
**Parameters**:
- `$y` (`float`)
diff --git a/docs/php/builtins/math/ceil.md b/docs/php/builtins/math/ceil.md
index 39a8860ae2..0f5564bbd9 100644
--- a/docs/php/builtins/math/ceil.md
+++ b/docs/php/builtins/math/ceil.md
@@ -1,8 +1,8 @@
---
title: "ceil()"
-description: "Lowers `ceil()` for concrete integer-like and floating operands."
+description: "Rounds a number up to the nearest integer."
sidebar:
- order: 223
+ order: 240
---
## ceil()
@@ -11,7 +11,7 @@ sidebar:
function ceil(float $num): float
```
-Lowers `ceil()` for concrete integer-like and floating operands.
+Rounds a number up to the nearest integer.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/clamp.md b/docs/php/builtins/math/clamp.md
index 628029bd22..1dc2122491 100644
--- a/docs/php/builtins/math/clamp.md
+++ b/docs/php/builtins/math/clamp.md
@@ -1,24 +1,24 @@
---
title: "clamp()"
-description: "Lowers numeric `clamp(value, min, max)` calls with PHP-compatible bound checks."
+description: "Clamps a value to be within a specified range."
sidebar:
- order: 224
+ order: 241
---
## clamp()
```php
-function clamp(int $value, int $min, int $max): string
+function clamp(int $value, int $min, int $max): mixed
```
-Lowers numeric `clamp(value, min, max)` calls with PHP-compatible bound checks.
+Clamps a value to be within a specified range.
**Parameters**:
- `$value` (`int`)
- `$min` (`int`)
- `$max` (`int`)
-**Returns**: `string`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/math/cos.md b/docs/php/builtins/math/cos.md
index 74f770335f..a07542ec1f 100644
--- a/docs/php/builtins/math/cos.md
+++ b/docs/php/builtins/math/cos.md
@@ -1,8 +1,8 @@
---
title: "cos()"
-description: "cos() — math builtin supported by Elephc."
+description: "Returns the cosine of a number (radians)."
sidebar:
- order: 225
+ order: 242
---
## cos()
@@ -11,7 +11,7 @@ sidebar:
function cos(float $num): float
```
-`cos()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the cosine of a number (radians).
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `cos` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/cos.md).
+
diff --git a/docs/php/builtins/math/cosh.md b/docs/php/builtins/math/cosh.md
index 224ecd785d..ea9da090fb 100644
--- a/docs/php/builtins/math/cosh.md
+++ b/docs/php/builtins/math/cosh.md
@@ -1,8 +1,8 @@
---
title: "cosh()"
-description: "cosh() — math builtin supported by Elephc."
+description: "Returns the hyperbolic cosine of a number."
sidebar:
- order: 226
+ order: 243
---
## cosh()
@@ -11,7 +11,7 @@ sidebar:
function cosh(float $num): float
```
-`cosh()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the hyperbolic cosine of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `cosh` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/cosh.md).
+
diff --git a/docs/php/builtins/math/deg2rad.md b/docs/php/builtins/math/deg2rad.md
index 6da814b064..4ad81c1f2f 100644
--- a/docs/php/builtins/math/deg2rad.md
+++ b/docs/php/builtins/math/deg2rad.md
@@ -1,8 +1,8 @@
---
title: "deg2rad()"
-description: "Lowers `deg2rad()` by multiplying with `PI / 180`."
+description: "Converts a degree value to radians."
sidebar:
- order: 227
+ order: 244
---
## deg2rad()
@@ -11,7 +11,7 @@ sidebar:
function deg2rad(float $num): float
```
-Lowers `deg2rad()` by multiplying with `PI / 180`.
+Converts a degree value to radians.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/exp.md b/docs/php/builtins/math/exp.md
index 730f7fda22..bb38c14642 100644
--- a/docs/php/builtins/math/exp.md
+++ b/docs/php/builtins/math/exp.md
@@ -1,8 +1,8 @@
---
title: "exp()"
-description: "exp() — math builtin supported by Elephc."
+description: "Returns e raised to the power of a number."
sidebar:
- order: 228
+ order: 245
---
## exp()
@@ -11,7 +11,7 @@ sidebar:
function exp(float $num): float
```
-`exp()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns e raised to the power of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `exp` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/exp.md).
+
diff --git a/docs/php/builtins/math/fdiv.md b/docs/php/builtins/math/fdiv.md
index 3b8fb9ab04..cb296e066c 100644
--- a/docs/php/builtins/math/fdiv.md
+++ b/docs/php/builtins/math/fdiv.md
@@ -1,8 +1,8 @@
---
title: "fdiv()"
-description: "Lowers `fdiv()` for concrete integer-like and floating operands."
+description: "Divides two numbers, according to IEEE 754."
sidebar:
- order: 229
+ order: 246
---
## fdiv()
@@ -11,7 +11,7 @@ sidebar:
function fdiv(float $num1, float $num2): float
```
-Lowers `fdiv()` for concrete integer-like and floating operands.
+Divides two numbers, according to IEEE 754.
**Parameters**:
- `$num1` (`float`)
diff --git a/docs/php/builtins/math/floor.md b/docs/php/builtins/math/floor.md
index 959a86348e..c920dc07b5 100644
--- a/docs/php/builtins/math/floor.md
+++ b/docs/php/builtins/math/floor.md
@@ -1,8 +1,8 @@
---
title: "floor()"
-description: "Lowers `floor()` for concrete integer-like and floating operands."
+description: "Rounds a number down to the nearest integer."
sidebar:
- order: 230
+ order: 247
---
## floor()
@@ -11,7 +11,7 @@ sidebar:
function floor(float $num): float
```
-Lowers `floor()` for concrete integer-like and floating operands.
+Rounds a number down to the nearest integer.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/fmod.md b/docs/php/builtins/math/fmod.md
index 518300705e..78a2c94254 100644
--- a/docs/php/builtins/math/fmod.md
+++ b/docs/php/builtins/math/fmod.md
@@ -1,8 +1,8 @@
---
title: "fmod()"
-description: "Lowers `fmod()` for concrete integer-like and floating operands."
+description: "Returns the floating point remainder of the division of the arguments."
sidebar:
- order: 231
+ order: 248
---
## fmod()
@@ -11,7 +11,7 @@ sidebar:
function fmod(float $num1, float $num2): float
```
-Lowers `fmod()` for concrete integer-like and floating operands.
+Returns the floating point remainder of the division of the arguments.
**Parameters**:
- `$num1` (`float`)
diff --git a/docs/php/builtins/math/hypot.md b/docs/php/builtins/math/hypot.md
index 389fbc3651..6b97393f53 100644
--- a/docs/php/builtins/math/hypot.md
+++ b/docs/php/builtins/math/hypot.md
@@ -1,8 +1,8 @@
---
title: "hypot()"
-description: "Lowers `hypot()` using the C ABI argument order `x, y`."
+description: "Calculates the length of the hypotenuse of a right-angle triangle."
sidebar:
- order: 232
+ order: 249
---
## hypot()
@@ -11,7 +11,7 @@ sidebar:
function hypot(float $x, float $y): float
```
-Lowers `hypot()` using the C ABI argument order `x, y`.
+Calculates the length of the hypotenuse of a right-angle triangle.
**Parameters**:
- `$x` (`float`)
diff --git a/docs/php/builtins/math/intdiv.md b/docs/php/builtins/math/intdiv.md
index e7a91be1b7..06740dddfb 100644
--- a/docs/php/builtins/math/intdiv.md
+++ b/docs/php/builtins/math/intdiv.md
@@ -1,8 +1,8 @@
---
title: "intdiv()"
-description: "Lowers `intdiv()` for concrete integer-like numeric operands."
+description: "Integer division."
sidebar:
- order: 233
+ order: 250
---
## intdiv()
@@ -11,7 +11,7 @@ sidebar:
function intdiv(int $num1, int $num2): int
```
-Lowers `intdiv()` for concrete integer-like numeric operands.
+Integer division.
**Parameters**:
- `$num1` (`int`)
diff --git a/docs/php/builtins/math/is_finite.md b/docs/php/builtins/math/is_finite.md
index 8fe56f1b03..eed5a5021e 100644
--- a/docs/php/builtins/math/is_finite.md
+++ b/docs/php/builtins/math/is_finite.md
@@ -1,8 +1,8 @@
---
title: "is_finite()"
-description: "Lowers `is_finite()` by rejecting NaN and both infinities."
+description: "Checks whether a float is finite."
sidebar:
- order: 234
+ order: 251
---
## is_finite()
@@ -11,7 +11,7 @@ sidebar:
function is_finite(float $num): bool
```
-Lowers `is_finite()` by rejecting NaN and both infinities.
+Checks whether a float is finite.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/is_infinite.md b/docs/php/builtins/math/is_infinite.md
index dec0ca3a0c..f2927a37a5 100644
--- a/docs/php/builtins/math/is_infinite.md
+++ b/docs/php/builtins/math/is_infinite.md
@@ -1,8 +1,8 @@
---
title: "is_infinite()"
-description: "Lowers `is_infinite()` by comparing the normalized float against +/- infinity."
+description: "Checks whether a float is infinite."
sidebar:
- order: 235
+ order: 252
---
## is_infinite()
@@ -11,7 +11,7 @@ sidebar:
function is_infinite(float $num): bool
```
-Lowers `is_infinite()` by comparing the normalized float against +/- infinity.
+Checks whether a float is infinite.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/is_nan.md b/docs/php/builtins/math/is_nan.md
index 659d4250b6..7b5ab79a73 100644
--- a/docs/php/builtins/math/is_nan.md
+++ b/docs/php/builtins/math/is_nan.md
@@ -1,8 +1,8 @@
---
title: "is_nan()"
-description: "Lowers `is_nan()` by checking whether the normalized float is unordered with itself."
+description: "Checks whether a float is NAN."
sidebar:
- order: 236
+ order: 253
---
## is_nan()
@@ -11,7 +11,7 @@ sidebar:
function is_nan(float $num): bool
```
-Lowers `is_nan()` by checking whether the normalized float is unordered with itself.
+Checks whether a float is NAN.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/log.md b/docs/php/builtins/math/log.md
index ad2a80ecc4..8973c6a012 100644
--- a/docs/php/builtins/math/log.md
+++ b/docs/php/builtins/math/log.md
@@ -1,21 +1,21 @@
---
title: "log()"
-description: "Lowers `log()` in one-argument and base-changing two-argument forms."
+description: "Natural logarithm."
sidebar:
- order: 237
+ order: 254
---
## log()
```php
-function log(float $num, float $base): float
+function log(float $num, float $base = 2.718281828459045): float
```
-Lowers `log()` in one-argument and base-changing two-argument forms.
+Natural logarithm.
**Parameters**:
- `$num` (`float`)
-- `$base` (`float`), optional
+- `$base` (`float`), default `2.718281828459045`, optional
**Returns**: `float`
diff --git a/docs/php/builtins/math/log10.md b/docs/php/builtins/math/log10.md
index e68d22ca77..ac21b08007 100644
--- a/docs/php/builtins/math/log10.md
+++ b/docs/php/builtins/math/log10.md
@@ -1,8 +1,8 @@
---
title: "log10()"
-description: "log10() — math builtin supported by Elephc."
+description: "Returns the base-10 logarithm of a number."
sidebar:
- order: 238
+ order: 255
---
## log10()
@@ -11,7 +11,7 @@ sidebar:
function log10(float $num): float
```
-`log10()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the base-10 logarithm of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `log10` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/log10.md).
+
diff --git a/docs/php/builtins/math/log2.md b/docs/php/builtins/math/log2.md
index 2b2f20cc61..0401ccc162 100644
--- a/docs/php/builtins/math/log2.md
+++ b/docs/php/builtins/math/log2.md
@@ -1,8 +1,8 @@
---
title: "log2()"
-description: "log2() — math builtin supported by Elephc."
+description: "Returns the base-2 logarithm of a number."
sidebar:
- order: 239
+ order: 256
---
## log2()
@@ -11,7 +11,7 @@ sidebar:
function log2(float $num): float
```
-`log2()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the base-2 logarithm of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `log2` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/log2.md).
+
diff --git a/docs/php/builtins/math/max.md b/docs/php/builtins/math/max.md
index 94f87f45b3..c92f69734a 100644
--- a/docs/php/builtins/math/max.md
+++ b/docs/php/builtins/math/max.md
@@ -1,23 +1,23 @@
---
title: "max()"
-description: "Lowers numeric `min()` and `max()` over concrete integer-like or float operands."
+description: "Find highest value."
sidebar:
- order: 240
+ order: 257
---
## max()
```php
-function max(mixed $value, ...$values): float
+function max(mixed $value, ...$values): mixed
```
-Lowers numeric `min()` and `max()` over concrete integer-like or float operands.
+Find highest value.
**Parameters**:
- `$value` (`mixed`)
- `...$values` — variadic: collects excess arguments into `$values`.
-**Returns**: `float`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/math/min.md b/docs/php/builtins/math/min.md
index 04dca7f93f..bb959c9892 100644
--- a/docs/php/builtins/math/min.md
+++ b/docs/php/builtins/math/min.md
@@ -1,23 +1,23 @@
---
title: "min()"
-description: "Lowers numeric `min()` and `max()` over concrete integer-like or float operands."
+description: "Find lowest value."
sidebar:
- order: 241
+ order: 258
---
## min()
```php
-function min(mixed $value, ...$values): float
+function min(mixed $value, ...$values): mixed
```
-Lowers numeric `min()` and `max()` over concrete integer-like or float operands.
+Find lowest value.
**Parameters**:
- `$value` (`mixed`)
- `...$values` — variadic: collects excess arguments into `$values`.
-**Returns**: `float`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/math/mt_rand.md b/docs/php/builtins/math/mt_rand.md
index d032e64679..83d6dd9791 100644
--- a/docs/php/builtins/math/mt_rand.md
+++ b/docs/php/builtins/math/mt_rand.md
@@ -1,8 +1,8 @@
---
title: "mt_rand()"
-description: "mt_rand() — math builtin supported by Elephc."
+description: "Generate a random value via the Mersenne Twister Random Number Generator."
sidebar:
- order: 242
+ order: 259
---
## mt_rand()
@@ -11,7 +11,7 @@ sidebar:
function mt_rand(int $min, int $max): int
```
-`mt_rand()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Generate a random value via the Mersenne Twister Random Number Generator.
**Parameters**:
- `$min` (`int`)
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `mt_rand` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/mt_rand.md).
+
diff --git a/docs/php/builtins/math/pi.md b/docs/php/builtins/math/pi.md
index ad468afc28..231447b7b1 100644
--- a/docs/php/builtins/math/pi.md
+++ b/docs/php/builtins/math/pi.md
@@ -1,8 +1,8 @@
---
title: "pi()"
-description: "Lowers `pi()` as the same data-section float constant used by the legacy backend."
+description: "Gets value of pi."
sidebar:
- order: 243
+ order: 260
---
## pi()
@@ -11,7 +11,7 @@ sidebar:
function pi(): float
```
-Lowers `pi()` as the same data-section float constant used by the legacy backend.
+Gets value of pi.
**Parameters**: none.
diff --git a/docs/php/builtins/math/pow.md b/docs/php/builtins/math/pow.md
index fc0c4b6987..9fd3f5b18c 100644
--- a/docs/php/builtins/math/pow.md
+++ b/docs/php/builtins/math/pow.md
@@ -1,8 +1,8 @@
---
title: "pow()"
-description: "Lowers `pow()` for concrete integer-like and floating operands."
+description: "Exponential expression."
sidebar:
- order: 244
+ order: 261
---
## pow()
@@ -11,7 +11,7 @@ sidebar:
function pow(float $num, float $exponent): float
```
-Lowers `pow()` for concrete integer-like and floating operands.
+Exponential expression.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/rad2deg.md b/docs/php/builtins/math/rad2deg.md
index f468e2413e..a0382b92c2 100644
--- a/docs/php/builtins/math/rad2deg.md
+++ b/docs/php/builtins/math/rad2deg.md
@@ -1,8 +1,8 @@
---
title: "rad2deg()"
-description: "Lowers `rad2deg()` by multiplying with `180 / PI`."
+description: "Converts a radian value to degrees."
sidebar:
- order: 245
+ order: 262
---
## rad2deg()
@@ -11,7 +11,7 @@ sidebar:
function rad2deg(float $num): float
```
-Lowers `rad2deg()` by multiplying with `180 / PI`.
+Converts a radian value to degrees.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/rand.md b/docs/php/builtins/math/rand.md
index 37d0a49b3a..264d5e63e5 100644
--- a/docs/php/builtins/math/rand.md
+++ b/docs/php/builtins/math/rand.md
@@ -1,8 +1,8 @@
---
title: "rand()"
-description: "rand() — math builtin supported by Elephc."
+description: "Generate a random integer."
sidebar:
- order: 246
+ order: 263
---
## rand()
@@ -11,7 +11,7 @@ sidebar:
function rand(int $min, int $max): int
```
-`rand()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Generate a random integer.
**Parameters**:
- `$min` (`int`)
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `rand` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/rand.md).
+
diff --git a/docs/php/builtins/math/random_bytes.md b/docs/php/builtins/math/random_bytes.md
new file mode 100644
index 0000000000..30805ea258
--- /dev/null
+++ b/docs/php/builtins/math/random_bytes.md
@@ -0,0 +1,32 @@
+---
+title: "random_bytes()"
+description: "Get a cryptographically secure random string of the given length."
+sidebar:
+ order: 264
+---
+
+## random_bytes()
+
+```php
+function random_bytes(int $length): string
+```
+
+Get a cryptographically secure random string of the given length.
+
+**Parameters**:
+- `$length` (`int`)
+
+**Returns**: `string`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `random_bytes` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/random_bytes.md).
+
diff --git a/docs/php/builtins/math/random_int.md b/docs/php/builtins/math/random_int.md
index 1494bfc696..2b0e488423 100644
--- a/docs/php/builtins/math/random_int.md
+++ b/docs/php/builtins/math/random_int.md
@@ -1,8 +1,8 @@
---
title: "random_int()"
-description: "Lowers `random_int()` over an inclusive integer range."
+description: "Get a cryptographically secure, uniformly selected integer."
sidebar:
- order: 247
+ order: 265
---
## random_int()
@@ -11,7 +11,7 @@ sidebar:
function random_int(int $min, int $max): int
```
-Lowers `random_int()` over an inclusive integer range.
+Get a cryptographically secure, uniformly selected integer.
**Parameters**:
- `$min` (`int`)
diff --git a/docs/php/builtins/math/round.md b/docs/php/builtins/math/round.md
index 13b37b90f0..38a00a8181 100644
--- a/docs/php/builtins/math/round.md
+++ b/docs/php/builtins/math/round.md
@@ -1,21 +1,21 @@
---
title: "round()"
-description: "Lowers `round()` for concrete integer-like and floating operands."
+description: "Rounds a float."
sidebar:
- order: 248
+ order: 266
---
## round()
```php
-function round(float $num, int $precision): float
+function round(float $num, int $precision = 0): float
```
-Lowers `round()` for concrete integer-like and floating operands.
+Rounds a float.
**Parameters**:
- `$num` (`float`)
-- `$precision` (`int`), optional
+- `$precision` (`int`), default `0`, optional
**Returns**: `float`
diff --git a/docs/php/builtins/math/sin.md b/docs/php/builtins/math/sin.md
index 7cf2311189..e72dc77583 100644
--- a/docs/php/builtins/math/sin.md
+++ b/docs/php/builtins/math/sin.md
@@ -1,8 +1,8 @@
---
title: "sin()"
-description: "sin() — math builtin supported by Elephc."
+description: "Returns the sine of a number (radians)."
sidebar:
- order: 249
+ order: 267
---
## sin()
@@ -11,7 +11,7 @@ sidebar:
function sin(float $num): float
```
-`sin()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the sine of a number (radians).
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `sin` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/sin.md).
+
diff --git a/docs/php/builtins/math/sinh.md b/docs/php/builtins/math/sinh.md
index 50b828fb67..63bf9e1ca5 100644
--- a/docs/php/builtins/math/sinh.md
+++ b/docs/php/builtins/math/sinh.md
@@ -1,8 +1,8 @@
---
title: "sinh()"
-description: "sinh() — math builtin supported by Elephc."
+description: "Returns the hyperbolic sine of a number."
sidebar:
- order: 250
+ order: 268
---
## sinh()
@@ -11,7 +11,7 @@ sidebar:
function sinh(float $num): float
```
-`sinh()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the hyperbolic sine of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `sinh` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/sinh.md).
+
diff --git a/docs/php/builtins/math/sqrt.md b/docs/php/builtins/math/sqrt.md
index 95c6b46a9d..ed2ed1d3a0 100644
--- a/docs/php/builtins/math/sqrt.md
+++ b/docs/php/builtins/math/sqrt.md
@@ -1,8 +1,8 @@
---
title: "sqrt()"
-description: "Lowers `sqrt()` for concrete integer-like and floating operands."
+description: "Returns the square root of a number."
sidebar:
- order: 251
+ order: 269
---
## sqrt()
@@ -11,7 +11,7 @@ sidebar:
function sqrt(float $num): float
```
-Lowers `sqrt()` for concrete integer-like and floating operands.
+Returns the square root of a number.
**Parameters**:
- `$num` (`float`)
diff --git a/docs/php/builtins/math/tan.md b/docs/php/builtins/math/tan.md
index d4c540be13..66bdd9f8c0 100644
--- a/docs/php/builtins/math/tan.md
+++ b/docs/php/builtins/math/tan.md
@@ -1,8 +1,8 @@
---
title: "tan()"
-description: "tan() — math builtin supported by Elephc."
+description: "Returns the tangent of a number (radians)."
sidebar:
- order: 252
+ order: 270
---
## tan()
@@ -11,7 +11,7 @@ sidebar:
function tan(float $num): float
```
-`tan()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the tangent of a number (radians).
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `tan` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/tan.md).
+
diff --git a/docs/php/builtins/math/tanh.md b/docs/php/builtins/math/tanh.md
index d669dc196e..cb6108a319 100644
--- a/docs/php/builtins/math/tanh.md
+++ b/docs/php/builtins/math/tanh.md
@@ -1,8 +1,8 @@
---
title: "tanh()"
-description: "tanh() — math builtin supported by Elephc."
+description: "Returns the hyperbolic tangent of a number."
sidebar:
- order: 253
+ order: 271
---
## tanh()
@@ -11,7 +11,7 @@ sidebar:
function tanh(float $num): float
```
-`tanh()` is a math builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Returns the hyperbolic tangent of a number.
**Parameters**:
- `$num` (`float`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `tanh` is implemented in the compiler, see [the internals page](../../../internals/builtins/math/tanh.md).
+
diff --git a/docs/php/builtins/misc.md b/docs/php/builtins/misc.md
index 02054d9255..ccb08155b6 100644
--- a/docs/php/builtins/misc.md
+++ b/docs/php/builtins/misc.md
@@ -10,16 +10,16 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
| [`buffer_new()`](./misc/buffer_new.md) | `(int $length): mixed` | `mixed` |
-| [`call_user_func()`](./misc/call_user_func.md) | `(callable $callback, ...$args): mixed` | `mixed` |
-| [`call_user_func_array()`](./misc/call_user_func_array.md) | `(callable $callback, array $args): mixed` | `mixed` |
-| [`define()`](./misc/define.md) | `(string $constant_name, mixed $value, bool $case_insensitive): bool` | `bool` |
+| [`define()`](./misc/define.md) | `(string $constant_name, mixed $value): bool` | `bool` |
| [`defined()`](./misc/defined.md) | `(string $constant_name): bool` | `bool` |
| [`empty()`](./misc/empty.md) | `(mixed $value): bool` | `bool` |
-| [`header()`](./misc/header.md) | `(mixed $header, mixed $replace, mixed $response_code): void` | `void` |
-| [`http_response_code()`](./misc/http_response_code.md) | `(mixed $response_code): int` | `int` |
+| [`header()`](./misc/header.md) | `(string $header, bool $replace = true, int $response_code = 0): void` | `void` |
+| [`http_response_code()`](./misc/http_response_code.md) | `(int $response_code = 0): int` | `int` |
| [`isset()`](./misc/isset.md) | `(mixed $var, ...$vars): bool` | `bool` |
-| [`php_uname()`](./misc/php_uname.md) | `(string $mode): string` | `string` |
-| [`phpversion()`](./misc/phpversion.md) | `(string $extension = null): string` | `string` |
-| [`print_r()`](./misc/print_r.md) | `(...$values): void` | `void` |
+| [`php_uname()`](./misc/php_uname.md) | `(string $mode = 'a'): string` | `string` |
+| [`phpversion()`](./misc/phpversion.md) | `(): string` | `string` |
+| [`print_r()`](./misc/print_r.md) | `(mixed $value): void` | `void` |
+| [`serialize()`](./misc/serialize.md) | `(mixed $value): string` | `string` |
+| [`unserialize()`](./misc/unserialize.md) | `(string $data, mixed $options = []): mixed` | `mixed` |
| [`unset()`](./misc/unset.md) | `(mixed $var, ...$vars): void` | `void` |
-| [`var_dump()`](./misc/var_dump.md) | `(...$values): void` | `void` |
+| [`var_dump()`](./misc/var_dump.md) | `(mixed $value): void` | `void` |
diff --git a/docs/php/builtins/misc/buffer_new.md b/docs/php/builtins/misc/buffer_new.md
index cb0e302967..a0ff226320 100644
--- a/docs/php/builtins/misc/buffer_new.md
+++ b/docs/php/builtins/misc/buffer_new.md
@@ -2,7 +2,7 @@
title: "buffer_new()"
description: "buffer_new() — misc builtin supported by Elephc."
sidebar:
- order: 254
+ order: 272
---
## buffer_new()
diff --git a/docs/php/builtins/misc/call_user_func.md b/docs/php/builtins/misc/call_user_func.md
deleted file mode 100644
index 4ae00e69b6..0000000000
--- a/docs/php/builtins/misc/call_user_func.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-title: "call_user_func()"
-description: "call_user_func() — misc builtin supported by Elephc."
-sidebar:
- order: 255
----
-
-## call_user_func()
-
-```php
-function call_user_func(callable $callback, ...$args): mixed
-```
-
-`call_user_func()` is a misc builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
-
-**Parameters**:
-- `$callback` (`callable`)
-- `...$args` — variadic: collects excess arguments into `$args`.
-
-**Returns**: `mixed`
-
-_No examples yet — check `examples/` and `showcases/` for usage patterns._
-
-
-
-
-
-
-
diff --git a/docs/php/builtins/misc/call_user_func_array.md b/docs/php/builtins/misc/call_user_func_array.md
deleted file mode 100644
index 44fae2128e..0000000000
--- a/docs/php/builtins/misc/call_user_func_array.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-title: "call_user_func_array()"
-description: "call_user_func_array() — misc builtin supported by Elephc."
-sidebar:
- order: 256
----
-
-## call_user_func_array()
-
-```php
-function call_user_func_array(callable $callback, array $args): mixed
-```
-
-`call_user_func_array()` is a misc builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
-
-**Parameters**:
-- `$callback` (`callable`)
-- `$args` (`array`)
-
-**Returns**: `mixed`
-
-_No examples yet — check `examples/` and `showcases/` for usage patterns._
-
-
-
-
-
-
-
diff --git a/docs/php/builtins/misc/define.md b/docs/php/builtins/misc/define.md
index de0ae60e73..9b462938b2 100644
--- a/docs/php/builtins/misc/define.md
+++ b/docs/php/builtins/misc/define.md
@@ -2,13 +2,13 @@
title: "define()"
description: "Defines a named constant at runtime."
sidebar:
- order: 257
+ order: 273
---
## define()
```php
-function define(string $constant_name, mixed $value, bool $case_insensitive): bool
+function define(string $constant_name, mixed $value): bool
```
Defines a named constant at runtime.
@@ -16,7 +16,6 @@ Defines a named constant at runtime.
**Parameters**:
- `$constant_name` (`string`)
- `$value` (`mixed`)
-- `$case_insensitive` (`bool`)
**Returns**: `bool`
diff --git a/docs/php/builtins/misc/defined.md b/docs/php/builtins/misc/defined.md
index 9a9627af0d..484fd739a8 100644
--- a/docs/php/builtins/misc/defined.md
+++ b/docs/php/builtins/misc/defined.md
@@ -2,7 +2,7 @@
title: "defined()"
description: "Checks whether a given named constant exists."
sidebar:
- order: 258
+ order: 274
---
## defined()
diff --git a/docs/php/builtins/misc/empty.md b/docs/php/builtins/misc/empty.md
index 6269e4e389..64bfb47e26 100644
--- a/docs/php/builtins/misc/empty.md
+++ b/docs/php/builtins/misc/empty.md
@@ -2,7 +2,7 @@
title: "empty()"
description: "Determines whether a variable is considered empty."
sidebar:
- order: 259
+ order: 275
---
## empty()
diff --git a/docs/php/builtins/misc/header.md b/docs/php/builtins/misc/header.md
index 62b057db7d..fcf940d5d7 100644
--- a/docs/php/builtins/misc/header.md
+++ b/docs/php/builtins/misc/header.md
@@ -1,22 +1,22 @@
---
title: "header()"
-description: "Lowers `header($line[, $replace[, $code]])` to `__rt_header`, materializing the"
+description: "Sends a raw HTTP header."
sidebar:
- order: 260
+ order: 276
---
## header()
```php
-function header(mixed $header, mixed $replace, mixed $response_code): void
+function header(string $header, bool $replace = true, int $response_code = 0): void
```
-Lowers `header($line[, $replace[, $code]])` to `__rt_header`, materializing the
+Sends a raw HTTP header.
**Parameters**:
-- `$header` (`mixed`)
-- `$replace` (`mixed`), optional
-- `$response_code` (`mixed`), optional
+- `$header` (`string`)
+- `$replace` (`bool`), default `true`, optional
+- `$response_code` (`int`), default `0`, optional
**Returns**: `void`
diff --git a/docs/php/builtins/misc/http_response_code.md b/docs/php/builtins/misc/http_response_code.md
index 837dd10313..0505e4f048 100644
--- a/docs/php/builtins/misc/http_response_code.md
+++ b/docs/php/builtins/misc/http_response_code.md
@@ -1,20 +1,20 @@
---
title: "http_response_code()"
-description: "Lowers `http_response_code([$code])` to `__rt_http_response_code`. The code (or"
+description: "Gets or sets the HTTP response code."
sidebar:
- order: 261
+ order: 277
---
## http_response_code()
```php
-function http_response_code(mixed $response_code): int
+function http_response_code(int $response_code = 0): int
```
-Lowers `http_response_code([$code])` to `__rt_http_response_code`. The code (or
+Gets or sets the HTTP response code.
**Parameters**:
-- `$response_code` (`mixed`), optional
+- `$response_code` (`int`), default `0`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/misc/isset.md b/docs/php/builtins/misc/isset.md
index de2e6b9e4c..cc1827b2ab 100644
--- a/docs/php/builtins/misc/isset.md
+++ b/docs/php/builtins/misc/isset.md
@@ -2,7 +2,7 @@
title: "isset()"
description: "Determines whether a variable is set and is not null."
sidebar:
- order: 262
+ order: 278
---
## isset()
diff --git a/docs/php/builtins/misc/php_uname.md b/docs/php/builtins/misc/php_uname.md
index f49718cb1b..aacf6904d9 100644
--- a/docs/php/builtins/misc/php_uname.md
+++ b/docs/php/builtins/misc/php_uname.md
@@ -2,19 +2,19 @@
title: "php_uname()"
description: "Returns information about the operating system PHP is running on."
sidebar:
- order: 263
+ order: 279
---
## php_uname()
```php
-function php_uname(string $mode): string
+function php_uname(string $mode = 'a'): string
```
Returns information about the operating system PHP is running on.
**Parameters**:
-- `$mode` (`string`), optional
+- `$mode` (`string`), default `'a'`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/misc/phpversion.md b/docs/php/builtins/misc/phpversion.md
index 206f14b811..488c639214 100644
--- a/docs/php/builtins/misc/phpversion.md
+++ b/docs/php/builtins/misc/phpversion.md
@@ -2,19 +2,18 @@
title: "phpversion()"
description: "Returns the current PHP version information."
sidebar:
- order: 264
+ order: 280
---
## phpversion()
```php
-function phpversion(string $extension = null): string
+function phpversion(): string
```
Returns the current PHP version information.
-**Parameters**:
-- `$extension` (`string`), default `null`, optional
+**Parameters**: none.
**Returns**: `string`
diff --git a/docs/php/builtins/misc/print_r.md b/docs/php/builtins/misc/print_r.md
index b8477db5b0..a4c3591433 100644
--- a/docs/php/builtins/misc/print_r.md
+++ b/docs/php/builtins/misc/print_r.md
@@ -2,19 +2,19 @@
title: "print_r()"
description: "Prints human-readable information about a variable."
sidebar:
- order: 265
+ order: 281
---
## print_r()
```php
-function print_r(...$values): void
+function print_r(mixed $value): void
```
Prints human-readable information about a variable.
**Parameters**:
-- `...$values` — variadic: collects excess arguments into `$values`.
+- `$value` (`mixed`)
**Returns**: `void`
diff --git a/docs/php/builtins/misc/serialize.md b/docs/php/builtins/misc/serialize.md
new file mode 100644
index 0000000000..1c0f7a5420
--- /dev/null
+++ b/docs/php/builtins/misc/serialize.md
@@ -0,0 +1,32 @@
+---
+title: "serialize()"
+description: "Generates a storable representation of a value."
+sidebar:
+ order: 282
+---
+
+## serialize()
+
+```php
+function serialize(mixed $value): string
+```
+
+Generates a storable representation of a value.
+
+**Parameters**:
+- `$value` (`mixed`)
+
+**Returns**: `string`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `serialize` is implemented in the compiler, see [the internals page](../../../internals/builtins/misc/serialize.md).
+
diff --git a/docs/php/builtins/misc/unserialize.md b/docs/php/builtins/misc/unserialize.md
new file mode 100644
index 0000000000..b925c3d412
--- /dev/null
+++ b/docs/php/builtins/misc/unserialize.md
@@ -0,0 +1,33 @@
+---
+title: "unserialize()"
+description: "Creates a PHP value from a stored representation."
+sidebar:
+ order: 283
+---
+
+## unserialize()
+
+```php
+function unserialize(string $data, mixed $options = []): mixed
+```
+
+Creates a PHP value from a stored representation.
+
+**Parameters**:
+- `$data` (`string`)
+- `$options` (`mixed`), default `[]`, optional
+
+**Returns**: `mixed`
+
+_No examples yet — check `examples/` and `showcases/` for usage patterns._
+
+
+
+
+
+
+
+## Internals
+
+For how `unserialize` is implemented in the compiler, see [the internals page](../../../internals/builtins/misc/unserialize.md).
+
diff --git a/docs/php/builtins/misc/unset.md b/docs/php/builtins/misc/unset.md
index 52ccee2a00..0aeedc0179 100644
--- a/docs/php/builtins/misc/unset.md
+++ b/docs/php/builtins/misc/unset.md
@@ -2,7 +2,7 @@
title: "unset()"
description: "Unsets the given variables."
sidebar:
- order: 266
+ order: 284
---
## unset()
diff --git a/docs/php/builtins/misc/var_dump.md b/docs/php/builtins/misc/var_dump.md
index 42dbaeca59..a4d7e8d7a5 100644
--- a/docs/php/builtins/misc/var_dump.md
+++ b/docs/php/builtins/misc/var_dump.md
@@ -2,19 +2,19 @@
title: "var_dump()"
description: "Dumps information about a variable, including its type and value."
sidebar:
- order: 267
+ order: 285
---
## var_dump()
```php
-function var_dump(...$values): void
+function var_dump(mixed $value): void
```
Dumps information about a variable, including its type and value.
**Parameters**:
-- `...$values` — variadic: collects excess arguments into `$values`.
+- `$value` (`mixed`)
**Returns**: `void`
diff --git a/docs/php/builtins/pointer.md b/docs/php/builtins/pointer.md
index 3951666478..74dcf78742 100644
--- a/docs/php/builtins/pointer.md
+++ b/docs/php/builtins/pointer.md
@@ -19,7 +19,7 @@ sidebar:
| [`ptr_read8()`](./pointer/ptr_read8.md) | `(pointer $pointer): int` | `int` |
| [`ptr_read_string()`](./pointer/ptr_read_string.md) | `(pointer $pointer, int $length): string` | `string` |
| [`ptr_set()`](./pointer/ptr_set.md) | `(pointer $pointer, mixed $value): void` | `void` |
-| [`ptr_sizeof()`](./pointer/ptr_sizeof.md) | `(string $type): mixed` | `mixed` |
+| [`ptr_sizeof()`](./pointer/ptr_sizeof.md) | `(string $type): int` | `int` |
| [`ptr_write16()`](./pointer/ptr_write16.md) | `(pointer $pointer, int $value): void` | `void` |
| [`ptr_write32()`](./pointer/ptr_write32.md) | `(pointer $pointer, int $value): void` | `void` |
| [`ptr_write8()`](./pointer/ptr_write8.md) | `(pointer $pointer, int $value): void` | `void` |
diff --git a/docs/php/builtins/pointer/ptr.md b/docs/php/builtins/pointer/ptr.md
index 3db4658840..c845062ff9 100644
--- a/docs/php/builtins/pointer/ptr.md
+++ b/docs/php/builtins/pointer/ptr.md
@@ -1,8 +1,8 @@
---
title: "ptr()"
-description: "Lowers `ptr(value)` by materializing the address of addressable local/global storage."
+description: "Returns a raw pointer to the given variable."
sidebar:
- order: 268
+ order: 286
---
## ptr()
@@ -11,7 +11,7 @@ sidebar:
function ptr(mixed $value): mixed
```
-Lowers `ptr(value)` by materializing the address of addressable local/global storage.
+Returns a raw pointer to the given variable.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/pointer/ptr_get.md b/docs/php/builtins/pointer/ptr_get.md
index 308ff603f1..0100fcbf47 100644
--- a/docs/php/builtins/pointer/ptr_get.md
+++ b/docs/php/builtins/pointer/ptr_get.md
@@ -1,8 +1,8 @@
---
title: "ptr_get()"
-description: "Lowers `ptr_get(pointer)` by reading one machine word through a checked pointer."
+description: "Reads one machine word through a raw pointer and returns it as an integer."
sidebar:
- order: 269
+ order: 287
---
## ptr_get()
@@ -11,7 +11,7 @@ sidebar:
function ptr_get(pointer $pointer): int
```
-Lowers `ptr_get(pointer)` by reading one machine word through a checked pointer.
+Reads one machine word through a raw pointer and returns it as an integer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_is_null.md b/docs/php/builtins/pointer/ptr_is_null.md
index 5c8f6700f3..16bca05dc4 100644
--- a/docs/php/builtins/pointer/ptr_is_null.md
+++ b/docs/php/builtins/pointer/ptr_is_null.md
@@ -1,8 +1,8 @@
---
title: "ptr_is_null()"
-description: "Lowers `ptr_is_null(pointer)` by comparing the raw pointer address to zero."
+description: "Returns true if the pointer is null."
sidebar:
- order: 270
+ order: 288
---
## ptr_is_null()
@@ -11,7 +11,7 @@ sidebar:
function ptr_is_null(pointer $pointer): bool
```
-Lowers `ptr_is_null(pointer)` by comparing the raw pointer address to zero.
+Returns true if the pointer is null.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_null.md b/docs/php/builtins/pointer/ptr_null.md
index 7914be61f8..dc5dbe5788 100644
--- a/docs/php/builtins/pointer/ptr_null.md
+++ b/docs/php/builtins/pointer/ptr_null.md
@@ -1,8 +1,8 @@
---
title: "ptr_null()"
-description: "Lowers `ptr_null()` by materializing the raw null pointer sentinel."
+description: "Returns a null raw pointer."
sidebar:
- order: 271
+ order: 289
---
## ptr_null()
@@ -11,7 +11,7 @@ sidebar:
function ptr_null(): mixed
```
-Lowers `ptr_null()` by materializing the raw null pointer sentinel.
+Returns a null raw pointer.
**Parameters**: none.
diff --git a/docs/php/builtins/pointer/ptr_offset.md b/docs/php/builtins/pointer/ptr_offset.md
index 6d01db5604..4a846c0844 100644
--- a/docs/php/builtins/pointer/ptr_offset.md
+++ b/docs/php/builtins/pointer/ptr_offset.md
@@ -1,8 +1,8 @@
---
title: "ptr_offset()"
-description: "Lowers `ptr_offset(pointer, offset)` by adding a byte offset to a raw address."
+description: "Returns a new pointer offset from the given pointer by the given byte count."
sidebar:
- order: 272
+ order: 290
---
## ptr_offset()
@@ -11,7 +11,7 @@ sidebar:
function ptr_offset(pointer $pointer, int $offset): mixed
```
-Lowers `ptr_offset(pointer, offset)` by adding a byte offset to a raw address.
+Returns a new pointer offset from the given pointer by the given byte count.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_read16.md b/docs/php/builtins/pointer/ptr_read16.md
index 5ce9b8fd14..929ce1b363 100644
--- a/docs/php/builtins/pointer/ptr_read16.md
+++ b/docs/php/builtins/pointer/ptr_read16.md
@@ -1,8 +1,8 @@
---
title: "ptr_read16()"
-description: "Lowers `ptr_read16(pointer)` by reading one unsigned 16-bit word through a checked pointer."
+description: "Reads one unsigned 16-bit word through a raw pointer and returns it as an integer."
sidebar:
- order: 273
+ order: 291
---
## ptr_read16()
@@ -11,7 +11,7 @@ sidebar:
function ptr_read16(pointer $pointer): int
```
-Lowers `ptr_read16(pointer)` by reading one unsigned 16-bit word through a checked pointer.
+Reads one unsigned 16-bit word through a raw pointer and returns it as an integer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_read32.md b/docs/php/builtins/pointer/ptr_read32.md
index 4e93335e75..c5c3231ce7 100644
--- a/docs/php/builtins/pointer/ptr_read32.md
+++ b/docs/php/builtins/pointer/ptr_read32.md
@@ -1,8 +1,8 @@
---
title: "ptr_read32()"
-description: "Lowers `ptr_read32(pointer)` by reading one unsigned 32-bit word through a checked pointer."
+description: "Reads one unsigned 32-bit word through a raw pointer and returns it as an integer."
sidebar:
- order: 274
+ order: 292
---
## ptr_read32()
@@ -11,7 +11,7 @@ sidebar:
function ptr_read32(pointer $pointer): int
```
-Lowers `ptr_read32(pointer)` by reading one unsigned 32-bit word through a checked pointer.
+Reads one unsigned 32-bit word through a raw pointer and returns it as an integer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_read8.md b/docs/php/builtins/pointer/ptr_read8.md
index 605f453dec..68aa17af17 100644
--- a/docs/php/builtins/pointer/ptr_read8.md
+++ b/docs/php/builtins/pointer/ptr_read8.md
@@ -1,8 +1,8 @@
---
title: "ptr_read8()"
-description: "Lowers `ptr_read8(pointer)` by reading one unsigned byte through a checked pointer."
+description: "Reads one unsigned byte through a raw pointer and returns it as an integer."
sidebar:
- order: 275
+ order: 293
---
## ptr_read8()
@@ -11,7 +11,7 @@ sidebar:
function ptr_read8(pointer $pointer): int
```
-Lowers `ptr_read8(pointer)` by reading one unsigned byte through a checked pointer.
+Reads one unsigned byte through a raw pointer and returns it as an integer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_read_string.md b/docs/php/builtins/pointer/ptr_read_string.md
index fca1020c8b..6254238376 100644
--- a/docs/php/builtins/pointer/ptr_read_string.md
+++ b/docs/php/builtins/pointer/ptr_read_string.md
@@ -1,8 +1,8 @@
---
title: "ptr_read_string()"
-description: "Lowers `ptr_read_string(pointer, length)` by copying raw bytes into an owned PHP string."
+description: "Copies raw bytes from a pointer into a PHP string of the given length."
sidebar:
- order: 276
+ order: 294
---
## ptr_read_string()
@@ -11,7 +11,7 @@ sidebar:
function ptr_read_string(pointer $pointer, int $length): string
```
-Lowers `ptr_read_string(pointer, length)` by copying raw bytes into an owned PHP string.
+Copies raw bytes from a pointer into a PHP string of the given length.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_set.md b/docs/php/builtins/pointer/ptr_set.md
index 69edde40c8..bf78a1d467 100644
--- a/docs/php/builtins/pointer/ptr_set.md
+++ b/docs/php/builtins/pointer/ptr_set.md
@@ -1,8 +1,8 @@
---
title: "ptr_set()"
-description: "Lowers `ptr_set(pointer, value)` by writing one machine word through a checked pointer."
+description: "Writes one machine word through a raw pointer."
sidebar:
- order: 277
+ order: 295
---
## ptr_set()
@@ -11,7 +11,7 @@ sidebar:
function ptr_set(pointer $pointer, mixed $value): void
```
-Lowers `ptr_set(pointer, value)` by writing one machine word through a checked pointer.
+Writes one machine word through a raw pointer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_sizeof.md b/docs/php/builtins/pointer/ptr_sizeof.md
index 0dbdcd96ff..de554e86d7 100644
--- a/docs/php/builtins/pointer/ptr_sizeof.md
+++ b/docs/php/builtins/pointer/ptr_sizeof.md
@@ -1,22 +1,22 @@
---
title: "ptr_sizeof()"
-description: "Lowers `ptr_sizeof(\"type\")` by materializing the checked static byte size."
+description: "Returns the byte size of the named pointer target type."
sidebar:
- order: 278
+ order: 296
---
## ptr_sizeof()
```php
-function ptr_sizeof(string $type): mixed
+function ptr_sizeof(string $type): int
```
-Lowers `ptr_sizeof("type")` by materializing the checked static byte size.
+Returns the byte size of the named pointer target type.
**Parameters**:
- `$type` (`string`)
-**Returns**: `mixed`
+**Returns**: `int`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/pointer/ptr_write16.md b/docs/php/builtins/pointer/ptr_write16.md
index 6eceb1abe7..3ba6deb729 100644
--- a/docs/php/builtins/pointer/ptr_write16.md
+++ b/docs/php/builtins/pointer/ptr_write16.md
@@ -1,8 +1,8 @@
---
title: "ptr_write16()"
-description: "Lowers `ptr_write16(pointer, value)` by writing one 16-bit word through a checked pointer."
+description: "Writes one 16-bit word through a raw pointer."
sidebar:
- order: 279
+ order: 297
---
## ptr_write16()
@@ -11,7 +11,7 @@ sidebar:
function ptr_write16(pointer $pointer, int $value): void
```
-Lowers `ptr_write16(pointer, value)` by writing one 16-bit word through a checked pointer.
+Writes one 16-bit word through a raw pointer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_write32.md b/docs/php/builtins/pointer/ptr_write32.md
index a89852cb66..1d4647c9d4 100644
--- a/docs/php/builtins/pointer/ptr_write32.md
+++ b/docs/php/builtins/pointer/ptr_write32.md
@@ -1,8 +1,8 @@
---
title: "ptr_write32()"
-description: "Lowers `ptr_write32(pointer, value)` by writing one 32-bit word through a checked pointer."
+description: "Writes one 32-bit word through a raw pointer."
sidebar:
- order: 280
+ order: 298
---
## ptr_write32()
@@ -11,7 +11,7 @@ sidebar:
function ptr_write32(pointer $pointer, int $value): void
```
-Lowers `ptr_write32(pointer, value)` by writing one 32-bit word through a checked pointer.
+Writes one 32-bit word through a raw pointer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_write8.md b/docs/php/builtins/pointer/ptr_write8.md
index d903fb5297..d4d0c1474d 100644
--- a/docs/php/builtins/pointer/ptr_write8.md
+++ b/docs/php/builtins/pointer/ptr_write8.md
@@ -1,8 +1,8 @@
---
title: "ptr_write8()"
-description: "Lowers `ptr_write8(pointer, value)` by writing one byte through a checked pointer."
+description: "Writes one byte through a raw pointer."
sidebar:
- order: 281
+ order: 299
---
## ptr_write8()
@@ -11,7 +11,7 @@ sidebar:
function ptr_write8(pointer $pointer, int $value): void
```
-Lowers `ptr_write8(pointer, value)` by writing one byte through a checked pointer.
+Writes one byte through a raw pointer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/pointer/ptr_write_string.md b/docs/php/builtins/pointer/ptr_write_string.md
index 936c658958..373db36ea0 100644
--- a/docs/php/builtins/pointer/ptr_write_string.md
+++ b/docs/php/builtins/pointer/ptr_write_string.md
@@ -1,8 +1,8 @@
---
title: "ptr_write_string()"
-description: "Lowers `ptr_write_string(pointer, string)` by copying PHP string bytes into raw memory."
+description: "Copies PHP string bytes into raw memory at the given pointer."
sidebar:
- order: 282
+ order: 300
---
## ptr_write_string()
@@ -11,7 +11,7 @@ sidebar:
function ptr_write_string(pointer $pointer, string $string): int
```
-Lowers `ptr_write_string(pointer, string)` by copying PHP string bytes into raw memory.
+Copies PHP string bytes into raw memory at the given pointer.
**Parameters**:
- `$pointer` (`pointer`)
diff --git a/docs/php/builtins/process.md b/docs/php/builtins/process.md
index ae2e46e680..dacbecf105 100644
--- a/docs/php/builtins/process.md
+++ b/docs/php/builtins/process.md
@@ -10,13 +10,13 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
| [`die()`](./process/die.md) | `(int $status): void` | `void` |
-| [`exec()`](./process/exec.md) | `(string $command, array $output, int $result_code): string` | `string` |
+| [`exec()`](./process/exec.md) | `(string $command): string` | `string` |
| [`exit()`](./process/exit.md) | `(int $status): void` | `void` |
-| [`passthru()`](./process/passthru.md) | `(string $command, int $result_code): void` | `void` |
+| [`passthru()`](./process/passthru.md) | `(string $command): void` | `void` |
| [`pclose()`](./process/pclose.md) | `(resource $handle): int` | `int` |
| [`popen()`](./process/popen.md) | `(string $command, string $mode): mixed` | `mixed` |
-| [`readline()`](./process/readline.md) | `(string $prompt): mixed` | `mixed` |
+| [`readline()`](./process/readline.md) | `(string $prompt = null): mixed` | `mixed` |
| [`shell_exec()`](./process/shell_exec.md) | `(string $command): string` | `string` |
| [`sleep()`](./process/sleep.md) | `(int $seconds): int` | `int` |
-| [`system()`](./process/system.md) | `(string $command, int $result_code): string` | `string` |
+| [`system()`](./process/system.md) | `(string $command): string` | `string` |
| [`usleep()`](./process/usleep.md) | `(int $microseconds): void` | `void` |
diff --git a/docs/php/builtins/process/die.md b/docs/php/builtins/process/die.md
index e4e9c568f4..00b2780d87 100644
--- a/docs/php/builtins/process/die.md
+++ b/docs/php/builtins/process/die.md
@@ -2,7 +2,7 @@
title: "die()"
description: "die() — process builtin supported by Elephc."
sidebar:
- order: 283
+ order: 301
---
## die()
diff --git a/docs/php/builtins/process/exec.md b/docs/php/builtins/process/exec.md
index d87bc5083d..a4906301cd 100644
--- a/docs/php/builtins/process/exec.md
+++ b/docs/php/builtins/process/exec.md
@@ -1,22 +1,20 @@
---
title: "exec()"
-description: "Lowers `exec(command)` by capturing shell stdout through the shared runtime helper."
+description: "Executes an external program and returns the last line of output."
sidebar:
- order: 284
+ order: 302
---
## exec()
```php
-function exec(string $command, array $output, int $result_code): string
+function exec(string $command): string
```
-Lowers `exec(command)` by capturing shell stdout through the shared runtime helper.
+Executes an external program and returns the last line of output.
**Parameters**:
- `$command` (`string`)
-- `$output` (`array`), passed by reference
-- `$result_code` (`int`), passed by reference
**Returns**: `string`
diff --git a/docs/php/builtins/process/exit.md b/docs/php/builtins/process/exit.md
index 72daab3e6a..d74c1d9415 100644
--- a/docs/php/builtins/process/exit.md
+++ b/docs/php/builtins/process/exit.md
@@ -2,7 +2,7 @@
title: "exit()"
description: "exit() — process builtin supported by Elephc."
sidebar:
- order: 285
+ order: 303
---
## exit()
diff --git a/docs/php/builtins/process/passthru.md b/docs/php/builtins/process/passthru.md
index c4670d922b..95c25c8e40 100644
--- a/docs/php/builtins/process/passthru.md
+++ b/docs/php/builtins/process/passthru.md
@@ -1,21 +1,20 @@
---
title: "passthru()"
-description: "Lowers `passthru(command)` through libc `system()` for direct stdout passthrough."
+description: "Executes an external program and passes its output directly."
sidebar:
- order: 286
+ order: 304
---
## passthru()
```php
-function passthru(string $command, int $result_code): void
+function passthru(string $command): void
```
-Lowers `passthru(command)` through libc `system()` for direct stdout passthrough.
+Executes an external program and passes its output directly.
**Parameters**:
- `$command` (`string`)
-- `$result_code` (`int`), passed by reference
**Returns**: `void`
diff --git a/docs/php/builtins/process/pclose.md b/docs/php/builtins/process/pclose.md
index 730e522cec..3f8ef82cb6 100644
--- a/docs/php/builtins/process/pclose.md
+++ b/docs/php/builtins/process/pclose.md
@@ -1,8 +1,8 @@
---
title: "pclose()"
-description: "Lowers `pclose(handle)` and returns the child process status."
+description: "Closes process file pointer."
sidebar:
- order: 287
+ order: 305
---
## pclose()
@@ -11,7 +11,7 @@ sidebar:
function pclose(resource $handle): int
```
-Lowers `pclose(handle)` and returns the child process status.
+Closes process file pointer.
**Parameters**:
- `$handle` (`resource`)
diff --git a/docs/php/builtins/process/popen.md b/docs/php/builtins/process/popen.md
index 84477f5020..c384e80f68 100644
--- a/docs/php/builtins/process/popen.md
+++ b/docs/php/builtins/process/popen.md
@@ -1,8 +1,8 @@
---
title: "popen()"
-description: "Lowers `popen(command, mode)` and boxes the process pipe as `resource|false`."
+description: "Opens process file pointer."
sidebar:
- order: 288
+ order: 306
---
## popen()
@@ -11,7 +11,7 @@ sidebar:
function popen(string $command, string $mode): mixed
```
-Lowers `popen(command, mode)` and boxes the process pipe as `resource|false`.
+Opens process file pointer.
**Parameters**:
- `$command` (`string`)
diff --git a/docs/php/builtins/process/readline.md b/docs/php/builtins/process/readline.md
index 6ab2a5891d..cb0d5de245 100644
--- a/docs/php/builtins/process/readline.md
+++ b/docs/php/builtins/process/readline.md
@@ -1,20 +1,20 @@
---
title: "readline()"
-description: "Lowers `readline(prompt?)` by optionally writing a prompt and reading stdin."
+description: "Reads a line from the user's terminal."
sidebar:
- order: 289
+ order: 307
---
## readline()
```php
-function readline(string $prompt): mixed
+function readline(string $prompt = null): mixed
```
-Lowers `readline(prompt?)` by optionally writing a prompt and reading stdin.
+Reads a line from the user's terminal.
**Parameters**:
-- `$prompt` (`string`), optional
+- `$prompt` (`string`), default `null`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/process/shell_exec.md b/docs/php/builtins/process/shell_exec.md
index 50b475645f..68cbc6d625 100644
--- a/docs/php/builtins/process/shell_exec.md
+++ b/docs/php/builtins/process/shell_exec.md
@@ -1,8 +1,8 @@
---
title: "shell_exec()"
-description: "Lowers `shell_exec(command)` by capturing shell stdout through the shared runtime helper."
+description: "Executes a command via the shell and returns the complete output as a string."
sidebar:
- order: 290
+ order: 308
---
## shell_exec()
@@ -11,7 +11,7 @@ sidebar:
function shell_exec(string $command): string
```
-Lowers `shell_exec(command)` by capturing shell stdout through the shared runtime helper.
+Executes a command via the shell and returns the complete output as a string.
**Parameters**:
- `$command` (`string`)
diff --git a/docs/php/builtins/process/sleep.md b/docs/php/builtins/process/sleep.md
index 0a303458c0..327b1f7399 100644
--- a/docs/php/builtins/process/sleep.md
+++ b/docs/php/builtins/process/sleep.md
@@ -1,8 +1,8 @@
---
title: "sleep()"
-description: "Lowers `sleep(seconds)` through the target's C library symbol."
+description: "Delays execution for a number of seconds."
sidebar:
- order: 291
+ order: 309
---
## sleep()
@@ -11,7 +11,7 @@ sidebar:
function sleep(int $seconds): int
```
-Lowers `sleep(seconds)` through the target's C library symbol.
+Delays execution for a number of seconds.
**Parameters**:
- `$seconds` (`int`)
diff --git a/docs/php/builtins/process/system.md b/docs/php/builtins/process/system.md
index 5e432fe9ca..84df401224 100644
--- a/docs/php/builtins/process/system.md
+++ b/docs/php/builtins/process/system.md
@@ -1,21 +1,20 @@
---
title: "system()"
-description: "Lowers `system(command)` through libc `system()` and returns the legacy empty string result."
+description: "Executes an external program and displays the output."
sidebar:
- order: 292
+ order: 310
---
## system()
```php
-function system(string $command, int $result_code): string
+function system(string $command): string
```
-Lowers `system(command)` through libc `system()` and returns the legacy empty string result.
+Executes an external program and displays the output.
**Parameters**:
- `$command` (`string`)
-- `$result_code` (`int`), passed by reference
**Returns**: `string`
diff --git a/docs/php/builtins/process/usleep.md b/docs/php/builtins/process/usleep.md
index 361e8dcda5..3b790742f7 100644
--- a/docs/php/builtins/process/usleep.md
+++ b/docs/php/builtins/process/usleep.md
@@ -1,8 +1,8 @@
---
title: "usleep()"
-description: "Lowers `usleep(microseconds)` through the target's C library symbol."
+description: "Delays execution for a number of microseconds."
sidebar:
- order: 293
+ order: 311
---
## usleep()
@@ -11,7 +11,7 @@ sidebar:
function usleep(int $microseconds): void
```
-Lowers `usleep(microseconds)` through the target's C library symbol.
+Delays execution for a number of microseconds.
**Parameters**:
- `$microseconds` (`int`)
diff --git a/docs/php/builtins/regex.md b/docs/php/builtins/regex.md
index 06d27a7aac..bce803832f 100644
--- a/docs/php/builtins/regex.md
+++ b/docs/php/builtins/regex.md
@@ -9,8 +9,8 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`preg_match()`](./regex/preg_match.md) | `(string $pattern, string $subject, array $matches): int` | `int` |
-| [`preg_match_all()`](./regex/preg_match_all.md) | `(string $pattern, string $subject, array $matches): int` | `int` |
-| [`preg_replace()`](./regex/preg_replace.md) | `(string $pattern, string $replacement, string $subject, int $limit = -1, int $count = null): string` | `string` |
-| [`preg_replace_callback()`](./regex/preg_replace_callback.md) | `(string $pattern, callable $callback, string $subject, int $limit = -1, int $count = null, int $flags = 0): array` | `array` |
-| [`preg_split()`](./regex/preg_split.md) | `(string $pattern, string $subject, int $limit, int $flags): array` | `array` |
+| [`preg_match()`](./regex/preg_match.md) | `(string $pattern, string $subject, array $matches = []): int` | `int` |
+| [`preg_match_all()`](./regex/preg_match_all.md) | `(string $pattern, string $subject): int` | `int` |
+| [`preg_replace()`](./regex/preg_replace.md) | `(string $pattern, string $replacement, string $subject): string` | `string` |
+| [`preg_replace_callback()`](./regex/preg_replace_callback.md) | `(string $pattern, callable $callback, string $subject): string` | `string` |
+| [`preg_split()`](./regex/preg_split.md) | `(string $pattern, string $subject, int $limit = -1, int $flags = 0): array` | `array` |
diff --git a/docs/php/builtins/regex/preg_match.md b/docs/php/builtins/regex/preg_match.md
index ddea72625c..481d9f5ea5 100644
--- a/docs/php/builtins/regex/preg_match.md
+++ b/docs/php/builtins/regex/preg_match.md
@@ -1,22 +1,22 @@
---
title: "preg_match()"
-description: "Lowers `preg_match(pattern, subject)` through the shared regex runtime helper."
+description: "Performs a regular expression match."
sidebar:
- order: 294
+ order: 312
---
## preg_match()
```php
-function preg_match(string $pattern, string $subject, array $matches): int
+function preg_match(string $pattern, string $subject, array $matches = []): int
```
-Lowers `preg_match(pattern, subject)` through the shared regex runtime helper.
+Performs a regular expression match.
**Parameters**:
- `$pattern` (`string`)
- `$subject` (`string`)
-- `$matches` (`array`), passed by reference, optional
+- `$matches` (`array`), passed by reference, default `[]`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/regex/preg_match_all.md b/docs/php/builtins/regex/preg_match_all.md
index caa03eb805..42fb2cc236 100644
--- a/docs/php/builtins/regex/preg_match_all.md
+++ b/docs/php/builtins/regex/preg_match_all.md
@@ -1,22 +1,21 @@
---
title: "preg_match_all()"
-description: "Lowers `preg_match_all(pattern, subject)` through the shared regex runtime helper."
+description: "Performs a global regular expression match and returns the number of matches."
sidebar:
- order: 295
+ order: 313
---
## preg_match_all()
```php
-function preg_match_all(string $pattern, string $subject, array $matches): int
+function preg_match_all(string $pattern, string $subject): int
```
-Lowers `preg_match_all(pattern, subject)` through the shared regex runtime helper.
+Performs a global regular expression match and returns the number of matches.
**Parameters**:
- `$pattern` (`string`)
- `$subject` (`string`)
-- `$matches` (`array`), passed by reference
**Returns**: `int`
diff --git a/docs/php/builtins/regex/preg_replace.md b/docs/php/builtins/regex/preg_replace.md
index fff5f64622..434ee8fcb8 100644
--- a/docs/php/builtins/regex/preg_replace.md
+++ b/docs/php/builtins/regex/preg_replace.md
@@ -1,24 +1,22 @@
---
title: "preg_replace()"
-description: "Lowers `preg_replace(pattern, replacement, subject)` through the regex replacement helper."
+description: "Performs a regular expression search and replace."
sidebar:
- order: 296
+ order: 314
---
## preg_replace()
```php
-function preg_replace(string $pattern, string $replacement, string $subject, int $limit = -1, int $count = null): string
+function preg_replace(string $pattern, string $replacement, string $subject): string
```
-Lowers `preg_replace(pattern, replacement, subject)` through the regex replacement helper.
+Performs a regular expression search and replace.
**Parameters**:
- `$pattern` (`string`)
- `$replacement` (`string`)
- `$subject` (`string`)
-- `$limit` (`int`), default `-1`, optional
-- `$count` (`int`), passed by reference, default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/regex/preg_replace_callback.md b/docs/php/builtins/regex/preg_replace_callback.md
index 5945c461be..a50397cf54 100644
--- a/docs/php/builtins/regex/preg_replace_callback.md
+++ b/docs/php/builtins/regex/preg_replace_callback.md
@@ -1,27 +1,24 @@
---
title: "preg_replace_callback()"
-description: "Lowers `preg_replace_callback(pattern, callback, subject)` through supported direct callbacks."
+description: "Performs a regular expression search and replace using a callback."
sidebar:
- order: 297
+ order: 315
---
## preg_replace_callback()
```php
-function preg_replace_callback(string $pattern, callable $callback, string $subject, int $limit = -1, int $count = null, int $flags = 0): array
+function preg_replace_callback(string $pattern, callable $callback, string $subject): string
```
-Lowers `preg_replace_callback(pattern, callback, subject)` through supported direct callbacks.
+Performs a regular expression search and replace using a callback.
**Parameters**:
- `$pattern` (`string`)
- `$callback` (`callable`)
- `$subject` (`string`)
-- `$limit` (`int`), default `-1`, optional
-- `$count` (`int`), passed by reference, default `null`, optional
-- `$flags` (`int`), default `0`, optional
-**Returns**: `array`
+**Returns**: `string`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/regex/preg_split.md b/docs/php/builtins/regex/preg_split.md
index 518d8ec2c0..1e63c8af68 100644
--- a/docs/php/builtins/regex/preg_split.md
+++ b/docs/php/builtins/regex/preg_split.md
@@ -1,23 +1,23 @@
---
title: "preg_split()"
-description: "Lowers `preg_split(pattern, subject, limit?, flags?)` through the regex split helper."
+description: "Splits a string by a regular expression."
sidebar:
- order: 298
+ order: 316
---
## preg_split()
```php
-function preg_split(string $pattern, string $subject, int $limit, int $flags): array
+function preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array
```
-Lowers `preg_split(pattern, subject, limit?, flags?)` through the regex split helper.
+Splits a string by a regular expression.
**Parameters**:
- `$pattern` (`string`)
- `$subject` (`string`)
-- `$limit` (`int`), optional
-- `$flags` (`int`), optional
+- `$limit` (`int`), default `-1`, optional
+- `$flags` (`int`), default `0`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/spl.md b/docs/php/builtins/spl.md
index 0041b8e697..c94612f328 100644
--- a/docs/php/builtins/spl.md
+++ b/docs/php/builtins/spl.md
@@ -9,14 +9,14 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`iterator_apply()`](./spl/iterator_apply.md) | `(traversable $iterator, callable $callback, array $args): int` | `int` |
+| [`iterator_apply()`](./spl/iterator_apply.md) | `(traversable $iterator, callable $callback, array $args = null): int` | `int` |
| [`iterator_count()`](./spl/iterator_count.md) | `(traversable $iterator): int` | `int` |
-| [`iterator_to_array()`](./spl/iterator_to_array.md) | `(traversable $iterator, bool $preserve_keys): array` | `array` |
-| [`spl_autoload()`](./spl/spl_autoload.md) | `(string $class, string $file_extensions): void` | `void` |
+| [`iterator_to_array()`](./spl/iterator_to_array.md) | `(traversable $iterator, bool $preserve_keys = true): array` | `array` |
+| [`spl_autoload()`](./spl/spl_autoload.md) | `(string $class, string $file_extensions = null): void` | `void` |
| [`spl_autoload_call()`](./spl/spl_autoload_call.md) | `(string $class): void` | `void` |
-| [`spl_autoload_extensions()`](./spl/spl_autoload_extensions.md) | `(string $file_extensions): string` | `string` |
+| [`spl_autoload_extensions()`](./spl/spl_autoload_extensions.md) | `(string $file_extensions = null): string` | `string` |
| [`spl_autoload_functions()`](./spl/spl_autoload_functions.md) | `(): array` | `array` |
-| [`spl_autoload_register()`](./spl/spl_autoload_register.md) | `(callable $callback, bool $throw, bool $prepend): bool` | `bool` |
+| [`spl_autoload_register()`](./spl/spl_autoload_register.md) | `(callable $callback = null, bool $throw = true, bool $prepend = false): bool` | `bool` |
| [`spl_autoload_unregister()`](./spl/spl_autoload_unregister.md) | `(callable $callback): bool` | `bool` |
| [`spl_classes()`](./spl/spl_classes.md) | `(): array` | `array` |
| [`spl_object_hash()`](./spl/spl_object_hash.md) | `(object $object): string` | `string` |
diff --git a/docs/php/builtins/spl/iterator_apply.md b/docs/php/builtins/spl/iterator_apply.md
index 5720a2874f..dc02145650 100644
--- a/docs/php/builtins/spl/iterator_apply.md
+++ b/docs/php/builtins/spl/iterator_apply.md
@@ -1,22 +1,22 @@
---
title: "iterator_apply()"
-description: "Lowers `iterator_apply()` over supported Traversable sources and callback forms."
+description: "Call a function for every element in an iterator."
sidebar:
- order: 299
+ order: 317
---
## iterator_apply()
```php
-function iterator_apply(traversable $iterator, callable $callback, array $args): int
+function iterator_apply(traversable $iterator, callable $callback, array $args = null): int
```
-Lowers `iterator_apply()` over supported Traversable sources and callback forms.
+Call a function for every element in an iterator.
**Parameters**:
- `$iterator` (`traversable`)
- `$callback` (`callable`)
-- `$args` (`array`), optional
+- `$args` (`array`), default `null`, optional
**Returns**: `int`
diff --git a/docs/php/builtins/spl/iterator_count.md b/docs/php/builtins/spl/iterator_count.md
index 45dc100556..43f4e9c7e4 100644
--- a/docs/php/builtins/spl/iterator_count.md
+++ b/docs/php/builtins/spl/iterator_count.md
@@ -1,8 +1,8 @@
---
title: "iterator_count()"
-description: "Lowers `iterator_count()` over arrays, `iterable`, and Traversable objects."
+description: "Count the elements in an iterator."
sidebar:
- order: 300
+ order: 318
---
## iterator_count()
@@ -11,7 +11,7 @@ sidebar:
function iterator_count(traversable $iterator): int
```
-Lowers `iterator_count()` over arrays, `iterable`, and Traversable objects.
+Count the elements in an iterator.
**Parameters**:
- `$iterator` (`traversable`)
diff --git a/docs/php/builtins/spl/iterator_to_array.md b/docs/php/builtins/spl/iterator_to_array.md
index c0e61c92c3..bfe3ff5d63 100644
--- a/docs/php/builtins/spl/iterator_to_array.md
+++ b/docs/php/builtins/spl/iterator_to_array.md
@@ -1,21 +1,21 @@
---
title: "iterator_to_array()"
-description: "Lowers `iterator_to_array()` over arrays, `iterable`, and Traversable objects."
+description: "Copy the iterator into an array."
sidebar:
- order: 301
+ order: 319
---
## iterator_to_array()
```php
-function iterator_to_array(traversable $iterator, bool $preserve_keys): array
+function iterator_to_array(traversable $iterator, bool $preserve_keys = true): array
```
-Lowers `iterator_to_array()` over arrays, `iterable`, and Traversable objects.
+Copy the iterator into an array.
**Parameters**:
- `$iterator` (`traversable`)
-- `$preserve_keys` (`bool`), optional
+- `$preserve_keys` (`bool`), default `true`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/spl/spl_autoload.md b/docs/php/builtins/spl/spl_autoload.md
index 865bd54aad..cd32f0639f 100644
--- a/docs/php/builtins/spl/spl_autoload.md
+++ b/docs/php/builtins/spl/spl_autoload.md
@@ -1,21 +1,21 @@
---
title: "spl_autoload()"
-description: "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
+description: "Default implementation for __autoload()."
sidebar:
- order: 302
+ order: 320
---
## spl_autoload()
```php
-function spl_autoload(string $class, string $file_extensions): void
+function spl_autoload(string $class, string $file_extensions = null): void
```
-Lowers no-op autoload calls by preserving arg effects and returning PHP null if used.
+Default implementation for __autoload().
**Parameters**:
- `$class` (`string`)
-- `$file_extensions` (`string`), optional
+- `$file_extensions` (`string`), default `null`, optional
**Returns**: `void`
diff --git a/docs/php/builtins/spl/spl_autoload_call.md b/docs/php/builtins/spl/spl_autoload_call.md
index 76ed1936ab..e8fd92ac37 100644
--- a/docs/php/builtins/spl/spl_autoload_call.md
+++ b/docs/php/builtins/spl/spl_autoload_call.md
@@ -1,8 +1,8 @@
---
title: "spl_autoload_call()"
-description: "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
+description: "Try all registered __autoload() functions to load the requested class."
sidebar:
- order: 303
+ order: 321
---
## spl_autoload_call()
@@ -11,7 +11,7 @@ sidebar:
function spl_autoload_call(string $class): void
```
-Lowers no-op autoload calls by preserving arg effects and returning PHP null if used.
+Try all registered __autoload() functions to load the requested class.
**Parameters**:
- `$class` (`string`)
diff --git a/docs/php/builtins/spl/spl_autoload_extensions.md b/docs/php/builtins/spl/spl_autoload_extensions.md
index 6431f96bb6..2064f3fa38 100644
--- a/docs/php/builtins/spl/spl_autoload_extensions.md
+++ b/docs/php/builtins/spl/spl_autoload_extensions.md
@@ -1,20 +1,20 @@
---
title: "spl_autoload_extensions()"
-description: "Lowers `spl_autoload_extensions()` against the legacy mutable extension globals."
+description: "Register and return default file extensions for spl_autoload."
sidebar:
- order: 304
+ order: 322
---
## spl_autoload_extensions()
```php
-function spl_autoload_extensions(string $file_extensions): string
+function spl_autoload_extensions(string $file_extensions = null): string
```
-Lowers `spl_autoload_extensions()` against the legacy mutable extension globals.
+Register and return default file extensions for spl_autoload.
**Parameters**:
-- `$file_extensions` (`string`), optional
+- `$file_extensions` (`string`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/spl/spl_autoload_functions.md b/docs/php/builtins/spl/spl_autoload_functions.md
index ed11b72f73..4923f54bac 100644
--- a/docs/php/builtins/spl/spl_autoload_functions.md
+++ b/docs/php/builtins/spl/spl_autoload_functions.md
@@ -1,8 +1,8 @@
---
title: "spl_autoload_functions()"
-description: "Lowers `spl_autoload_functions()` to an indexed array of AOT rule placeholders."
+description: "Return all registered __autoload() functions."
sidebar:
- order: 305
+ order: 323
---
## spl_autoload_functions()
@@ -11,7 +11,7 @@ sidebar:
function spl_autoload_functions(): array
```
-Lowers `spl_autoload_functions()` to an indexed array of AOT rule placeholders.
+Return all registered __autoload() functions.
**Parameters**: none.
diff --git a/docs/php/builtins/spl/spl_autoload_register.md b/docs/php/builtins/spl/spl_autoload_register.md
index e4166a6d7d..8cfa2dcb01 100644
--- a/docs/php/builtins/spl/spl_autoload_register.md
+++ b/docs/php/builtins/spl/spl_autoload_register.md
@@ -1,22 +1,22 @@
---
title: "spl_autoload_register()"
-description: "Lowers autoload registration stubs by preserving arg effects and returning true."
+description: "Register given function as __autoload() implementation."
sidebar:
- order: 306
+ order: 324
---
## spl_autoload_register()
```php
-function spl_autoload_register(callable $callback, bool $throw, bool $prepend): bool
+function spl_autoload_register(callable $callback = null, bool $throw = true, bool $prepend = false): bool
```
-Lowers autoload registration stubs by preserving arg effects and returning true.
+Register given function as __autoload() implementation.
**Parameters**:
-- `$callback` (`callable`), optional
-- `$throw` (`bool`), optional
-- `$prepend` (`bool`), optional
+- `$callback` (`callable`), default `null`, optional
+- `$throw` (`bool`), default `true`, optional
+- `$prepend` (`bool`), default `false`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/spl/spl_autoload_unregister.md b/docs/php/builtins/spl/spl_autoload_unregister.md
index 132836e70e..76efe13a57 100644
--- a/docs/php/builtins/spl/spl_autoload_unregister.md
+++ b/docs/php/builtins/spl/spl_autoload_unregister.md
@@ -1,8 +1,8 @@
---
title: "spl_autoload_unregister()"
-description: "Lowers autoload registration stubs by preserving arg effects and returning true."
+description: "Unregister given function as __autoload() implementation."
sidebar:
- order: 307
+ order: 325
---
## spl_autoload_unregister()
@@ -11,7 +11,7 @@ sidebar:
function spl_autoload_unregister(callable $callback): bool
```
-Lowers autoload registration stubs by preserving arg effects and returning true.
+Unregister given function as __autoload() implementation.
**Parameters**:
- `$callback` (`callable`)
diff --git a/docs/php/builtins/spl/spl_classes.md b/docs/php/builtins/spl/spl_classes.md
index daaa2d3aa8..fe3f1ec019 100644
--- a/docs/php/builtins/spl/spl_classes.md
+++ b/docs/php/builtins/spl/spl_classes.md
@@ -1,8 +1,8 @@
---
title: "spl_classes()"
-description: "Lowers `spl_classes()` to the static compiler-shipped SPL/core type snapshot."
+description: "Return available SPL classes."
sidebar:
- order: 308
+ order: 326
---
## spl_classes()
@@ -11,7 +11,7 @@ sidebar:
function spl_classes(): array
```
-Lowers `spl_classes()` to the static compiler-shipped SPL/core type snapshot.
+Return available SPL classes.
**Parameters**: none.
diff --git a/docs/php/builtins/spl/spl_object_hash.md b/docs/php/builtins/spl/spl_object_hash.md
index ed80db38a9..14c5edb5e7 100644
--- a/docs/php/builtins/spl/spl_object_hash.md
+++ b/docs/php/builtins/spl/spl_object_hash.md
@@ -1,8 +1,8 @@
---
title: "spl_object_hash()"
-description: "Lowers `spl_object_hash(object)` by formatting the loaded object pointer as a string."
+description: "Return hash id for given object."
sidebar:
- order: 309
+ order: 327
---
## spl_object_hash()
@@ -11,7 +11,7 @@ sidebar:
function spl_object_hash(object $object): string
```
-Lowers `spl_object_hash(object)` by formatting the loaded object pointer as a string.
+Return hash id for given object.
**Parameters**:
- `$object` (`object`)
diff --git a/docs/php/builtins/spl/spl_object_id.md b/docs/php/builtins/spl/spl_object_id.md
index 3445fc66bc..f2adf84e6a 100644
--- a/docs/php/builtins/spl/spl_object_id.md
+++ b/docs/php/builtins/spl/spl_object_id.md
@@ -1,8 +1,8 @@
---
title: "spl_object_id()"
-description: "Lowers `spl_object_id(object)` by returning the loaded object pointer as an integer."
+description: "Return the integer object handle for given object."
sidebar:
- order: 310
+ order: 328
---
## spl_object_id()
@@ -11,7 +11,7 @@ sidebar:
function spl_object_id(object $object): int
```
-Lowers `spl_object_id(object)` by returning the loaded object pointer as an integer.
+Return the integer object handle for given object.
**Parameters**:
- `$object` (`object`)
diff --git a/docs/php/builtins/streams.md b/docs/php/builtins/streams.md
index 5af11fc0cf..a2ab662d99 100644
--- a/docs/php/builtins/streams.md
+++ b/docs/php/builtins/streams.md
@@ -9,9 +9,9 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
-| [`fsockopen()`](./streams/fsockopen.md) | `(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed` | `mixed` |
-| [`pfsockopen()`](./streams/pfsockopen.md) | `(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed` | `mixed` |
+| [`fsockopen()`](./streams/fsockopen.md) | `(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed` | `mixed` |
+| [`pfsockopen()`](./streams/pfsockopen.md) | `(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed` | `mixed` |
| [`stream_bucket_append()`](./streams/stream_bucket_append.md) | `(mixed $brigade, mixed $bucket): void` | `void` |
| [`stream_bucket_prepend()`](./streams/stream_bucket_prepend.md) | `(mixed $brigade, mixed $bucket): void` | `void` |
-| [`stream_filter_append()`](./streams/stream_filter_append.md) | `(resource $stream, string $filter_name, int $mode, mixed $params): mixed` | `mixed` |
-| [`stream_filter_prepend()`](./streams/stream_filter_prepend.md) | `(resource $stream, string $filter_name, int $mode, mixed $params): mixed` | `mixed` |
+| [`stream_filter_append()`](./streams/stream_filter_append.md) | `(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed` | `mixed` |
+| [`stream_filter_prepend()`](./streams/stream_filter_prepend.md) | `(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed` | `mixed` |
diff --git a/docs/php/builtins/streams/fsockopen.md b/docs/php/builtins/streams/fsockopen.md
index 667ea54dd9..ed08cb072e 100644
--- a/docs/php/builtins/streams/fsockopen.md
+++ b/docs/php/builtins/streams/fsockopen.md
@@ -1,24 +1,24 @@
---
title: "fsockopen()"
-description: "fsockopen() — streams builtin supported by Elephc."
+description: "Open Internet or Unix domain socket connection."
sidebar:
- order: 311
+ order: 329
---
## fsockopen()
```php
-function fsockopen(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed
+function fsockopen(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed
```
-`fsockopen()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Open Internet or Unix domain socket connection.
**Parameters**:
- `$hostname` (`string`)
- `$port` (`int`)
-- `$error_code` (`int`), passed by reference, optional
-- `$error_message` (`string`), passed by reference, optional
-- `$timeout` (`float`), optional
+- `$error_code` (`int`), passed by reference, default `null`, optional
+- `$error_message` (`string`), passed by reference, default `null`, optional
+- `$timeout` (`float`), default `null`, optional
**Returns**: `mixed`
@@ -30,3 +30,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `fsockopen` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/fsockopen.md).
+
diff --git a/docs/php/builtins/streams/pfsockopen.md b/docs/php/builtins/streams/pfsockopen.md
index 7bd8d5a712..747af755ed 100644
--- a/docs/php/builtins/streams/pfsockopen.md
+++ b/docs/php/builtins/streams/pfsockopen.md
@@ -1,24 +1,24 @@
---
title: "pfsockopen()"
-description: "pfsockopen() — streams builtin supported by Elephc."
+description: "Open persistent Internet or Unix domain socket connection."
sidebar:
- order: 312
+ order: 330
---
## pfsockopen()
```php
-function pfsockopen(string $hostname, int $port, int $error_code, string $error_message, float $timeout): mixed
+function pfsockopen(string $hostname, int $port, int $error_code = null, string $error_message = null, float $timeout = null): mixed
```
-`pfsockopen()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Open persistent Internet or Unix domain socket connection.
**Parameters**:
- `$hostname` (`string`)
- `$port` (`int`)
-- `$error_code` (`int`), passed by reference, optional
-- `$error_message` (`string`), passed by reference, optional
-- `$timeout` (`float`), optional
+- `$error_code` (`int`), passed by reference, default `null`, optional
+- `$error_message` (`string`), passed by reference, default `null`, optional
+- `$timeout` (`float`), default `null`, optional
**Returns**: `mixed`
@@ -30,3 +30,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `pfsockopen` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/pfsockopen.md).
+
diff --git a/docs/php/builtins/streams/stream_bucket_append.md b/docs/php/builtins/streams/stream_bucket_append.md
index 26ce7bfa13..8ba572a055 100644
--- a/docs/php/builtins/streams/stream_bucket_append.md
+++ b/docs/php/builtins/streams/stream_bucket_append.md
@@ -1,8 +1,8 @@
---
title: "stream_bucket_append()"
-description: "stream_bucket_append() — streams builtin supported by Elephc."
+description: "Appends a bucket to the brigade."
sidebar:
- order: 313
+ order: 331
---
## stream_bucket_append()
@@ -11,7 +11,7 @@ sidebar:
function stream_bucket_append(mixed $brigade, mixed $bucket): void
```
-`stream_bucket_append()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Appends a bucket to the brigade.
**Parameters**:
- `$brigade` (`mixed`)
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `stream_bucket_append` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/stream_bucket_append.md).
+
diff --git a/docs/php/builtins/streams/stream_bucket_prepend.md b/docs/php/builtins/streams/stream_bucket_prepend.md
index 0f8a015932..fa6d27bff8 100644
--- a/docs/php/builtins/streams/stream_bucket_prepend.md
+++ b/docs/php/builtins/streams/stream_bucket_prepend.md
@@ -1,8 +1,8 @@
---
title: "stream_bucket_prepend()"
-description: "stream_bucket_prepend() — streams builtin supported by Elephc."
+description: "Prepends a bucket to the brigade."
sidebar:
- order: 314
+ order: 332
---
## stream_bucket_prepend()
@@ -11,7 +11,7 @@ sidebar:
function stream_bucket_prepend(mixed $brigade, mixed $bucket): void
```
-`stream_bucket_prepend()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Prepends a bucket to the brigade.
**Parameters**:
- `$brigade` (`mixed`)
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `stream_bucket_prepend` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/stream_bucket_prepend.md).
+
diff --git a/docs/php/builtins/streams/stream_filter_append.md b/docs/php/builtins/streams/stream_filter_append.md
index 4f7dc54cc8..503f83bc7b 100644
--- a/docs/php/builtins/streams/stream_filter_append.md
+++ b/docs/php/builtins/streams/stream_filter_append.md
@@ -1,23 +1,23 @@
---
title: "stream_filter_append()"
-description: "stream_filter_append() — streams builtin supported by Elephc."
+description: "Attaches a filter to a stream."
sidebar:
- order: 315
+ order: 333
---
## stream_filter_append()
```php
-function stream_filter_append(resource $stream, string $filter_name, int $mode, mixed $params): mixed
+function stream_filter_append(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed
```
-`stream_filter_append()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Attaches a filter to a stream.
**Parameters**:
- `$stream` (`resource`)
-- `$filter_name` (`string`)
-- `$mode` (`int`), optional
-- `$params` (`mixed`), optional
+- `$filtername` (`string`)
+- `$read_write` (`int`), default `3`, optional
+- `$params` (`mixed`), default `null`, optional
**Returns**: `mixed`
@@ -29,3 +29,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `stream_filter_append` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/stream_filter_append.md).
+
diff --git a/docs/php/builtins/streams/stream_filter_prepend.md b/docs/php/builtins/streams/stream_filter_prepend.md
index 714dcfc4c3..15ee3fcc5c 100644
--- a/docs/php/builtins/streams/stream_filter_prepend.md
+++ b/docs/php/builtins/streams/stream_filter_prepend.md
@@ -1,23 +1,23 @@
---
title: "stream_filter_prepend()"
-description: "stream_filter_prepend() — streams builtin supported by Elephc."
+description: "Attaches a filter to a stream (prepend)."
sidebar:
- order: 316
+ order: 334
---
## stream_filter_prepend()
```php
-function stream_filter_prepend(resource $stream, string $filter_name, int $mode, mixed $params): mixed
+function stream_filter_prepend(resource $stream, string $filtername, int $read_write = 3, mixed $params = null): mixed
```
-`stream_filter_prepend()` is a streams builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Attaches a filter to a stream (prepend).
**Parameters**:
- `$stream` (`resource`)
-- `$filter_name` (`string`)
-- `$mode` (`int`), optional
-- `$params` (`mixed`), optional
+- `$filtername` (`string`)
+- `$read_write` (`int`), default `3`, optional
+- `$params` (`mixed`), default `null`, optional
**Returns**: `mixed`
@@ -29,3 +29,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `stream_filter_prepend` is implemented in the compiler, see [the internals page](../../../internals/builtins/streams/stream_filter_prepend.md).
+
diff --git a/docs/php/builtins/string.md b/docs/php/builtins/string.md
index bf2eda7a71..2ec01ad8d9 100644
--- a/docs/php/builtins/string.md
+++ b/docs/php/builtins/string.md
@@ -10,73 +10,73 @@ sidebar:
| Function | Signature | Returns |
|---|---|---|
| [`addslashes()`](./string/addslashes.md) | `(string $string): string` | `string` |
-| [`base64_decode()`](./string/base64_decode.md) | `(string $string, bool $strict): string` | `string` |
+| [`base64_decode()`](./string/base64_decode.md) | `(string $string): string` | `string` |
| [`base64_encode()`](./string/base64_encode.md) | `(string $string): string` | `string` |
| [`bin2hex()`](./string/bin2hex.md) | `(string $string): string` | `string` |
-| [`chop()`](./string/chop.md) | `(string $string, string $characters): string` | `string` |
+| [`chop()`](./string/chop.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
| [`chr()`](./string/chr.md) | `(int $codepoint): string` | `string` |
| [`crc32()`](./string/crc32.md) | `(string $string): int` | `int` |
-| [`explode()`](./string/explode.md) | `(string $separator, string $string, int $limit): array` | `array` |
+| [`explode()`](./string/explode.md) | `(string $separator, string $string, int $limit = PHP_INT_MAX): array` | `array` |
| [`grapheme_strrev()`](./string/grapheme_strrev.md) | `(string $string): mixed` | `mixed` |
-| [`gzcompress()`](./string/gzcompress.md) | `(string $data, int $level, int $encoding): string` | `string` |
-| [`gzdeflate()`](./string/gzdeflate.md) | `(string $data, int $level, int $encoding): string` | `string` |
-| [`gzinflate()`](./string/gzinflate.md) | `(string $data, int $max_length): string` | `string` |
-| [`gzuncompress()`](./string/gzuncompress.md) | `(string $data, int $max_length): string` | `string` |
-| [`hash()`](./string/hash.md) | `(string $algo, string $data, bool $binary = false, array $options = []): string` | `string` |
+| [`gzcompress()`](./string/gzcompress.md) | `(string $data, int $level = -1): string` | `string` |
+| [`gzdeflate()`](./string/gzdeflate.md) | `(string $data, int $level = -1): string` | `string` |
+| [`gzinflate()`](./string/gzinflate.md) | `(string $data, int $max_length = 0): mixed` | `mixed` |
+| [`gzuncompress()`](./string/gzuncompress.md) | `(string $data, int $max_length = 0): mixed` | `mixed` |
+| [`hash()`](./string/hash.md) | `(string $algo, string $data, bool $binary = false): string` | `string` |
| [`hash_algos()`](./string/hash_algos.md) | `(): array` | `array` |
| [`hash_copy()`](./string/hash_copy.md) | `(resource $context): mixed` | `mixed` |
| [`hash_equals()`](./string/hash_equals.md) | `(string $known_string, string $user_string): bool` | `bool` |
-| [`hash_final()`](./string/hash_final.md) | `(resource $context, bool $binary): string` | `string` |
-| [`hash_hmac()`](./string/hash_hmac.md) | `(string $algo, string $data, string $key, bool $binary): string` | `string` |
-| [`hash_init()`](./string/hash_init.md) | `(string $algo, int $flags = 0, string $key = '', array $options = []): mixed` | `mixed` |
+| [`hash_final()`](./string/hash_final.md) | `(resource $context, bool $binary = false): string` | `string` |
+| [`hash_hmac()`](./string/hash_hmac.md) | `(string $algo, string $data, string $key, bool $binary = false): string` | `string` |
+| [`hash_init()`](./string/hash_init.md) | `(string $algo, int $flags = 0, string $key = ''): mixed` | `mixed` |
| [`hash_update()`](./string/hash_update.md) | `(resource $context, string $data): bool` | `bool` |
| [`hex2bin()`](./string/hex2bin.md) | `(string $string): string` | `string` |
-| [`html_entity_decode()`](./string/html_entity_decode.md) | `(string $string, int $flags, string $encoding): string` | `string` |
-| [`htmlentities()`](./string/htmlentities.md) | `(string $string, int $flags, string $encoding, bool $double_encode): string` | `string` |
-| [`htmlspecialchars()`](./string/htmlspecialchars.md) | `(string $string, int $flags, string $encoding, bool $double_encode): string` | `string` |
-| [`implode()`](./string/implode.md) | `(string $separator, array $array): string` | `string` |
+| [`html_entity_decode()`](./string/html_entity_decode.md) | `(string $string): string` | `string` |
+| [`htmlentities()`](./string/htmlentities.md) | `(string $string): string` | `string` |
+| [`htmlspecialchars()`](./string/htmlspecialchars.md) | `(string $string): string` | `string` |
+| [`implode()`](./string/implode.md) | `(string $separator, array $array = null): string` | `string` |
| [`inet_ntop()`](./string/inet_ntop.md) | `(string $ip): mixed` | `mixed` |
| [`inet_pton()`](./string/inet_pton.md) | `(string $ip): mixed` | `mixed` |
| [`ip2long()`](./string/ip2long.md) | `(string $ip): mixed` | `mixed` |
| [`lcfirst()`](./string/lcfirst.md) | `(string $string): string` | `string` |
| [`long2ip()`](./string/long2ip.md) | `(int $ip): string` | `string` |
-| [`ltrim()`](./string/ltrim.md) | `(string $string, string $characters): string` | `string` |
-| [`md5()`](./string/md5.md) | `(string $string, bool $binary): string` | `string` |
-| [`nl2br()`](./string/nl2br.md) | `(string $string, bool $use_xhtml): string` | `string` |
-| [`number_format()`](./string/number_format.md) | `(float $num, int $decimals, string $decimal_separator, string $thousands_separator): string` | `string` |
+| [`ltrim()`](./string/ltrim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
+| [`md5()`](./string/md5.md) | `(string $string, bool $binary = false): string` | `string` |
+| [`nl2br()`](./string/nl2br.md) | `(string $string): string` | `string` |
+| [`number_format()`](./string/number_format.md) | `(float $num, int $decimals = 0, string $decimal_separator = '.', string $thousands_separator = ','): string` | `string` |
| [`ord()`](./string/ord.md) | `(string $character): int` | `int` |
| [`printf()`](./string/printf.md) | `(string $format, ...$values): int` | `int` |
| [`rawurldecode()`](./string/rawurldecode.md) | `(string $string): string` | `string` |
| [`rawurlencode()`](./string/rawurlencode.md) | `(string $string): string` | `string` |
-| [`rtrim()`](./string/rtrim.md) | `(string $string, string $characters): string` | `string` |
-| [`sha1()`](./string/sha1.md) | `(string $string, bool $binary): string` | `string` |
+| [`rtrim()`](./string/rtrim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
+| [`sha1()`](./string/sha1.md) | `(string $string, bool $binary = false): string` | `string` |
| [`sprintf()`](./string/sprintf.md) | `(string $format, ...$values): string` | `string` |
| [`sscanf()`](./string/sscanf.md) | `(string $string, string $format, ...$vars): array` | `array` |
| [`str_contains()`](./string/str_contains.md) | `(string $haystack, string $needle): bool` | `bool` |
| [`str_ends_with()`](./string/str_ends_with.md) | `(string $haystack, string $needle): bool` | `bool` |
-| [`str_ireplace()`](./string/str_ireplace.md) | `(mixed $search, mixed $replace, mixed $subject, int $count): mixed` | `mixed` |
-| [`str_pad()`](./string/str_pad.md) | `(string $string, int $length, string $pad_string, int $pad_type): string` | `string` |
+| [`str_ireplace()`](./string/str_ireplace.md) | `(string $search, string $replace, string $subject, int $count = null): string` | `string` |
+| [`str_pad()`](./string/str_pad.md) | `(string $string, int $length, string $pad_string = ' ', int $pad_type = 1): string` | `string` |
| [`str_repeat()`](./string/str_repeat.md) | `(string $string, int $times): string` | `string` |
-| [`str_replace()`](./string/str_replace.md) | `(string $search, string $replace, string $subject, int $count): mixed` | `mixed` |
-| [`str_split()`](./string/str_split.md) | `(string $string, int $length): array` | `array` |
+| [`str_replace()`](./string/str_replace.md) | `(string $search, string $replace, string $subject, int $count = null): string` | `string` |
+| [`str_split()`](./string/str_split.md) | `(string $string, int $length = 1): array` | `array` |
| [`str_starts_with()`](./string/str_starts_with.md) | `(string $haystack, string $needle): bool` | `bool` |
| [`strcasecmp()`](./string/strcasecmp.md) | `(string $string1, string $string2): int` | `int` |
| [`strcmp()`](./string/strcmp.md) | `(string $string1, string $string2): int` | `int` |
| [`stripslashes()`](./string/stripslashes.md) | `(string $string): string` | `string` |
| [`strlen()`](./string/strlen.md) | `(string $string): int` | `int` |
-| [`strpos()`](./string/strpos.md) | `(string $haystack, string $needle, int $offset): mixed` | `mixed` |
+| [`strpos()`](./string/strpos.md) | `(string $haystack, string $needle, int $offset = 0): mixed` | `mixed` |
| [`strrev()`](./string/strrev.md) | `(string $string): string` | `string` |
-| [`strrpos()`](./string/strrpos.md) | `(string $haystack, string $needle, int $offset): mixed` | `mixed` |
-| [`strstr()`](./string/strstr.md) | `(string $haystack, string $needle, bool $before_needle): string` | `string` |
+| [`strrpos()`](./string/strrpos.md) | `(string $haystack, string $needle, int $offset = 0): mixed` | `mixed` |
+| [`strstr()`](./string/strstr.md) | `(string $haystack, string $needle, bool $before_needle = false): string` | `string` |
| [`strtolower()`](./string/strtolower.md) | `(string $string): string` | `string` |
| [`strtoupper()`](./string/strtoupper.md) | `(string $string): string` | `string` |
-| [`substr()`](./string/substr.md) | `(string $string, int $offset, int $length): string` | `string` |
-| [`substr_replace()`](./string/substr_replace.md) | `(string $string, string $replace, int $offset, int $length): string` | `string` |
-| [`trim()`](./string/trim.md) | `(string $string, string $characters): string` | `string` |
+| [`substr()`](./string/substr.md) | `(string $string, int $offset, int $length = null): string` | `string` |
+| [`substr_replace()`](./string/substr_replace.md) | `(string $string, string $replace, int $offset, int $length = null): string` | `string` |
+| [`trim()`](./string/trim.md) | `(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string` | `string` |
| [`ucfirst()`](./string/ucfirst.md) | `(string $string): string` | `string` |
-| [`ucwords()`](./string/ucwords.md) | `(string $string, string $separators): string` | `string` |
+| [`ucwords()`](./string/ucwords.md) | `(string $string, string $separators = ' \t\r\n\x0c\x0b'): string` | `string` |
| [`urldecode()`](./string/urldecode.md) | `(string $string): string` | `string` |
| [`urlencode()`](./string/urlencode.md) | `(string $string): string` | `string` |
| [`vprintf()`](./string/vprintf.md) | `(string $format, array $values): int` | `int` |
| [`vsprintf()`](./string/vsprintf.md) | `(string $format, array $values): string` | `string` |
-| [`wordwrap()`](./string/wordwrap.md) | `(string $string, int $width, string $break, bool $cut_long_words): string` | `string` |
+| [`wordwrap()`](./string/wordwrap.md) | `(string $string, int $width = 75, string $break = '\n', bool $cut_long_words = false): string` | `string` |
diff --git a/docs/php/builtins/string/addslashes.md b/docs/php/builtins/string/addslashes.md
index 11cfaf7eab..7a455bc8b3 100644
--- a/docs/php/builtins/string/addslashes.md
+++ b/docs/php/builtins/string/addslashes.md
@@ -1,8 +1,8 @@
---
title: "addslashes()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Adds backslashes before characters that need to be escaped."
sidebar:
- order: 317
+ order: 335
---
## addslashes()
@@ -11,7 +11,7 @@ sidebar:
function addslashes(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Adds backslashes before characters that need to be escaped.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/base64_decode.md b/docs/php/builtins/string/base64_decode.md
index 6a49a55922..15df010bf0 100644
--- a/docs/php/builtins/string/base64_decode.md
+++ b/docs/php/builtins/string/base64_decode.md
@@ -1,21 +1,20 @@
---
title: "base64_decode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Decodes a Base64-encoded string back into its original data."
sidebar:
- order: 318
+ order: 336
---
## base64_decode()
```php
-function base64_decode(string $string, bool $strict): string
+function base64_decode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Decodes a Base64-encoded string back into its original data.
**Parameters**:
- `$string` (`string`)
-- `$strict` (`bool`)
**Returns**: `string`
diff --git a/docs/php/builtins/string/base64_encode.md b/docs/php/builtins/string/base64_encode.md
index 11e10dd396..65004319bc 100644
--- a/docs/php/builtins/string/base64_encode.md
+++ b/docs/php/builtins/string/base64_encode.md
@@ -1,8 +1,8 @@
---
title: "base64_encode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Encodes binary data into a Base64 string."
sidebar:
- order: 319
+ order: 337
---
## base64_encode()
@@ -11,7 +11,7 @@ sidebar:
function base64_encode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Encodes binary data into a Base64 string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/bin2hex.md b/docs/php/builtins/string/bin2hex.md
index afb3330e29..50bdf61de2 100644
--- a/docs/php/builtins/string/bin2hex.md
+++ b/docs/php/builtins/string/bin2hex.md
@@ -1,8 +1,8 @@
---
title: "bin2hex()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts binary data into its hexadecimal string representation."
sidebar:
- order: 320
+ order: 338
---
## bin2hex()
@@ -11,7 +11,7 @@ sidebar:
function bin2hex(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts binary data into its hexadecimal string representation.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/chop.md b/docs/php/builtins/string/chop.md
index 2f6ed8c194..5788c4d61b 100644
--- a/docs/php/builtins/string/chop.md
+++ b/docs/php/builtins/string/chop.md
@@ -1,21 +1,21 @@
---
title: "chop()"
-description: "chop() — string builtin supported by Elephc."
+description: "Alias of rtrim: strips whitespace (or other characters) from the end of a string."
sidebar:
- order: 321
+ order: 339
---
## chop()
```php
-function chop(string $string, string $characters): string
+function chop(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
-`chop()` is a string builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Alias of rtrim: strips whitespace (or other characters) from the end of a string.
**Parameters**:
- `$string` (`string`)
-- `$characters` (`string`), optional
+- `$characters` (`string`), default `' \n\r\t\x0b\x0c\x00'`, optional
**Returns**: `string`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `chop` is implemented in the compiler, see [the internals page](../../../internals/builtins/string/chop.md).
+
diff --git a/docs/php/builtins/string/chr.md b/docs/php/builtins/string/chr.md
index 5a188707af..2d564533cc 100644
--- a/docs/php/builtins/string/chr.md
+++ b/docs/php/builtins/string/chr.md
@@ -1,8 +1,8 @@
---
title: "chr()"
-description: "Lowers `chr()` by converting an integer code point into a one-byte string."
+description: "Returns a one-character string from the given byte code point."
sidebar:
- order: 322
+ order: 340
---
## chr()
@@ -11,7 +11,7 @@ sidebar:
function chr(int $codepoint): string
```
-Lowers `chr()` by converting an integer code point into a one-byte string.
+Returns a one-character string from the given byte code point.
**Parameters**:
- `$codepoint` (`int`)
diff --git a/docs/php/builtins/string/crc32.md b/docs/php/builtins/string/crc32.md
index 4243ccd1c2..776a487ad0 100644
--- a/docs/php/builtins/string/crc32.md
+++ b/docs/php/builtins/string/crc32.md
@@ -1,8 +1,8 @@
---
title: "crc32()"
-description: "Lowers `crc32(string)` through the shared checksum runtime helper."
+description: "Calculates the CRC32 polynomial of a string."
sidebar:
- order: 323
+ order: 341
---
## crc32()
@@ -11,7 +11,7 @@ sidebar:
function crc32(string $string): int
```
-Lowers `crc32(string)` through the shared checksum runtime helper.
+Calculates the CRC32 polynomial of a string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/explode.md b/docs/php/builtins/string/explode.md
index e770c731b8..e0fa0f540b 100644
--- a/docs/php/builtins/string/explode.md
+++ b/docs/php/builtins/string/explode.md
@@ -1,22 +1,22 @@
---
title: "explode()"
-description: "Lowers `explode(delimiter, string)` into the shared string-array splitter helper."
+description: "Splits a string by a separator into an array of substrings."
sidebar:
- order: 324
+ order: 342
---
## explode()
```php
-function explode(string $separator, string $string, int $limit): array
+function explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
```
-Lowers `explode(delimiter, string)` into the shared string-array splitter helper.
+Splits a string by a separator into an array of substrings.
**Parameters**:
- `$separator` (`string`)
- `$string` (`string`)
-- `$limit` (`int`), optional
+- `$limit` (`int`), default `PHP_INT_MAX`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/string/grapheme_strrev.md b/docs/php/builtins/string/grapheme_strrev.md
index 60f1b2139d..c50439bd1c 100644
--- a/docs/php/builtins/string/grapheme_strrev.md
+++ b/docs/php/builtins/string/grapheme_strrev.md
@@ -1,8 +1,8 @@
---
title: "grapheme_strrev()"
-description: "Lowers `grapheme_strrev()` and boxes its `string|false` result as `Mixed`."
+description: "Reverses a string by grapheme cluster, returning false on failure."
sidebar:
- order: 325
+ order: 343
---
## grapheme_strrev()
@@ -11,7 +11,7 @@ sidebar:
function grapheme_strrev(string $string): mixed
```
-Lowers `grapheme_strrev()` and boxes its `string|false` result as `Mixed`.
+Reverses a string by grapheme cluster, returning false on failure.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/gzcompress.md b/docs/php/builtins/string/gzcompress.md
index 1eec8ff52b..5bdf0c8ff3 100644
--- a/docs/php/builtins/string/gzcompress.md
+++ b/docs/php/builtins/string/gzcompress.md
@@ -1,22 +1,21 @@
---
title: "gzcompress()"
-description: "Lowers `gzcompress(data, level?)` through inline zlib `compress2` calls."
+description: "Compress a string using the ZLIB data format."
sidebar:
- order: 326
+ order: 344
---
## gzcompress()
```php
-function gzcompress(string $data, int $level, int $encoding): string
+function gzcompress(string $data, int $level = -1): string
```
-Lowers `gzcompress(data, level?)` through inline zlib `compress2` calls.
+Compress a string using the ZLIB data format.
**Parameters**:
- `$data` (`string`)
-- `$level` (`int`), optional
-- `$encoding` (`int`)
+- `$level` (`int`), default `-1`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/gzdeflate.md b/docs/php/builtins/string/gzdeflate.md
index 8b40df02d6..c97a3f7200 100644
--- a/docs/php/builtins/string/gzdeflate.md
+++ b/docs/php/builtins/string/gzdeflate.md
@@ -1,22 +1,21 @@
---
title: "gzdeflate()"
-description: "Lowers `gzdeflate(data, level?)` through inline raw-DEFLATE zlib calls."
+description: "Deflate a string using the DEFLATE data format."
sidebar:
- order: 327
+ order: 345
---
## gzdeflate()
```php
-function gzdeflate(string $data, int $level, int $encoding): string
+function gzdeflate(string $data, int $level = -1): string
```
-Lowers `gzdeflate(data, level?)` through inline raw-DEFLATE zlib calls.
+Deflate a string using the DEFLATE data format.
**Parameters**:
- `$data` (`string`)
-- `$level` (`int`), optional
-- `$encoding` (`int`)
+- `$level` (`int`), default `-1`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/gzinflate.md b/docs/php/builtins/string/gzinflate.md
index 45a5023334..693151fef7 100644
--- a/docs/php/builtins/string/gzinflate.md
+++ b/docs/php/builtins/string/gzinflate.md
@@ -1,23 +1,23 @@
---
title: "gzinflate()"
-description: "Lowers `gzinflate(data, max_length?)` and boxes zlib failures as PHP false."
+description: "Inflate a deflated string."
sidebar:
- order: 328
+ order: 346
---
## gzinflate()
```php
-function gzinflate(string $data, int $max_length): string
+function gzinflate(string $data, int $max_length = 0): mixed
```
-Lowers `gzinflate(data, max_length?)` and boxes zlib failures as PHP false.
+Inflate a deflated string.
**Parameters**:
- `$data` (`string`)
-- `$max_length` (`int`), optional
+- `$max_length` (`int`), default `0`, optional
-**Returns**: `string`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/string/gzuncompress.md b/docs/php/builtins/string/gzuncompress.md
index 4f93607318..794d1535c1 100644
--- a/docs/php/builtins/string/gzuncompress.md
+++ b/docs/php/builtins/string/gzuncompress.md
@@ -1,23 +1,23 @@
---
title: "gzuncompress()"
-description: "Lowers `gzuncompress(data, max_length?)` and boxes zlib failures as PHP false."
+description: "Uncompress a compressed string."
sidebar:
- order: 329
+ order: 347
---
## gzuncompress()
```php
-function gzuncompress(string $data, int $max_length): string
+function gzuncompress(string $data, int $max_length = 0): mixed
```
-Lowers `gzuncompress(data, max_length?)` and boxes zlib failures as PHP false.
+Uncompress a compressed string.
**Parameters**:
- `$data` (`string`)
-- `$max_length` (`int`), optional
+- `$max_length` (`int`), default `0`, optional
-**Returns**: `string`
+**Returns**: `mixed`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/string/hash.md b/docs/php/builtins/string/hash.md
index 83bae1664a..4cd47569f3 100644
--- a/docs/php/builtins/string/hash.md
+++ b/docs/php/builtins/string/hash.md
@@ -1,23 +1,22 @@
---
title: "hash()"
-description: "Lowers `hash(algo, data, binary?)` through the shared runtime digest dispatcher."
+description: "Generates a hash value using the given algorithm."
sidebar:
- order: 330
+ order: 348
---
## hash()
```php
-function hash(string $algo, string $data, bool $binary = false, array $options = []): string
+function hash(string $algo, string $data, bool $binary = false): string
```
-Lowers `hash(algo, data, binary?)` through the shared runtime digest dispatcher.
+Generates a hash value using the given algorithm.
**Parameters**:
- `$algo` (`string`)
- `$data` (`string`)
- `$binary` (`bool`), default `false`, optional
-- `$options` (`array`), default `[]`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/hash_algos.md b/docs/php/builtins/string/hash_algos.md
index 0f171d9e98..efeeae8015 100644
--- a/docs/php/builtins/string/hash_algos.md
+++ b/docs/php/builtins/string/hash_algos.md
@@ -1,8 +1,8 @@
---
title: "hash_algos()"
-description: "Lowers `hash_algos()` through the runtime algorithm-list builder."
+description: "Returns an array of supported hashing algorithm names."
sidebar:
- order: 331
+ order: 349
---
## hash_algos()
@@ -11,7 +11,7 @@ sidebar:
function hash_algos(): array
```
-Lowers `hash_algos()` through the runtime algorithm-list builder.
+Returns an array of supported hashing algorithm names.
**Parameters**: none.
diff --git a/docs/php/builtins/string/hash_copy.md b/docs/php/builtins/string/hash_copy.md
index 45c969c6d6..b998ac4f0c 100644
--- a/docs/php/builtins/string/hash_copy.md
+++ b/docs/php/builtins/string/hash_copy.md
@@ -1,8 +1,8 @@
---
title: "hash_copy()"
-description: "Lowers `hash_copy(context)` through the incremental hash clone helper."
+description: "Copies the state of an incremental hashing context."
sidebar:
- order: 332
+ order: 350
---
## hash_copy()
@@ -11,7 +11,7 @@ sidebar:
function hash_copy(resource $context): mixed
```
-Lowers `hash_copy(context)` through the incremental hash clone helper.
+Copies the state of an incremental hashing context.
**Parameters**:
- `$context` (`resource`)
diff --git a/docs/php/builtins/string/hash_equals.md b/docs/php/builtins/string/hash_equals.md
index d2612215f9..369d4118ad 100644
--- a/docs/php/builtins/string/hash_equals.md
+++ b/docs/php/builtins/string/hash_equals.md
@@ -1,8 +1,8 @@
---
title: "hash_equals()"
-description: "Lowers `hash_equals(known, user)` through the timing-safe runtime compare helper."
+description: "Compares two strings using a constant-time algorithm."
sidebar:
- order: 333
+ order: 351
---
## hash_equals()
@@ -11,7 +11,7 @@ sidebar:
function hash_equals(string $known_string, string $user_string): bool
```
-Lowers `hash_equals(known, user)` through the timing-safe runtime compare helper.
+Compares two strings using a constant-time algorithm.
**Parameters**:
- `$known_string` (`string`)
diff --git a/docs/php/builtins/string/hash_final.md b/docs/php/builtins/string/hash_final.md
index bdb2bee3d4..a1a62e766b 100644
--- a/docs/php/builtins/string/hash_final.md
+++ b/docs/php/builtins/string/hash_final.md
@@ -1,21 +1,21 @@
---
title: "hash_final()"
-description: "Lowers `hash_final(context, binary?)` through the incremental hash finalizer."
+description: "Finalizes an incremental hash and returns the digest string."
sidebar:
- order: 334
+ order: 352
---
## hash_final()
```php
-function hash_final(resource $context, bool $binary): string
+function hash_final(resource $context, bool $binary = false): string
```
-Lowers `hash_final(context, binary?)` through the incremental hash finalizer.
+Finalizes an incremental hash and returns the digest string.
**Parameters**:
- `$context` (`resource`)
-- `$binary` (`bool`), optional
+- `$binary` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/hash_hmac.md b/docs/php/builtins/string/hash_hmac.md
index 4f2593023b..33302cdb4a 100644
--- a/docs/php/builtins/string/hash_hmac.md
+++ b/docs/php/builtins/string/hash_hmac.md
@@ -1,23 +1,23 @@
---
title: "hash_hmac()"
-description: "Lowers `hash_hmac(algo, data, key, binary?)` through the shared HMAC runtime dispatcher."
+description: "Generates a keyed hash value using the HMAC method."
sidebar:
- order: 335
+ order: 353
---
## hash_hmac()
```php
-function hash_hmac(string $algo, string $data, string $key, bool $binary): string
+function hash_hmac(string $algo, string $data, string $key, bool $binary = false): string
```
-Lowers `hash_hmac(algo, data, key, binary?)` through the shared HMAC runtime dispatcher.
+Generates a keyed hash value using the HMAC method.
**Parameters**:
- `$algo` (`string`)
- `$data` (`string`)
- `$key` (`string`)
-- `$binary` (`bool`), optional
+- `$binary` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/hash_init.md b/docs/php/builtins/string/hash_init.md
index 4bfb9b6c9b..43d3203fd0 100644
--- a/docs/php/builtins/string/hash_init.md
+++ b/docs/php/builtins/string/hash_init.md
@@ -1,23 +1,22 @@
---
title: "hash_init()"
-description: "Lowers `hash_init(algo)` and returns a boxed HashContext resource."
+description: "Initialize an incremental hashing context."
sidebar:
- order: 336
+ order: 354
---
## hash_init()
```php
-function hash_init(string $algo, int $flags = 0, string $key = '', array $options = []): mixed
+function hash_init(string $algo, int $flags = 0, string $key = ''): mixed
```
-Lowers `hash_init(algo)` and returns a boxed HashContext resource.
+Initialize an incremental hashing context.
**Parameters**:
- `$algo` (`string`)
- `$flags` (`int`), default `0`, optional
- `$key` (`string`), default `''`, optional
-- `$options` (`array`), default `[]`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/string/hash_update.md b/docs/php/builtins/string/hash_update.md
index 0ada2580f2..ebfdb6678c 100644
--- a/docs/php/builtins/string/hash_update.md
+++ b/docs/php/builtins/string/hash_update.md
@@ -1,8 +1,8 @@
---
title: "hash_update()"
-description: "Lowers `hash_update(context, data)` through the incremental hash runtime helper."
+description: "Pumps data into an active incremental hashing context."
sidebar:
- order: 337
+ order: 355
---
## hash_update()
@@ -11,7 +11,7 @@ sidebar:
function hash_update(resource $context, string $data): bool
```
-Lowers `hash_update(context, data)` through the incremental hash runtime helper.
+Pumps data into an active incremental hashing context.
**Parameters**:
- `$context` (`resource`)
diff --git a/docs/php/builtins/string/hex2bin.md b/docs/php/builtins/string/hex2bin.md
index 7e476789c4..9014f9f4dc 100644
--- a/docs/php/builtins/string/hex2bin.md
+++ b/docs/php/builtins/string/hex2bin.md
@@ -1,8 +1,8 @@
---
title: "hex2bin()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Decodes a hexadecimal string back into its binary representation."
sidebar:
- order: 338
+ order: 356
---
## hex2bin()
@@ -11,7 +11,7 @@ sidebar:
function hex2bin(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Decodes a hexadecimal string back into its binary representation.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/html_entity_decode.md b/docs/php/builtins/string/html_entity_decode.md
index 0bd75c38cb..73b4e900d3 100644
--- a/docs/php/builtins/string/html_entity_decode.md
+++ b/docs/php/builtins/string/html_entity_decode.md
@@ -1,22 +1,20 @@
---
title: "html_entity_decode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts HTML entities in a string back into their corresponding characters."
sidebar:
- order: 339
+ order: 357
---
## html_entity_decode()
```php
-function html_entity_decode(string $string, int $flags, string $encoding): string
+function html_entity_decode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts HTML entities in a string back into their corresponding characters.
**Parameters**:
- `$string` (`string`)
-- `$flags` (`int`)
-- `$encoding` (`string`)
**Returns**: `string`
diff --git a/docs/php/builtins/string/htmlentities.md b/docs/php/builtins/string/htmlentities.md
index 2996bc5b18..27ea5de4a9 100644
--- a/docs/php/builtins/string/htmlentities.md
+++ b/docs/php/builtins/string/htmlentities.md
@@ -1,23 +1,20 @@
---
title: "htmlentities()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts all applicable characters in a string into their HTML entities."
sidebar:
- order: 340
+ order: 358
---
## htmlentities()
```php
-function htmlentities(string $string, int $flags, string $encoding, bool $double_encode): string
+function htmlentities(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts all applicable characters in a string into their HTML entities.
**Parameters**:
- `$string` (`string`)
-- `$flags` (`int`)
-- `$encoding` (`string`)
-- `$double_encode` (`bool`)
**Returns**: `string`
diff --git a/docs/php/builtins/string/htmlspecialchars.md b/docs/php/builtins/string/htmlspecialchars.md
index 2cadff365e..2d79e70a84 100644
--- a/docs/php/builtins/string/htmlspecialchars.md
+++ b/docs/php/builtins/string/htmlspecialchars.md
@@ -1,23 +1,20 @@
---
title: "htmlspecialchars()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts the HTML special characters in a string into their entities."
sidebar:
- order: 341
+ order: 359
---
## htmlspecialchars()
```php
-function htmlspecialchars(string $string, int $flags, string $encoding, bool $double_encode): string
+function htmlspecialchars(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts the HTML special characters in a string into their entities.
**Parameters**:
- `$string` (`string`)
-- `$flags` (`int`)
-- `$encoding` (`string`)
-- `$double_encode` (`bool`)
**Returns**: `string`
diff --git a/docs/php/builtins/string/implode.md b/docs/php/builtins/string/implode.md
index 47f792991b..c7f672b688 100644
--- a/docs/php/builtins/string/implode.md
+++ b/docs/php/builtins/string/implode.md
@@ -1,21 +1,21 @@
---
title: "implode()"
-description: "Lowers `implode(glue, array)` by selecting the string or integer array helper."
+description: "Joins array elements into a single string using a separator."
sidebar:
- order: 342
+ order: 360
---
## implode()
```php
-function implode(string $separator, array $array): string
+function implode(string $separator, array $array = null): string
```
-Lowers `implode(glue, array)` by selecting the string or integer array helper.
+Joins array elements into a single string using a separator.
**Parameters**:
- `$separator` (`string`)
-- `$array` (`array`), optional
+- `$array` (`array`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/inet_ntop.md b/docs/php/builtins/string/inet_ntop.md
index ac4e5e4adf..04a2dd3401 100644
--- a/docs/php/builtins/string/inet_ntop.md
+++ b/docs/php/builtins/string/inet_ntop.md
@@ -1,8 +1,8 @@
---
title: "inet_ntop()"
-description: "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+description: "Converts a packed internet address to a human-readable representation."
sidebar:
- order: 343
+ order: 361
---
## inet_ntop()
@@ -11,7 +11,7 @@ sidebar:
function inet_ntop(string $ip): mixed
```
-Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false.
+Converts a packed internet address to a human-readable representation.
**Parameters**:
- `$ip` (`string`)
diff --git a/docs/php/builtins/string/inet_pton.md b/docs/php/builtins/string/inet_pton.md
index 042789730c..805889b4a2 100644
--- a/docs/php/builtins/string/inet_pton.md
+++ b/docs/php/builtins/string/inet_pton.md
@@ -1,8 +1,8 @@
---
title: "inet_pton()"
-description: "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+description: "Converts a human-readable IP address to its packed in_addr representation."
sidebar:
- order: 344
+ order: 362
---
## inet_pton()
@@ -11,7 +11,7 @@ sidebar:
function inet_pton(string $ip): mixed
```
-Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false.
+Converts a human-readable IP address to its packed in_addr representation.
**Parameters**:
- `$ip` (`string`)
diff --git a/docs/php/builtins/string/ip2long.md b/docs/php/builtins/string/ip2long.md
index d8afe6ab5c..472ce6aa06 100644
--- a/docs/php/builtins/string/ip2long.md
+++ b/docs/php/builtins/string/ip2long.md
@@ -1,8 +1,8 @@
---
title: "ip2long()"
-description: "Lowers `ip2long(string)` and boxes invalid-address results as PHP false."
+description: "Converts a string containing an IPv4 address into a long integer."
sidebar:
- order: 345
+ order: 363
---
## ip2long()
@@ -11,7 +11,7 @@ sidebar:
function ip2long(string $ip): mixed
```
-Lowers `ip2long(string)` and boxes invalid-address results as PHP false.
+Converts a string containing an IPv4 address into a long integer.
**Parameters**:
- `$ip` (`string`)
diff --git a/docs/php/builtins/string/lcfirst.md b/docs/php/builtins/string/lcfirst.md
index 0a5660e02e..59a435a662 100644
--- a/docs/php/builtins/string/lcfirst.md
+++ b/docs/php/builtins/string/lcfirst.md
@@ -1,8 +1,8 @@
---
title: "lcfirst()"
-description: "Lowers `lcfirst()` by copying the string and lowercasing the first ASCII byte."
+description: "Lowercases the first character of a string."
sidebar:
- order: 346
+ order: 364
---
## lcfirst()
@@ -11,7 +11,7 @@ sidebar:
function lcfirst(string $string): string
```
-Lowers `lcfirst()` by copying the string and lowercasing the first ASCII byte.
+Lowercases the first character of a string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/long2ip.md b/docs/php/builtins/string/long2ip.md
index 9b7e998ad9..7650ec0318 100644
--- a/docs/php/builtins/string/long2ip.md
+++ b/docs/php/builtins/string/long2ip.md
@@ -1,8 +1,8 @@
---
title: "long2ip()"
-description: "Lowers `long2ip(value)` through the IPv4 formatting runtime helper."
+description: "Converts an IPv4 address from long integer to dotted string notation."
sidebar:
- order: 347
+ order: 365
---
## long2ip()
@@ -11,7 +11,7 @@ sidebar:
function long2ip(int $ip): string
```
-Lowers `long2ip(value)` through the IPv4 formatting runtime helper.
+Converts an IPv4 address from long integer to dotted string notation.
**Parameters**:
- `$ip` (`int`)
diff --git a/docs/php/builtins/string/ltrim.md b/docs/php/builtins/string/ltrim.md
index d1a2575129..59d998f293 100644
--- a/docs/php/builtins/string/ltrim.md
+++ b/docs/php/builtins/string/ltrim.md
@@ -1,21 +1,21 @@
---
title: "ltrim()"
-description: "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks."
+description: "Strips whitespace (or other characters) from the beginning of a string."
sidebar:
- order: 348
+ order: 366
---
## ltrim()
```php
-function ltrim(string $string, string $characters): string
+function ltrim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
-Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.
+Strips whitespace (or other characters) from the beginning of a string.
**Parameters**:
- `$string` (`string`)
-- `$characters` (`string`), optional
+- `$characters` (`string`), default `' \n\r\t\x0b\x0c\x00'`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/md5.md b/docs/php/builtins/string/md5.md
index 59a0f54e7a..4ce3cc3ddf 100644
--- a/docs/php/builtins/string/md5.md
+++ b/docs/php/builtins/string/md5.md
@@ -1,21 +1,21 @@
---
title: "md5()"
-description: "Lowers `md5(data, binary?)` through the shared crypto-backed runtime helper."
+description: "Calculates the MD5 hash of a string."
sidebar:
- order: 349
+ order: 367
---
## md5()
```php
-function md5(string $string, bool $binary): string
+function md5(string $string, bool $binary = false): string
```
-Lowers `md5(data, binary?)` through the shared crypto-backed runtime helper.
+Calculates the MD5 hash of a string.
**Parameters**:
- `$string` (`string`)
-- `$binary` (`bool`), optional
+- `$binary` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/nl2br.md b/docs/php/builtins/string/nl2br.md
index 6ca426af56..2bbcb0c902 100644
--- a/docs/php/builtins/string/nl2br.md
+++ b/docs/php/builtins/string/nl2br.md
@@ -1,21 +1,20 @@
---
title: "nl2br()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Inserts HTML line breaks before newlines in a string."
sidebar:
- order: 350
+ order: 368
---
## nl2br()
```php
-function nl2br(string $string, bool $use_xhtml): string
+function nl2br(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Inserts HTML line breaks before newlines in a string.
**Parameters**:
- `$string` (`string`)
-- `$use_xhtml` (`bool`)
**Returns**: `string`
diff --git a/docs/php/builtins/string/number_format.md b/docs/php/builtins/string/number_format.md
index 612140ce08..d5bf58cdf7 100644
--- a/docs/php/builtins/string/number_format.md
+++ b/docs/php/builtins/string/number_format.md
@@ -1,23 +1,23 @@
---
title: "number_format()"
-description: "Lowers `number_format()` by arranging its runtime helper arguments."
+description: "Formats a number with grouped thousands."
sidebar:
- order: 351
+ order: 369
---
## number_format()
```php
-function number_format(float $num, int $decimals, string $decimal_separator, string $thousands_separator): string
+function number_format(float $num, int $decimals = 0, string $decimal_separator = '.', string $thousands_separator = ','): string
```
-Lowers `number_format()` by arranging its runtime helper arguments.
+Formats a number with grouped thousands.
**Parameters**:
- `$num` (`float`)
-- `$decimals` (`int`), optional
-- `$decimal_separator` (`string`), optional
-- `$thousands_separator` (`string`), optional
+- `$decimals` (`int`), default `0`, optional
+- `$decimal_separator` (`string`), default `'.'`, optional
+- `$thousands_separator` (`string`), default `','`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/ord.md b/docs/php/builtins/string/ord.md
index 4144807112..6b5322b846 100644
--- a/docs/php/builtins/string/ord.md
+++ b/docs/php/builtins/string/ord.md
@@ -1,8 +1,8 @@
---
title: "ord()"
-description: "Lowers `ord()` by returning the first byte of a string or zero for empty input."
+description: "Returns the ASCII value of the first character of a string."
sidebar:
- order: 352
+ order: 370
---
## ord()
@@ -11,7 +11,7 @@ sidebar:
function ord(string $character): int
```
-Lowers `ord()` by returning the first byte of a string or zero for empty input.
+Returns the ASCII value of the first character of a string.
**Parameters**:
- `$character` (`string`)
diff --git a/docs/php/builtins/string/printf.md b/docs/php/builtins/string/printf.md
index 019abf015a..edf4bd4eb1 100644
--- a/docs/php/builtins/string/printf.md
+++ b/docs/php/builtins/string/printf.md
@@ -1,8 +1,8 @@
---
title: "printf()"
-description: "Lowers `printf(format, values...)` as `sprintf()` followed by stdout emission."
+description: "Outputs a formatted string."
sidebar:
- order: 353
+ order: 371
---
## printf()
@@ -11,7 +11,7 @@ sidebar:
function printf(string $format, ...$values): int
```
-Lowers `printf(format, values...)` as `sprintf()` followed by stdout emission.
+Outputs a formatted string.
**Parameters**:
- `$format` (`string`)
diff --git a/docs/php/builtins/string/rawurldecode.md b/docs/php/builtins/string/rawurldecode.md
index 38b48a96ca..3cd1fafc2d 100644
--- a/docs/php/builtins/string/rawurldecode.md
+++ b/docs/php/builtins/string/rawurldecode.md
@@ -1,8 +1,8 @@
---
title: "rawurldecode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Decodes an RFC 3986 percent-encoded string without treating '+' as a space."
sidebar:
- order: 354
+ order: 372
---
## rawurldecode()
@@ -11,7 +11,7 @@ sidebar:
function rawurldecode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Decodes an RFC 3986 percent-encoded string without treating '+' as a space.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/rawurlencode.md b/docs/php/builtins/string/rawurlencode.md
index 6a56cc3e8a..cca0db1b5b 100644
--- a/docs/php/builtins/string/rawurlencode.md
+++ b/docs/php/builtins/string/rawurlencode.md
@@ -1,8 +1,8 @@
---
title: "rawurlencode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "URL-encodes a string using RFC 3986 percent-encoding (no '+' for spaces)."
sidebar:
- order: 355
+ order: 373
---
## rawurlencode()
@@ -11,7 +11,7 @@ sidebar:
function rawurlencode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+URL-encodes a string using RFC 3986 percent-encoding (no '+' for spaces).
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/rtrim.md b/docs/php/builtins/string/rtrim.md
index 851b9b8cd8..cfea4e4644 100644
--- a/docs/php/builtins/string/rtrim.md
+++ b/docs/php/builtins/string/rtrim.md
@@ -1,21 +1,21 @@
---
title: "rtrim()"
-description: "rtrim() — string builtin supported by Elephc."
+description: "Strips whitespace (or other characters) from the end of a string."
sidebar:
- order: 356
+ order: 374
---
## rtrim()
```php
-function rtrim(string $string, string $characters): string
+function rtrim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
-`rtrim()` is a string builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Strips whitespace (or other characters) from the end of a string.
**Parameters**:
- `$string` (`string`)
-- `$characters` (`string`), optional
+- `$characters` (`string`), default `' \n\r\t\x0b\x0c\x00'`, optional
**Returns**: `string`
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `rtrim` is implemented in the compiler, see [the internals page](../../../internals/builtins/string/rtrim.md).
+
diff --git a/docs/php/builtins/string/sha1.md b/docs/php/builtins/string/sha1.md
index 0b9699436c..c35a89513a 100644
--- a/docs/php/builtins/string/sha1.md
+++ b/docs/php/builtins/string/sha1.md
@@ -1,21 +1,21 @@
---
title: "sha1()"
-description: "Lowers `sha1(data, binary?)` through the shared crypto-backed runtime helper."
+description: "Calculates the SHA-1 hash of a string."
sidebar:
- order: 357
+ order: 375
---
## sha1()
```php
-function sha1(string $string, bool $binary): string
+function sha1(string $string, bool $binary = false): string
```
-Lowers `sha1(data, binary?)` through the shared crypto-backed runtime helper.
+Calculates the SHA-1 hash of a string.
**Parameters**:
- `$string` (`string`)
-- `$binary` (`bool`), optional
+- `$binary` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/sprintf.md b/docs/php/builtins/string/sprintf.md
index a5bef57c82..4e15f37a7d 100644
--- a/docs/php/builtins/string/sprintf.md
+++ b/docs/php/builtins/string/sprintf.md
@@ -1,8 +1,8 @@
---
title: "sprintf()"
-description: "Lowers `sprintf(format, values...)` by packing variadic records for `__rt_sprintf`."
+description: "Returns a formatted string."
sidebar:
- order: 358
+ order: 376
---
## sprintf()
@@ -11,7 +11,7 @@ sidebar:
function sprintf(string $format, ...$values): string
```
-Lowers `sprintf(format, values...)` by packing variadic records for `__rt_sprintf`.
+Returns a formatted string.
**Parameters**:
- `$format` (`string`)
diff --git a/docs/php/builtins/string/sscanf.md b/docs/php/builtins/string/sscanf.md
index 01384673bc..e7e85c2df9 100644
--- a/docs/php/builtins/string/sscanf.md
+++ b/docs/php/builtins/string/sscanf.md
@@ -1,8 +1,8 @@
---
title: "sscanf()"
-description: "Lowers `sscanf(string, format)` into the shared scanner helper."
+description: "Parses a string according to a format."
sidebar:
- order: 359
+ order: 377
---
## sscanf()
@@ -11,7 +11,7 @@ sidebar:
function sscanf(string $string, string $format, ...$vars): array
```
-Lowers `sscanf(string, format)` into the shared scanner helper.
+Parses a string according to a format.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/str_contains.md b/docs/php/builtins/string/str_contains.md
index 5156daab8a..62b03be0bf 100644
--- a/docs/php/builtins/string/str_contains.md
+++ b/docs/php/builtins/string/str_contains.md
@@ -1,8 +1,8 @@
---
title: "str_contains()"
-description: "Lowers `str_contains()` through `strpos()` and converts found positions to bool."
+description: "Determines if a string contains a given substring."
sidebar:
- order: 360
+ order: 378
---
## str_contains()
@@ -11,7 +11,7 @@ sidebar:
function str_contains(string $haystack, string $needle): bool
```
-Lowers `str_contains()` through `strpos()` and converts found positions to bool.
+Determines if a string contains a given substring.
**Parameters**:
- `$haystack` (`string`)
diff --git a/docs/php/builtins/string/str_ends_with.md b/docs/php/builtins/string/str_ends_with.md
index a9cad96688..f61c89befc 100644
--- a/docs/php/builtins/string/str_ends_with.md
+++ b/docs/php/builtins/string/str_ends_with.md
@@ -1,8 +1,8 @@
---
title: "str_ends_with()"
-description: "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+description: "Checks if a string ends with a given substring."
sidebar:
- order: 361
+ order: 379
---
## str_ends_with()
@@ -11,7 +11,7 @@ sidebar:
function str_ends_with(string $haystack, string $needle): bool
```
-Lowers a two-argument string builtin that directly delegates to a runtime helper.
+Checks if a string ends with a given substring.
**Parameters**:
- `$haystack` (`string`)
diff --git a/docs/php/builtins/string/str_ireplace.md b/docs/php/builtins/string/str_ireplace.md
index 0fd77754f6..74155ff2a0 100644
--- a/docs/php/builtins/string/str_ireplace.md
+++ b/docs/php/builtins/string/str_ireplace.md
@@ -1,25 +1,25 @@
---
title: "str_ireplace()"
-description: "str_ireplace() — string builtin supported by Elephc."
+description: "Case-insensitive version of str_replace()."
sidebar:
- order: 362
+ order: 380
---
## str_ireplace()
```php
-function str_ireplace(mixed $search, mixed $replace, mixed $subject, int $count): mixed
+function str_ireplace(string $search, string $replace, string $subject, int $count = null): string
```
-`str_ireplace()` is a string builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Case-insensitive version of str_replace().
**Parameters**:
-- `$search` (`mixed`)
-- `$replace` (`mixed`)
-- `$subject` (`mixed`)
-- `$count` (`int`), passed by reference, optional
+- `$search` (`string`)
+- `$replace` (`string`)
+- `$subject` (`string`)
+- `$count` (`int`), default `null`, optional
-**Returns**: `mixed`
+**Returns**: `string`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
@@ -29,3 +29,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `str_ireplace` is implemented in the compiler, see [the internals page](../../../internals/builtins/string/str_ireplace.md).
+
diff --git a/docs/php/builtins/string/str_pad.md b/docs/php/builtins/string/str_pad.md
index 88d433b44c..c045d64553 100644
--- a/docs/php/builtins/string/str_pad.md
+++ b/docs/php/builtins/string/str_pad.md
@@ -1,23 +1,23 @@
---
title: "str_pad()"
-description: "Lowers `str_pad(string, length, pad_string?, pad_type?)` through the shared runtime helper."
+description: "Pads a string to a certain length with another string."
sidebar:
- order: 363
+ order: 381
---
## str_pad()
```php
-function str_pad(string $string, int $length, string $pad_string, int $pad_type): string
+function str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = 1): string
```
-Lowers `str_pad(string, length, pad_string?, pad_type?)` through the shared runtime helper.
+Pads a string to a certain length with another string.
**Parameters**:
- `$string` (`string`)
- `$length` (`int`)
-- `$pad_string` (`string`), optional
-- `$pad_type` (`int`), optional
+- `$pad_string` (`string`), default `' '`, optional
+- `$pad_type` (`int`), default `1`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/str_repeat.md b/docs/php/builtins/string/str_repeat.md
index ca2d1755b6..deed273045 100644
--- a/docs/php/builtins/string/str_repeat.md
+++ b/docs/php/builtins/string/str_repeat.md
@@ -1,8 +1,8 @@
---
title: "str_repeat()"
-description: "Lowers `str_repeat(string, times)` through the shared runtime helper."
+description: "Repeats a string a given number of times."
sidebar:
- order: 364
+ order: 382
---
## str_repeat()
@@ -11,7 +11,7 @@ sidebar:
function str_repeat(string $string, int $times): string
```
-Lowers `str_repeat(string, times)` through the shared runtime helper.
+Repeats a string a given number of times.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/str_replace.md b/docs/php/builtins/string/str_replace.md
index 563d0bf09d..a99ccd9442 100644
--- a/docs/php/builtins/string/str_replace.md
+++ b/docs/php/builtins/string/str_replace.md
@@ -1,25 +1,25 @@
---
title: "str_replace()"
-description: "Lowers `str_replace()`/`str_ireplace()` with three string operands."
+description: "Replaces all occurrences of a search string with a replacement string."
sidebar:
- order: 365
+ order: 383
---
## str_replace()
```php
-function str_replace(string $search, string $replace, string $subject, int $count): mixed
+function str_replace(string $search, string $replace, string $subject, int $count = null): string
```
-Lowers `str_replace()`/`str_ireplace()` with three string operands.
+Replaces all occurrences of a search string with a replacement string.
**Parameters**:
- `$search` (`string`)
- `$replace` (`string`)
- `$subject` (`string`)
-- `$count` (`int`), passed by reference, optional
+- `$count` (`int`), default `null`, optional
-**Returns**: `mixed`
+**Returns**: `string`
_No examples yet — check `examples/` and `showcases/` for usage patterns._
diff --git a/docs/php/builtins/string/str_split.md b/docs/php/builtins/string/str_split.md
index 0b9a625bde..dd04dddc38 100644
--- a/docs/php/builtins/string/str_split.md
+++ b/docs/php/builtins/string/str_split.md
@@ -1,21 +1,21 @@
---
title: "str_split()"
-description: "Lowers `str_split(string, length?)` into the fixed-width string-array splitter."
+description: "Converts a string into an array of chunks of the given length."
sidebar:
- order: 366
+ order: 384
---
## str_split()
```php
-function str_split(string $string, int $length): array
+function str_split(string $string, int $length = 1): array
```
-Lowers `str_split(string, length?)` into the fixed-width string-array splitter.
+Converts a string into an array of chunks of the given length.
**Parameters**:
- `$string` (`string`)
-- `$length` (`int`), optional
+- `$length` (`int`), default `1`, optional
**Returns**: `array`
diff --git a/docs/php/builtins/string/str_starts_with.md b/docs/php/builtins/string/str_starts_with.md
index 19cde9337a..46734f6bb1 100644
--- a/docs/php/builtins/string/str_starts_with.md
+++ b/docs/php/builtins/string/str_starts_with.md
@@ -1,8 +1,8 @@
---
title: "str_starts_with()"
-description: "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+description: "Checks if a string starts with a given substring."
sidebar:
- order: 367
+ order: 385
---
## str_starts_with()
@@ -11,7 +11,7 @@ sidebar:
function str_starts_with(string $haystack, string $needle): bool
```
-Lowers a two-argument string builtin that directly delegates to a runtime helper.
+Checks if a string starts with a given substring.
**Parameters**:
- `$haystack` (`string`)
diff --git a/docs/php/builtins/string/strcasecmp.md b/docs/php/builtins/string/strcasecmp.md
index b19ef09403..91fa638ab0 100644
--- a/docs/php/builtins/string/strcasecmp.md
+++ b/docs/php/builtins/string/strcasecmp.md
@@ -1,8 +1,8 @@
---
title: "strcasecmp()"
-description: "strcasecmp() — string builtin supported by Elephc."
+description: "Binary safe case-insensitive string comparison. Returns negative, zero, or positive."
sidebar:
- order: 368
+ order: 386
---
## strcasecmp()
@@ -11,7 +11,7 @@ sidebar:
function strcasecmp(string $string1, string $string2): int
```
-`strcasecmp()` is a string builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Binary safe case-insensitive string comparison. Returns negative, zero, or positive.
**Parameters**:
- `$string1` (`string`)
@@ -27,3 +27,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `strcasecmp` is implemented in the compiler, see [the internals page](../../../internals/builtins/string/strcasecmp.md).
+
diff --git a/docs/php/builtins/string/strcmp.md b/docs/php/builtins/string/strcmp.md
index e946fa74f2..7b969d9a02 100644
--- a/docs/php/builtins/string/strcmp.md
+++ b/docs/php/builtins/string/strcmp.md
@@ -1,8 +1,8 @@
---
title: "strcmp()"
-description: "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+description: "Binary safe string comparison. Returns negative, zero, or positive."
sidebar:
- order: 369
+ order: 387
---
## strcmp()
@@ -11,7 +11,7 @@ sidebar:
function strcmp(string $string1, string $string2): int
```
-Lowers a two-argument string builtin that directly delegates to a runtime helper.
+Binary safe string comparison. Returns negative, zero, or positive.
**Parameters**:
- `$string1` (`string`)
diff --git a/docs/php/builtins/string/stripslashes.md b/docs/php/builtins/string/stripslashes.md
index c56f3555ba..2b7107d02f 100644
--- a/docs/php/builtins/string/stripslashes.md
+++ b/docs/php/builtins/string/stripslashes.md
@@ -1,8 +1,8 @@
---
title: "stripslashes()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Removes backslashes from a string previously escaped by addslashes."
sidebar:
- order: 370
+ order: 388
---
## stripslashes()
@@ -11,7 +11,7 @@ sidebar:
function stripslashes(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Removes backslashes from a string previously escaped by addslashes.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/strlen.md b/docs/php/builtins/string/strlen.md
index 556d62beff..b62478578c 100644
--- a/docs/php/builtins/string/strlen.md
+++ b/docs/php/builtins/string/strlen.md
@@ -1,8 +1,8 @@
---
title: "strlen()"
-description: "Lowers `strlen()` by coercing string-like values and returning the byte length."
+description: "Returns the length of a string."
sidebar:
- order: 371
+ order: 389
---
## strlen()
@@ -11,7 +11,7 @@ sidebar:
function strlen(string $string): int
```
-Lowers `strlen()` by coercing string-like values and returning the byte length.
+Returns the length of a string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/strpos.md b/docs/php/builtins/string/strpos.md
index e3f726bd34..bfda184641 100644
--- a/docs/php/builtins/string/strpos.md
+++ b/docs/php/builtins/string/strpos.md
@@ -1,22 +1,22 @@
---
title: "strpos()"
-description: "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
+description: "Finds the numeric position of the first occurrence of a substring."
sidebar:
- order: 372
+ order: 390
---
## strpos()
```php
-function strpos(string $haystack, string $needle, int $offset): mixed
+function strpos(string $haystack, string $needle, int $offset = 0): mixed
```
-Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed.
+Finds the numeric position of the first occurrence of a substring.
**Parameters**:
- `$haystack` (`string`)
- `$needle` (`string`)
-- `$offset` (`int`), optional
+- `$offset` (`int`), default `0`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/string/strrev.md b/docs/php/builtins/string/strrev.md
index a3590c906a..c4339ed1bb 100644
--- a/docs/php/builtins/string/strrev.md
+++ b/docs/php/builtins/string/strrev.md
@@ -1,8 +1,8 @@
---
title: "strrev()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Reverses a string."
sidebar:
- order: 373
+ order: 391
---
## strrev()
@@ -11,7 +11,7 @@ sidebar:
function strrev(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Reverses a string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/strrpos.md b/docs/php/builtins/string/strrpos.md
index dc682f0886..a588437387 100644
--- a/docs/php/builtins/string/strrpos.md
+++ b/docs/php/builtins/string/strrpos.md
@@ -1,22 +1,22 @@
---
title: "strrpos()"
-description: "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
+description: "Finds the numeric position of the last occurrence of a substring."
sidebar:
- order: 374
+ order: 392
---
## strrpos()
```php
-function strrpos(string $haystack, string $needle, int $offset): mixed
+function strrpos(string $haystack, string $needle, int $offset = 0): mixed
```
-Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed.
+Finds the numeric position of the last occurrence of a substring.
**Parameters**:
- `$haystack` (`string`)
- `$needle` (`string`)
-- `$offset` (`int`), optional
+- `$offset` (`int`), default `0`, optional
**Returns**: `mixed`
diff --git a/docs/php/builtins/string/strstr.md b/docs/php/builtins/string/strstr.md
index 39017fdffd..62477b8350 100644
--- a/docs/php/builtins/string/strstr.md
+++ b/docs/php/builtins/string/strstr.md
@@ -1,22 +1,22 @@
---
title: "strstr()"
-description: "Lowers `strstr(haystack, needle)` by searching and returning the matching suffix."
+description: "Returns the portion of a string starting at the first occurrence of a substring."
sidebar:
- order: 375
+ order: 393
---
## strstr()
```php
-function strstr(string $haystack, string $needle, bool $before_needle): string
+function strstr(string $haystack, string $needle, bool $before_needle = false): string
```
-Lowers `strstr(haystack, needle)` by searching and returning the matching suffix.
+Returns the portion of a string starting at the first occurrence of a substring.
**Parameters**:
- `$haystack` (`string`)
- `$needle` (`string`)
-- `$before_needle` (`bool`), optional
+- `$before_needle` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/strtolower.md b/docs/php/builtins/string/strtolower.md
index b0b55b121e..407f9a563f 100644
--- a/docs/php/builtins/string/strtolower.md
+++ b/docs/php/builtins/string/strtolower.md
@@ -1,8 +1,8 @@
---
title: "strtolower()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts a string to lowercase."
sidebar:
- order: 376
+ order: 394
---
## strtolower()
@@ -11,7 +11,7 @@ sidebar:
function strtolower(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts a string to lowercase.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/strtoupper.md b/docs/php/builtins/string/strtoupper.md
index 6a69f98519..14afa212b9 100644
--- a/docs/php/builtins/string/strtoupper.md
+++ b/docs/php/builtins/string/strtoupper.md
@@ -1,8 +1,8 @@
---
title: "strtoupper()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Converts a string to uppercase."
sidebar:
- order: 377
+ order: 395
---
## strtoupper()
@@ -11,7 +11,7 @@ sidebar:
function strtoupper(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Converts a string to uppercase.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/substr.md b/docs/php/builtins/string/substr.md
index 93e6cacf98..4297ed688d 100644
--- a/docs/php/builtins/string/substr.md
+++ b/docs/php/builtins/string/substr.md
@@ -1,22 +1,22 @@
---
title: "substr()"
-description: "Lowers `substr(string, offset, length?)` with target-local pointer arithmetic."
+description: "Returns a portion of a string specified by the offset and length."
sidebar:
- order: 378
+ order: 396
---
## substr()
```php
-function substr(string $string, int $offset, int $length): string
+function substr(string $string, int $offset, int $length = null): string
```
-Lowers `substr(string, offset, length?)` with target-local pointer arithmetic.
+Returns a portion of a string specified by the offset and length.
**Parameters**:
- `$string` (`string`)
- `$offset` (`int`)
-- `$length` (`int`), optional
+- `$length` (`int`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/substr_replace.md b/docs/php/builtins/string/substr_replace.md
index 6a27e672b5..2653b8715c 100644
--- a/docs/php/builtins/string/substr_replace.md
+++ b/docs/php/builtins/string/substr_replace.md
@@ -1,23 +1,23 @@
---
title: "substr_replace()"
-description: "Lowers `substr_replace(string, replacement, start, length?)`."
+description: "Replaces text within a portion of a string."
sidebar:
- order: 379
+ order: 397
---
## substr_replace()
```php
-function substr_replace(string $string, string $replace, int $offset, int $length): string
+function substr_replace(string $string, string $replace, int $offset, int $length = null): string
```
-Lowers `substr_replace(string, replacement, start, length?)`.
+Replaces text within a portion of a string.
**Parameters**:
- `$string` (`string`)
- `$replace` (`string`)
- `$offset` (`int`)
-- `$length` (`int`), optional
+- `$length` (`int`), default `null`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/trim.md b/docs/php/builtins/string/trim.md
index 83b9f69c5f..2ce8500ac5 100644
--- a/docs/php/builtins/string/trim.md
+++ b/docs/php/builtins/string/trim.md
@@ -1,21 +1,21 @@
---
title: "trim()"
-description: "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks."
+description: "Strips whitespace (or other characters) from the beginning and end of a string."
sidebar:
- order: 380
+ order: 398
---
## trim()
```php
-function trim(string $string, string $characters): string
+function trim(string $string, string $characters = ' \n\r\t\x0b\x0c\x00'): string
```
-Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.
+Strips whitespace (or other characters) from the beginning and end of a string.
**Parameters**:
- `$string` (`string`)
-- `$characters` (`string`), optional
+- `$characters` (`string`), default `' \n\r\t\x0b\x0c\x00'`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/ucfirst.md b/docs/php/builtins/string/ucfirst.md
index be43ff7917..3f4d065622 100644
--- a/docs/php/builtins/string/ucfirst.md
+++ b/docs/php/builtins/string/ucfirst.md
@@ -1,8 +1,8 @@
---
title: "ucfirst()"
-description: "Lowers `ucfirst()` by copying the string and uppercasing the first ASCII byte."
+description: "Uppercases the first character of a string."
sidebar:
- order: 381
+ order: 399
---
## ucfirst()
@@ -11,7 +11,7 @@ sidebar:
function ucfirst(string $string): string
```
-Lowers `ucfirst()` by copying the string and uppercasing the first ASCII byte.
+Uppercases the first character of a string.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/ucwords.md b/docs/php/builtins/string/ucwords.md
index 7bf077fb30..fc680a333b 100644
--- a/docs/php/builtins/string/ucwords.md
+++ b/docs/php/builtins/string/ucwords.md
@@ -1,21 +1,21 @@
---
title: "ucwords()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Uppercases the first character of each word in a string."
sidebar:
- order: 382
+ order: 400
---
## ucwords()
```php
-function ucwords(string $string, string $separators): string
+function ucwords(string $string, string $separators = ' \t\r\n\x0c\x0b'): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Uppercases the first character of each word in a string.
**Parameters**:
- `$string` (`string`)
-- `$separators` (`string`), optional
+- `$separators` (`string`), default `' \t\r\n\x0c\x0b'`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/string/urldecode.md b/docs/php/builtins/string/urldecode.md
index b8e0f01a04..fa67b82a7b 100644
--- a/docs/php/builtins/string/urldecode.md
+++ b/docs/php/builtins/string/urldecode.md
@@ -1,8 +1,8 @@
---
title: "urldecode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "Decodes a URL-encoded string, including '+' as a space."
sidebar:
- order: 383
+ order: 401
---
## urldecode()
@@ -11,7 +11,7 @@ sidebar:
function urldecode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+Decodes a URL-encoded string, including '+' as a space.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/urlencode.md b/docs/php/builtins/string/urlencode.md
index 3661efa1ae..2b4f8f3268 100644
--- a/docs/php/builtins/string/urlencode.md
+++ b/docs/php/builtins/string/urlencode.md
@@ -1,8 +1,8 @@
---
title: "urlencode()"
-description: "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+description: "URL-encodes a string using application/x-www-form-urlencoded rules."
sidebar:
- order: 384
+ order: 402
---
## urlencode()
@@ -11,7 +11,7 @@ sidebar:
function urlencode(string $string): string
```
-Lowers a one-argument string builtin that directly delegates to a runtime helper.
+URL-encodes a string using application/x-www-form-urlencoded rules.
**Parameters**:
- `$string` (`string`)
diff --git a/docs/php/builtins/string/vprintf.md b/docs/php/builtins/string/vprintf.md
index faae9df645..1971d3f33b 100644
--- a/docs/php/builtins/string/vprintf.md
+++ b/docs/php/builtins/string/vprintf.md
@@ -1,8 +1,8 @@
---
title: "vprintf()"
-description: "Lowers `vprintf(format, values)` as `vsprintf()` followed by stdout emission."
+description: "Outputs a formatted string using an array of values."
sidebar:
- order: 385
+ order: 403
---
## vprintf()
@@ -11,7 +11,7 @@ sidebar:
function vprintf(string $format, array $values): int
```
-Lowers `vprintf(format, values)` as `vsprintf()` followed by stdout emission.
+Outputs a formatted string using an array of values.
**Parameters**:
- `$format` (`string`)
diff --git a/docs/php/builtins/string/vsprintf.md b/docs/php/builtins/string/vsprintf.md
index 1a394a5ebd..a4d642052c 100644
--- a/docs/php/builtins/string/vsprintf.md
+++ b/docs/php/builtins/string/vsprintf.md
@@ -1,8 +1,8 @@
---
title: "vsprintf()"
-description: "Lowers `vsprintf(format, values)` through the array-to-sprintf runtime bridge."
+description: "Returns a formatted string using an array of values."
sidebar:
- order: 386
+ order: 404
---
## vsprintf()
@@ -11,7 +11,7 @@ sidebar:
function vsprintf(string $format, array $values): string
```
-Lowers `vsprintf(format, values)` through the array-to-sprintf runtime bridge.
+Returns a formatted string using an array of values.
**Parameters**:
- `$format` (`string`)
diff --git a/docs/php/builtins/string/wordwrap.md b/docs/php/builtins/string/wordwrap.md
index 276f0a1616..2189e74a36 100644
--- a/docs/php/builtins/string/wordwrap.md
+++ b/docs/php/builtins/string/wordwrap.md
@@ -1,23 +1,23 @@
---
title: "wordwrap()"
-description: "Lowers `wordwrap(string, width?, break?, cut?)` through the shared runtime helper."
+description: "Wraps a string to a given number of characters."
sidebar:
- order: 387
+ order: 405
---
## wordwrap()
```php
-function wordwrap(string $string, int $width, string $break, bool $cut_long_words): string
+function wordwrap(string $string, int $width = 75, string $break = '\n', bool $cut_long_words = false): string
```
-Lowers `wordwrap(string, width?, break?, cut?)` through the shared runtime helper.
+Wraps a string to a given number of characters.
**Parameters**:
- `$string` (`string`)
-- `$width` (`int`), optional
-- `$break` (`string`), optional
-- `$cut_long_words` (`bool`), optional
+- `$width` (`int`), default `75`, optional
+- `$break` (`string`), default `'\n'`, optional
+- `$cut_long_words` (`bool`), default `false`, optional
**Returns**: `string`
diff --git a/docs/php/builtins/type.md b/docs/php/builtins/type.md
index 9eea9cf168..75386c75ec 100644
--- a/docs/php/builtins/type.md
+++ b/docs/php/builtins/type.md
@@ -18,10 +18,10 @@ sidebar:
| [`get_resource_id()`](./type/get_resource_id.md) | `(resource $resource): int` | `int` |
| [`get_resource_type()`](./type/get_resource_type.md) | `(resource $resource): string` | `string` |
| [`gettype()`](./type/gettype.md) | `(mixed $value): string` | `string` |
-| [`intval()`](./type/intval.md) | `(mixed $value, int $base): int` | `int` |
+| [`intval()`](./type/intval.md) | `(mixed $value): int` | `int` |
| [`is_array()`](./type/is_array.md) | `(mixed $value): bool` | `bool` |
| [`is_bool()`](./type/is_bool.md) | `(mixed $value): bool` | `bool` |
-| [`is_callable()`](./type/is_callable.md) | `(mixed $value, bool $syntax_only = false, string $callable_name = null): bool` | `bool` |
+| [`is_callable()`](./type/is_callable.md) | `(mixed $value): bool` | `bool` |
| [`is_float()`](./type/is_float.md) | `(mixed $value): bool` | `bool` |
| [`is_int()`](./type/is_int.md) | `(mixed $value): bool` | `bool` |
| [`is_iterable()`](./type/is_iterable.md) | `(mixed $value): bool` | `bool` |
diff --git a/docs/php/builtins/type/boolval.md b/docs/php/builtins/type/boolval.md
index e854d8b571..a8d071bc1c 100644
--- a/docs/php/builtins/type/boolval.md
+++ b/docs/php/builtins/type/boolval.md
@@ -1,8 +1,8 @@
---
title: "boolval()"
-description: "Lowers `boolval()` using the same concrete scalar PHP truthiness rules as `IsTruthy`."
+description: "Returns the boolean value of a variable."
sidebar:
- order: 388
+ order: 406
---
## boolval()
@@ -11,7 +11,7 @@ sidebar:
function boolval(mixed $value): bool
```
-Lowers `boolval()` using the same concrete scalar PHP truthiness rules as `IsTruthy`.
+Returns the boolean value of a variable.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/ctype_alnum.md b/docs/php/builtins/type/ctype_alnum.md
index 4c51c2922e..e051fd33a5 100644
--- a/docs/php/builtins/type/ctype_alnum.md
+++ b/docs/php/builtins/type/ctype_alnum.md
@@ -1,8 +1,8 @@
---
title: "ctype_alnum()"
-description: "Lowers `ctype_alnum(string)` by checking every byte against ASCII alpha or digit ranges."
+description: "Checks if all characters in the string are alphanumeric."
sidebar:
- order: 389
+ order: 407
---
## ctype_alnum()
@@ -11,7 +11,7 @@ sidebar:
function ctype_alnum(string $text): bool
```
-Lowers `ctype_alnum(string)` by checking every byte against ASCII alpha or digit ranges.
+Checks if all characters in the string are alphanumeric.
**Parameters**:
- `$text` (`string`)
diff --git a/docs/php/builtins/type/ctype_alpha.md b/docs/php/builtins/type/ctype_alpha.md
index 38781f5145..ff8ecaabea 100644
--- a/docs/php/builtins/type/ctype_alpha.md
+++ b/docs/php/builtins/type/ctype_alpha.md
@@ -1,8 +1,8 @@
---
title: "ctype_alpha()"
-description: "Lowers `ctype_alpha(string)` by checking every byte against ASCII alpha ranges."
+description: "Checks if all characters in the string are alphabetic."
sidebar:
- order: 390
+ order: 408
---
## ctype_alpha()
@@ -11,7 +11,7 @@ sidebar:
function ctype_alpha(string $text): bool
```
-Lowers `ctype_alpha(string)` by checking every byte against ASCII alpha ranges.
+Checks if all characters in the string are alphabetic.
**Parameters**:
- `$text` (`string`)
diff --git a/docs/php/builtins/type/ctype_digit.md b/docs/php/builtins/type/ctype_digit.md
index ef231ecff6..d80449f902 100644
--- a/docs/php/builtins/type/ctype_digit.md
+++ b/docs/php/builtins/type/ctype_digit.md
@@ -1,8 +1,8 @@
---
title: "ctype_digit()"
-description: "Lowers `ctype_digit(string)` by checking every byte against the ASCII digit range."
+description: "Checks if all characters in the string are digits."
sidebar:
- order: 391
+ order: 409
---
## ctype_digit()
@@ -11,7 +11,7 @@ sidebar:
function ctype_digit(string $text): bool
```
-Lowers `ctype_digit(string)` by checking every byte against the ASCII digit range.
+Checks if all characters in the string are digits.
**Parameters**:
- `$text` (`string`)
diff --git a/docs/php/builtins/type/ctype_space.md b/docs/php/builtins/type/ctype_space.md
index 17fae70cfe..2f08643b6c 100644
--- a/docs/php/builtins/type/ctype_space.md
+++ b/docs/php/builtins/type/ctype_space.md
@@ -1,8 +1,8 @@
---
title: "ctype_space()"
-description: "Lowers `ctype_space(string)` by checking every byte against PHP's ASCII whitespace set."
+description: "Checks if all characters in the string are whitespace characters."
sidebar:
- order: 392
+ order: 410
---
## ctype_space()
@@ -11,7 +11,7 @@ sidebar:
function ctype_space(string $text): bool
```
-Lowers `ctype_space(string)` by checking every byte against PHP's ASCII whitespace set.
+Checks if all characters in the string are whitespace characters.
**Parameters**:
- `$text` (`string`)
diff --git a/docs/php/builtins/type/floatval.md b/docs/php/builtins/type/floatval.md
index 8d7b7537e5..2a747a687e 100644
--- a/docs/php/builtins/type/floatval.md
+++ b/docs/php/builtins/type/floatval.md
@@ -1,8 +1,8 @@
---
title: "floatval()"
-description: "Lowers `floatval()` for concrete scalar operands."
+description: "Returns the float value of a variable."
sidebar:
- order: 393
+ order: 411
---
## floatval()
@@ -11,7 +11,7 @@ sidebar:
function floatval(mixed $value): float
```
-Lowers `floatval()` for concrete scalar operands.
+Returns the float value of a variable.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/get_resource_id.md b/docs/php/builtins/type/get_resource_id.md
index a00a692499..e0d7e455df 100644
--- a/docs/php/builtins/type/get_resource_id.md
+++ b/docs/php/builtins/type/get_resource_id.md
@@ -1,8 +1,8 @@
---
title: "get_resource_id()"
-description: "Lowers `get_resource_id(resource)` by unboxing the native handle and making it one-based."
+description: "Returns an integer identifier for the given resource."
sidebar:
- order: 394
+ order: 412
---
## get_resource_id()
@@ -11,7 +11,7 @@ sidebar:
function get_resource_id(resource $resource): int
```
-Lowers `get_resource_id(resource)` by unboxing the native handle and making it one-based.
+Returns an integer identifier for the given resource.
**Parameters**:
- `$resource` (`resource`)
diff --git a/docs/php/builtins/type/get_resource_type.md b/docs/php/builtins/type/get_resource_type.md
index a4bdcab397..58f692113c 100644
--- a/docs/php/builtins/type/get_resource_type.md
+++ b/docs/php/builtins/type/get_resource_type.md
@@ -1,8 +1,8 @@
---
title: "get_resource_type()"
-description: "Lowers `get_resource_type(resource)` to elephc's current resource type label."
+description: "Returns the type of a resource."
sidebar:
- order: 395
+ order: 413
---
## get_resource_type()
@@ -11,7 +11,7 @@ sidebar:
function get_resource_type(resource $resource): string
```
-Lowers `get_resource_type(resource)` to elephc's current resource type label.
+Returns the type of a resource.
**Parameters**:
- `$resource` (`resource`)
diff --git a/docs/php/builtins/type/gettype.md b/docs/php/builtins/type/gettype.md
index 35844528bb..99b61a5330 100644
--- a/docs/php/builtins/type/gettype.md
+++ b/docs/php/builtins/type/gettype.md
@@ -1,8 +1,8 @@
---
title: "gettype()"
-description: "Lowers `gettype(value)` for statically concrete PHP types."
+description: "Returns the type of a variable as a string."
sidebar:
- order: 396
+ order: 414
---
## gettype()
@@ -11,7 +11,7 @@ sidebar:
function gettype(mixed $value): string
```
-Lowers `gettype(value)` for statically concrete PHP types.
+Returns the type of a variable as a string.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/intval.md b/docs/php/builtins/type/intval.md
index fd99a94fea..73882f61b4 100644
--- a/docs/php/builtins/type/intval.md
+++ b/docs/php/builtins/type/intval.md
@@ -1,21 +1,20 @@
---
title: "intval()"
-description: "Lowers `intval()` for concrete scalar operands."
+description: "Returns the integer value of a variable."
sidebar:
- order: 397
+ order: 415
---
## intval()
```php
-function intval(mixed $value, int $base): int
+function intval(mixed $value): int
```
-Lowers `intval()` for concrete scalar operands.
+Returns the integer value of a variable.
**Parameters**:
- `$value` (`mixed`)
-- `$base` (`int`)
**Returns**: `int`
diff --git a/docs/php/builtins/type/is_array.md b/docs/php/builtins/type/is_array.md
index f5814388de..fd5ce4590f 100644
--- a/docs/php/builtins/type/is_array.md
+++ b/docs/php/builtins/type/is_array.md
@@ -1,8 +1,8 @@
---
title: "is_array()"
-description: "Lowers `is_array()`: true for statically-known arrays/hashes, or a boxed Mixed/Union value"
+description: "Checks whether a variable is an array."
sidebar:
- order: 398
+ order: 416
---
## is_array()
@@ -11,7 +11,7 @@ sidebar:
function is_array(mixed $value): bool
```
-Lowers `is_array()`: true for statically-known arrays/hashes, or a boxed Mixed/Union value
+Checks whether a variable is an array.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_bool.md b/docs/php/builtins/type/is_bool.md
index c075304721..04f70bbb33 100644
--- a/docs/php/builtins/type/is_bool.md
+++ b/docs/php/builtins/type/is_bool.md
@@ -1,8 +1,8 @@
---
title: "is_bool()"
-description: "is_bool() — type builtin supported by Elephc."
+description: "Checks whether a variable is a boolean."
sidebar:
- order: 399
+ order: 417
---
## is_bool()
@@ -11,7 +11,7 @@ sidebar:
function is_bool(mixed $value): bool
```
-`is_bool()` is a type builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether a variable is a boolean.
**Parameters**:
- `$value` (`mixed`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_bool` is implemented in the compiler, see [the internals page](../../../internals/builtins/type/is_bool.md).
+
diff --git a/docs/php/builtins/type/is_callable.md b/docs/php/builtins/type/is_callable.md
index 0e9c05cb6d..a39b02bf2a 100644
--- a/docs/php/builtins/type/is_callable.md
+++ b/docs/php/builtins/type/is_callable.md
@@ -1,22 +1,20 @@
---
title: "is_callable()"
-description: "Lowers `is_callable(value)` through static lookup or runtime callable-shape helpers."
+description: "Checks whether a variable can be called as a function."
sidebar:
- order: 400
+ order: 418
---
## is_callable()
```php
-function is_callable(mixed $value, bool $syntax_only = false, string $callable_name = null): bool
+function is_callable(mixed $value): bool
```
-Lowers `is_callable(value)` through static lookup or runtime callable-shape helpers.
+Checks whether a variable can be called as a function.
**Parameters**:
- `$value` (`mixed`)
-- `$syntax_only` (`bool`), default `false`, optional
-- `$callable_name` (`string`), passed by reference, default `null`, optional
**Returns**: `bool`
diff --git a/docs/php/builtins/type/is_float.md b/docs/php/builtins/type/is_float.md
index 13a6d50b0d..844baa0e05 100644
--- a/docs/php/builtins/type/is_float.md
+++ b/docs/php/builtins/type/is_float.md
@@ -1,8 +1,8 @@
---
title: "is_float()"
-description: "is_float() — type builtin supported by Elephc."
+description: "Checks whether a variable is a floating-point number."
sidebar:
- order: 401
+ order: 419
---
## is_float()
@@ -11,7 +11,7 @@ sidebar:
function is_float(mixed $value): bool
```
-`is_float()` is a type builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether a variable is a floating-point number.
**Parameters**:
- `$value` (`mixed`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_float` is implemented in the compiler, see [the internals page](../../../internals/builtins/type/is_float.md).
+
diff --git a/docs/php/builtins/type/is_int.md b/docs/php/builtins/type/is_int.md
index 081c0cf152..12b0081047 100644
--- a/docs/php/builtins/type/is_int.md
+++ b/docs/php/builtins/type/is_int.md
@@ -1,8 +1,8 @@
---
title: "is_int()"
-description: "is_int() — type builtin supported by Elephc."
+description: "Checks whether a variable is an integer."
sidebar:
- order: 402
+ order: 420
---
## is_int()
@@ -11,7 +11,7 @@ sidebar:
function is_int(mixed $value): bool
```
-`is_int()` is a type builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether a variable is an integer.
**Parameters**:
- `$value` (`mixed`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_int` is implemented in the compiler, see [the internals page](../../../internals/builtins/type/is_int.md).
+
diff --git a/docs/php/builtins/type/is_iterable.md b/docs/php/builtins/type/is_iterable.md
index 30b5a086e3..176e0c1470 100644
--- a/docs/php/builtins/type/is_iterable.md
+++ b/docs/php/builtins/type/is_iterable.md
@@ -1,8 +1,8 @@
---
title: "is_iterable()"
-description: "Lowers `is_iterable()` for concrete values and boxed Mixed payloads."
+description: "Checks whether a variable is iterable."
sidebar:
- order: 403
+ order: 421
---
## is_iterable()
@@ -11,7 +11,7 @@ sidebar:
function is_iterable(mixed $value): bool
```
-Lowers `is_iterable()` for concrete values and boxed Mixed payloads.
+Checks whether a variable is iterable.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_null.md b/docs/php/builtins/type/is_null.md
index 667787976a..673e9c4c4c 100644
--- a/docs/php/builtins/type/is_null.md
+++ b/docs/php/builtins/type/is_null.md
@@ -1,8 +1,8 @@
---
title: "is_null()"
-description: "is_null() — type builtin supported by Elephc."
+description: "Checks whether a variable is null."
sidebar:
- order: 404
+ order: 422
---
## is_null()
@@ -11,7 +11,7 @@ sidebar:
function is_null(mixed $value): bool
```
-`is_null()` is a type builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether a variable is null.
**Parameters**:
- `$value` (`mixed`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_null` is implemented in the compiler, see [the internals page](../../../internals/builtins/type/is_null.md).
+
diff --git a/docs/php/builtins/type/is_numeric.md b/docs/php/builtins/type/is_numeric.md
index 9f9c9efd16..312e1f6fe4 100644
--- a/docs/php/builtins/type/is_numeric.md
+++ b/docs/php/builtins/type/is_numeric.md
@@ -1,8 +1,8 @@
---
title: "is_numeric()"
-description: "Lowers `is_numeric()` for concrete scalar values."
+description: "Checks whether a variable is a number or a numeric string."
sidebar:
- order: 405
+ order: 423
---
## is_numeric()
@@ -11,7 +11,7 @@ sidebar:
function is_numeric(mixed $value): bool
```
-Lowers `is_numeric()` for concrete scalar values.
+Checks whether a variable is a number or a numeric string.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_object.md b/docs/php/builtins/type/is_object.md
index acf0ef3316..cf63b4b8fd 100644
--- a/docs/php/builtins/type/is_object.md
+++ b/docs/php/builtins/type/is_object.md
@@ -1,8 +1,8 @@
---
title: "is_object()"
-description: "Lowers `is_object()`: true for statically-known objects, or a boxed Mixed/Union value whose"
+description: "Checks whether a variable is an object."
sidebar:
- order: 406
+ order: 424
---
## is_object()
@@ -11,7 +11,7 @@ sidebar:
function is_object(mixed $value): bool
```
-Lowers `is_object()`: true for statically-known objects, or a boxed Mixed/Union value whose
+Checks whether a variable is an object.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_resource.md b/docs/php/builtins/type/is_resource.md
index 6d7a8e8019..62e0b8f618 100644
--- a/docs/php/builtins/type/is_resource.md
+++ b/docs/php/builtins/type/is_resource.md
@@ -1,8 +1,8 @@
---
title: "is_resource()"
-description: "Lowers `is_resource(value)` for static resources and boxed Mixed resource cells."
+description: "Checks whether a variable is a resource."
sidebar:
- order: 407
+ order: 425
---
## is_resource()
@@ -11,7 +11,7 @@ sidebar:
function is_resource(mixed $value): bool
```
-Lowers `is_resource(value)` for static resources and boxed Mixed resource cells.
+Checks whether a variable is a resource.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_scalar.md b/docs/php/builtins/type/is_scalar.md
index 496308d455..f348e8026a 100644
--- a/docs/php/builtins/type/is_scalar.md
+++ b/docs/php/builtins/type/is_scalar.md
@@ -1,8 +1,8 @@
---
title: "is_scalar()"
-description: "Lowers `is_scalar()`: true for int/float/string/bool, a non-null tagged scalar, or a boxed"
+description: "Checks whether a variable is a scalar."
sidebar:
- order: 408
+ order: 426
---
## is_scalar()
@@ -11,7 +11,7 @@ sidebar:
function is_scalar(mixed $value): bool
```
-Lowers `is_scalar()`: true for int/float/string/bool, a non-null tagged scalar, or a boxed
+Checks whether a variable is a scalar.
**Parameters**:
- `$value` (`mixed`)
diff --git a/docs/php/builtins/type/is_string.md b/docs/php/builtins/type/is_string.md
index 44183c2dad..fe632c3b83 100644
--- a/docs/php/builtins/type/is_string.md
+++ b/docs/php/builtins/type/is_string.md
@@ -1,8 +1,8 @@
---
title: "is_string()"
-description: "is_string() — type builtin supported by Elephc."
+description: "Checks whether a variable is a string."
sidebar:
- order: 409
+ order: 427
---
## is_string()
@@ -11,7 +11,7 @@ sidebar:
function is_string(mixed $value): bool
```
-`is_string()` is a type builtin supported by Elephc. Behavior matches the PHP manual unless noted below.
+Checks whether a variable is a string.
**Parameters**:
- `$value` (`mixed`)
@@ -26,3 +26,7 @@ _No examples yet — check `examples/` and `showcases/` for usage patterns._
+## Internals
+
+For how `is_string` is implemented in the compiler, see [the internals page](../../../internals/builtins/type/is_string.md).
+
diff --git a/docs/php/builtins/type/settype.md b/docs/php/builtins/type/settype.md
index e69829366b..60cad609de 100644
--- a/docs/php/builtins/type/settype.md
+++ b/docs/php/builtins/type/settype.md
@@ -1,8 +1,8 @@
---
title: "settype()"
-description: "Lowers `settype($local, \"type\")` by mutating the resolved local slot and returning true."
+description: "Sets the type of a variable."
sidebar:
- order: 410
+ order: 428
---
## settype()
@@ -11,7 +11,7 @@ sidebar:
function settype(mixed $var, string $type): bool
```
-Lowers `settype($local, "type")` by mutating the resolved local slot and returning true.
+Sets the type of a variable.
**Parameters**:
- `$var` (`mixed`), passed by reference
diff --git a/docs/php/classes.md b/docs/php/classes.md
index 3c9f07affa..0eaaa42a18 100644
--- a/docs/php/classes.md
+++ b/docs/php/classes.md
@@ -94,8 +94,7 @@ Classes implementing `ArrayAccess` can use PHP subscript syntax:
`unset($obj[$key])` dispatches to `offsetUnset()`.
`Serializable` is intentionally not provided: it is deprecated since
-PHP 8.1. Use `__serialize` / `__unserialize` magic methods instead
-(when those land).
+PHP 8.1. Use the `__serialize` / `__unserialize` magic methods instead.
### Built-in SPL containers and storage iterators
@@ -220,6 +219,8 @@ final class InvoiceNumber {
- Static properties with `public static`, `protected static`, or `private static`, including typed static properties
- `readonly class` makes all instance properties readonly; static properties stay mutable
+Statically-known access violations — calling a `private`/`protected` method from an inaccessible scope, or writing a `readonly` property outside its declaring constructor — raise a catchable `Error` exception at runtime, matching PHP. Without a `try`/`catch` handler the exception is a fatal uncaught exit.
+
```php
name` property (the case identifier); backed cases also expose `->value`. Plus `::from()`, `::tryFrom()`, `::cases()`. Only `int` and `string` backing types.
+Like PHP, an `int`-backed enum's `::from()` / `::tryFrom()` accept a numeric string and coerce it to the integer backing value (`Color::from("2")` returns `Color::Green`). A numeric string with no matching case throws `ValueError`; a non-numeric string (e.g. `"x"`, `"1abc"`, `"0x1"`, `"INF"`, or `"NAN"`) throws `TypeError`, matching PHP's coercive typing.
+
+A dynamically-typed (`mixed`) argument — such as a `foreach` value or an untyped parameter — is also accepted and coerced on its runtime type: an integer or numeric string resolves (or throws `ValueError`), a float truncates, a bool/null coerces, and an array/object/resource/closure throws `TypeError` naming the given type (objects report `object` rather than the class name).
+
### Enum methods, constants, and interfaces
Enums may declare instance methods, static methods, constants, and an `implements` clause. Instance methods dispatch on the case singleton, so `$this` is the case:
diff --git a/docs/php/streams.md b/docs/php/streams.md
index 980dbc0e56..260b899f26 100644
--- a/docs/php/streams.md
+++ b/docs/php/streams.md
@@ -85,22 +85,66 @@ entry in a SHA1-signed PHAR archive while preserving existing entries.
`file_put_contents()` and write-mode `fopen()` also accept runtime-built
`phar://` URLs. Native PHAR, tar-based PHAR, and zip-based PHAR containers are
writable; ZIP writes preserve stored/deflated entries and compression controls
-can rewrite ZIP entries between stored and deflated forms. `Phar` and
-`PharData` expose a
+can rewrite ZIP entries between stored and deflated forms. ZIP entries written
+with a streaming data descriptor are read transparently, and ZIP64 archives
+(over 65535 entries, or sizes/offsets over 4 GiB) are both read and written.
+Traditional-PKWARE (ZipCrypto) encrypted ZIP entries can be read and written
+after calling the `setZipPassword(string $password)` compiler extension on a
+`Phar`/`PharData` object: once a password is set, encrypted entries are decrypted
+on read and zip entries are encrypted on write (the stub is encrypted too; the
+`.phar/signature.bin` entry stays in the clear). ZipCrypto is cryptographically
+weak — it is kept for compatibility with legacy archives, not as a real
+confidentiality mechanism. `Phar` and `PharData` expose a
baseline OOP surface with constructors, format/compression/signature constants,
`addFromString()`, `delete()`, `compressFiles()`, `decompressFiles()`,
mixed metadata/string stub accessors, path helpers, and ArrayAccess read/write/isset
over the same `phar://` paths. ArrayAccess reads return `PharFileInfo` objects
-with `getContent()` for payload reads. `foreach` over a `Phar` / `PharData`
+with `getContent()` for payload reads and
+`setMetadata()`/`getMetadata()`/`hasMetadata()`/`delMetadata()` for per-file
+metadata. `foreach` over a `Phar` / `PharData`
object visits entries scanned from the archive at construction time plus entries
written through that object, yielding `entryName => PharFileInfo`.
`unlink("phar://archive/entry")` and `unset($phar["entry"])` remove entries
while preserving sibling entries. Native PHAR compression controls support
`Phar::GZ`, `Phar::BZ2`, and `Phar::NONE`; ZIP compression controls support
-`Phar::GZ` and `Phar::NONE`. Current limits: metadata values and stub strings
-are stored on the archive object and are not serialized into the archive file;
-tar compression controls and key/private-key signing variants are not
-implemented.
+`Phar::GZ` and `Phar::NONE`.
+
+`setMetadata()`/`getMetadata()`/`hasMetadata()`/`delMetadata()` and
+`setStub()`/`getStub()` **persist into the archive file** for all three families,
+so the global metadata and stub round-trip across fresh `Phar`/`PharData` objects
+and across processes (and are interchangeable with the PHP interpreter). Metadata
+is stored PHP-`serialize()`d — in the manifest metadata field for native PHAR, in a
+`.phar/.metadata.bin` entry for tar, and in the ZIP archive comment for zip; the stub
+is stored as the byte prefix for native PHAR and as a `.phar/stub.php` entry for
+tar/zip. `setStub()` requires the stub to contain `__HALT_COMPILER();` (matching PHP).
+The reserved `.phar/*` control entries are hidden from the entry listing and iteration.
+
+Per-file metadata persists the same way through the `PharFileInfo` returned by
+ArrayAccess: `$phar["entry"]->setMetadata(...)`, `getMetadata()`, `hasMetadata()`,
+and `delMetadata()` round-trip across fresh objects and the PHP interpreter. It is
+stored in the per-entry manifest field for native PHAR, in a
+`.phar/.metadata//.metadata.bin` side entry for tar, and in the per-entry ZIP
+central-directory file comment for zip.
+
+Whole-archive compression is supported on tar-based `PharData`: `compress(Phar::GZ)`
+and `compress(Phar::BZ2)` write a sibling `.tar.gz` / `.tar.bz2` and return a fresh
+`PharData` for it, while `decompress()` writes the plain `.tar` back; the compressed
+archives are read transparently (and are interchangeable with the PHP interpreter).
+Per-entry compression for native PHAR / zip stays on `compressFiles()` /
+`decompressFiles()`.
+
+Signatures are supported through `setSignatureAlgorithm()` / `getSignature()` across
+native PHAR, tar, and zip phars. `setSignatureAlgorithm(Phar::MD5|Phar::SHA1|Phar::SHA256|Phar::SHA512)`
+applies a hash signature, and `setSignatureAlgorithm(Phar::OPENSSL, $privateKey)` signs
+with RSA-SHA1 using a PEM private key (PKCS#1 or PKCS#8). Native PHARs store the signature
+in their trailer; tar and zip phars store it in a `.phar/signature.bin` control entry. The
+resulting signature is verifiable by the PHP interpreter (for OpenSSL, place the matching
+public key in `.pubkey`). `getSignature()` returns `['hash' => ,
+'hash_type' => 'MD5'|'SHA-1'|'SHA-256'|'SHA-512'|'OpenSSL']`.
+
+Metadata persistence covers the same scalar+array subset as
+[`serialize()`/`unserialize()`](system-and-io.md#serialization); object metadata is not
+serialized.
`file_get_contents($url)` recognizes runtime `http://`, `https://`, `ftp://`,
and `ftps://` strings before falling back to `phar://`/filesystem handling.
diff --git a/docs/php/system-and-io.md b/docs/php/system-and-io.md
index 9b98b9cdfa..c8c6cb1e04 100644
--- a/docs/php/system-and-io.md
+++ b/docs/php/system-and-io.md
@@ -182,6 +182,47 @@ Encoding rules for objects:
- Floats encode at PHP's `serialize_precision = -1` — the shortest decimal that round-trips back to the same `double` (so `json_encode(1.0/3.0)` is `0.3333333333333333`, not the 14-digit `echo`/`(string)` form, and `json_encode(0.1 + 0.2)` is `0.30000000000000004`). The JSON number layout differs from `var_export`: integer-valued floats drop the fraction (`json_encode(100.0)` is `100`, not `100.0`) unless `JSON_PRESERVE_ZERO_FRACTION` is set, and exponential magnitudes use a lowercase `e` with a `d.d` mantissa and a no-leading-zero exponent (`1.0e+17`, `1.0e-6`). The decimal/exponential boundary matches PHP (`zend_gcvt`): exponential when `decpt < -3` or `decpt > 17`. The dedicated `__rt_json_ftoa` runtime helper finds the shortest precision by probing `snprintf("%.*e", p, x)` against a `strtod` re-parse, independent of the default `precision` used elsewhere.
- JSON helpers are emitted through the shared runtime surface on every supported target. Structural decode into `Mixed`, stdClass dynamic-property helpers, JsonSerializable-aware object encoding, validation, pretty-printing, depth tracking, and JSON error-message lookup are all part of that target-aware runtime path.
+## Serialization
+
+| Function | Signature | Notes |
+|---|---|---|
+| `serialize()` | `serialize($value): string` | Produces PHP's `serialize()` wire format, byte-for-byte: `N;` (null), `b:0;`/`b:1;` (bool), `i:;` (int), `d:;` (float, shortest round-trip at `serialize_precision = -1`, with `INF`/`-INF`/`NAN` for non-finite values), `s::"";` (string, raw bytes with the exact byte length and no escaping), `a::{...}` (indexed and associative arrays, nested, with int keys as `i:K;` and string keys as `s:N:"...";`, in insertion order), and `O::""::{...}` (objects). |
+| `unserialize()` | `unserialize($data, $options = []): mixed` | Parses the `serialize()` wire format back into a boxed `Mixed` value. Scalars, arrays, and objects round-trip exactly. Malformed or unsupported input returns `false`, matching PHP's failure indicator. The `$options` argument is accepted for signature compatibility and currently ignored. |
+
+`serialize()`/`unserialize()` round-trip the scalar, array, and object subset exactly,
+and the produced bytes are interchangeable with the PHP interpreter. They share the same
+runtime walker family as `json_encode`/`json_decode` and reuse the shortest-float
+formatter, so float output matches `json_encode`'s precision.
+
+### Objects
+
+Objects serialize as `O::""::{...}` with PHP's exact property-key
+mangling: public properties use the bare name, protected use `\0*\0name`, and private
+use `\0Class\0name`. Properties are emitted in declaration order (inherited first).
+
+Serialization magic methods are honoured:
+
+- **`__serialize(): array`** — when defined, the object body is the returned array's
+ `key;value;` pairs instead of the raw properties.
+- **`__unserialize(array $data): void`** — when defined, the parsed body is passed to it
+ to restore the object (instead of injecting properties by name). Its `$data` parameter
+ is treated as a string/int-keyed array so `$data['key']` works (a bare `array` hint
+ otherwise resolves to an integer-indexed array).
+- **`__sleep(): array`** — serializes only the named properties, in `__sleep()`'s order,
+ using their mangled keys.
+- **`__wakeup(): void`** — runs after properties are injected (when `__unserialize()` is
+ not defined).
+
+Repeated objects within a single `serialize()` call are emitted as `r:;`
+back-references (PHP's global value counter: every value consumes the next index, array
+keys do not), and `unserialize()` rebuilds them as a single shared instance so `===`
+identity is preserved. This is the same machinery used to persist `Phar` global metadata
+(see [Streams](streams.md)).
+
+**Limitations:** a cyclic reference *inside an object's own properties* resolves to
+`null` on `unserialize()` (serialization itself handles cycles correctly), and the
+deprecated `Serializable` interface (`C:` wire form) is not supported.
+
## Regex
Regex functions and SPL regex iterators are documented in [Regex](regex.md),
diff --git a/docs/php/types.md b/docs/php/types.md
index 3d79ab31b8..864c6765b8 100644
--- a/docs/php/types.md
+++ b/docs/php/types.md
@@ -233,14 +233,15 @@ Narrowing applies to function and method parameters. A parameter whose call site
### Known incompatibilities with PHP
- `$argv[0]` returns the compiled binary path, not the `.php` file path.
-- Integer `+`, `-`, and `*` overflow promotes to `float` only for **constant-folded** arithmetic (compile-time-constant operands), matching PHP. At **runtime**, `int op int` has the static type `int`, so an overflowing operation does **not** promote to `float`: the result is clamped toward the 64-bit integer boundary and `is_float()` stays `false`, whereas PHP returns a `float`. Promoting at runtime would require boxing every arithmetic result, which elephc's unboxed scalar representation avoids. For the same reason, `intval()`/`(int)` of an integer-valued string near the 64-bit boundary (e.g. `intval("9223372036854775807")`) is lossy.
+- Integer `+`, `-`, and `*` overflow promotes to `float` for both constant-folded and runtime arithmetic, matching PHP on 64-bit builds. `intval()`/`(int)` of an integer-valued string near the 64-bit boundary (e.g. `intval("9223372036854775807")`) is still lossy at the string-conversion boundary.
- Converting an array to a string (via `.` concatenation, `echo`, or string interpolation) yields the literal `"Array"`, matching PHP's value, but elephc does not emit PHP's `E_WARNING` "Array to string conversion".
- Scalar loose comparison (`==`, `!=`) follows PHP-style bool truthiness, null-vs-empty-string, numeric-string, non-numeric string byte-comparison, and numeric `int`-vs-`float` rules for constant-folded literals and non-folded runtime scalar operands. One known gap: when an **untyped (`mixed`) operand holds a `float`** at runtime — e.g. `switch ($x)` over an untyped `$x = 1.5`, or `$x == 1` — the value is truncated to `int` before comparing, so `1.5` wrongly compares equal to `1`. Statically-typed `float` operands compare correctly; only untyped float-bearing values are affected.
- `??=` is checked against typed assignment storage for variables, object properties, static properties, and non-append array elements. For concrete local variable types, the fallback must keep the same type or be a literal `null`.
- Plain array numeric casts (`(int)$array`, `(float)$array`) follow elephc's existing array cast semantics (return the element count rather than PHP's `0`/`1`). Direct `iterable` numeric casts use PHP's empty/non-empty `0`/`1` semantics.
- `__destruct` runs when an object's refcount reaches zero (scope exit, reassignment, `unset`, program end), matching PHP's timing, but **object resurrection is not supported**: re-storing `$this` so the object would outlive the destructor does not keep it alive — the object is still freed once `__destruct` returns.
-- Under the legacy `--null-repr=sentinel` opt-out, the integer `9223372036854775806` (`PHP_INT_MAX - 1`) collides with elephc's internal null marker in unboxed scalar slots and is misread as `null` by `echo`, `var_dump()`, `is_null()`, `??`, and related null checks. The default tagged null representation does not have this collision: the full 64-bit integer range round-trips.
+- Under the compatibility `--null-repr=sentinel` opt-out, the integer `9223372036854775806` (`PHP_INT_MAX - 1`) collides with elephc's internal null marker in unboxed scalar slots and is misread as `null` by `echo`, `var_dump()`, `is_null()`, `??`, and related null checks. The default tagged null representation does not have this collision: the full 64-bit integer range round-trips.
- Variable variables (`$$name`, `${$expr}`) are not supported. elephc allocates each local to a fixed compile-time stack slot and keeps no per-frame variable-name table, so a variable whose name is computed at runtime cannot be resolved. Use an array keyed by the dynamic name instead.
+- `serialize()`/`unserialize()` cover scalars, arrays, and objects (including the `__serialize`/`__unserialize`/`__sleep`/`__wakeup` magic methods and `r:`/`R:` object back-references) byte-for-byte compatibly with PHP. Remaining gaps: a cyclic reference inside an object's own properties resolves to `null` on `unserialize()` (serialization handles cycles), the deprecated `Serializable` interface (`C:` wire form) is unsupported, writing a property of an unserialized object held in a `Mixed` does not persist (a separate `Mixed` property-write limitation), and `unserialize()` does not emit PHP's `E_WARNING` / `E_NOTICE` on malformed input — it just returns `false`.
### Filesystem functions not implemented
diff --git a/examples/arithmetic/main.php b/examples/arithmetic/main.php
index b5fc5a5af2..f8bfce211e 100644
--- a/examples/arithmetic/main.php
+++ b/examples/arithmetic/main.php
@@ -10,3 +10,6 @@
echo "b % a = " . ($b % $a) . "\n";
echo "2 + 3 * 4 = " . (2 + 3 * 4) . "\n";
echo "(2 + 3) * 4 = " . ((2 + 3) * 4) . "\n";
+
+$overflow = PHP_INT_MAX + $argc;
+echo "overflow type = " . gettype($overflow) . "\n";
diff --git a/examples/array-parity/.gitignore b/examples/array-parity/.gitignore
new file mode 100644
index 0000000000..fc9576aeb9
--- /dev/null
+++ b/examples/array-parity/.gitignore
@@ -0,0 +1,3 @@
+*.s
+*.o
+main
diff --git a/examples/array-parity/main.php b/examples/array-parity/main.php
new file mode 100644
index 0000000000..9df2c643bd
--- /dev/null
+++ b/examples/array-parity/main.php
@@ -0,0 +1,75 @@
+ 1, "b" => 2, "c" => 3];
+
+echo "is_list(list): " . (array_is_list($list) ? "true" : "false") . "\n";
+echo "is_list(hash): " . (array_is_list($hash) ? "true" : "false") . "\n";
+echo "first key: " . array_key_first($hash) . "\n";
+echo "last key: " . array_key_last($hash) . "\n";
+
+// --- hash set operations (right-wins replace, recursive replace) ---
+$base = ["host" => "localhost", "port" => 80];
+$over = ["port" => 443, "tls" => 1];
+$merged = array_replace($base, $over);
+echo "\nreplace: port=" . $merged["port"] . " tls=" . $merged["tls"] . "\n";
+
+$deepA = ["db" => ["host" => "a", "port" => 1]];
+$deepB = ["db" => ["port" => 2]];
+$deep = array_replace_recursive($deepA, $deepB);
+echo "replace_rec: host=" . $deep["db"]["host"] . " port=" . $deep["db"]["port"] . "\n";
+
+// --- associative diff / intersect (compare key AND value) ---
+$left = ["a" => 1, "b" => 2, "c" => 3];
+$right = ["a" => 1, "b" => 99];
+echo "diff_assoc: ";
+foreach (array_diff_assoc($left, $right) as $k => $v) {
+ echo "$k=$v ";
+}
+echo "\nintersect_assoc:";
+foreach (array_intersect_assoc($left, $right) as $k => $v) {
+ echo " $k=$v";
+}
+echo "\n";
+
+// --- recursive merge (scalar collisions combine into lists) ---
+$mr = array_merge_recursive(["tag" => "a"], ["tag" => "b"]);
+echo "merge_rec: tag has " . count($mr["tag"]) . " values\n";
+
+// --- predicate helpers (PHP 8.4): find / any / all ---
+function gtTwo($n) { return $n > 2; }
+$nums = [1, 2, 3, 4];
+echo "\nfind > 2: " . array_find($nums, "gtTwo") . "\n";
+echo "any > 2: " . (array_any($nums, "gtTwo") ? "true" : "false") . "\n";
+echo "all > 2: " . (array_all($nums, fn($n) => $n > 0) ? "true" : "false") . "\n";
+
+// --- user-comparator set operations ---
+function cmp($a, $b) { return $a - $b; }
+echo "udiff: ";
+foreach (array_udiff([1, 2, 3, 4], [2, 4], "cmp") as $v) {
+ echo $v;
+}
+echo "\nuintersect: ";
+foreach (array_uintersect([1, 2, 3, 4], [2, 4], "cmp") as $v) {
+ echo $v;
+}
+echo "\n";
+
+// --- recursive walk over nested arrays ---
+function visit($leaf) { echo $leaf; echo ","; }
+$nested = [[1, 2], [3, 4], [5, 6]];
+echo "walk_recursive: ";
+array_walk_recursive($nested, "visit");
+echo "\n";
+
+// --- multisort: sort one array, reorder a parallel array in tandem ---
+$keys = [3, 1, 2];
+$vals = [30, 10, 20];
+array_multisort($keys, $vals);
+echo "multisort keys: ";
+foreach ($keys as $v) { echo $v; }
+echo "\nmultisort vals: ";
+foreach ($vals as $v) { echo $v; }
+echo "\n";
diff --git a/examples/catchable-access-errors/.gitignore b/examples/catchable-access-errors/.gitignore
new file mode 100644
index 0000000000..2c5756d149
--- /dev/null
+++ b/examples/catchable-access-errors/.gitignore
@@ -0,0 +1,3 @@
+*.s
+*.o
+main
\ No newline at end of file
diff --git a/examples/catchable-access-errors/main.php b/examples/catchable-access-errors/main.php
new file mode 100644
index 0000000000..b448ac234c
--- /dev/null
+++ b/examples/catchable-access-errors/main.php
@@ -0,0 +1,51 @@
+value = $value;
+ }
+}
+
+$vault = new Vault();
+
+// Private method access from the global scope throws a catchable Error.
+try {
+ echo $vault->secret();
+ echo "no";
+} catch (Error $e) {
+ echo "private: " . $e->getMessage() . "\n";
+}
+
+// Protected method access from the global scope throws a catchable Error.
+try {
+ echo $vault->guarded();
+ echo "no";
+} catch (Error $e) {
+ echo "protected: " . $e->getMessage() . "\n";
+}
+
+// Readonly property write outside the constructor throws a catchable Error.
+$container = new Container(1);
+try {
+ $container->value = 99;
+ echo "no";
+} catch (Error $e) {
+ echo "readonly: " . $e->getMessage() . "\n";
+}
+
+echo "done\n";
\ No newline at end of file
diff --git a/examples/hashing/main.php b/examples/hashing/main.php
index bfe24dad09..9679ee647e 100644
--- a/examples/hashing/main.php
+++ b/examples/hashing/main.php
@@ -41,6 +41,18 @@
echo "branch A: " . hash_final($base) . "\n";
echo "branch B: " . hash_final($branch) . "\n";
+// A context that is never finalized is auto-freed at scope exit: the runtime
+// calls elephc_crypto_free through __rt_mixed_free_deep (tag-9 kind-2) when
+// the Mixed box's refcount reaches zero. No leak, no double-free.
+function leak_ctx(): void {
+ $ctx = hash_init("sha256");
+ hash_update($ctx, "abandoned");
+ // No hash_final() — $ctx is auto-freed when the function returns.
+}
+leak_ctx();
+echo "\n--- auto-free scope cleanup ---\n";
+echo "unfinalized context freed without crash\n";
+
echo "\n--- supported algorithms ---\n";
echo "count: " . count(hash_algos()) . "\n";
diff --git a/examples/phar-write/.gitignore b/examples/phar-write/.gitignore
index 0d4bae516f..58e887e7ab 100644
--- a/examples/phar-write/.gitignore
+++ b/examples/phar-write/.gitignore
@@ -2,3 +2,7 @@
*.o
main
*.phar
+*.tar
+*.tar.gz
+*.tar.bz2
+*.pubkey
diff --git a/examples/phar-write/main.php b/examples/phar-write/main.php
index 512de391f8..8279a73d43 100644
--- a/examples/phar-write/main.php
+++ b/examples/phar-write/main.php
@@ -62,3 +62,29 @@
echo "oop scanned count: " . $scan->count() . "\n";
$oop->delete("array-access.txt");
echo "oop delete removed array-access entry: " . (isset($oop["array-access.txt"]) ? "no\n" : "yes\n");
+
+// Per-file metadata persists into the archive on each entry's PharFileInfo and
+// round-trips across a fresh Phar object (and the PHP interpreter).
+$oop["hello.txt"]->setMetadata(["author" => "elephc", "lines" => 1]);
+$reopened = new Phar("oop.phar");
+$fileMeta = $reopened["hello.txt"]->getMetadata();
+echo "per-file metadata author: " . $fileMeta["author"] . "\n";
+
+// A native PHAR can be re-signed; getSignature() reports the algorithm and digest.
+$oop->setSignatureAlgorithm(Phar::SHA256);
+$sig = $oop->getSignature();
+echo "signature type: " . $sig["hash_type"] . "\n";
+
+// Tar and zip phars can be signed too: the signature lives in a .phar/signature.bin
+// control entry (rather than a trailer), and is verifiable by the PHP interpreter.
+$tar = new PharData("bundle.tar");
+$tar->addFromString("doc.txt", "bundled document\n");
+$tar->setSignatureAlgorithm(Phar::SHA1);
+$tarSig = $tar->getSignature();
+echo "tar signature type: " . $tarSig["hash_type"] . "\n";
+
+// PharData supports whole-archive compression: compress() writes a sibling
+// .tar.gz and returns a fresh PharData that reads back transparently.
+$gz = $tar->compress(Phar::GZ);
+echo "compressed bundle entry count: " . $gz->count() . "\n";
+echo "compressed bundle reads back: " . $gz["doc.txt"]->getContent();
diff --git a/examples/serialize/.gitignore b/examples/serialize/.gitignore
new file mode 100644
index 0000000000..0d4bae516f
--- /dev/null
+++ b/examples/serialize/.gitignore
@@ -0,0 +1,4 @@
+*.s
+*.o
+main
+*.phar
diff --git a/examples/serialize/main.php b/examples/serialize/main.php
new file mode 100644
index 0000000000..94ee000e23
--- /dev/null
+++ b/examples/serialize/main.php
@@ -0,0 +1,63 @@
+ "Ada", "age" => 36]), "\n";
+
+// unserialize() is the inverse — round-trips back to the original value.
+$blob = serialize(["lang" => "PHP", "stars" => 5, "tags" => ["fast", "native"]]);
+$restored = unserialize($blob);
+echo $restored["lang"], " has ", $restored["stars"], " stars\n";
+echo "first tag: ", $restored["tags"][0], "\n";
+
+// unserialize() returns false on malformed input.
+var_dump(unserialize("not valid"));
+
+// Objects serialize as O::""::{...} with PHP's exact key
+// mangling (public bare, protected \0*\0name, private \0Class\0name).
+class Point { public int $x = 1; protected int $y = 2; private int $z = 3; }
+echo serialize(new Point()), "\n";
+
+// __serialize()/__unserialize() customise the wire form: the object body is the
+// returned array, and __unserialize() restores it.
+class Money {
+ public int $cents = 0;
+ public string $currency = "USD";
+ public function __serialize(): array {
+ return ["cents" => $this->cents, "currency" => $this->currency];
+ }
+ public function __unserialize(array $data): void {
+ $this->cents = (int) $data["cents"];
+ $this->currency = (string) $data["currency"];
+ }
+}
+$m = new Money();
+$m->cents = 1299;
+$m->currency = "EUR";
+$back = unserialize(serialize($m));
+echo $back->cents, " ", $back->currency, "\n"; // 1299 EUR
+
+// Repeated objects become r:; back-references and rebuild as one shared
+// instance, so identity (===) survives a serialize()/unserialize() round-trip.
+$shared = new Point();
+$pair = unserialize(serialize([$shared, $shared]));
+echo ($pair[0] === $pair[1] ? "same instance" : "two instances"), "\n";
+
+// Phar stores its global metadata as a serialize()d blob, so metadata set on one
+// Phar object is read back by another — and by the PHP interpreter.
+$path = "build.phar";
+$phar = new Phar($path);
+$phar->addFromString("app.php", "setMetadata(["version" => "1.0.0", "author" => "elephc"]);
+
+$reopened = new Phar($path);
+$meta = $reopened->getMetadata();
+echo "phar version: ", $meta["version"], " by ", $meta["author"], "\n";
+echo "has metadata: ", $reopened->hasMetadata() ? "yes" : "no", "\n";
diff --git a/examples/variadic/main.php b/examples/variadic/main.php
index e0bfec5720..722117833c 100644
--- a/examples/variadic/main.php
+++ b/examples/variadic/main.php
@@ -73,3 +73,12 @@ function labeled_pair($left, $right) {
echo $v . " ";
}
echo "\n";
+
+// Spread of associative arrays: string keys are preserved, integer keys are reindexed
+$config = ['host' => 'localhost', 'port' => 8080];
+$defaults = ['host' => '0.0.0.0', 'timeout' => 30];
+$merged_config = [...$defaults, ...$config];
+foreach ($merged_config as $key => $value) {
+ echo $key . "=" . $value . " ";
+}
+echo "\n";
diff --git a/examples/web-framework/main.php b/examples/web-framework/main.php
index 7b50812c92..68d79fd543 100644
--- a/examples/web-framework/main.php
+++ b/examples/web-framework/main.php
@@ -239,7 +239,7 @@ public function handle(Request $request, callable $next): void
class Home implements Handler
{
- public function handle(Request $request): void
+ public function handle(Request $_request): void
{
$help = "elephc-web mini-framework\n\n"
. "GET / this page\n"
@@ -262,7 +262,7 @@ public function handle(Request $request): void
class UserList implements Handler
{
- public function handle(Request $request): void
+ public function handle(Request $_request): void
{
$users = [
['id' => 1, 'name' => 'Ada'],
@@ -274,7 +274,7 @@ public function handle(Request $request): void
class UserCreate implements Handler
{
- public function handle(Request $request): void
+ public function handle(Request $_request): void
{
$name = $_POST['name'] ?? '';
if ($name === '') {
@@ -287,7 +287,7 @@ public function handle(Request $request): void
class Secret implements Handler
{
- public function handle(Request $request): void
+ public function handle(Request $_request): void
{
Response::json(json_encode(['secret' => 'the cake is a lie']))->send();
}
@@ -295,7 +295,7 @@ public function handle(Request $request): void
class Boom implements Handler
{
- public function handle(Request $request): void
+ public function handle(Request $_request): void
{
throw new \Exception('intentional failure');
}
diff --git a/rustfmt.toml b/rustfmt.toml
index 268c949cf4..5627a72273 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,4 +1,3 @@
-ignore = [
- "src/codegen/runtime",
- "src/codegen/builtins",
-]
+# Keep `cargo fmt` as a no-op in this repository. Large automatic rustfmt diffs
+# are intentionally avoided; use focused manual formatting for touched code.
+disable_all_formatting = true
diff --git a/scripts/check_asm_comments.py b/scripts/check_asm_comments.py
new file mode 100755
index 0000000000..dac0e5aac7
--- /dev/null
+++ b/scripts/check_asm_comments.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+"""Check inline assembly-comment alignment in elephc codegen files.
+
+Every `emitter.instruction(...)` call must carry an inline `//` comment that
+starts at column 81 (1-indexed). See "Assembly comment alignment" in
+CONTRIBUTING.md for the full policy.
+
+Usage:
+ scripts/check_asm_comments.py FILE.rs [FILE.rs ...]
+
+Reports every `emitter.instruction(...)` whose `//` comment is misaligned, as
+`path:line: // at col N (expected 81)`. Exits non-zero if any problem is found,
+so it can be used in pre-commit hooks or CI.
+"""
+
+from __future__ import annotations
+
+import argparse
+import sys
+from pathlib import Path
+
+# 1-indexed column where the `//` of an inline comment must start.
+COMMENT_COLUMN = 81
+
+
+def check_file(path: Path) -> list[str]:
+ """Return a list of human-readable problems for a single file."""
+ try:
+ text = path.read_text()
+ except OSError as exc:
+ return [f"{path}: cannot read file ({exc})"]
+
+ problems: list[str] = []
+ for lineno, line in enumerate(text.splitlines(), 1):
+ stripped = line.rstrip()
+ if "emitter.instruction" not in stripped or "//" not in stripped:
+ continue
+ pos = stripped.index("//") # 0-indexed position of the comment
+ # The `//` must sit at column 81 (index 80). Lines whose code already
+ # reaches 80 characters may use a single space before `//` instead, so
+ # they are exempt from the column check.
+ if pos != COMMENT_COLUMN - 1 and len(line[:pos].rstrip()) < 80:
+ problems.append(f"{path}:{lineno}: // at col {pos + 1} (expected {COMMENT_COLUMN})")
+ return problems
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument("files", nargs="+", type=Path, help="Rust codegen files to check")
+ args = parser.parse_args()
+
+ problems: list[str] = []
+ for path in args.files:
+ problems.extend(check_file(path))
+
+ for problem in problems:
+ print(problem)
+
+ if problems:
+ print(f"\n{len(problems)} misaligned comment(s) found.", file=sys.stderr)
+ return 1
+
+ print("All assembly comments aligned.")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/scripts/docs/README.md b/scripts/docs/README.md
index c62e080e52..b4e77c40d0 100644
--- a/scripts/docs/README.md
+++ b/scripts/docs/README.md
@@ -10,16 +10,24 @@ tree into one Markdown page per supported PHP builtin, in two flavours:
source file lowers the call, which runtime helper it dispatches to,
what the type checker enforces.
-The script is data-driven: it parses three layers of the Rust source to
-build a single JSON registry (`scripts/docs/builtin_registry.json`) which
-the Markdown renderer consumes. The registry is the canonical source of
-truth; everything else is generated.
+The script is data-driven. Its source of truth is the single-source
+`builtin!` registry (`src/builtins/`), read via the `gen_builtins` binary
+(`cargo run --bin gen_builtins --include-internal`). It enriches that data
+with each builtin's lowering location (parsed from the home file's `lower`
+hook) and documentation area, then writes a single JSON registry
+(`scripts/docs/builtin_registry.json`) which the Markdown renderer consumes.
+Everything else is generated.
## Usage
-From the repo root:
+From the repo root. The generator invokes the `gen_builtins` binary, so build
+it first (the extractor prefers the prebuilt binary at `target/debug/gen_builtins`
+and otherwise falls back to `cargo run`):
```bash
+# 0. Build the registry exporter the generator reads from
+cargo build --bin gen_builtins
+
# 1. Parse the source and write the JSON registry
python3 scripts/docs/extract_builtins.py
@@ -36,18 +44,22 @@ everything.
## What the script reads
-| Layer | File | What we extract |
+| Layer | Source | What we extract |
|---|---|---|
-| Catalog | `src/types/checker/builtins/catalog.rs` | The authoritative list of PHP-visible builtins. |
-| Signatures | `src/types/signatures.rs` | Per-builtin parameter names, defaults, variadics, by-ref flags, and first-class return types. |
-| Lowering | `src/codegen_ir/lower_inst/builtins.rs` + per-area submodules | For each builtin: the lowering function, its location, runtime helpers it calls, and the leading `///` doc comment. |
+| Registry | `gen_builtins` binary (reads `src/builtins/`) | The authoritative set of builtins (incl. `internal` helpers) with exact signatures: parameter names, types, defaults, by-ref flags, variadics, and return types. |
+| Lowering | Home files `src/builtins/ /.rs` + `src/codegen/lower_inst/builtins/` | Each home's `lower` hook names the emitter it dispatches to; we resolve that emitter's file, line, `__rt_*` runtime helpers, and leading `///` doc comment. |
+| Precision | `elephc_builtins/registry.py` | Presentation refinements the registry represents coarsely as `Mixed`: `PARAM_TYPES` (param display types) and `RETURN_TYPE_OVERRIDES`. Return types are also recovered from a home's `check` hook when possible. |
+
+The registry represents non-scalar params/returns as `Mixed`; the generator
+recovers array/typed returns from the home file's `check` hook and applies
+`PARAM_TYPES` for param display types. Builtins whose emitter cannot be
+resolved are still emitted, but the internals page notes that no dedicated
+lowering was found.
-The renderer currently reads the dispatch table in `builtins.rs` (root)
-plus the `lower_*` function definitions in the submodule files. When a
-builtin has no dedicated dispatch arm (e.g. it is handled by a multi-name
-catch-all), the renderer falls back to a `lower_` heuristic on the
-root file. Builtins that cannot be mapped to a lowering are still emitted,
-but the internals page will note that no dedicated lowering was found.
+The 8 PHP language constructs that stay checker-resident
+(`isset`/`unset`/`empty`/`exit`/`die`/`buffer_len`/`buffer_free`/`buffer_new`)
+are not in the registry; they are added from a hand-curated table
+(`LANGUAGE_CONSTRUCTS`) in `extract.py`.
## Layout
@@ -57,9 +69,9 @@ scripts/docs/
├── extract_builtins.py # CLI entry point
├── builtin_registry.json # generated — do not edit by hand
└── elephc_builtins/ # Python package
- ├── extract.py # parses .rs files
+ ├── extract.py # reads gen_builtins + resolves lowering
├── render.py # emits Markdown
- └── registry.py # data model
+ └── registry.py # data model + area maps + precision tables
```
## Output tree
@@ -82,24 +94,22 @@ docs/
└── …
```
-Every builtin lives in a subfolder that matches its area. The 3 internal
+Every builtin lives in a subfolder that matches its area. The internal
`__elephc_*` helpers live under `_internal/`.
## Known limitations
-- **Signature precision depends on `src/types/signatures.rs` and several
- hand-curated tables in `registry.py`.** Parameter names, types, return
- types, by-ref flags, optional defaults, and variadic shape are refined
- through `PARAM_TYPES`, `PARAM_NAME_OVERRIDES`, `PARAM_TYPE_OVERRIDES`,
- `REF_PARAM_OVERRIDES`, `OPTIONAL_PARAM_OVERRIDES`, `RETURN_TYPE_OVERRIDES`,
- and `VARIADIC_OVERRIDES`. The generator also reads
- `first_class_callable_builtin_sig()` and `check_builtin()` arms for
- additional precision. A few builtins still differ from PHP because Elephc
- intentionally supports a smaller surface (e.g. fewer optional parameters).
-- **About 48 catalog builtins have no captured lowering.** These are
- usually handled by multi-name catch-all dispatchers (e.g. libm unary
- functions) or by special compiler paths that the heuristic does not
- yet recognize. Their internals page will show `(not lowered)`.
+- **Parameter names, defaults, by-ref flags, variadic shape, and arity are
+ exact** — they come straight from the `builtin!` registry, so they match
+ Elephc's actual supported surface (which is sometimes smaller than PHP's,
+ e.g. fewer optional parameters). **Non-scalar types are coarse**: the
+ registry declares arrays/callables/unions as `Mixed`. The generator
+ recovers array/typed *return* types from each home's `check` hook, and
+ refines *param* display types via `PARAM_TYPES` in `registry.py`; where
+ neither applies, a non-scalar shows as `mixed`.
+- **A few builtins have no captured lowering.** When a home's `lower` hook
+ cannot be resolved to an emitter definition, the internals page notes that
+ no dedicated lowering was found.
- **Areas are inferred from the dispatch module** in `builtins.rs` and the
file path of the lowering function. Hand-curated overrides live in
`elephc_builtins/registry.py` (`AREA_BY_NAME`, `AREA_BY_LOWERING_FN`,
diff --git a/scripts/docs/builtin_registry.json b/scripts/docs/builtin_registry.json
index 79015963b8..26dd3bb52c 100644
--- a/scripts/docs/builtin_registry.json
+++ b/scripts/docs/builtin_registry.json
@@ -2,13 +2,13 @@
{
"area": "Misc",
"canonical_name": "__elephc_gmmktime_raw",
- "description": "Internal helper used by the gmmktime() builtin.",
- "in_catalog": true,
+ "description": "Internal raw gmmktime alias used by the synthetic DateTime body.",
+ "in_catalog": false,
"is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_gmmktime",
"codegen_line": 151,
"notes": [
@@ -21,7 +21,7 @@
"__rt_gmmktime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/__elephc_gmmktime_raw.rs",
"sig_line": null
},
"name": "__elephc_gmmktime_raw",
@@ -79,13 +79,13 @@
{
"area": "Misc",
"canonical_name": "__elephc_mktime_raw",
- "description": "Internal helper used by the mktime() builtin.",
- "in_catalog": true,
+ "description": "Internal raw mktime alias used by the synthetic DateTime body.",
+ "in_catalog": false,
"is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_mktime",
"codegen_line": 140,
"notes": [
@@ -98,7 +98,7 @@
"__rt_mktime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/__elephc_mktime_raw.rs",
"sig_line": null
},
"name": "__elephc_mktime_raw",
@@ -154,156 +154,145 @@
"sub_area": "System"
},
{
- "area": "Misc",
- "canonical_name": "__elephc_strtotime_raw",
- "description": "Internal helper used by the strtotime() builtin.",
- "in_catalog": true,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_bzip2_archive",
+ "description": "Compresses a PHAR archive using bzip2.",
+ "in_catalog": false,
"is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_elephc_strtotime_raw",
- "codegen_line": 543,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_bzip2_archive",
+ "codegen_line": 4120,
"notes": [
- "Internal helper used by the strtotime() builtin.",
- "Provides a raw timestamp parsing path for the runtime strtotime helper."
- ],
- "runtime_helpers": [
- "__rt_strtotime"
+ "Lowers `__elephc_phar_bzip2_archive(src)` into the whole-archive bzip2 bridge,",
+ "returning the written destination path (or an empty string on failure)."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_bzip2_archive.rs",
"sig_line": null
},
- "name": "__elephc_strtotime_raw",
+ "name": "__elephc_phar_bzip2_archive",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "datetime",
+ "name": "src",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "baseTimestamp",
- "optional": true,
- "type": "int"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "__elephc_strtotime_raw",
- "sub_area": "System"
+ "slug": "__elephc_phar_bzip2_archive",
+ "sub_area": "IO"
},
{
- "area": "Math",
- "canonical_name": "abs",
- "description": "Lowers `abs()` for concrete integer-like and floating operands.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_decompress_archive",
+ "description": "Decompresses a PHAR archive to a new path.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_abs",
- "codegen_line": 43,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_decompress_archive",
+ "codegen_line": 4135,
"notes": [
- "Lowers `abs()` for concrete integer-like and floating operands."
- ],
- "runtime_helpers": [
- "__rt_abs_mixed"
+ "Lowers `__elephc_phar_decompress_archive(src)` into the whole-archive decompression",
+ "bridge, returning the written destination path (or an empty string on failure)."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_decompress_archive.rs",
"sig_line": null
},
- "name": "abs",
+ "name": "__elephc_phar_decompress_archive",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "src",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "abs",
- "sub_area": "Math"
+ "slug": "__elephc_phar_decompress_archive",
+ "sub_area": "IO"
},
{
- "area": "Math",
- "canonical_name": "acos",
- "description": "",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_get_file_metadata",
+ "description": "Reads the serialized per-file metadata blob.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_get_file_metadata",
+ "codegen_line": 4074,
+ "notes": [
+ "Lowers `__elephc_phar_get_file_metadata()` into the per-file metadata-read bridge."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/__elephc_phar_get_file_metadata.rs",
"sig_line": null
},
- "name": "acos",
+ "name": "__elephc_phar_get_file_metadata",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "url",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "acos",
- "sub_area": "Math"
+ "slug": "__elephc_phar_get_file_metadata",
+ "sub_area": "IO"
},
{
- "area": "String",
- "canonical_name": "addslashes",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_get_metadata",
+ "description": "Reads the serialized PHAR-level metadata blob.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_get_metadata",
+ "codegen_line": 3859,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "Lowers `__elephc_phar_get_metadata()` into the metadata-read bridge call."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_get_metadata.rs",
"sig_line": null
},
- "name": "addslashes",
+ "name": "__elephc_phar_get_metadata",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "filename",
"optional": false,
"type": "string"
}
@@ -311,791 +300,734 @@
"return_type": "string",
"variadic": null
},
- "slug": "addslashes",
- "sub_area": "String"
+ "slug": "__elephc_phar_get_metadata",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_chunk",
- "description": "Lowers `array_chunk()` by splitting an indexed array into nested indexed arrays.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_get_signature_hash",
+ "description": "Returns the PHAR signature hash bytes.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_chunk",
- "codegen_line": 81,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_get_signature_hash",
+ "codegen_line": 4192,
"notes": [
- "Lowers `array_chunk()` by splitting an indexed array into nested indexed arrays."
+ "Lowers `__elephc_phar_get_signature_hash(path)` into the signature-hash read bridge."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_get_signature_hash.rs",
"sig_line": null
},
- "name": "array_chunk",
+ "name": "__elephc_phar_get_signature_hash",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "preserve_keys",
+ "name": "path",
"optional": false,
- "type": "bool"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "string",
"variadic": null
},
- "slug": "array_chunk",
- "sub_area": "Array"
+ "slug": "__elephc_phar_get_signature_hash",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_column",
- "description": "Lowers `array_column()` by dispatching to the helper matching row value ownership.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_get_signature_type",
+ "description": "Returns the PHAR signature algorithm name.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/column.rs",
- "codegen_function": "lower_array_column",
- "codegen_line": 23,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_get_signature_type",
+ "codegen_line": 4206,
"notes": [
- "Lowers `array_column()` by dispatching to the helper matching row value ownership."
+ "Lowers `__elephc_phar_get_signature_type(path)` into the signature-type read bridge."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_get_signature_type.rs",
"sig_line": null
},
- "name": "array_column",
+ "name": "__elephc_phar_get_signature_type",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "column_key",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "index_key",
+ "name": "path",
"optional": false,
"type": "string"
}
],
- "return_type": "array",
+ "return_type": "string",
"variadic": null
},
- "slug": "array_column",
- "sub_area": "Array"
+ "slug": "__elephc_phar_get_signature_type",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_combine",
- "description": "Lowers `array_combine()` through the legacy hash-building runtime helpers.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_get_stub",
+ "description": "Reads the PHAR stub script.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_combine",
- "codegen_line": 152,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_get_stub",
+ "codegen_line": 3873,
"notes": [
- "Lowers `array_combine()` through the legacy hash-building runtime helpers."
+ "Lowers `__elephc_phar_get_stub()` into the stub-read bridge call."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_get_stub.rs",
"sig_line": null
},
- "name": "array_combine",
+ "name": "__elephc_phar_get_stub",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "keys",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "values",
+ "name": "filename",
"optional": false,
- "type": "array"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "string",
"variadic": null
},
- "slug": "array_combine",
- "sub_area": "Array"
+ "slug": "__elephc_phar_get_stub",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_diff",
- "description": "Lowers `array_diff()` for two compatible indexed arrays with pointer-sized payload slots.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_gzip_archive",
+ "description": "Compresses a PHAR archive using gzip.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_diff",
- "codegen_line": 874,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_gzip_archive",
+ "codegen_line": 4105,
"notes": [
- "Lowers `array_diff()` for two compatible indexed arrays with pointer-sized payload slots."
- ],
- "runtime_helpers": [
- "__rt_array_diff",
- "__rt_array_diff_key",
- "__rt_array_diff_refcounted",
- "__rt_array_intersect",
- "__rt_array_intersect_key",
- "__rt_array_intersect_refcounted"
+ "Lowers `__elephc_phar_gzip_archive(src)` into the whole-archive gzip bridge,",
+ "returning the written destination path (or an empty string on failure)."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_gzip_archive.rs",
"sig_line": null
},
- "name": "array_diff",
+ "name": "__elephc_phar_gzip_archive",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "src",
"optional": false,
- "type": "array"
+ "type": "string"
}
],
- "return_type": "array",
- "variadic": "arrays"
+ "return_type": "string",
+ "variadic": null
},
- "slug": "array_diff",
- "sub_area": "Array"
+ "slug": "__elephc_phar_gzip_archive",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_diff_key",
- "description": "Lowers `array_diff_key()` for two associative arrays by filtering first-operand keys.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_list_entries",
+ "description": "Lists the file paths within a PHAR archive.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_diff_key",
- "codegen_line": 896,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_list_entries",
+ "codegen_line": 4277,
"notes": [
- "Lowers `array_diff_key()` for two associative arrays by filtering first-operand keys."
- ],
- "runtime_helpers": [
- "__rt_array_diff_key",
- "__rt_array_intersect_key"
+ "Internal helper used by the built-in Phar / PharData support to enumerate archive entries.",
+ "Calls the native PHAR listing bridge and returns the entries as an array."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_list_entries.rs",
"sig_line": null
},
- "name": "array_diff_key",
+ "name": "__elephc_phar_list_entries",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "filename",
"optional": false,
- "type": "array"
+ "type": "string"
}
],
"return_type": "array",
- "variadic": "arrays"
+ "variadic": null
},
- "slug": "array_diff_key",
- "sub_area": "Array"
+ "slug": "__elephc_phar_list_entries",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_fill",
- "description": "Lowers `array_fill()` for pointer-sized scalar and refcounted payloads.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_set_compression",
+ "description": "Sets the compression algorithm for a PHAR archive.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_fill",
- "codegen_line": 115,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_set_compression",
+ "codegen_line": 3801,
"notes": [
- "Lowers `array_fill()` for pointer-sized scalar and refcounted payloads."
+ "Internal helper used by the built-in Phar / PharData support to change archive compression.",
+ "Calls the native PHAR compression-control bridge and returns whether the update succeeded."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_set_compression.rs",
"sig_line": null
},
- "name": "array_fill",
+ "name": "__elephc_phar_set_compression",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "start_index",
+ "name": "filename",
"optional": false,
- "type": "int"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "count",
+ "name": "compression",
"optional": false,
"type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "value",
- "optional": false,
- "type": "mixed"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_fill",
- "sub_area": "Array"
+ "slug": "__elephc_phar_set_compression",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_fill_keys",
- "description": "Lowers `array_fill_keys()` through the legacy hash-building runtime helpers.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_set_file_metadata",
+ "description": "Writes the serialized per-file metadata blob.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_fill_keys",
- "codegen_line": 138,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_set_file_metadata",
+ "codegen_line": 4090,
"notes": [
- "Lowers `array_fill_keys()` through the legacy hash-building runtime helpers."
+ "Lowers `__elephc_phar_set_file_metadata()` into the per-file metadata-write bridge.",
+ "The single `phar://archive/entry` URL argument is split by the bridge, so this",
+ "reuses the same `(url, data) -> bool` shape as the archive-level metadata writer."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_set_file_metadata.rs",
"sig_line": null
},
- "name": "array_fill_keys",
+ "name": "__elephc_phar_set_file_metadata",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "keys",
+ "name": "url",
"optional": false,
- "type": "array"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "metadata",
"optional": false,
- "type": "mixed"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_fill_keys",
- "sub_area": "Array"
+ "slug": "__elephc_phar_set_file_metadata",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_filter",
- "description": "Lowers `array_filter()` for static and first-class callbacks through the runtime helper.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_set_metadata",
+ "description": "Writes the serialized PHAR-level metadata blob.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_filter",
- "codegen_line": 211,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_set_metadata",
+ "codegen_line": 3882,
"notes": [
- "Lowers `array_filter()` for static and first-class callbacks through the runtime helper."
- ],
- "runtime_helpers": [
- "__rt_array_filter",
- "__rt_array_filter_refcounted"
+ "Lowers `__elephc_phar_set_metadata()` into the metadata-write bridge call."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_set_metadata.rs",
"sig_line": null
},
- "name": "array_filter",
+ "name": "__elephc_phar_set_metadata",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "filename",
"optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "callback",
- "optional": true,
- "type": "callable"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "mode",
- "optional": true,
- "type": "int"
+ "name": "metadata",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_filter",
- "sub_area": "Array"
+ "slug": "__elephc_phar_set_metadata",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_flip",
- "description": "Lowers `array_flip()` through the legacy hash-building runtime helpers.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_set_stub",
+ "description": "Writes the PHAR stub script.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_flip",
- "codegen_line": 171,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_set_stub",
+ "codegen_line": 3896,
"notes": [
- "Lowers `array_flip()` through the legacy hash-building runtime helpers."
+ "Lowers `__elephc_phar_set_stub()` into the stub-write bridge call."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_set_stub.rs",
"sig_line": null
},
- "name": "array_flip",
+ "name": "__elephc_phar_set_stub",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "filename",
"optional": false,
- "type": "array"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "stub",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_flip",
- "sub_area": "Array"
+ "slug": "__elephc_phar_set_stub",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_intersect",
- "description": "Lowers `array_intersect()` for two compatible indexed arrays with pointer-sized payload slots.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_set_zip_password",
+ "description": "Sets the encryption password for a PHAR ZIP archive.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_intersect",
- "codegen_line": 885,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_set_zip_password",
+ "codegen_line": 4178,
"notes": [
- "Lowers `array_intersect()` for two compatible indexed arrays with pointer-sized payload slots."
- ],
- "runtime_helpers": [
- "__rt_array_diff_key",
- "__rt_array_intersect",
- "__rt_array_intersect_key",
- "__rt_array_intersect_refcounted"
+ "Lowers `__elephc_phar_set_zip_password(password)` into the ZipCrypto password",
+ "bridge that lets later reads decrypt encrypted ZIP entries."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_set_zip_password.rs",
"sig_line": null
},
- "name": "array_intersect",
+ "name": "__elephc_phar_set_zip_password",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "password",
"optional": false,
- "type": "array"
+ "type": "string"
}
],
- "return_type": "array",
- "variadic": "arrays"
+ "return_type": "bool",
+ "variadic": null
},
- "slug": "array_intersect",
- "sub_area": "Array"
+ "slug": "__elephc_phar_set_zip_password",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_intersect_key",
- "description": "Lowers `array_intersect_key()` for two associative arrays by keeping shared first-operand keys.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_sign_hash",
+ "description": "Signs a PHAR archive with the given hash algorithm.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_intersect_key",
- "codegen_line": 901,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_sign_hash",
+ "codegen_line": 4163,
"notes": [
- "Lowers `array_intersect_key()` for two associative arrays by keeping shared first-operand keys."
- ],
- "runtime_helpers": [
- "__rt_array_intersect_key"
+ "Lowers `__elephc_phar_sign_hash(path, algo)` into the hash-based signing bridge."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_sign_hash.rs",
"sig_line": null
},
- "name": "array_intersect_key",
+ "name": "__elephc_phar_sign_hash",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "path",
"optional": false,
- "type": "array"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "algo",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "array",
- "variadic": "arrays"
+ "return_type": "bool",
+ "variadic": null
},
- "slug": "array_intersect_key",
- "sub_area": "Array"
+ "slug": "__elephc_phar_sign_hash",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_key_exists",
- "description": "Lowers `array_key_exists()` for indexed arrays and associative arrays.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "IO",
+ "canonical_name": "__elephc_phar_sign_openssl",
+ "description": "Signs a PHAR archive using an OpenSSL private key.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/key_exists.rs",
- "codegen_function": "lower_array_key_exists",
- "codegen_line": 22,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_elephc_phar_sign_openssl",
+ "codegen_line": 4149,
"notes": [
- "Lowers `array_key_exists()` for indexed arrays and associative arrays."
+ "Lowers `__elephc_phar_sign_openssl(path, keyPem)` into the RSA-SHA1 signing bridge."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/__elephc_phar_sign_openssl.rs",
"sig_line": null
},
- "name": "array_key_exists",
+ "name": "__elephc_phar_sign_openssl",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "key",
+ "name": "path",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "key",
"optional": false,
- "type": "array"
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "array_key_exists",
- "sub_area": "Array"
+ "slug": "__elephc_phar_sign_openssl",
+ "sub_area": "IO"
},
{
- "area": "Array",
- "canonical_name": "array_keys",
- "description": "Lowers `array_keys()` for indexed arrays and associative arrays.",
- "in_catalog": true,
- "is_internal": false,
+ "area": "Misc",
+ "canonical_name": "__elephc_strtotime_raw",
+ "description": "Internal raw strtotime alias returning a plain integer.",
+ "in_catalog": false,
+ "is_internal": true,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/keys.rs",
- "codegen_function": "lower_array_keys",
- "codegen_line": 23,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_elephc_strtotime_raw",
+ "codegen_line": 543,
"notes": [
- "Lowers `array_keys()` for indexed arrays and associative arrays."
+ "Internal helper used by the strtotime() builtin.",
+ "Provides a raw timestamp parsing path for the runtime strtotime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_strtotime"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/__elephc_strtotime_raw.rs",
"sig_line": null
},
- "name": "array_keys",
+ "name": "__elephc_strtotime_raw",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "filter_value",
+ "name": "datetime",
"optional": false,
"type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "strict",
- "optional": false,
- "type": "bool"
+ "default": "null",
+ "name": "baseTimestamp",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "array",
+ "return_type": "int",
"variadic": null
},
- "slug": "array_keys",
- "sub_area": "Array"
+ "slug": "__elephc_strtotime_raw",
+ "sub_area": "System"
},
{
- "area": "Array",
- "canonical_name": "array_map",
- "description": "Lowers `array_map()` through the callback runtime helper matching the callback result type.",
+ "area": "Math",
+ "canonical_name": "abs",
+ "description": "Absolute value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_map",
- "codegen_line": 312,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_abs",
+ "codegen_line": 43,
"notes": [
- "Lowers `array_map()` through the callback runtime helper matching the callback result type."
+ "Lowers `abs()` for concrete integer-like and floating operands."
],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": null,
+ "runtime_helpers": [
+ "__rt_abs_mixed"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/math/abs.rs",
"sig_line": null
},
- "name": "array_map",
+ "name": "abs",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "callback",
- "optional": false,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "array",
+ "name": "num",
"optional": false,
- "type": "array"
+ "type": "int"
}
],
- "return_type": "array",
- "variadic": "arrays"
+ "return_type": "mixed",
+ "variadic": null
},
- "slug": "array_map",
- "sub_area": "Array"
+ "slug": "abs",
+ "sub_area": "Math"
},
{
- "area": "Array",
- "canonical_name": "array_merge",
- "description": "Lowers `array_merge()` for two compatible indexed arrays with 8-byte payload slots.",
+ "area": "Math",
+ "canonical_name": "acos",
+ "description": "Returns the arccosine of a number in radians.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_merge",
- "codegen_line": 850,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `array_merge()` for two compatible indexed arrays with 8-byte payload slots."
- ],
- "runtime_helpers": [
- "__rt_array_diff",
- "__rt_array_diff_refcounted"
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/acos.rs",
"sig_line": null
},
- "name": "array_merge",
+ "name": "acos",
"sig": {
- "params": [],
- "return_type": "array",
- "variadic": "arrays"
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "float",
+ "variadic": null
},
- "slug": "array_merge",
- "sub_area": "Array"
+ "slug": "acos",
+ "sub_area": "Math"
},
{
- "area": "Array",
- "canonical_name": "array_pad",
- "description": "Lowers `array_pad()` by copying an indexed array and filling missing slots.",
+ "area": "String",
+ "canonical_name": "addslashes",
+ "description": "Adds backslashes before characters that need to be escaped.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_pad",
- "codegen_line": 99,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `array_pad()` by copying an indexed array and filling missing slots."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/addslashes.rs",
"sig_line": null
},
- "name": "array_pad",
+ "name": "addslashes",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "value",
+ "name": "string",
"optional": false,
- "type": "mixed"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "string",
"variadic": null
},
- "slug": "array_pad",
- "sub_area": "Array"
+ "slug": "addslashes",
+ "sub_area": "String"
},
{
"area": "Array",
- "canonical_name": "array_pop",
- "description": "Lowers `array_pop()` for indexed arrays by mutating length and boxing `T|null` as Mixed.",
+ "canonical_name": "array_all",
+ "description": "Returns true when every array element satisfies the predicate callback.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_pop",
- "codegen_line": 1048,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_all",
+ "codegen_line": 1578,
"notes": [
- "Lowers `array_pop()` for indexed arrays by mutating length and boxing `T|null` as Mixed."
+ "Lowers `array_all()`: returns true when every element satisfies the predicate."
],
"runtime_helpers": [
- "__rt_sort_int",
- "__rt_sort_str"
+ "__rt_array_udiff_uintersect",
+ "__rt_array_walk_recursive"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_all.rs",
"sig_line": null
},
- "name": "array_pop",
+ "name": "array_all",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
- "type": "array"
+ "type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "mixed"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_pop",
+ "slug": "array_all",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_product",
- "description": "Lowers `array_product()` over supported indexed-array payloads.",
+ "canonical_name": "array_any",
+ "description": "Returns true when at least one array element satisfies the predicate callback.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_product",
- "codegen_line": 56,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_any",
+ "codegen_line": 1573,
"notes": [
- "Lowers `array_product()` over supported indexed-array payloads."
+ "Lowers `array_any()`: returns true when some element satisfies the predicate."
],
"runtime_helpers": [
- "__rt_array_product"
+ "__rt_array_walk_recursive"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_any.rs",
"sig_line": null
},
- "name": "array_product",
+ "name": "array_any",
"sig": {
"params": [
{
@@ -1103,76 +1035,87 @@
"default": null,
"name": "array",
"optional": false,
- "type": "array"
+ "type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "array_product",
+ "slug": "array_any",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_push",
- "description": "Lowers `array_push()` by appending one value and publishing the mutated array.",
+ "canonical_name": "array_chunk",
+ "description": "Splits an array into chunks of the given size.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_push",
- "codegen_line": 61,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_chunk",
+ "codegen_line": 80,
"notes": [
- "Lowers `array_push()` by appending one value and publishing the mutated array."
+ "Lowers `array_chunk()` by splitting an indexed array into nested indexed arrays."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_chunk.rs",
"sig_line": null
},
- "name": "array_push",
+ "name": "array_chunk",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
"type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "length",
+ "optional": false,
+ "type": "int"
}
],
- "return_type": "void",
- "variadic": "values"
+ "return_type": "array",
+ "variadic": null
},
- "slug": "array_push",
+ "slug": "array_chunk",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_rand",
- "description": "Lowers `array_rand()` for indexed arrays.",
+ "canonical_name": "array_column",
+ "description": "Returns the values from a single column of an array of arrays.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_rand",
- "codegen_line": 1007,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/column.rs",
+ "codegen_function": "lower_array_column",
+ "codegen_line": 23,
"notes": [
- "Lowers `array_rand()` for indexed arrays."
- ],
- "runtime_helpers": [
- "__rt_array_rand",
- "__rt_mixed_cast_int"
+ "Lowers `array_column()` by dispatching to the helper matching row value ownership."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_column.rs",
"sig_line": null
},
- "name": "array_rand",
+ "name": "array_column",
"sig": {
"params": [
{
@@ -1185,91 +1128,88 @@
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "column_key",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "array_rand",
+ "slug": "array_column",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_reduce",
- "description": "Lowers `array_reduce()` through the callback-driven runtime helper.",
+ "canonical_name": "array_combine",
+ "description": "Creates an array by using one array for keys and another for values.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_reduce",
- "codegen_line": 701,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_combine",
+ "codegen_line": 160,
"notes": [
- "Lowers `array_reduce()` through the callback-driven runtime helper."
- ],
- "runtime_helpers": [
- "__rt_array_reduce"
+ "Lowers `array_combine()` through the hash-building runtime helpers."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_combine.rs",
"sig_line": null
},
- "name": "array_reduce",
+ "name": "array_combine",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
+ "name": "keys",
"optional": false,
"type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "callback",
+ "name": "values",
"optional": false,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "initial",
- "optional": true,
- "type": "mixed"
+ "type": "array"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "array_reduce",
+ "slug": "array_combine",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_reverse",
- "description": "Lowers `array_reverse()` for indexed arrays with 8-byte payload slots.",
+ "canonical_name": "array_diff",
+ "description": "Computes the difference of arrays.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_reverse",
- "codegen_line": 185,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_diff",
+ "codegen_line": 870,
"notes": [
- "Lowers `array_reverse()` for indexed arrays with 8-byte payload slots."
+ "Lowers `array_diff()` for two compatible indexed arrays with pointer-sized payload slots."
+ ],
+ "runtime_helpers": [
+ "__rt_array_diff",
+ "__rt_array_diff_key",
+ "__rt_array_diff_refcounted",
+ "__rt_array_intersect",
+ "__rt_array_intersect_refcounted"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_diff.rs",
"sig_line": null
},
- "name": "array_reverse",
+ "name": "array_diff",
"sig": {
"params": [
{
@@ -1278,249 +1218,212 @@
"name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "preserve_keys",
- "optional": false,
- "type": "bool"
}
],
"return_type": "array",
- "variadic": null
+ "variadic": "arrays"
},
- "slug": "array_reverse",
+ "slug": "array_diff",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_search",
- "description": "Lowers `array_search()` for indexed arrays with integer-like payloads.",
+ "canonical_name": "array_diff_assoc",
+ "description": "Computes the difference of arrays with additional index check.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_search",
- "codegen_line": 1141,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_diff_assoc",
+ "codegen_line": 1359,
"notes": [
- "Lowers `array_search()` for indexed arrays with integer-like payloads."
+ "Lowers `array_diff_assoc()` via the shared associative diff/intersect helper (mode 0 = diff)."
+ ],
+ "runtime_helpers": [
+ "__rt_assoc_diff_intersect"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_diff_assoc.rs",
"sig_line": null
},
- "name": "array_search",
+ "name": "array_diff_assoc",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "needle",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "haystack",
+ "name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "strict",
- "optional": true,
- "type": "bool"
}
],
"return_type": "mixed",
- "variadic": null
+ "variadic": "arrays"
},
- "slug": "array_search",
+ "slug": "array_diff_assoc",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_shift",
- "description": "Lowers `array_shift()` for indexed arrays by compacting slots and boxing `T|null` as Mixed.",
+ "canonical_name": "array_diff_key",
+ "description": "Computes the difference of arrays using keys for comparison.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/shift.rs",
- "codegen_function": "lower_array_shift",
- "codegen_line": 23,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_diff_key",
+ "codegen_line": 895,
"notes": [
- "Lowers `array_shift()` for indexed arrays by compacting slots and boxing `T|null` as Mixed."
+ "Lowers `array_diff_key()` for two associative arrays by filtering first-operand keys."
+ ],
+ "runtime_helpers": [
+ "__rt_array_diff_key",
+ "__rt_array_intersect_key"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_diff_key.rs",
"sig_line": null
},
- "name": "array_shift",
+ "name": "array_diff_key",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
"type": "array"
}
],
- "return_type": "mixed",
- "variadic": null
+ "return_type": "array",
+ "variadic": "arrays"
},
- "slug": "array_shift",
+ "slug": "array_diff_key",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_slice",
- "description": "Lowers `array_slice()` for indexed arrays with pointer-sized payload slots.",
+ "canonical_name": "array_fill",
+ "description": "Fill an array with values.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_slice",
- "codegen_line": 906,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_fill",
+ "codegen_line": 116,
"notes": [
- "Lowers `array_slice()` for indexed arrays with pointer-sized payload slots."
+ "Lowers `array_fill()` for pointer-sized scalar and refcounted payloads."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_fill.rs",
"sig_line": null
},
- "name": "array_slice",
+ "name": "array_fill",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "offset",
+ "name": "start_index",
"optional": false,
"type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "length",
- "optional": true,
+ "name": "count",
+ "optional": false,
"type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "preserve_keys",
+ "name": "value",
"optional": false,
- "type": "bool"
+ "type": "mixed"
}
],
"return_type": "array",
"variadic": null
},
- "slug": "array_slice",
+ "slug": "array_fill",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_splice",
- "description": "Lowers `array_splice()` by mutating an indexed source array and returning removed elements.",
+ "canonical_name": "array_fill_keys",
+ "description": "Fill an array with values, specifying keys.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_splice",
- "codegen_line": 949,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_fill_keys",
+ "codegen_line": 139,
"notes": [
- "Lowers `array_splice()` by mutating an indexed source array and returning removed elements."
+ "Lowers `array_fill_keys()` through the hash-building runtime helpers."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_fill_keys.rs",
"sig_line": null
},
- "name": "array_splice",
+ "name": "array_fill_keys",
"sig": {
"params": [
- {
- "by_ref": true,
- "default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
{
"by_ref": false,
"default": null,
- "name": "offset",
+ "name": "keys",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": true,
- "type": "int"
+ "type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "replacement",
+ "name": "value",
"optional": false,
- "type": "array"
+ "type": "mixed"
}
],
"return_type": "array",
"variadic": null
},
- "slug": "array_splice",
+ "slug": "array_fill_keys",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_sum",
- "description": "Lowers `array_sum()` over supported indexed-array payloads.",
+ "canonical_name": "array_filter",
+ "description": "Filters elements of an array using a callback function.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_sum",
- "codegen_line": 51,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_filter",
+ "codegen_line": 221,
"notes": [
- "Lowers `array_sum()` over supported indexed-array payloads."
+ "Lowers `array_filter()` for static and first-class callbacks through the runtime helper."
],
"runtime_helpers": [
- "__rt_array_product",
- "__rt_array_sum"
+ "__rt_array_filter",
+ "__rt_array_filter_refcounted"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_filter.rs",
"sig_line": null
},
- "name": "array_sum",
+ "name": "array_filter",
"sig": {
"params": [
{
@@ -1529,38 +1432,51 @@
"name": "array",
"optional": false,
"type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "callback",
+ "optional": true,
+ "type": "callable"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "mode",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "float",
+ "return_type": "array",
"variadic": null
},
- "slug": "array_sum",
+ "slug": "array_filter",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_unique",
- "description": "Lowers `array_unique()` for indexed arrays with 8-byte payload slots.",
+ "canonical_name": "array_find",
+ "description": "Returns the first element satisfying a predicate callback, or null.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_unique",
- "codegen_line": 198,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_find",
+ "codegen_line": 1568,
"notes": [
- "Lowers `array_unique()` for indexed arrays with 8-byte payload slots."
+ "Lowers `array_find()`: returns the first element satisfying the predicate, boxed as Mixed (or null)."
],
"runtime_helpers": [
- "__rt_array_filter",
- "__rt_array_filter_refcounted"
+ "__rt_array_walk_recursive"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_find.rs",
"sig_line": null
},
- "name": "array_unique",
+ "name": "array_find",
"sig": {
"params": [
{
@@ -1568,80 +1484,85 @@
"default": null,
"name": "array",
"optional": false,
- "type": "array"
+ "type": "mixed"
},
{
"by_ref": false,
"default": null,
- "name": "flags",
+ "name": "callback",
"optional": false,
- "type": "int"
+ "type": "mixed"
}
],
- "return_type": "array",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "array_unique",
+ "slug": "array_find",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_unshift",
- "description": "Lowers `array_unshift()` by ensuring uniqueness, prepending one scalar value, and returning count.",
+ "canonical_name": "array_flip",
+ "description": "Exchanges all keys with their associated values in an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/unshift.rs",
- "codegen_function": "lower_array_unshift",
- "codegen_line": 23,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_flip",
+ "codegen_line": 179,
"notes": [
- "Lowers `array_unshift()` by ensuring uniqueness, prepending one scalar value, and returning count."
+ "Lowers `array_flip()` through the hash-building runtime helpers."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_flip.rs",
"sig_line": null
},
- "name": "array_unshift",
+ "name": "array_flip",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
"type": "array"
}
],
- "return_type": "int",
- "variadic": "values"
+ "return_type": "array",
+ "variadic": null
},
- "slug": "array_unshift",
+ "slug": "array_flip",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_values",
- "description": "Lowers `array_values()` for indexed arrays as an alias or associative arrays as a new values array.",
+ "canonical_name": "array_intersect",
+ "description": "Computes the intersection of arrays.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays/values.rs",
- "codegen_function": "lower_array_values",
- "codegen_line": 22,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_intersect",
+ "codegen_line": 881,
"notes": [
- "Lowers `array_values()` for indexed arrays as an alias or associative arrays as a new values array."
+ "Lowers `array_intersect()` for two compatible indexed arrays with pointer-sized payload slots."
+ ],
+ "runtime_helpers": [
+ "__rt_array_diff_key",
+ "__rt_array_intersect",
+ "__rt_array_intersect_key",
+ "__rt_array_intersect_refcounted"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_intersect.rs",
"sig_line": null
},
- "name": "array_values",
+ "name": "array_intersect",
"sig": {
"params": [
{
@@ -1653,1750 +1574,1737 @@
}
],
"return_type": "array",
- "variadic": null
+ "variadic": "arrays"
},
- "slug": "array_values",
+ "slug": "array_intersect",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "array_walk",
- "description": "Lowers `array_walk()` through the callback-driven runtime helper.",
+ "canonical_name": "array_intersect_assoc",
+ "description": "Computes the intersection of arrays with additional index check.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_array_walk",
- "codegen_line": 783,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_intersect_assoc",
+ "codegen_line": 1373,
"notes": [
- "Lowers `array_walk()` through the callback-driven runtime helper."
+ "Lowers `array_intersect_assoc()` via the shared associative diff/intersect helper (mode 1 = intersect)."
],
"runtime_helpers": [
- "__rt_array_walk"
+ "__rt_array_find_any_all",
+ "__rt_array_merge_recursive",
+ "__rt_array_udiff_uintersect",
+ "__rt_assoc_diff_intersect"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_intersect_assoc.rs",
"sig_line": null
},
- "name": "array_walk",
+ "name": "array_intersect_assoc",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "callback",
- "optional": false,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "arg",
- "optional": false,
- "type": "mixed"
}
],
- "return_type": "void",
- "variadic": null
+ "return_type": "mixed",
+ "variadic": "arrays"
},
- "slug": "array_walk",
+ "slug": "array_intersect_assoc",
"sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "arsort",
- "description": "Lowers `arsort()` for indexed integer arrays through the descending value-sort wrapper.",
+ "canonical_name": "array_intersect_key",
+ "description": "Computes the intersection of arrays using keys for comparison.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_arsort",
- "codegen_line": 1091,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_intersect_key",
+ "codegen_line": 903,
"notes": [
- "Lowers `arsort()` for indexed integer arrays through the descending value-sort wrapper."
+ "Lowers `array_intersect_key()` for two associative arrays by keeping shared first-operand keys."
],
"runtime_helpers": [
- "__rt_arsort",
- "__rt_krsort",
- "__rt_ksort",
- "__rt_natcasesort",
- "__rt_natsort"
+ "__rt_array_intersect_key"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_intersect_key.rs",
"sig_line": null
},
- "name": "arsort",
+ "name": "array_intersect_key",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
"name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
}
],
- "return_type": "bool",
- "variadic": null
+ "return_type": "array",
+ "variadic": "arrays"
},
- "slug": "arsort",
+ "slug": "array_intersect_key",
"sub_area": "Array"
},
{
- "area": "Math",
- "canonical_name": "asin",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_is_list",
+ "description": "Checks whether an array is a list (sequential 0-based integer keys).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": "src/types/signatures.rs",
- "sig_line": null
- },
- "name": "asin",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_is_list",
+ "codegen_line": 1150,
+ "notes": [
+ "Lowers `array_is_list()` to the `__rt_array_is_list` runtime predicate, returning a bool.",
+ "The runtime helper accepts any array kind (indexed, associative hash, or boxed mixed cell) and",
+ "reports `1` when the keys are the sequential integers `0..n-1` in insertion order, `0` otherwise."
+ ],
+ "runtime_helpers": [
+ "__rt_array_edge_key",
+ "__rt_array_is_list",
+ "__rt_mixed_from_value"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/array/array_is_list.rs",
+ "sig_line": null
+ },
+ "name": "array_is_list",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "array",
"optional": false,
- "type": "float"
+ "type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "asin",
- "sub_area": "Math"
+ "slug": "array_is_list",
+ "sub_area": "Array"
},
{
"area": "Array",
- "canonical_name": "asort",
- "description": "Lowers `asort()` for indexed integer arrays through the value-sort runtime wrapper.",
+ "canonical_name": "array_key_exists",
+ "description": "Checks if the given key or index exists in the array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_asort",
- "codegen_line": 1086,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/key_exists.rs",
+ "codegen_function": "lower_array_key_exists",
+ "codegen_line": 22,
"notes": [
- "Lowers `asort()` for indexed integer arrays through the value-sort runtime wrapper."
- ],
- "runtime_helpers": [
- "__rt_arsort",
- "__rt_asort",
- "__rt_krsort",
- "__rt_ksort",
- "__rt_natcasesort",
- "__rt_natsort"
+ "Lowers `array_key_exists()` for indexed arrays and associative arrays."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_key_exists.rs",
"sig_line": null
},
- "name": "asort",
+ "name": "array_key_exists",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "array",
+ "name": "key",
"optional": false,
- "type": "array"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "flags",
+ "name": "array",
"optional": false,
- "type": "int"
+ "type": "array"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "asort",
+ "slug": "array_key_exists",
"sub_area": "Array"
},
{
- "area": "Math",
- "canonical_name": "atan",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_key_first",
+ "description": "Gets the first key of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_key_first",
+ "codegen_line": 1161,
+ "notes": [
+ "Lowers `array_key_first()` through the shared edge-key helper with selector `0`."
+ ],
+ "runtime_helpers": [
+ "__rt_array_edge_key",
+ "__rt_mixed_from_value"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/array/array_key_first.rs",
"sig_line": null
},
- "name": "atan",
+ "name": "array_key_first",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "array",
"optional": false,
- "type": "float"
+ "type": "array"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "atan",
- "sub_area": "Math"
+ "slug": "array_key_first",
+ "sub_area": "Array"
},
{
- "area": "Math",
- "canonical_name": "atan2",
- "description": "Lowers `atan2()` using the C ABI argument order `y, x`.",
+ "area": "Array",
+ "canonical_name": "array_key_last",
+ "description": "Gets the last key of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/libm.rs",
- "codegen_function": "lower_atan2",
- "codegen_line": 35,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_key_last",
+ "codegen_line": 1169,
"notes": [
- "Lowers `atan2()` using the C ABI argument order `y, x`."
+ "Lowers `array_key_last()` through the shared edge-key helper with selector `1`."
+ ],
+ "runtime_helpers": [
+ "__rt_array_edge_key",
+ "__rt_mixed_from_value"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_key_last.rs",
"sig_line": null
},
- "name": "atan2",
+ "name": "array_key_last",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "y",
- "optional": false,
- "type": "float"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "x",
+ "name": "array",
"optional": false,
- "type": "float"
+ "type": "array"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "atan2",
- "sub_area": "Math"
+ "slug": "array_key_last",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "base64_decode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_keys",
+ "description": "Returns all the keys of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/keys.rs",
+ "codegen_function": "lower_array_keys",
+ "codegen_line": 23,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "Lowers `array_keys()` for indexed arrays and associative arrays."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_keys.rs",
"sig_line": null
},
- "name": "base64_decode",
+ "name": "array_keys",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "strict",
+ "name": "array",
"optional": false,
- "type": "bool"
+ "type": "array"
}
],
- "return_type": "string",
+ "return_type": "array",
"variadic": null
},
- "slug": "base64_decode",
- "sub_area": "String"
+ "slug": "array_keys",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "base64_encode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_map",
+ "description": "Applies a callback to the elements of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_map",
+ "codegen_line": 326,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "Lowers `array_map()` through the callback runtime helper matching the callback result type."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_map.rs",
"sig_line": null
},
- "name": "base64_encode",
+ "name": "array_map",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "callback",
"optional": false,
- "type": "string"
+ "type": "callable"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "array",
+ "optional": false,
+ "type": "array"
}
],
- "return_type": "string",
- "variadic": null
+ "return_type": "array",
+ "variadic": "arrays"
},
- "slug": "base64_encode",
- "sub_area": "String"
+ "slug": "array_map",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "basename",
- "description": "Lowers `basename(path, suffix?)` through the target-aware runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_merge",
+ "description": "Merges the elements of two arrays.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_basename",
- "codegen_line": 3893,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_merge",
+ "codegen_line": 846,
"notes": [
- "Lowers `basename(path, suffix?)` through the target-aware runtime helper."
+ "Lowers `array_merge()` for two compatible indexed arrays with 8-byte payload slots."
+ ],
+ "runtime_helpers": [
+ "__rt_array_diff",
+ "__rt_array_diff_refcounted"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_merge.rs",
"sig_line": null
},
- "name": "basename",
+ "name": "array_merge",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "path",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "suffix",
- "optional": true,
- "type": "string"
- }
- ],
- "return_type": "string",
- "variadic": null
+ "params": [],
+ "return_type": "array",
+ "variadic": "arrays"
},
- "slug": "basename",
- "sub_area": "Filesystem"
+ "slug": "array_merge",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "bin2hex",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_merge_recursive",
+ "description": "Recursively merges two arrays, combining scalar collisions into lists.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_merge_recursive",
+ "codegen_line": 1387,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `array_merge_recursive()` (recursive merge with scalar collisions combined into lists)."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_array_find_any_all",
+ "__rt_array_merge_recursive",
+ "__rt_array_udiff_uintersect"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_merge_recursive.rs",
"sig_line": null
},
- "name": "bin2hex",
+ "name": "array_merge_recursive",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "string",
- "optional": false,
- "type": "string"
- }
- ],
- "return_type": "string",
- "variadic": null
+ "params": [],
+ "return_type": "array",
+ "variadic": "arrays"
},
- "slug": "bin2hex",
- "sub_area": "String"
+ "slug": "array_merge_recursive",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "gzcompress",
- "description": "Lowers `gzcompress(data, level?)` through inline zlib `compress2` calls.",
+ "area": "Array",
+ "canonical_name": "array_multisort",
+ "description": "Sorts multiple arrays or multi-dimensional arrays.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_gzcompress",
- "codegen_line": 402,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_multisort",
+ "codegen_line": 1719,
"notes": [
- "Lowers `gzcompress(data, level?)` through inline zlib `compress2` calls."
+ "Lowers `array_multisort()`: stable-sorts the first indexed array ascending and reorders the second",
+ "in tandem, both in place. Both arguments are by-reference, so each is copy-on-write split with",
+ "`ensure_unique_sort_source` and the (possibly relocated) pointer is written back to its local",
+ "before the runtime mutates the storage. Returns `true`. Supports 8-byte scalar indexed arrays."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_multisort.rs",
"sig_line": null
},
- "name": "gzcompress",
+ "name": "array_multisort",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "data",
+ "name": "array1",
"optional": false,
- "type": "string"
+ "type": "array"
},
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "level",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "encoding",
+ "name": "array2",
"optional": false,
"type": "int"
}
],
- "return_type": "string",
+ "return_type": "bool",
"variadic": null
},
- "slug": "gzcompress",
- "sub_area": "String"
+ "slug": "array_multisort",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "gzdeflate",
- "description": "Lowers `gzdeflate(data, level?)` through inline raw-DEFLATE zlib calls.",
+ "area": "Array",
+ "canonical_name": "array_pad",
+ "description": "Pads an array to the specified length with a value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_gzdeflate",
- "codegen_line": 418,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_pad",
+ "codegen_line": 99,
"notes": [
- "Lowers `gzdeflate(data, level?)` through inline raw-DEFLATE zlib calls."
+ "Lowers `array_pad()` by copying an indexed array and filling missing slots."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_pad.rs",
"sig_line": null
},
- "name": "gzdeflate",
+ "name": "array_pad",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "data",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "level",
- "optional": true,
+ "name": "length",
+ "optional": false,
"type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "encoding",
+ "name": "value",
"optional": false,
- "type": "int"
+ "type": "mixed"
}
],
- "return_type": "string",
+ "return_type": "array",
"variadic": null
},
- "slug": "gzdeflate",
- "sub_area": "String"
+ "slug": "array_pad",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "gzinflate",
- "description": "Lowers `gzinflate(data, max_length?)` and boxes zlib failures as PHP false.",
+ "area": "Array",
+ "canonical_name": "array_pop",
+ "description": "Pops the element off the end of array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_gzinflate",
- "codegen_line": 436,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_pop",
+ "codegen_line": 1051,
"notes": [
- "Lowers `gzinflate(data, max_length?)` and boxes zlib failures as PHP false."
+ "Lowers `array_pop()` for indexed arrays by mutating length and boxing `T|null` as Mixed."
+ ],
+ "runtime_helpers": [
+ "__rt_sort_int",
+ "__rt_sort_str"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_pop.rs",
"sig_line": null
},
- "name": "gzinflate",
+ "name": "array_pop",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "data",
+ "name": "array",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "max_length",
- "optional": true,
- "type": "int"
+ "type": "array"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "gzinflate",
- "sub_area": "String"
+ "slug": "array_pop",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "gzuncompress",
- "description": "Lowers `gzuncompress(data, max_length?)` and boxes zlib failures as PHP false.",
+ "area": "Array",
+ "canonical_name": "array_product",
+ "description": "Calculate the product of values in an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_gzuncompress",
- "codegen_line": 457,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_product",
+ "codegen_line": 55,
"notes": [
- "Lowers `gzuncompress(data, max_length?)` and boxes zlib failures as PHP false."
+ "Lowers `array_product()` over supported indexed-array payloads."
],
"runtime_helpers": [
- "__rt_long2ip"
+ "__rt_array_product"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_product.rs",
"sig_line": null
},
- "name": "gzuncompress",
+ "name": "array_product",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "data",
+ "name": "array",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "max_length",
- "optional": true,
- "type": "int"
+ "type": "array"
}
],
- "return_type": "string",
+ "return_type": "int",
"variadic": null
},
- "slug": "gzuncompress",
- "sub_area": "String"
+ "slug": "array_product",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "long2ip",
- "description": "Lowers `long2ip(value)` through the IPv4 formatting runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_push",
+ "description": "Pushes one or more elements onto the end of array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_long2ip",
- "codegen_line": 476,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_push",
+ "codegen_line": 60,
"notes": [
- "Lowers `long2ip(value)` through the IPv4 formatting runtime helper."
- ],
- "runtime_helpers": [
- "__rt_ip2long",
- "__rt_long2ip"
+ "Lowers `array_push()` by appending one value and publishing the mutated array."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_push.rs",
"sig_line": null
},
- "name": "long2ip",
+ "name": "array_push",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "ip",
+ "name": "array",
"optional": false,
- "type": "int"
+ "type": "array"
}
],
- "return_type": "string",
- "variadic": null
+ "return_type": "void",
+ "variadic": "values"
},
- "slug": "long2ip",
- "sub_area": "String"
+ "slug": "array_push",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "ip2long",
- "description": "Lowers `ip2long(string)` and boxes invalid-address results as PHP false.",
+ "area": "Array",
+ "canonical_name": "array_rand",
+ "description": "Pick one or more random keys out of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_ip2long",
- "codegen_line": 488,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_rand",
+ "codegen_line": 1010,
"notes": [
- "Lowers `ip2long(string)` and boxes invalid-address results as PHP false."
+ "Lowers `array_rand()` for indexed arrays."
],
"runtime_helpers": [
- "__rt_ip2long",
- "__rt_sprintf"
+ "__rt_array_rand",
+ "__rt_mixed_cast_int"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_rand.rs",
"sig_line": null
},
- "name": "ip2long",
+ "name": "array_rand",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "ip",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "ip2long",
- "sub_area": "String"
+ "slug": "array_rand",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "inet_ntop",
- "description": "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false.",
+ "area": "Array",
+ "canonical_name": "array_reduce",
+ "description": "Iteratively reduces an array to a single value using a callback function.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_inet",
- "codegen_line": 497,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_reduce",
+ "codegen_line": 695,
"notes": [
- "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+ "Lowers `array_reduce()` through the callback-driven runtime helper."
],
"runtime_helpers": [
- "__rt_sprintf"
+ "__rt_array_reduce"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_reduce.rs",
"sig_line": null
},
- "name": "inet_ntop",
+ "name": "array_reduce",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "ip",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "initial",
+ "optional": true,
+ "type": "mixed"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "inet_ntop",
- "sub_area": "String"
+ "slug": "array_reduce",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "inet_pton",
- "description": "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false.",
+ "area": "Array",
+ "canonical_name": "array_replace",
+ "description": "Replaces elements from passed arrays into the first array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_inet",
- "codegen_line": 497,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_replace",
+ "codegen_line": 1340,
"notes": [
- "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+ "Lowers `array_replace()` (right-wins hash merge of two hashes)."
],
"runtime_helpers": [
- "__rt_sprintf"
+ "__rt_array_replace",
+ "__rt_array_replace_recursive",
+ "__rt_assoc_diff_intersect"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_replace.rs",
"sig_line": null
},
- "name": "inet_pton",
+ "name": "array_replace",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "ip",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "replacements",
+ "optional": false,
+ "type": "array"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "inet_pton",
- "sub_area": "String"
+ "slug": "array_replace",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "disk_free_space",
- "description": "Lowers `disk_free_space(path)` through the shared disk-space runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_replace_recursive",
+ "description": "Replaces elements from passed arrays into the first array recursively.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_disk_free_space",
- "codegen_line": 3149,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_replace_recursive",
+ "codegen_line": 1345,
"notes": [
- "Lowers `disk_free_space(path)` through the shared disk-space runtime helper."
+ "Lowers `array_replace_recursive()` (recursive right-wins hash merge)."
],
"runtime_helpers": [
- "__rt_disk_space"
+ "__rt_array_replace_recursive",
+ "__rt_assoc_diff_intersect"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_replace_recursive.rs",
"sig_line": null
},
- "name": "disk_free_space",
+ "name": "array_replace_recursive",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "replacements",
+ "optional": false,
+ "type": "array"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "disk_free_space",
- "sub_area": "Filesystem"
+ "slug": "array_replace_recursive",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "disk_total_space",
- "description": "Lowers `disk_total_space(path)` through the shared disk-space runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_reverse",
+ "description": "Returns an array with the elements in reverse order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_disk_total_space",
- "codegen_line": 3157,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_reverse",
+ "codegen_line": 193,
"notes": [
- "Lowers `disk_total_space(path)` through the shared disk-space runtime helper."
- ],
- "runtime_helpers": [
- "__rt_disk_space"
+ "Lowers `array_reverse()` for indexed arrays with 8-byte payload slots."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_reverse.rs",
"sig_line": null
},
- "name": "disk_total_space",
+ "name": "array_reverse",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
}
],
- "return_type": "float",
+ "return_type": "array",
"variadic": null
},
- "slug": "disk_total_space",
- "sub_area": "Filesystem"
+ "slug": "array_reverse",
+ "sub_area": "Array"
},
{
- "area": "Type",
- "canonical_name": "boolval",
- "description": "Lowers `boolval()` using the same concrete scalar PHP truthiness rules as `IsTruthy`.",
+ "area": "Array",
+ "canonical_name": "array_search",
+ "description": "Searches the array for a given value and returns the first corresponding key if successful.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_boolval",
- "codegen_line": 1064,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_search",
+ "codegen_line": 1761,
"notes": [
- "Lowers `boolval()` using the same concrete scalar PHP truthiness rules as `IsTruthy`."
+ "Lowers `array_search()` for indexed arrays with integer-like payloads."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_search.rs",
"sig_line": null
},
- "name": "boolval",
+ "name": "array_search",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "needle",
"optional": false,
"type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "haystack",
+ "optional": false,
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "strict",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "boolval",
- "sub_area": "Casts"
+ "slug": "array_search",
+ "sub_area": "Array"
},
{
- "area": "Buffer",
- "canonical_name": "buffer_free",
- "description": "Lowers `buffer_free()` through the direct buffer opcode helper.",
+ "area": "Array",
+ "canonical_name": "array_shift",
+ "description": "Shifts an element off the beginning of array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/buffers.rs",
- "codegen_function": "lower_buffer_free",
- "codegen_line": 24,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/shift.rs",
+ "codegen_function": "lower_array_shift",
+ "codegen_line": 23,
"notes": [
- "Lowers `buffer_free()` through the direct buffer opcode helper."
+ "Lowers `array_shift()` for indexed arrays by compacting slots and boxing `T|null` as Mixed."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_shift.rs",
"sig_line": null
},
- "name": "buffer_free",
+ "name": "array_shift",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "buffer",
+ "name": "array",
"optional": false,
- "type": "buffer"
+ "type": "array"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "buffer_free",
- "sub_area": "Buffer"
+ "slug": "array_shift",
+ "sub_area": "Array"
},
{
- "area": "Buffer",
- "canonical_name": "buffer_len",
- "description": "Lowers `buffer_len()` through the direct buffer opcode helper.",
+ "area": "Array",
+ "canonical_name": "array_slice",
+ "description": "Extracts a slice of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/buffers.rs",
- "codegen_function": "lower_buffer_len",
- "codegen_line": 19,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_slice",
+ "codegen_line": 911,
"notes": [
- "Lowers `buffer_len()` through the direct buffer opcode helper."
+ "Lowers `array_slice()` for indexed arrays with pointer-sized payload slots."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_slice.rs",
"sig_line": null
},
- "name": "buffer_len",
+ "name": "array_slice",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "buffer",
+ "name": "array",
"optional": false,
- "type": "buffer"
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "offset",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "buffer_len",
- "sub_area": "Buffer"
+ "slug": "array_slice",
+ "sub_area": "Array"
},
{
- "area": "Misc",
- "canonical_name": "buffer_new",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_splice",
+ "description": "Removes a portion of the array and replaces it with something else.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_splice",
+ "codegen_line": 956,
+ "notes": [
+ "Lowers `array_splice()` by mutating an indexed source array and returning removed elements."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/array/array_splice.rs",
"sig_line": null
},
- "name": "buffer_new",
+ "name": "array_splice",
"sig": {
"params": [
+ {
+ "by_ref": true,
+ "default": null,
+ "name": "array",
+ "optional": false,
+ "type": "array"
+ },
{
"by_ref": false,
"default": null,
- "name": "length",
+ "name": "offset",
"optional": false,
"type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "array",
"variadic": null
},
- "slug": "buffer_new",
- "sub_area": "Misc"
+ "slug": "array_splice",
+ "sub_area": "Array"
},
{
- "area": "Misc",
- "canonical_name": "call_user_func",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_sum",
+ "description": "Calculate the sum of values in an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_sum",
+ "codegen_line": 50,
+ "notes": [
+ "Lowers `array_sum()` over supported indexed-array payloads."
+ ],
+ "runtime_helpers": [
+ "__rt_array_product",
+ "__rt_array_sum"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/array/array_sum.rs",
"sig_line": null
},
- "name": "call_user_func",
+ "name": "array_sum",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "callback",
+ "name": "array",
"optional": false,
- "type": "callable"
+ "type": "array"
}
],
- "return_type": "mixed",
- "variadic": "args"
+ "return_type": "int",
+ "variadic": null
},
- "slug": "call_user_func",
- "sub_area": "Misc"
+ "slug": "array_sum",
+ "sub_area": "Array"
},
{
- "area": "Misc",
- "canonical_name": "call_user_func_array",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_udiff",
+ "description": "Computes the difference of arrays using a callback comparator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_udiff",
+ "codegen_line": 1703,
+ "notes": [
+ "Lowers `array_udiff()`: keeps first-array elements not equal (per comparator) to any second-array element."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/array/array_udiff.rs",
"sig_line": null
},
- "name": "call_user_func_array",
+ "name": "array_udiff",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "callback",
+ "name": "array1",
"optional": false,
- "type": "callable"
+ "type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "args",
+ "name": "array2",
"optional": false,
"type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
}
],
- "return_type": "mixed",
+ "return_type": "array",
"variadic": null
},
- "slug": "call_user_func_array",
- "sub_area": "Misc"
+ "slug": "array_udiff",
+ "sub_area": "Array"
},
{
- "area": "Math",
- "canonical_name": "ceil",
- "description": "Lowers `ceil()` for concrete integer-like and floating operands.",
+ "area": "Array",
+ "canonical_name": "array_uintersect",
+ "description": "Computes the intersection of arrays using a callback comparator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_ceil",
- "codegen_line": 75,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_uintersect",
+ "codegen_line": 1708,
"notes": [
- "Lowers `ceil()` for concrete integer-like and floating operands."
+ "Lowers `array_uintersect()`: keeps first-array elements equal (per comparator) to some second-array element."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_uintersect.rs",
"sig_line": null
},
- "name": "ceil",
+ "name": "array_uintersect",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "array1",
"optional": false,
- "type": "float"
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "array2",
+ "optional": false,
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
}
],
- "return_type": "float",
+ "return_type": "array",
"variadic": null
},
- "slug": "ceil",
- "sub_area": "Math"
+ "slug": "array_uintersect",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "chgrp",
- "description": "Lowers `chgrp(path, group)` for integer GIDs and string group names.",
+ "area": "Array",
+ "canonical_name": "array_unique",
+ "description": "Removes duplicate values from an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_chgrp",
- "codegen_line": 3835,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_unique",
+ "codegen_line": 207,
"notes": [
- "Lowers `chgrp(path, group)` for integer GIDs and string group names."
+ "Lowers `array_unique()` for indexed arrays with 8-byte payload slots."
],
"runtime_helpers": [
- "__rt_umask"
+ "__rt_array_filter",
+ "__rt_array_filter_refcounted"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_unique.rs",
"sig_line": null
},
- "name": "chgrp",
+ "name": "array_unique",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "array",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "group",
- "optional": false,
- "type": "int"
+ "type": "array"
}
],
- "return_type": "bool",
+ "return_type": "array",
"variadic": null
},
- "slug": "chgrp",
- "sub_area": "Filesystem"
+ "slug": "array_unique",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "chmod",
- "description": "Lowers `chmod(path, mode)` through the target-aware runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_unshift",
+ "description": "Prepends one or more elements to the beginning of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_chmod",
- "codegen_line": 3825,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/unshift.rs",
+ "codegen_function": "lower_array_unshift",
+ "codegen_line": 23,
"notes": [
- "Lowers `chmod(path, mode)` through the target-aware runtime helper."
+ "Lowers `array_unshift()` by ensuring uniqueness, prepending one scalar value, and returning count."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_unshift.rs",
"sig_line": null
},
- "name": "chmod",
+ "name": "array_unshift",
"sig": {
"params": [
{
- "by_ref": false,
- "default": null,
- "name": "filename",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "permissions",
+ "name": "array",
"optional": false,
- "type": "int"
+ "type": "array"
}
],
- "return_type": "bool",
- "variadic": null
+ "return_type": "int",
+ "variadic": "values"
},
- "slug": "chmod",
- "sub_area": "Filesystem"
+ "slug": "array_unshift",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "chop",
- "description": "",
+ "area": "Array",
+ "canonical_name": "array_values",
+ "description": "Returns all the values of an array, re-indexed numerically.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays/values.rs",
+ "codegen_function": "lower_array_values",
+ "codegen_line": 22,
+ "notes": [
+ "Lowers `array_values()` for indexed arrays as an alias or associative arrays as a new values array."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/array/array_values.rs",
"sig_line": null
},
- "name": "chop",
+ "name": "array_values",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "array",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "characters",
- "optional": true,
- "type": "string"
+ "type": "array"
}
],
- "return_type": "string",
+ "return_type": "array",
"variadic": null
},
- "slug": "chop",
- "sub_area": "String"
+ "slug": "array_values",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "chown",
- "description": "Lowers `chown(path, owner)` for integer UIDs and string user names.",
+ "area": "Array",
+ "canonical_name": "array_walk",
+ "description": "Applies a user function to every member of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_chown",
- "codegen_line": 3830,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_walk",
+ "codegen_line": 779,
"notes": [
- "Lowers `chown(path, owner)` for integer UIDs and string user names."
+ "Lowers `array_walk()` through the callback-driven runtime helper."
],
"runtime_helpers": [
- "__rt_umask"
+ "__rt_array_walk"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_walk.rs",
"sig_line": null
},
- "name": "chown",
+ "name": "array_walk",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "filename",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "user",
+ "name": "callback",
"optional": false,
- "type": "int"
+ "type": "callable"
}
],
- "return_type": "bool",
+ "return_type": "void",
"variadic": null
},
- "slug": "chown",
- "sub_area": "Filesystem"
+ "slug": "array_walk",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "chdir",
- "description": "Lowers `chdir(path)` through the target-aware runtime helper.",
+ "area": "Array",
+ "canonical_name": "array_walk_recursive",
+ "description": "Applies a user function recursively to every member of an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_chdir",
- "codegen_line": 3795,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_array_walk_recursive",
+ "codegen_line": 1584,
"notes": [
- "Lowers `chdir(path)` through the target-aware runtime helper."
+ "Lowers `array_walk_recursive()`: invokes the callback on each scalar leaf of a (possibly nested)",
+ "array, descending into array-valued elements. Returns void; leaves are passed as 8-byte scalars."
],
"runtime_helpers": [
- "__rt_chdir",
- "__rt_copy",
- "__rt_glob",
- "__rt_scandir",
- "__rt_tempnam"
+ "__rt_array_udiff_uintersect",
+ "__rt_array_walk_recursive"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/array_walk_recursive.rs",
"sig_line": null
},
- "name": "chdir",
+ "name": "array_walk_recursive",
"sig": {
"params": [
+ {
+ "by_ref": true,
+ "default": null,
+ "name": "array",
+ "optional": false,
+ "type": "array"
+ },
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "callback",
"optional": false,
- "type": "string"
+ "type": "callable"
}
],
- "return_type": "bool",
+ "return_type": "void",
"variadic": null
},
- "slug": "chdir",
- "sub_area": "Filesystem"
+ "slug": "array_walk_recursive",
+ "sub_area": "Array"
},
{
- "area": "Date",
- "canonical_name": "checkdate",
- "description": "Lowers `checkdate(month, day, year)` through the shared Gregorian-validation runtime helper.",
+ "area": "Array",
+ "canonical_name": "arsort",
+ "description": "Sorts an array in descending order and maintains index association.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_checkdate",
- "codegen_line": 163,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_arsort",
+ "codegen_line": 1094,
"notes": [
- "Lowers `checkdate(month, day, year)` through the shared Gregorian-validation runtime helper.",
- "Marshals the three integers into the leading ABI argument registers (unboxing any boxed",
- "`Mixed`/`Union` argument), then calls `__rt_checkdate`, which returns PHP `true`/`false` in the",
- "integer result register for a valid/invalid date."
+ "Lowers `arsort()` for indexed integer arrays through the descending value-sort wrapper."
],
"runtime_helpers": [
- "__rt_checkdate",
- "__rt_getdate"
+ "__rt_arsort",
+ "__rt_krsort",
+ "__rt_ksort",
+ "__rt_natcasesort",
+ "__rt_natsort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/arsort.rs",
"sig_line": null
},
- "name": "checkdate",
+ "name": "arsort",
"sig": {
"params": [
{
- "by_ref": false,
- "default": null,
- "name": "month",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "day",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "year",
+ "name": "array",
"optional": false,
- "type": "int"
+ "type": "array"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "checkdate",
- "sub_area": "Date"
+ "slug": "arsort",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "chr",
- "description": "Lowers `chr()` by converting an integer code point into a one-byte string.",
+ "area": "Math",
+ "canonical_name": "asin",
+ "description": "Returns the arcsine of a number in radians.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_chr",
- "codegen_line": 858,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `chr()` by converting an integer code point into a one-byte string."
- ],
- "runtime_helpers": [
- "__rt_chr"
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/asin.rs",
"sig_line": null
},
- "name": "chr",
+ "name": "asin",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "codepoint",
+ "name": "num",
"optional": false,
- "type": "int"
+ "type": "float"
}
],
- "return_type": "string",
+ "return_type": "float",
"variadic": null
},
- "slug": "chr",
- "sub_area": "String"
+ "slug": "asin",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "clearstatcache",
- "description": "Lowers `clearstatcache(...)` as an ordered no-op after EIR operand evaluation.",
+ "area": "Array",
+ "canonical_name": "asort",
+ "description": "Sorts an array and maintains index association.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_clearstatcache",
- "codegen_line": 4935,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_asort",
+ "codegen_line": 1089,
"notes": [
- "Lowers `clearstatcache(...)` as an ordered no-op after EIR operand evaluation."
+ "Lowers `asort()` for indexed integer arrays through the value-sort runtime wrapper."
],
"runtime_helpers": [
- "__rt_is_dir"
+ "__rt_arsort",
+ "__rt_asort",
+ "__rt_krsort",
+ "__rt_ksort",
+ "__rt_natcasesort",
+ "__rt_natsort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/asort.rs",
"sig_line": null
},
- "name": "clearstatcache",
+ "name": "asort",
"sig": {
"params": [
{
- "by_ref": false,
- "default": null,
- "name": "clear_realpath_cache",
- "optional": true,
- "type": "bool"
- },
- {
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "filename",
- "optional": true,
- "type": "string"
+ "name": "array",
+ "optional": false,
+ "type": "array"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "clearstatcache",
- "sub_area": "Filesystem"
+ "slug": "asort",
+ "sub_area": "Array"
},
{
"area": "Math",
- "canonical_name": "clamp",
- "description": "Lowers numeric `clamp(value, min, max)` calls with PHP-compatible bound checks.",
+ "canonical_name": "atan",
+ "description": "Returns the arctangent of a number in radians.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_clamp",
- "codegen_line": 80,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers numeric `clamp(value, min, max)` calls with PHP-compatible bound checks."
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/atan.rs",
"sig_line": null
},
- "name": "clamp",
+ "name": "atan",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "min",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "max",
+ "name": "num",
"optional": false,
- "type": "int"
+ "type": "float"
}
],
- "return_type": "string",
+ "return_type": "float",
"variadic": null
},
- "slug": "clamp",
+ "slug": "atan",
"sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "copy",
- "description": "Lowers `copy(source, dest)` through the target-aware runtime helper.",
+ "area": "Math",
+ "canonical_name": "atan2",
+ "description": "Returns the arc tangent of two variables.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_copy",
- "codegen_line": 3800,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_atan2",
+ "codegen_line": 35,
"notes": [
- "Lowers `copy(source, dest)` through the target-aware runtime helper."
- ],
- "runtime_helpers": [
- "__rt_copy",
- "__rt_glob",
- "__rt_scandir",
- "__rt_tempnam"
+ "Lowers `atan2()` using the C ABI argument order `y, x`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/atan2.rs",
"sig_line": null
},
- "name": "copy",
+ "name": "atan2",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "from",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "to",
+ "name": "y",
"optional": false,
- "type": "string"
+ "type": "float"
},
{
"by_ref": false,
"default": null,
- "name": "context",
+ "name": "x",
"optional": false,
- "type": "mixed"
+ "type": "float"
}
],
- "return_type": "bool",
+ "return_type": "float",
"variadic": null
},
- "slug": "copy",
- "sub_area": "Filesystem"
+ "slug": "atan2",
+ "sub_area": "Math"
},
{
- "area": "Math",
- "canonical_name": "cos",
- "description": "",
+ "area": "String",
+ "canonical_name": "base64_decode",
+ "description": "Decodes a Base64-encoded string back into its original data.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
+ "notes": [
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/string/base64_decode.rs",
"sig_line": null
},
- "name": "cos",
+ "name": "base64_decode",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "string",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "cos",
- "sub_area": "Math"
+ "slug": "base64_decode",
+ "sub_area": "String"
},
{
- "area": "Math",
- "canonical_name": "cosh",
- "description": "",
+ "area": "String",
+ "canonical_name": "base64_encode",
+ "description": "Encodes binary data into a Base64 string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
+ "notes": [
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/string/base64_encode.rs",
"sig_line": null
},
- "name": "cosh",
+ "name": "base64_encode",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "string",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "cosh",
- "sub_area": "Math"
+ "slug": "base64_encode",
+ "sub_area": "String"
},
{
- "area": "Array",
- "canonical_name": "count",
- "description": "Lowers `count(array)` for concrete array values by reading the runtime length header.",
+ "area": "Filesystem",
+ "canonical_name": "basename",
+ "description": "Returns the trailing name component of a path.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_count",
- "codegen_line": 917,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_basename",
+ "codegen_line": 4536,
"notes": [
- "Lowers `count(array)` for concrete array values by reading the runtime length header."
- ],
- "runtime_helpers": [
- "__rt_mixed_count"
+ "Lowers `basename(path, suffix?)` through the target-aware runtime helper."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/basename.rs",
"sig_line": null
},
- "name": "count",
+ "name": "basename",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "path",
"optional": false,
- "type": "array"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "mode",
+ "default": "''",
+ "name": "suffix",
"optional": true,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "count",
- "sub_area": "Array"
+ "slug": "basename",
+ "sub_area": "Filesystem"
},
{
"area": "String",
- "canonical_name": "crc32",
- "description": "Lowers `crc32(string)` through the shared checksum runtime helper.",
+ "canonical_name": "bin2hex",
+ "description": "Converts binary data into its hexadecimal string representation.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_crc32",
- "codegen_line": 348,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `crc32(string)` through the shared checksum runtime helper."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_crc32",
- "__rt_hash",
- "__rt_md5",
- "__rt_sha1"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/bin2hex.rs",
"sig_line": null
},
- "name": "crc32",
+ "name": "bin2hex",
"sig": {
"params": [
{
@@ -3407,276 +3315,315 @@
"type": "string"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "crc32",
+ "slug": "bin2hex",
"sub_area": "String"
},
{
"area": "Type",
- "canonical_name": "ctype_alnum",
- "description": "Lowers `ctype_alnum(string)` by checking every byte against ASCII alpha or digit ranges.",
+ "canonical_name": "boolval",
+ "description": "Returns the boolean value of a variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/ctype.rs",
- "codegen_function": "lower_ctype_alnum",
- "codegen_line": 30,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_boolval",
+ "codegen_line": 586,
"notes": [
- "Lowers `ctype_alnum(string)` by checking every byte against ASCII alpha or digit ranges."
+ "Lowers `boolval()` using the same concrete scalar PHP truthiness rules as `IsTruthy`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/boolval.rs",
"sig_line": null
},
- "name": "ctype_alnum",
+ "name": "boolval",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "text",
+ "name": "value",
"optional": false,
- "type": "string"
+ "type": "mixed"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "ctype_alnum",
- "sub_area": "Ctype"
+ "slug": "boolval",
+ "sub_area": "Casts"
},
{
- "area": "Type",
- "canonical_name": "ctype_alpha",
- "description": "Lowers `ctype_alpha(string)` by checking every byte against ASCII alpha ranges.",
+ "area": "Buffer",
+ "canonical_name": "buffer_free",
+ "description": "Lowers `buffer_free()` through the direct buffer opcode helper.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/ctype.rs",
- "codegen_function": "lower_ctype_alpha",
- "codegen_line": 20,
+ "codegen_file": "src/codegen/lower_inst/builtins/buffers.rs",
+ "codegen_function": "lower_buffer_free",
+ "codegen_line": 24,
"notes": [
- "Lowers `ctype_alpha(string)` by checking every byte against ASCII alpha ranges."
+ "Lowers `buffer_free()` through the direct buffer opcode helper."
],
"runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "ctype_alpha",
+ "name": "buffer_free",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "text",
+ "name": "buffer",
"optional": false,
- "type": "string"
+ "type": "buffer"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ctype_alpha",
- "sub_area": "Ctype"
+ "slug": "buffer_free",
+ "sub_area": "Buffer"
},
{
- "area": "Type",
- "canonical_name": "ctype_digit",
- "description": "Lowers `ctype_digit(string)` by checking every byte against the ASCII digit range.",
+ "area": "Buffer",
+ "canonical_name": "buffer_len",
+ "description": "Lowers `buffer_len()` through the direct buffer opcode helper.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/ctype.rs",
- "codegen_function": "lower_ctype_digit",
- "codegen_line": 25,
+ "codegen_file": "src/codegen/lower_inst/builtins/buffers.rs",
+ "codegen_function": "lower_buffer_len",
+ "codegen_line": 19,
"notes": [
- "Lowers `ctype_digit(string)` by checking every byte against the ASCII digit range."
+ "Lowers `buffer_len()` through the direct buffer opcode helper."
],
"runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "ctype_digit",
+ "name": "buffer_len",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "text",
+ "name": "buffer",
"optional": false,
- "type": "string"
+ "type": "buffer"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "ctype_digit",
- "sub_area": "Ctype"
+ "slug": "buffer_len",
+ "sub_area": "Buffer"
},
{
- "area": "Type",
- "canonical_name": "ctype_space",
- "description": "Lowers `ctype_space(string)` by checking every byte against PHP's ASCII whitespace set.",
+ "area": "Misc",
+ "canonical_name": "buffer_new",
+ "description": "",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/ctype.rs",
- "codegen_function": "lower_ctype_space",
- "codegen_line": 35,
- "notes": [
- "Lowers `ctype_space(string)` by checking every byte against PHP's ASCII whitespace set."
- ],
+ "codegen_file": null,
+ "codegen_function": null,
+ "codegen_line": null,
+ "notes": [],
"runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "ctype_space",
+ "name": "buffer_new",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "text",
+ "name": "length",
"optional": false,
- "type": "string"
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ctype_space",
- "sub_area": "Ctype"
+ "slug": "buffer_new",
+ "sub_area": "Misc"
},
{
- "area": "Date",
- "canonical_name": "date",
- "description": "Lowers `date(format, timestamp?)` through the shared formatter runtime helper.",
+ "area": "Array",
+ "canonical_name": "call_user_func",
+ "description": "Calls a callback with the given arguments.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_date",
- "codegen_line": 22,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_call_user_func_builtin_escape",
+ "codegen_line": 37,
"notes": [
- "Lowers `date(format, timestamp?)` through the shared formatter runtime helper."
+ "Rejects `call_user_func*` calls that escaped the dedicated EIR callback lowering path."
],
"runtime_helpers": [
- "__rt_date",
- "__rt_gmdate"
+ "__rt_array_product",
+ "__rt_array_sum"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/call_user_func.rs",
"sig_line": null
},
- "name": "date",
+ "name": "call_user_func",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "format",
+ "name": "callback",
"optional": false,
- "type": "string"
+ "type": "callable"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": "args"
+ },
+ "slug": "call_user_func",
+ "sub_area": "Array"
+ },
+ {
+ "area": "Array",
+ "canonical_name": "call_user_func_array",
+ "description": "Calls a callback with an array of parameters.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_call_user_func_builtin_escape",
+ "codegen_line": 37,
+ "notes": [
+ "Rejects `call_user_func*` calls that escaped the dedicated EIR callback lowering path."
+ ],
+ "runtime_helpers": [
+ "__rt_array_product",
+ "__rt_array_sum"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/call_user_func_array.rs",
+ "sig_line": null
+ },
+ "name": "call_user_func_array",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
},
{
"by_ref": false,
"default": null,
- "name": "timestamp",
- "optional": true,
- "type": "int"
+ "name": "args",
+ "optional": false,
+ "type": "array"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "date",
- "sub_area": "Date"
+ "slug": "call_user_func_array",
+ "sub_area": "Array"
},
{
- "area": "Date",
- "canonical_name": "date_default_timezone_get",
- "description": "Lowers `date_default_timezone_get()` through the shared runtime helper.",
+ "area": "Math",
+ "canonical_name": "ceil",
+ "description": "Rounds a number up to the nearest integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_date_default_timezone_get",
- "codegen_line": 70,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_ceil",
+ "codegen_line": 75,
"notes": [
- "Lowers `date_default_timezone_get()` through the shared runtime helper.",
- "Takes no arguments; `__rt_date_default_timezone_get` returns the stored timezone",
- "identifier (or the literal `\"UTC\"` when none was set) in the string-result registers."
- ],
- "runtime_helpers": [
- "__rt_date_default_timezone_get",
- "__rt_date_default_timezone_set"
+ "Lowers `ceil()` for concrete integer-like and floating operands."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/ceil.rs",
"sig_line": null
},
- "name": "date_default_timezone_get",
+ "name": "ceil",
"sig": {
- "params": [],
- "return_type": "string",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "float",
"variadic": null
},
- "slug": "date_default_timezone_get",
- "sub_area": "Date"
+ "slug": "ceil",
+ "sub_area": "Math"
},
{
- "area": "Date",
- "canonical_name": "date_default_timezone_set",
- "description": "Lowers `date_default_timezone_set(timezoneId)` through the shared runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "chdir",
+ "description": "Changes the current directory.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_date_default_timezone_set",
- "codegen_line": 84,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_chdir",
+ "codegen_line": 4438,
"notes": [
- "Lowers `date_default_timezone_set(timezoneId)` through the shared runtime helper.",
- "Materializes the identifier string into the registers the helper reads (ptr/len in",
- "`x1`/`x2` on ARM64, `rax`/`rdx` on x86_64), then `__rt_date_default_timezone_set`",
- "applies it via libc `putenv`+`tzset` and returns PHP `true` in the integer-result register."
+ "Lowers `chdir(path)` through the target-aware runtime helper."
],
"runtime_helpers": [
- "__rt_date_default_timezone_set",
- "__rt_microtime",
- "__rt_microtime_mixed",
- "__rt_microtime_str"
+ "__rt_chdir",
+ "__rt_copy",
+ "__rt_glob",
+ "__rt_scandir",
+ "__rt_tempnam"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/chdir.rs",
"sig_line": null
},
- "name": "date_default_timezone_set",
+ "name": "chdir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "timezoneId",
+ "name": "directory",
"optional": false,
"type": "string"
}
@@ -3684,87 +3631,102 @@
"return_type": "bool",
"variadic": null
},
- "slug": "date_default_timezone_set",
- "sub_area": "Date"
+ "slug": "chdir",
+ "sub_area": "Filesystem"
},
{
- "area": "Misc",
- "canonical_name": "define",
- "description": "Defines a named constant at runtime.",
+ "area": "Date",
+ "canonical_name": "checkdate",
+ "description": "Validates a Gregorian date.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_define",
- "codegen_line": 549,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_checkdate",
+ "codegen_line": 163,
"notes": [
- "Lowers `define(\"NAME\", value)` with the legacy duplicate-name runtime guard."
+ "Lowers `checkdate(month, day, year)` through the shared Gregorian-validation runtime helper.",
+ "Marshals the three integers into the leading ABI argument registers (unboxing any boxed",
+ "`Mixed`/`Union` argument), then calls `__rt_checkdate`, which returns PHP `true`/`false` in the",
+ "integer result register for a valid/invalid date."
+ ],
+ "runtime_helpers": [
+ "__rt_checkdate",
+ "__rt_getdate"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/checkdate.rs",
"sig_line": null
},
- "name": "define",
+ "name": "checkdate",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "constant_name",
+ "name": "month",
"optional": false,
- "type": "string"
+ "type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "day",
"optional": false,
- "type": "mixed"
+ "type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "case_insensitive",
+ "name": "year",
"optional": false,
- "type": "bool"
+ "type": "int"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "define",
- "sub_area": "Constants"
+ "slug": "checkdate",
+ "sub_area": "Date"
},
{
- "area": "Misc",
- "canonical_name": "defined",
- "description": "Checks whether a given named constant exists.",
+ "area": "Filesystem",
+ "canonical_name": "chgrp",
+ "description": "Changes file group.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_defined",
- "codegen_line": 744,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_chgrp",
+ "codegen_line": 4478,
"notes": [
- "Lowers `defined(\"NAME\")` for compile-time string constant names."
+ "Lowers `chgrp(path, group)` for integer GIDs and string group names."
+ ],
+ "runtime_helpers": [
+ "__rt_umask"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/chgrp.rs",
"sig_line": null
},
- "name": "defined",
+ "name": "chgrp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "constant_name",
+ "name": "filename",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "group",
"optional": false,
"type": "string"
}
@@ -3772,962 +3734,945 @@
"return_type": "bool",
"variadic": null
},
- "slug": "defined",
- "sub_area": "Constants"
+ "slug": "chgrp",
+ "sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "deg2rad",
- "description": "Lowers `deg2rad()` by multiplying with `PI / 180`.",
+ "area": "Filesystem",
+ "canonical_name": "chmod",
+ "description": "Changes file mode.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/libm.rs",
- "codegen_function": "lower_deg2rad",
- "codegen_line": 75,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_chmod",
+ "codegen_line": 4468,
"notes": [
- "Lowers `deg2rad()` by multiplying with `PI / 180`."
+ "Lowers `chmod(path, mode)` through the target-aware runtime helper."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/chmod.rs",
"sig_line": null
},
- "name": "deg2rad",
+ "name": "chmod",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "filename",
"optional": false,
- "type": "float"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "permissions",
+ "optional": false,
+ "type": "int"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "deg2rad",
- "sub_area": "Math"
+ "slug": "chmod",
+ "sub_area": "Filesystem"
},
{
- "area": "Process",
- "canonical_name": "die",
- "description": "",
+ "area": "String",
+ "canonical_name": "chop",
+ "description": "Alias of rtrim: strips whitespace (or other characters) from the end of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_trim_like",
+ "codegen_line": 112,
+ "notes": [
+ "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/string/chop.rs",
"sig_line": null
},
- "name": "die",
+ "name": "chop",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "status",
+ "name": "string",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "' \\n\\r\\t\\x0b\\x0c\\x00'",
+ "name": "characters",
"optional": true,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "void",
+ "return_type": "string",
"variadic": null
},
- "slug": "die",
- "sub_area": "Process"
+ "slug": "chop",
+ "sub_area": "String"
},
{
"area": "Filesystem",
- "canonical_name": "dirname",
- "description": "Lowers `dirname(path, levels?)` through the target-aware runtime helper.",
+ "canonical_name": "chown",
+ "description": "Changes file owner.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_dirname",
- "codegen_line": 3932,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_chown",
+ "codegen_line": 4473,
"notes": [
- "Lowers `dirname(path, levels?)` through the target-aware runtime helper."
+ "Lowers `chown(path, owner)` for integer UIDs and string user names."
],
"runtime_helpers": [
- "__rt_dirname",
- "__rt_dirname_levels"
+ "__rt_umask"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/chown.rs",
"sig_line": null
},
- "name": "dirname",
+ "name": "chown",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "path",
+ "name": "filename",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "levels",
- "optional": true,
- "type": "int"
+ "name": "user",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "string",
+ "return_type": "bool",
"variadic": null
},
- "slug": "dirname",
+ "slug": "chown",
"sub_area": "Filesystem"
},
{
- "area": "Misc",
- "canonical_name": "empty",
- "description": "Determines whether a variable is considered empty.",
+ "area": "String",
+ "canonical_name": "chr",
+ "description": "Returns a one-character string from the given byte code point.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_empty",
- "codegen_line": 1096,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_chr",
+ "codegen_line": 858,
"notes": [
- "Lowers `empty()` for concrete scalar and array-like operands."
+ "Lowers `chr()` by converting an integer code point into a one-byte string."
],
"runtime_helpers": [
- "__rt_mixed_is_empty"
+ "__rt_chr"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/chr.rs",
"sig_line": null
},
- "name": "empty",
+ "name": "chr",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "codepoint",
"optional": false,
- "type": "mixed"
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "string",
"variadic": null
},
- "slug": "empty",
- "sub_area": "Variable"
+ "slug": "chr",
+ "sub_area": "String"
},
{
- "area": "Process",
- "canonical_name": "exec",
- "description": "Lowers `exec(command)` by capturing shell stdout through the shared runtime helper.",
+ "area": "Math",
+ "canonical_name": "clamp",
+ "description": "Clamps a value to be within a specified range.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_exec",
- "codegen_line": 690,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_clamp",
+ "codegen_line": 80,
"notes": [
- "Lowers `exec(command)` by capturing shell stdout through the shared runtime helper."
+ "Lowers numeric `clamp(value, min, max)` calls with PHP-compatible bound checks."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/clamp.rs",
"sig_line": null
},
- "name": "exec",
+ "name": "clamp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "command",
+ "name": "value",
"optional": false,
- "type": "string"
+ "type": "int"
},
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "output",
+ "name": "min",
"optional": false,
- "type": "array"
+ "type": "int"
},
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "result_code",
+ "name": "max",
"optional": false,
"type": "int"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "exec",
- "sub_area": "Process"
+ "slug": "clamp",
+ "sub_area": "Math"
},
{
- "area": "Process",
- "canonical_name": "exit",
- "description": "",
+ "area": "Class",
+ "canonical_name": "class_alias",
+ "description": "Creates an alias for a class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_class_alias",
+ "codegen_line": 41,
+ "notes": [
+ "Lowers the defensive `class_alias()` fallback that remains after AOT alias extraction."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/callables/class_alias.rs",
"sig_line": null
},
- "name": "exit",
+ "name": "class_alias",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "status",
+ "name": "class",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "alias",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
"optional": true,
- "type": "int"
+ "type": "bool"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "exit",
- "sub_area": "Process"
+ "slug": "class_alias",
+ "sub_area": "Class"
},
{
- "area": "Math",
- "canonical_name": "exp",
- "description": "",
+ "area": "Class",
+ "canonical_name": "class_attribute_args",
+ "description": "Returns the constructor arguments of a named attribute applied to a class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/attributes.rs",
+ "codegen_function": "lower_class_attribute_args",
+ "codegen_line": 52,
+ "notes": [
+ "Lowers `class_attribute_args(class, attr)` into an indexed Mixed array."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/system/class_attribute_args.rs",
"sig_line": null
},
- "name": "exp",
+ "name": "class_attribute_args",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "class_name",
"optional": false,
- "type": "float"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "attribute_name",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "array",
"variadic": null
},
- "slug": "exp",
- "sub_area": "Math"
+ "slug": "class_attribute_args",
+ "sub_area": "Attributes"
},
{
- "area": "String",
- "canonical_name": "explode",
- "description": "Lowers `explode(delimiter, string)` into the shared string-array splitter helper.",
+ "area": "Class",
+ "canonical_name": "class_attribute_names",
+ "description": "Returns the list of attribute names applied to a class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_explode",
- "codegen_line": 151,
+ "codegen_file": "src/codegen/lower_inst/builtins/attributes.rs",
+ "codegen_function": "lower_class_attribute_names",
+ "codegen_line": 36,
"notes": [
- "Lowers `explode(delimiter, string)` into the shared string-array splitter helper."
- ],
- "runtime_helpers": [
- "__rt_explode",
- "__rt_sscanf"
+ "Lowers `class_attribute_names(class)` into an indexed string array."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/class_attribute_names.rs",
"sig_line": null
},
- "name": "explode",
+ "name": "class_attribute_names",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "separator",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "string",
+ "name": "class_name",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "limit",
- "optional": true,
- "type": "int"
}
],
"return_type": "array",
"variadic": null
},
- "slug": "explode",
- "sub_area": "String"
+ "slug": "class_attribute_names",
+ "sub_area": "Attributes"
},
{
- "area": "IO",
- "canonical_name": "fclose",
- "description": "Lowers `fclose(stream)` after validating and unboxing the stream handle.",
+ "area": "Class",
+ "canonical_name": "class_exists",
+ "description": "Checks whether the given class has been defined.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fclose",
- "codegen_line": 2488,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_class_like_exists",
+ "codegen_line": 293,
"notes": [
- "Lowers `fclose(stream)` after validating and unboxing the stream handle."
+ "Lowers AOT class/interface/enum existence checks for literal names."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/class_exists.rs",
"sig_line": null
},
- "name": "fclose",
+ "name": "class_exists",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "class",
"optional": false,
- "type": "resource"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "fclose",
- "sub_area": "IO"
+ "slug": "class_exists",
+ "sub_area": "Class"
},
{
- "area": "IO",
- "canonical_name": "fdatasync",
- "description": "Lowers `fdatasync(stream)` through the shared fd data-sync runtime helper.",
+ "area": "Class",
+ "canonical_name": "class_get_attributes",
+ "description": "Returns an array of ReflectionAttribute objects for all attributes of a class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fdatasync",
- "codegen_line": 3083,
+ "codegen_file": "src/codegen/lower_inst/builtins/attributes.rs",
+ "codegen_function": "lower_class_get_attributes",
+ "codegen_line": 68,
"notes": [
- "Lowers `fdatasync(stream)` through the shared fd data-sync runtime helper."
- ],
- "runtime_helpers": [
- "__rt_fdatasync"
+ "Lowers `class_get_attributes(class)` into an array of `ReflectionAttribute` objects."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/class_get_attributes.rs",
"sig_line": null
},
- "name": "fdatasync",
+ "name": "class_get_attributes",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "class_name",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "bool",
+ "return_type": "array",
"variadic": null
},
- "slug": "fdatasync",
- "sub_area": "IO"
+ "slug": "class_get_attributes",
+ "sub_area": "Attributes"
},
{
- "area": "Math",
- "canonical_name": "fdiv",
- "description": "Lowers `fdiv()` for concrete integer-like and floating operands.",
+ "area": "Class",
+ "canonical_name": "class_implements",
+ "description": "Returns the interfaces which are implemented by the given class or its parents.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/binary.rs",
- "codegen_function": "lower_fdiv",
- "codegen_line": 60,
+ "codegen_file": "src/codegen/lower_inst/builtins/class_relations.rs",
+ "codegen_function": "lower_class_relation",
+ "codegen_line": 32,
"notes": [
- "Lowers `fdiv()` for concrete integer-like and floating operands."
+ "Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/class_implements.rs",
"sig_line": null
},
- "name": "fdiv",
+ "name": "class_implements",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num1",
+ "name": "object_or_class",
"optional": false,
- "type": "float"
+ "type": "mixed"
},
{
"by_ref": false,
- "default": null,
- "name": "num2",
- "optional": false,
- "type": "float"
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "fdiv",
- "sub_area": "Math"
+ "slug": "class_implements",
+ "sub_area": "Class"
},
{
- "area": "IO",
- "canonical_name": "feof",
- "description": "Lowers `feof(stream)` through the runtime EOF-flag table helper.",
+ "area": "Class",
+ "canonical_name": "class_parents",
+ "description": "Returns the parent classes of the given class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_feof",
- "codegen_line": 2900,
+ "codegen_file": "src/codegen/lower_inst/builtins/class_relations.rs",
+ "codegen_function": "lower_class_relation",
+ "codegen_line": 32,
"notes": [
- "Lowers `feof(stream)` through the runtime EOF-flag table helper."
- ],
- "runtime_helpers": [
- "__rt_feof",
- "__rt_user_wrapper_ftell"
+ "Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/class_parents.rs",
"sig_line": null
},
- "name": "feof",
+ "name": "class_parents",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "object_or_class",
"optional": false,
- "type": "resource"
+ "type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "feof",
- "sub_area": "IO"
+ "slug": "class_parents",
+ "sub_area": "Class"
},
{
- "area": "IO",
- "canonical_name": "fflush",
- "description": "Lowers `fflush(stream)` through the shared fd flush runtime helper.",
+ "area": "Class",
+ "canonical_name": "class_uses",
+ "description": "Returns the traits used by the given class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fflush",
- "codegen_line": 3045,
+ "codegen_file": "src/codegen/lower_inst/builtins/class_relations.rs",
+ "codegen_function": "lower_class_relation",
+ "codegen_line": 32,
"notes": [
- "Lowers `fflush(stream)` through the shared fd flush runtime helper."
- ],
- "runtime_helpers": [
- "__rt_fflush"
+ "Lowers `class_implements()`, `class_parents()`, and `class_uses()` from static metadata."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/class_uses.rs",
"sig_line": null
},
- "name": "fflush",
+ "name": "class_uses",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "object_or_class",
"optional": false,
- "type": "resource"
+ "type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "fflush",
- "sub_area": "IO"
+ "slug": "class_uses",
+ "sub_area": "Class"
},
{
- "area": "IO",
- "canonical_name": "fgetc",
- "description": "Lowers `fgetc(stream)` and boxes the one-byte string or PHP false result.",
+ "area": "Filesystem",
+ "canonical_name": "clearstatcache",
+ "description": "Clears file status cache.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fgetc",
- "codegen_line": 2759,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_clearstatcache",
+ "codegen_line": 5578,
"notes": [
- "Lowers `fgetc(stream)` and boxes the one-byte string or PHP false result."
+ "Lowers `clearstatcache(...)` as an ordered no-op after EIR operand evaluation."
],
"runtime_helpers": [
- "__rt_fgetc",
- "__rt_fgetcsv"
+ "__rt_is_dir"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/clearstatcache.rs",
"sig_line": null
},
- "name": "fgetc",
+ "name": "clearstatcache",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
+ "default": "false",
+ "name": "clear_realpath_cache",
+ "optional": true,
+ "type": "bool"
+ },
+ {
+ "by_ref": false,
+ "default": "''",
+ "name": "filename",
+ "optional": true,
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "void",
"variadic": null
},
- "slug": "fgetc",
- "sub_area": "IO"
+ "slug": "clearstatcache",
+ "sub_area": "Filesystem"
},
{
"area": "IO",
- "canonical_name": "fgetcsv",
- "description": "Lowers `fgetcsv(stream, separator?, enclosure?)` through the CSV row runtime helper.",
+ "canonical_name": "closedir",
+ "description": "Closes directory handle.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fgetcsv",
- "codegen_line": 2772,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_closedir",
+ "codegen_line": 3572,
"notes": [
- "Lowers `fgetcsv(stream, separator?, enclosure?)` through the CSV row runtime helper."
+ "Lowers `closedir(dir_handle)` for libc, glob, and userspace-wrapper handles."
],
"runtime_helpers": [
- "__rt_fgetcsv",
- "__rt_fputcsv"
+ "__rt_closedir",
+ "__rt_rewinddir",
+ "__rt_user_wrapper_dir_closedir",
+ "__rt_user_wrapper_dir_rewinddir"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/closedir.rs",
"sig_line": null
},
- "name": "fgetcsv",
+ "name": "closedir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "dir_handle",
"optional": false,
"type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "separator",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "enclosure",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "escape",
- "optional": false,
- "type": "string"
}
],
- "return_type": "array",
+ "return_type": "void",
"variadic": null
},
- "slug": "fgetcsv",
+ "slug": "closedir",
"sub_area": "IO"
},
{
- "area": "IO",
- "canonical_name": "fgets",
- "description": "Lowers `fgets(stream)` through the shared line-read runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "copy",
+ "description": "Copies a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fgets",
- "codegen_line": 2746,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_copy",
+ "codegen_line": 4443,
"notes": [
- "Lowers `fgets(stream)` through the shared line-read runtime helper."
+ "Lowers `copy(source, dest)` through the target-aware runtime helper."
],
"runtime_helpers": [
- "__rt_fgetc",
- "__rt_fgets"
+ "__rt_copy",
+ "__rt_glob",
+ "__rt_scandir",
+ "__rt_tempnam"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/copy.rs",
"sig_line": null
},
- "name": "fgets",
+ "name": "copy",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "from",
"optional": false,
- "type": "resource"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "length",
+ "name": "to",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fgets",
- "sub_area": "IO"
+ "slug": "copy",
+ "sub_area": "Filesystem"
},
{
- "area": "IO",
- "canonical_name": "fscanf",
- "description": "Lowers `fscanf(stream, format)` through `__rt_fgets` and `__rt_sscanf`.",
+ "area": "Math",
+ "canonical_name": "cos",
+ "description": "Returns the cosine of a number (radians).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fscanf",
- "codegen_line": 2717,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `fscanf(stream, format)` through `__rt_fgets` and `__rt_sscanf`."
- ],
- "runtime_helpers": [
- "__rt_fgets",
- "__rt_sscanf"
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/cos.rs",
"sig_line": null
},
- "name": "fscanf",
+ "name": "cos",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "format",
+ "name": "num",
"optional": false,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "array",
- "variadic": "vars"
+ "return_type": "float",
+ "variadic": null
},
- "slug": "fscanf",
- "sub_area": "IO"
+ "slug": "cos",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "flock",
- "description": "Lowers `flock(stream, operation, would_block?)` through the libc flock wrapper.",
+ "area": "Math",
+ "canonical_name": "cosh",
+ "description": "Returns the hyperbolic cosine of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_flock",
- "codegen_line": 3088,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `flock(stream, operation, would_block?)` through the libc flock wrapper."
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/cosh.rs",
"sig_line": null
},
- "name": "flock",
+ "name": "cosh",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "operation",
+ "name": "num",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "would_block",
- "optional": true,
- "type": "bool"
+ "type": "float"
}
],
- "return_type": "bool",
+ "return_type": "float",
"variadic": null
},
- "slug": "flock",
- "sub_area": "IO"
+ "slug": "cosh",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "file",
- "description": "Lowers `file(path)` through the target-aware runtime line-array helper.",
+ "area": "Array",
+ "canonical_name": "count",
+ "description": "Counts all elements in an array or Countable object.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_file",
- "codegen_line": 3460,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_count",
+ "codegen_line": 439,
"notes": [
- "Lowers `file(path)` through the target-aware runtime line-array helper."
+ "Lowers `count(array)` for concrete array values by reading the runtime length header.",
+ "Called from `crate::builtins::array::count` (the registry home) via a thin wrapper.",
+ "Handles Array/AssocArray (reads length directly from the runtime header), Mixed/Union",
+ "(delegates to `__rt_mixed_count`), and Countable Object (calls the object's `count`",
+ "method via intrinsic or dynamic dispatch)."
],
"runtime_helpers": [
- "__rt_file",
- "__rt_realpath"
+ "__rt_mixed_count"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/count.rs",
"sig_line": null
},
- "name": "file",
+ "name": "count",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "value",
"optional": false,
- "type": "string"
+ "type": "array"
},
{
"by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
+ "default": "0",
+ "name": "mode",
+ "optional": true,
"type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": false,
- "type": "mixed"
}
],
- "return_type": "array",
+ "return_type": "int",
"variadic": null
},
- "slug": "file",
- "sub_area": "IO"
+ "slug": "count",
+ "sub_area": "Array"
},
{
- "area": "Filesystem",
- "canonical_name": "fileatime",
- "description": "Lowers `fileatime(path)` and boxes the runtime integer-or-false result.",
+ "area": "String",
+ "canonical_name": "crc32",
+ "description": "Calculates the CRC32 polynomial of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fileatime",
- "codegen_line": 4825,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_crc32",
+ "codegen_line": 348,
"notes": [
- "Lowers `fileatime(path)` and boxes the runtime integer-or-false result."
+ "Lowers `crc32(string)` through the shared checksum runtime helper."
],
"runtime_helpers": [
- "__rt_fileatime",
- "__rt_filectime",
- "__rt_fileowner",
- "__rt_fileperms"
+ "__rt_crc32",
+ "__rt_hash",
+ "__rt_md5",
+ "__rt_sha1"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/crc32.rs",
"sig_line": null
},
- "name": "fileatime",
+ "name": "crc32",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "string",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "fileatime",
- "sub_area": "Filesystem"
+ "slug": "crc32",
+ "sub_area": "String"
},
{
- "area": "Filesystem",
- "canonical_name": "filectime",
- "description": "Lowers `filectime(path)` and boxes the runtime integer-or-false result.",
+ "area": "Type",
+ "canonical_name": "ctype_alnum",
+ "description": "Checks if all characters in the string are alphanumeric.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_filectime",
- "codegen_line": 4833,
+ "codegen_file": "src/codegen/lower_inst/builtins/ctype.rs",
+ "codegen_function": "lower_ctype_alnum",
+ "codegen_line": 30,
"notes": [
- "Lowers `filectime(path)` and boxes the runtime integer-or-false result."
- ],
- "runtime_helpers": [
- "__rt_filectime",
- "__rt_filegroup",
- "__rt_fileowner",
- "__rt_fileperms"
+ "Lowers `ctype_alnum(string)` by checking every byte against ASCII alpha or digit ranges."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ctype_alnum.rs",
"sig_line": null
},
- "name": "filectime",
+ "name": "ctype_alnum",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "text",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "filectime",
- "sub_area": "Filesystem"
+ "slug": "ctype_alnum",
+ "sub_area": "Ctype"
},
{
- "area": "Filesystem",
- "canonical_name": "file_exists",
- "description": "Lowers `file_exists(path)` through the target-aware runtime stat helper.",
+ "area": "Type",
+ "canonical_name": "ctype_alpha",
+ "description": "Checks if all characters in the string are alphabetic.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_file_exists",
- "codegen_line": 3756,
+ "codegen_file": "src/codegen/lower_inst/builtins/ctype.rs",
+ "codegen_function": "lower_ctype_alpha",
+ "codegen_line": 20,
"notes": [
- "Lowers `file_exists(path)` through the target-aware runtime stat helper."
- ],
- "runtime_helpers": [
- "__rt_mkdir",
- "__rt_unlink"
+ "Lowers `ctype_alpha(string)` by checking every byte against ASCII alpha ranges."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ctype_alpha.rs",
"sig_line": null
},
- "name": "file_exists",
+ "name": "ctype_alpha",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "text",
"optional": false,
"type": "string"
}
@@ -4735,942 +4680,793 @@
"return_type": "bool",
"variadic": null
},
- "slug": "file_exists",
- "sub_area": "Filesystem"
+ "slug": "ctype_alpha",
+ "sub_area": "Ctype"
},
{
- "area": "Filesystem",
- "canonical_name": "filegroup",
- "description": "Lowers `filegroup(path)` and boxes the runtime integer-or-false result.",
+ "area": "Type",
+ "canonical_name": "ctype_digit",
+ "description": "Checks if all characters in the string are digits.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_filegroup",
- "codegen_line": 4857,
+ "codegen_file": "src/codegen/lower_inst/builtins/ctype.rs",
+ "codegen_function": "lower_ctype_digit",
+ "codegen_line": 25,
"notes": [
- "Lowers `filegroup(path)` and boxes the runtime integer-or-false result."
- ],
- "runtime_helpers": [
- "__rt_filegroup",
- "__rt_fileinode",
- "__rt_filetype",
- "__rt_stat_array"
+ "Lowers `ctype_digit(string)` by checking every byte against the ASCII digit range."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ctype_digit.rs",
"sig_line": null
},
- "name": "filegroup",
+ "name": "ctype_digit",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "text",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "filegroup",
- "sub_area": "Filesystem"
+ "slug": "ctype_digit",
+ "sub_area": "Ctype"
},
{
- "area": "IO",
- "canonical_name": "file_get_contents",
- "description": "Lowers `file_get_contents(path)` and boxes the runtime string-or-false result.",
+ "area": "Type",
+ "canonical_name": "ctype_space",
+ "description": "Checks if all characters in the string are whitespace characters.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_file_get_contents",
- "codegen_line": 39,
+ "codegen_file": "src/codegen/lower_inst/builtins/ctype.rs",
+ "codegen_function": "lower_ctype_space",
+ "codegen_line": 35,
"notes": [
- "Lowers `file_get_contents(path)` and boxes the runtime string-or-false result."
- ],
- "runtime_helpers": [
- "__rt_file_get_contents_maybe_url",
- "__rt_php_input"
+ "Lowers `ctype_space(string)` by checking every byte against PHP's ASCII whitespace set."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ctype_space.rs",
"sig_line": null
},
- "name": "file_get_contents",
+ "name": "ctype_space",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "text",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "use_include_path",
- "optional": false,
- "type": "bool"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "offset",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "file_get_contents",
- "sub_area": "IO"
+ "slug": "ctype_space",
+ "sub_area": "Ctype"
},
{
- "area": "Filesystem",
- "canonical_name": "fileinode",
- "description": "Lowers `fileinode(path)` and boxes the runtime integer-or-false result.",
+ "area": "Date",
+ "canonical_name": "date",
+ "description": "Formats a local time/date.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fileinode",
- "codegen_line": 4865,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_date",
+ "codegen_line": 22,
"notes": [
- "Lowers `fileinode(path)` and boxes the runtime integer-or-false result."
+ "Lowers `date(format, timestamp?)` through the shared formatter runtime helper."
],
"runtime_helpers": [
- "__rt_fileinode",
- "__rt_filetype",
- "__rt_lstat_array",
- "__rt_stat_array"
+ "__rt_date",
+ "__rt_gmdate"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/date.rs",
"sig_line": null
},
- "name": "fileinode",
+ "name": "date",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "format",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "timestamp",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "fileinode",
- "sub_area": "Filesystem"
+ "slug": "date",
+ "sub_area": "Date"
},
{
- "area": "IO",
- "canonical_name": "file_put_contents",
- "description": "Lowers `file_put_contents(path, data)` through the target-aware runtime writer.",
+ "area": "Date",
+ "canonical_name": "date_default_timezone_get",
+ "description": "Gets the default timezone.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_file_put_contents",
- "codegen_line": 3502,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_date_default_timezone_get",
+ "codegen_line": 70,
"notes": [
- "Lowers `file_put_contents(path, data)` through the target-aware runtime writer."
+ "Lowers `date_default_timezone_get()` through the shared runtime helper.",
+ "Takes no arguments; `__rt_date_default_timezone_get` returns the stored timezone",
+ "identifier (or the literal `\"UTC\"` when none was set) in the string-result registers."
],
"runtime_helpers": [
- "__rt_file_put_contents",
- "__rt_file_put_contents_maybe_phar"
+ "__rt_date_default_timezone_get",
+ "__rt_date_default_timezone_set"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/date_default_timezone_get.rs",
"sig_line": null
},
- "name": "file_put_contents",
+ "name": "date_default_timezone_get",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "filename",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "data",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": "0",
- "name": "flags",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": "null",
- "name": "context",
- "optional": true,
- "type": "mixed"
- }
- ],
- "return_type": "int",
+ "params": [],
+ "return_type": "string",
"variadic": null
},
- "slug": "file_put_contents",
- "sub_area": "IO"
+ "slug": "date_default_timezone_get",
+ "sub_area": "Date"
},
{
- "area": "Filesystem",
- "canonical_name": "fileowner",
- "description": "Lowers `fileowner(path)` and boxes the runtime integer-or-false result.",
+ "area": "Date",
+ "canonical_name": "date_default_timezone_set",
+ "description": "Sets the default timezone.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fileowner",
- "codegen_line": 4849,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_date_default_timezone_set",
+ "codegen_line": 84,
"notes": [
- "Lowers `fileowner(path)` and boxes the runtime integer-or-false result."
+ "Lowers `date_default_timezone_set(timezoneId)` through the shared runtime helper.",
+ "Materializes the identifier string into the registers the helper reads (ptr/len in",
+ "`x1`/`x2` on ARM64, `rax`/`rdx` on x86_64), then `__rt_date_default_timezone_set`",
+ "applies it via libc `putenv`+`tzset` and returns PHP `true` in the integer-result register."
],
"runtime_helpers": [
- "__rt_filegroup",
- "__rt_fileinode",
- "__rt_fileowner"
+ "__rt_date_default_timezone_set",
+ "__rt_microtime",
+ "__rt_microtime_mixed",
+ "__rt_microtime_str"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/date_default_timezone_set.rs",
"sig_line": null
},
- "name": "fileowner",
+ "name": "date_default_timezone_set",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "timezoneId",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fileowner",
- "sub_area": "Filesystem"
+ "slug": "date_default_timezone_set",
+ "sub_area": "Date"
},
{
- "area": "Filesystem",
- "canonical_name": "fileperms",
- "description": "Lowers `fileperms(path)` and boxes the runtime integer-or-false result.",
+ "area": "Misc",
+ "canonical_name": "define",
+ "description": "Defines a named constant at runtime.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fileperms",
- "codegen_line": 4841,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_define",
+ "codegen_line": 82,
"notes": [
- "Lowers `fileperms(path)` and boxes the runtime integer-or-false result."
- ],
- "runtime_helpers": [
- "__rt_filegroup",
- "__rt_fileinode",
- "__rt_fileowner",
- "__rt_fileperms"
+ "Lowers `define(\"NAME\", value)` with the duplicate-name runtime guard."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/define.rs",
"sig_line": null
},
- "name": "fileperms",
+ "name": "define",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "constant_name",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "mixed"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fileperms",
- "sub_area": "Filesystem"
+ "slug": "define",
+ "sub_area": "Constants"
},
{
- "area": "Filesystem",
- "canonical_name": "filetype",
- "description": "Lowers `filetype(path)` and boxes the runtime string-or-false result.",
+ "area": "Misc",
+ "canonical_name": "defined",
+ "description": "Checks whether a given named constant exists.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_filetype",
- "codegen_line": 4873,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_defined",
+ "codegen_line": 261,
"notes": [
- "Lowers `filetype(path)` and boxes the runtime string-or-false result."
- ],
- "runtime_helpers": [
- "__rt_filetype",
- "__rt_lstat_array",
- "__rt_stat_array"
+ "Lowers `defined(\"NAME\")` for compile-time string constant names."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/defined.rs",
"sig_line": null
},
- "name": "filetype",
+ "name": "defined",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "constant_name",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "filetype",
- "sub_area": "Filesystem"
+ "slug": "defined",
+ "sub_area": "Constants"
},
{
- "area": "Filesystem",
- "canonical_name": "filemtime",
- "description": "Lowers `filemtime(path)` through the target-aware runtime stat helper.",
+ "area": "Math",
+ "canonical_name": "deg2rad",
+ "description": "Converts a degree value to radians.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_filemtime",
- "codegen_line": 4789,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_deg2rad",
+ "codegen_line": 75,
"notes": [
- "Lowers `filemtime(path)` through the target-aware runtime stat helper."
- ],
- "runtime_helpers": [
- "__rt_filemtime",
- "__rt_link",
- "__rt_linkinfo",
- "__rt_readlink",
- "__rt_symlink"
+ "Lowers `deg2rad()` by multiplying with `PI / 180`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/deg2rad.rs",
"sig_line": null
},
- "name": "filemtime",
+ "name": "deg2rad",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "num",
"optional": false,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "filemtime",
- "sub_area": "Filesystem"
+ "slug": "deg2rad",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "filesize",
- "description": "Lowers `filesize(path)` through the target-aware runtime stat helper.",
+ "area": "Process",
+ "canonical_name": "die",
+ "description": "",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_filesize",
- "codegen_line": 4781,
- "notes": [
- "Lowers `filesize(path)` through the target-aware runtime stat helper."
- ],
- "runtime_helpers": [
- "__rt_filemtime",
- "__rt_link",
- "__rt_linkinfo",
- "__rt_symlink"
- ],
+ "codegen_file": null,
+ "codegen_function": null,
+ "codegen_line": null,
+ "notes": [],
+ "runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "filesize",
+ "name": "die",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
- "optional": false,
- "type": "string"
+ "name": "status",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
+ "return_type": "void",
"variadic": null
},
- "slug": "filesize",
- "sub_area": "Filesystem"
+ "slug": "die",
+ "sub_area": "Process"
},
{
- "area": "Type",
- "canonical_name": "floatval",
- "description": "Lowers `floatval()` for concrete scalar operands.",
+ "area": "Filesystem",
+ "canonical_name": "dirname",
+ "description": "Returns a parent directory's path.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_floatval",
- "codegen_line": 1034,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_dirname",
+ "codegen_line": 4575,
"notes": [
- "Lowers `floatval()` for concrete scalar operands."
+ "Lowers `dirname(path, levels?)` through the target-aware runtime helper."
],
"runtime_helpers": [
- "__rt_str_to_number"
+ "__rt_dirname",
+ "__rt_dirname_levels"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/dirname.rs",
"sig_line": null
},
- "name": "floatval",
+ "name": "dirname",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "path",
"optional": false,
- "type": "mixed"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "1",
+ "name": "levels",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "floatval",
- "sub_area": "Casts"
+ "slug": "dirname",
+ "sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "floor",
- "description": "Lowers `floor()` for concrete integer-like and floating operands.",
+ "area": "Filesystem",
+ "canonical_name": "disk_free_space",
+ "description": "Returns available space on filesystem or disk partition.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_floor",
- "codegen_line": 70,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_disk_free_space",
+ "codegen_line": 3370,
"notes": [
- "Lowers `floor()` for concrete integer-like and floating operands."
+ "Lowers `disk_free_space(path)` through the shared disk-space runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_disk_space"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/disk_free_space.rs",
"sig_line": null
},
- "name": "floor",
+ "name": "disk_free_space",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "directory",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
"return_type": "float",
"variadic": null
},
- "slug": "floor",
- "sub_area": "Math"
+ "slug": "disk_free_space",
+ "sub_area": "Filesystem"
},
{
"area": "Filesystem",
- "canonical_name": "fnmatch",
- "description": "Lowers `fnmatch(pattern, filename, flags?)` through the target-aware runtime helper.",
+ "canonical_name": "disk_total_space",
+ "description": "Returns the total size of a filesystem or disk partition.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fnmatch",
- "codegen_line": 3960,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_disk_total_space",
+ "codegen_line": 3378,
"notes": [
- "Lowers `fnmatch(pattern, filename, flags?)` through the target-aware runtime helper."
+ "Lowers `disk_total_space(path)` through the shared disk-space runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_disk_space"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/disk_total_space.rs",
"sig_line": null
},
- "name": "fnmatch",
+ "name": "disk_total_space",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
+ "name": "directory",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "filename",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": true,
- "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "float",
"variadic": null
},
- "slug": "fnmatch",
+ "slug": "disk_total_space",
"sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "fmod",
- "description": "Lowers `fmod()` for concrete integer-like and floating operands.",
+ "area": "Misc",
+ "canonical_name": "empty",
+ "description": "Determines whether a variable is considered empty.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/binary.rs",
- "codegen_function": "lower_fmod",
- "codegen_line": 85,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_empty",
+ "codegen_line": 618,
"notes": [
- "Lowers `fmod()` for concrete integer-like and floating operands."
+ "Lowers `empty()` for concrete scalar and array-like operands."
+ ],
+ "runtime_helpers": [
+ "__rt_mixed_is_empty"
],
- "runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "fmod",
+ "name": "empty",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num1",
- "optional": false,
- "type": "float"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "num2",
+ "name": "value",
"optional": false,
- "type": "float"
+ "type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fmod",
- "sub_area": "Math"
+ "slug": "empty",
+ "sub_area": "Variable"
},
{
- "area": "IO",
- "canonical_name": "fopen",
- "description": "Lowers `fopen(filename, mode)` and boxes stream resources or PHP false.",
+ "area": "Class",
+ "canonical_name": "enum_exists",
+ "description": "Checks if the enum has been defined.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fopen",
- "codegen_line": 233,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_class_like_exists",
+ "codegen_line": 293,
"notes": [
- "Lowers `fopen(filename, mode)` and boxes stream resources or PHP false."
- ],
- "runtime_helpers": [
- "__rt_tmpfile"
+ "Lowers AOT class/interface/enum existence checks for literal names."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/enum_exists.rs",
"sig_line": null
},
- "name": "fopen",
+ "name": "enum_exists",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "mode",
+ "name": "enum",
"optional": false,
"type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "use_include_path",
+ "default": "true",
+ "name": "autoload",
"optional": true,
"type": "bool"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": true,
- "type": "mixed"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fopen",
- "sub_area": "IO"
+ "slug": "enum_exists",
+ "sub_area": "Class"
},
{
- "area": "IO",
- "canonical_name": "fpassthru",
- "description": "Lowers `fpassthru(stream)` through the remaining-bytes stream runtime helper.",
+ "area": "Process",
+ "canonical_name": "exec",
+ "description": "Executes an external program and returns the last line of output.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fpassthru",
- "codegen_line": 2806,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_exec",
+ "codegen_line": 690,
"notes": [
- "Lowers `fpassthru(stream)` through the remaining-bytes stream runtime helper."
- ],
- "runtime_helpers": [
- "__rt_feof",
- "__rt_fpassthru"
+ "Lowers `exec(command)` by capturing shell stdout through the shared runtime helper."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/exec.rs",
"sig_line": null
},
- "name": "fpassthru",
+ "name": "exec",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "command",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "fpassthru",
- "sub_area": "IO"
+ "slug": "exec",
+ "sub_area": "Process"
},
{
- "area": "IO",
- "canonical_name": "fprintf",
- "description": "Lowers `fprintf(stream, format, values...)` as `sprintf()` plus stream write.",
+ "area": "Process",
+ "canonical_name": "exit",
+ "description": "",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fprintf",
- "codegen_line": 2641,
- "notes": [
- "Lowers `fprintf(stream, format, values...)` as `sprintf()` plus stream write."
- ],
- "runtime_helpers": [
- "__rt_sprintf"
- ],
+ "codegen_file": null,
+ "codegen_function": null,
+ "codegen_line": null,
+ "notes": [],
+ "runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "fprintf",
+ "name": "exit",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "format",
- "optional": false,
- "type": "string"
+ "name": "status",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
- "variadic": "values"
+ "return_type": "void",
+ "variadic": null
},
- "slug": "fprintf",
- "sub_area": "IO"
+ "slug": "exit",
+ "sub_area": "Process"
},
{
- "area": "IO",
- "canonical_name": "fputcsv",
- "description": "Lowers `fputcsv(stream, fields, separator?, enclosure?)` for string arrays.",
+ "area": "Math",
+ "canonical_name": "exp",
+ "description": "Returns e raised to the power of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fputcsv",
- "codegen_line": 2784,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `fputcsv(stream, fields, separator?, enclosure?)` for string arrays."
- ],
- "runtime_helpers": [
- "__rt_fputcsv"
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/exp.rs",
"sig_line": null
},
- "name": "fputcsv",
+ "name": "exp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "fields",
+ "name": "num",
"optional": false,
- "type": "array"
- },
- {
- "by_ref": false,
- "default": "','",
- "name": "separator",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": "'\"'",
- "name": "enclosure",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": "'\\\\'",
- "name": "escape",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": "'\\n'",
- "name": "eol",
- "optional": true,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "fputcsv",
- "sub_area": "IO"
+ "slug": "exp",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "fread",
- "description": "Lowers `fread(stream, length)` using the shared runtime file-read helper.",
+ "area": "String",
+ "canonical_name": "explode",
+ "description": "Splits a string by a separator into an array of substrings.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fread",
- "codegen_line": 2595,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_explode",
+ "codegen_line": 151,
"notes": [
- "Lowers `fread(stream, length)` using the shared runtime file-read helper."
+ "Lowers `explode(delimiter, string)` into the shared string-array splitter helper."
],
"runtime_helpers": [
- "__rt_fread"
+ "__rt_explode",
+ "__rt_sscanf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/explode.rs",
"sig_line": null
},
- "name": "fread",
+ "name": "explode",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "separator",
"optional": false,
- "type": "resource"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "length",
+ "name": "string",
"optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "PHP_INT_MAX",
+ "name": "limit",
+ "optional": true,
"type": "int"
}
],
- "return_type": "string",
+ "return_type": "array",
"variadic": null
},
- "slug": "fread",
- "sub_area": "IO"
+ "slug": "explode",
+ "sub_area": "String"
},
{
- "area": "Filesystem",
- "canonical_name": "readfile",
- "description": "Lowers `readfile(path)` and boxes the runtime byte-count-or-false result.",
+ "area": "IO",
+ "canonical_name": "fclose",
+ "description": "Closes an open file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_readfile",
- "codegen_line": 193,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fclose",
+ "codegen_line": 2707,
"notes": [
- "Lowers `readfile(path)` and boxes the runtime byte-count-or-false result."
+ "Lowers `fclose(stream)` after validating and unboxing the stream handle."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fclose.rs",
"sig_line": null
},
- "name": "readfile",
+ "name": "fclose",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "stream",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "use_include_path",
- "optional": false,
- "type": "bool"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": false,
- "type": "mixed"
+ "type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "readfile",
- "sub_area": "Filesystem"
+ "slug": "fclose",
+ "sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "fstat",
- "description": "Lowers `fstat(stream)` and boxes the runtime stat array or PHP false result.",
+ "canonical_name": "fdatasync",
+ "description": "Synchronizes data (but not meta-data) to file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fstat",
- "codegen_line": 4896,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fdatasync",
+ "codegen_line": 3304,
"notes": [
- "Lowers `fstat(stream)` and boxes the runtime stat array or PHP false result."
+ "Lowers `fdatasync(stream)` through the shared fd data-sync runtime helper."
],
"runtime_helpers": [
- "__rt_fstat_array"
+ "__rt_fdatasync"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fdatasync.rs",
"sig_line": null
},
- "name": "fstat",
+ "name": "fdatasync",
"sig": {
"params": [
{
@@ -5681,87 +5477,80 @@
"type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "fstat",
+ "slug": "fdatasync",
"sub_area": "IO"
},
{
- "area": "IO",
- "canonical_name": "fseek",
- "description": "Lowers `fseek(stream, offset, whence?)` and clears EOF state on success.",
+ "area": "Math",
+ "canonical_name": "fdiv",
+ "description": "Divides two numbers, according to IEEE 754.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fseek",
- "codegen_line": 2951,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/binary.rs",
+ "codegen_function": "lower_fdiv",
+ "codegen_line": 67,
"notes": [
- "Lowers `fseek(stream, offset, whence?)` and clears EOF state on success."
+ "Lowers `fdiv()` for concrete integer-like and floating operands."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/fdiv.rs",
"sig_line": null
},
- "name": "fseek",
+ "name": "fdiv",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "num1",
"optional": false,
- "type": "resource"
+ "type": "float"
},
{
"by_ref": false,
"default": null,
- "name": "offset",
+ "name": "num2",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "whence",
- "optional": true,
- "type": "int"
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "fseek",
- "sub_area": "IO"
+ "slug": "fdiv",
+ "sub_area": "Math"
},
{
"area": "IO",
- "canonical_name": "fsync",
- "description": "Lowers `fsync(stream)` through the shared fd sync runtime helper.",
+ "canonical_name": "feof",
+ "description": "Tests for end-of-file on a file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fsync",
- "codegen_line": 3040,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_feof",
+ "codegen_line": 3121,
"notes": [
- "Lowers `fsync(stream)` through the shared fd sync runtime helper."
+ "Lowers `feof(stream)` through the runtime EOF-flag table helper."
],
"runtime_helpers": [
- "__rt_fflush",
- "__rt_fsync"
+ "__rt_feof",
+ "__rt_user_wrapper_ftell"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/feof.rs",
"sig_line": null
},
- "name": "fsync",
+ "name": "feof",
"sig": {
"params": [
{
@@ -5775,32 +5564,32 @@
"return_type": "bool",
"variadic": null
},
- "slug": "fsync",
+ "slug": "feof",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "ftell",
- "description": "Lowers `ftell(stream)` as `lseek(fd, 0, SEEK_CUR)`.",
+ "canonical_name": "fflush",
+ "description": "Flushes the output to a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_ftell",
- "codegen_line": 2912,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fflush",
+ "codegen_line": 3266,
"notes": [
- "Lowers `ftell(stream)` as `lseek(fd, 0, SEEK_CUR)`."
+ "Lowers `fflush(stream)` through the shared fd flush runtime helper."
],
"runtime_helpers": [
- "__rt_user_wrapper_ftell"
+ "__rt_fflush"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fflush.rs",
"sig_line": null
},
- "name": "ftell",
+ "name": "fflush",
"sig": {
"params": [
{
@@ -5811,33 +5600,36 @@
"type": "resource"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "ftell",
+ "slug": "fflush",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "ftruncate",
- "description": "Lowers `ftruncate(stream, size)` through the shared fd truncate runtime helper.",
+ "canonical_name": "fgetc",
+ "description": "Gets a character from the given file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_ftruncate",
- "codegen_line": 2989,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fgetc",
+ "codegen_line": 2980,
"notes": [
- "Lowers `ftruncate(stream, size)` through the shared fd truncate runtime helper."
+ "Lowers `fgetc(stream)` and boxes the one-byte string or PHP false result."
+ ],
+ "runtime_helpers": [
+ "__rt_fgetc",
+ "__rt_fgetcsv"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fgetc.rs",
"sig_line": null
},
- "name": "ftruncate",
+ "name": "fgetc",
"sig": {
"params": [
{
@@ -5846,143 +5638,138 @@
"name": "stream",
"optional": false,
"type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "size",
- "optional": false,
- "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ftruncate",
+ "slug": "fgetc",
"sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_alias",
- "description": "Lowers the defensive `class_alias()` fallback that remains after AOT alias extraction.",
+ "area": "IO",
+ "canonical_name": "fgetcsv",
+ "description": "Gets line from file pointer and parse for CSV fields.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
- "codegen_function": "lower_class_alias",
- "codegen_line": 41,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fgetcsv",
+ "codegen_line": 2993,
"notes": [
- "Lowers the defensive `class_alias()` fallback that remains after AOT alias extraction."
+ "Lowers `fgetcsv(stream, separator?, enclosure?)` through the CSV row runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_fgetcsv",
+ "__rt_fputcsv"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fgetcsv.rs",
"sig_line": null
},
- "name": "class_alias",
+ "name": "fgetcsv",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
- "default": null,
- "name": "alias",
- "optional": false,
- "type": "string"
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
},
{
"by_ref": false,
- "default": null,
- "name": "autoload",
+ "default": "','",
+ "name": "separator",
"optional": true,
- "type": "bool"
+ "type": "string"
}
],
- "return_type": "bool",
+ "return_type": "array",
"variadic": null
},
- "slug": "class_alias",
- "sub_area": "Class"
+ "slug": "fgetcsv",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_attribute_args",
- "description": "Lowers `class_attribute_args(class, attr)` into an indexed Mixed array.",
+ "area": "IO",
+ "canonical_name": "fgets",
+ "description": "Gets line from file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/attributes.rs",
- "codegen_function": "lower_class_attribute_args",
- "codegen_line": 52,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fgets",
+ "codegen_line": 2967,
"notes": [
- "Lowers `class_attribute_args(class, attr)` into an indexed Mixed array."
+ "Lowers `fgets(stream)` through the shared line-read runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_fgetc",
+ "__rt_fgets"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fgets.rs",
"sig_line": null
},
- "name": "class_attribute_args",
+ "name": "fgets",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class_name",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "attribute_name",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
}
],
- "return_type": "array",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "class_attribute_args",
- "sub_area": "Attributes"
+ "slug": "fgets",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_attribute_names",
- "description": "Lowers `class_attribute_names(class)` into an indexed string array.",
+ "area": "IO",
+ "canonical_name": "file",
+ "description": "Reads an entire file into an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/attributes.rs",
- "codegen_function": "lower_class_attribute_names",
- "codegen_line": 36,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_file",
+ "codegen_line": 3685,
"notes": [
- "Lowers `class_attribute_names(class)` into an indexed string array."
+ "Lowers `file(path)` through the target-aware runtime line-array helper."
+ ],
+ "runtime_helpers": [
+ "__rt_file",
+ "__rt_realpath"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/file.rs",
"sig_line": null
},
- "name": "class_attribute_names",
+ "name": "file",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class_name",
+ "name": "filename",
"optional": false,
"type": "string"
}
@@ -5990,78 +5777,79 @@
"return_type": "array",
"variadic": null
},
- "slug": "class_attribute_names",
- "sub_area": "Attributes"
+ "slug": "file",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_exists",
- "description": "Checks whether the given class has been defined.",
+ "area": "Filesystem",
+ "canonical_name": "file_exists",
+ "description": "Checks whether a file or directory exists.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_file_exists",
+ "codegen_line": 4399,
+ "notes": [
+ "Lowers `file_exists(path)` through the target-aware runtime stat helper."
+ ],
+ "runtime_helpers": [
+ "__rt_mkdir",
+ "__rt_unlink"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/file_exists.rs",
"sig_line": null
},
- "name": "class_exists",
+ "name": "file_exists",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class",
+ "name": "filename",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "class_exists",
- "sub_area": "Class"
+ "slug": "file_exists",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "class_get_attributes",
- "description": "Lowers `class_get_attributes(class)` into an array of `ReflectionAttribute` objects.",
+ "area": "IO",
+ "canonical_name": "file_get_contents",
+ "description": "Reads an entire file into a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/attributes.rs",
- "codegen_function": "lower_class_get_attributes",
- "codegen_line": 68,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_file_get_contents",
+ "codegen_line": 39,
"notes": [
- "Lowers `class_get_attributes(class)` into an array of `ReflectionAttribute` objects."
+ "Lowers `file_get_contents(path)` and boxes the runtime string-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_file_get_contents_maybe_url",
+ "__rt_php_input"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/file_get_contents.rs",
"sig_line": null
},
- "name": "class_get_attributes",
+ "name": "file_get_contents",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class_name",
+ "name": "filename",
"optional": false,
"type": "string"
}
@@ -6069,476 +5857,513 @@
"return_type": "mixed",
"variadic": null
},
- "slug": "class_get_attributes",
- "sub_area": "Attributes"
+ "slug": "file_get_contents",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_implements",
- "description": "",
+ "area": "IO",
+ "canonical_name": "file_put_contents",
+ "description": "Writes data to a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_file_put_contents",
+ "codegen_line": 3727,
+ "notes": [
+ "Lowers `file_put_contents(path, data)` through the target-aware runtime writer."
+ ],
+ "runtime_helpers": [
+ "__rt_file_put_contents",
+ "__rt_file_put_contents_maybe_phar"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/file_put_contents.rs",
"sig_line": null
},
- "name": "class_implements",
+ "name": "file_put_contents",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
+ "name": "filename",
"optional": false,
- "type": "mixed"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
+ "name": "data",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "class_implements",
- "sub_area": "Class"
+ "slug": "file_put_contents",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "class_parents",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "fileatime",
+ "description": "Gets last access time of file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fileatime",
+ "codegen_line": 5468,
+ "notes": [
+ "Lowers `fileatime(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_fileatime",
+ "__rt_filectime",
+ "__rt_fileowner",
+ "__rt_fileperms"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/fileatime.rs",
"sig_line": null
},
- "name": "class_parents",
+ "name": "fileatime",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
+ "name": "filename",
"optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
+ "type": "string"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "class_parents",
- "sub_area": "Class"
+ "slug": "fileatime",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "class_uses",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "filectime",
+ "description": "Gets inode change time of file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_filectime",
+ "codegen_line": 5476,
+ "notes": [
+ "Lowers `filectime(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_filectime",
+ "__rt_filegroup",
+ "__rt_fileowner",
+ "__rt_fileperms"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/filectime.rs",
"sig_line": null
},
- "name": "class_uses",
+ "name": "filectime",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
+ "name": "filename",
"optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
+ "type": "string"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "class_uses",
- "sub_area": "Class"
+ "slug": "filectime",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "enum_exists",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "filegroup",
+ "description": "Gets file group.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_filegroup",
+ "codegen_line": 5500,
+ "notes": [
+ "Lowers `filegroup(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_filegroup",
+ "__rt_fileinode",
+ "__rt_filetype",
+ "__rt_stat_array"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/filegroup.rs",
"sig_line": null
},
- "name": "enum_exists",
+ "name": "filegroup",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "enum",
+ "name": "filename",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "enum_exists",
- "sub_area": "Class"
+ "slug": "filegroup",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "function_exists",
- "description": "Lowers `function_exists(\"name\")` for compile-time string names.",
+ "area": "Filesystem",
+ "canonical_name": "fileinode",
+ "description": "Gets file inode.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_function_exists",
- "codegen_line": 759,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fileinode",
+ "codegen_line": 5508,
"notes": [
- "Lowers `function_exists(\"name\")` for compile-time string names.",
- "Recognizes user functions, externs, catalog builtins, and the date/time procedural aliases",
- "that `name_resolver` desugars (including the injected timezone-introspection prelude",
- "functions). The aliases are matched through `is_date_procedural_alias` rather than the catalog",
- "because their call sites are rewritten before codegen, so they never reach the builtin catalog",
- "yet must still report as existing to match PHP."
+ "Lowers `fileinode(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_fileinode",
+ "__rt_filetype",
+ "__rt_lstat_array",
+ "__rt_stat_array"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fileinode.rs",
"sig_line": null
},
- "name": "function_exists",
+ "name": "fileinode",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "function",
+ "name": "filename",
"optional": false,
"type": "string"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "function_exists",
- "sub_area": "Class"
+ "slug": "fileinode",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "get_class",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "filemtime",
+ "description": "Gets file modification time.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_filemtime",
+ "codegen_line": 5432,
+ "notes": [
+ "Lowers `filemtime(path)` through the target-aware runtime stat helper."
+ ],
+ "runtime_helpers": [
+ "__rt_filemtime",
+ "__rt_link",
+ "__rt_linkinfo",
+ "__rt_readlink",
+ "__rt_symlink"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/filemtime.rs",
"sig_line": null
},
- "name": "get_class",
+ "name": "filemtime",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object",
- "optional": true,
- "type": "object"
+ "name": "filename",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "string",
+ "return_type": "int",
"variadic": null
},
- "slug": "get_class",
- "sub_area": "Class"
+ "slug": "filemtime",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "get_parent_class",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "fileowner",
+ "description": "Gets file owner.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fileowner",
+ "codegen_line": 5492,
+ "notes": [
+ "Lowers `fileowner(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_filegroup",
+ "__rt_fileinode",
+ "__rt_fileowner"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/fileowner.rs",
"sig_line": null
},
- "name": "get_parent_class",
+ "name": "fileowner",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
- "optional": true,
- "type": "mixed"
+ "name": "filename",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "get_parent_class",
- "sub_area": "Class"
+ "slug": "fileowner",
+ "sub_area": "Filesystem"
},
{
- "area": "Type",
- "canonical_name": "get_resource_id",
- "description": "Lowers `get_resource_id(resource)` by unboxing the native handle and making it one-based.",
+ "area": "Filesystem",
+ "canonical_name": "fileperms",
+ "description": "Gets file permissions.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
- "codegen_function": "lower_get_resource_id",
- "codegen_line": 424,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fileperms",
+ "codegen_line": 5484,
"notes": [
- "Lowers `get_resource_id(resource)` by unboxing the native handle and making it one-based."
+ "Lowers `fileperms(path)` and boxes the runtime integer-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_filegroup",
+ "__rt_fileinode",
+ "__rt_fileowner",
+ "__rt_fileperms"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fileperms.rs",
"sig_line": null
},
- "name": "get_resource_id",
+ "name": "fileperms",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "resource",
+ "name": "filename",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "get_resource_id",
- "sub_area": "Type"
+ "slug": "fileperms",
+ "sub_area": "Filesystem"
},
{
- "area": "Type",
- "canonical_name": "get_resource_type",
- "description": "Lowers `get_resource_type(resource)` to elephc's current resource type label.",
+ "area": "Filesystem",
+ "canonical_name": "filesize",
+ "description": "Gets file size.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
- "codegen_function": "lower_get_resource_type",
- "codegen_line": 412,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_filesize",
+ "codegen_line": 5424,
"notes": [
- "Lowers `get_resource_type(resource)` to elephc's current resource type label."
+ "Lowers `filesize(path)` through the target-aware runtime stat helper."
+ ],
+ "runtime_helpers": [
+ "__rt_filemtime",
+ "__rt_link",
+ "__rt_linkinfo",
+ "__rt_symlink"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/filesize.rs",
"sig_line": null
},
- "name": "get_resource_type",
+ "name": "filesize",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "resource",
+ "name": "filename",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "string",
- "variadic": null
- },
- "slug": "get_resource_type",
- "sub_area": "Type"
- },
- {
- "area": "Class",
- "canonical_name": "get_declared_classes",
- "description": "",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": "src/types/signatures.rs",
- "sig_line": null
- },
- "name": "get_declared_classes",
- "sig": {
- "params": [],
- "return_type": "array",
+ "return_type": "int",
"variadic": null
},
- "slug": "get_declared_classes",
- "sub_area": "Class"
+ "slug": "filesize",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "get_declared_interfaces",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "filetype",
+ "description": "Gets file type.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_filetype",
+ "codegen_line": 5516,
+ "notes": [
+ "Lowers `filetype(path)` and boxes the runtime string-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_filetype",
+ "__rt_lstat_array",
+ "__rt_stat_array"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/filetype.rs",
"sig_line": null
},
- "name": "get_declared_interfaces",
+ "name": "filetype",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "filename",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
"variadic": null
},
- "slug": "get_declared_interfaces",
- "sub_area": "Class"
+ "slug": "filetype",
+ "sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "get_declared_traits",
- "description": "",
+ "area": "Type",
+ "canonical_name": "floatval",
+ "description": "Returns the float value of a variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_floatval",
+ "codegen_line": 556,
+ "notes": [
+ "Lowers `floatval()` for concrete scalar operands."
+ ],
+ "runtime_helpers": [
+ "__rt_str_to_number"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/floatval.rs",
"sig_line": null
},
- "name": "get_declared_traits",
+ "name": "floatval",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "mixed"
+ }
+ ],
+ "return_type": "float",
"variadic": null
},
- "slug": "get_declared_traits",
- "sub_area": "Class"
+ "slug": "floatval",
+ "sub_area": "Casts"
},
{
- "area": "Class",
- "canonical_name": "interface_exists",
- "description": "",
+ "area": "IO",
+ "canonical_name": "flock",
+ "description": "Portable advisory file locking.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_flock",
+ "codegen_line": 3309,
+ "notes": [
+ "Lowers `flock(stream, operation, would_block?)` through the libc flock wrapper."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/flock.rs",
"sig_line": null
},
- "name": "interface_exists",
+ "name": "flock",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "interface",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "autoload",
+ "name": "operation",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": true,
+ "default": "null",
+ "name": "would_block",
"optional": true,
"type": "bool"
}
@@ -6546,708 +6371,2078 @@
"return_type": "bool",
"variadic": null
},
- "slug": "interface_exists",
- "sub_area": "Class"
+ "slug": "flock",
+ "sub_area": "IO"
},
{
- "area": "Class",
- "canonical_name": "trait_exists",
- "description": "",
+ "area": "Math",
+ "canonical_name": "floor",
+ "description": "Rounds a number down to the nearest integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_floor",
+ "codegen_line": 70,
+ "notes": [
+ "Lowers `floor()` for concrete integer-like and floating operands."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/math/floor.rs",
"sig_line": null
},
- "name": "trait_exists",
+ "name": "floor",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "trait",
+ "name": "num",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "autoload",
- "optional": true,
- "type": "bool"
+ "type": "float"
}
],
- "return_type": "bool",
+ "return_type": "float",
"variadic": null
},
- "slug": "trait_exists",
- "sub_area": "Class"
+ "slug": "floor",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "fwrite",
- "description": "Lowers `fwrite(stream, data)` and returns the number of bytes written.",
+ "area": "Math",
+ "canonical_name": "fmod",
+ "description": "Returns the floating point remainder of the division of the arguments.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_fwrite",
- "codegen_line": 2617,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/binary.rs",
+ "codegen_function": "lower_fmod",
+ "codegen_line": 92,
"notes": [
- "Lowers `fwrite(stream, data)` and returns the number of bytes written."
- ],
- "runtime_helpers": [
- "__rt_fwrite"
+ "Lowers `fmod()` for concrete integer-like and floating operands."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/fmod.rs",
"sig_line": null
},
- "name": "fwrite",
+ "name": "fmod",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "num1",
"optional": false,
- "type": "resource"
+ "type": "float"
},
{
"by_ref": false,
"default": null,
- "name": "data",
+ "name": "num2",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "fwrite",
- "sub_area": "IO"
+ "slug": "fmod",
+ "sub_area": "Math"
},
{
"area": "Filesystem",
- "canonical_name": "getcwd",
- "description": "Lowers `getcwd()` through the target-aware runtime helper.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_getcwd",
- "codegen_line": 4753,
- "notes": [
- "Lowers `getcwd()` through the target-aware runtime helper."
- ],
- "runtime_helpers": [
- "__rt_getcwd",
- "__rt_tmpfile"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "getcwd",
- "sig": {
- "params": [],
- "return_type": "string",
- "variadic": null
- },
- "slug": "getcwd",
- "sub_area": "Filesystem"
- },
- {
- "area": "Date",
- "canonical_name": "getdate",
- "description": "Lowers `getdate([$timestamp])` through the shared decomposition runtime helper.",
+ "canonical_name": "fnmatch",
+ "description": "Matches a filename against a pattern.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_getdate",
- "codegen_line": 183,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fnmatch",
+ "codegen_line": 4603,
"notes": [
- "Lowers `getdate([$timestamp])` through the shared decomposition runtime helper.",
- "Marshals the optional timestamp (the `-1` current-time sentinel when omitted; a boxed",
- "`Mixed`/`Union` argument is unboxed) into the integer result register where `__rt_getdate`",
- "reads it, then boxes the returned associative-array hash pointer into a `Mixed` cell \u2014 the same",
- "representation `stat`/`getdate` use, so the checker types the result `Mixed`."
- ],
- "runtime_helpers": [
- "__rt_getdate",
- "__rt_mixed_from_value"
+ "Lowers `fnmatch(pattern, filename, flags?)` through the target-aware runtime helper."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fnmatch.rs",
"sig_line": null
},
- "name": "getdate",
+ "name": "fnmatch",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "timestamp",
+ "name": "pattern",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "filename",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "flags",
"optional": true,
"type": "int"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "getdate",
- "sub_area": "Date"
+ "slug": "fnmatch",
+ "sub_area": "Filesystem"
},
{
- "area": "Filesystem",
- "canonical_name": "getenv",
- "description": "Lowers `getenv(name)` through the target-aware environment lookup helper.",
+ "area": "IO",
+ "canonical_name": "fopen",
+ "description": "Opens file or URL.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_getenv",
- "codegen_line": 645,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fopen",
+ "codegen_line": 340,
"notes": [
- "Lowers `getenv(name)` through the target-aware environment lookup helper."
+ "Lowers `fopen(filename, mode)` and boxes stream resources or PHP false."
],
"runtime_helpers": [
- "__rt_getenv"
+ "__rt_tmpfile"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fopen.rs",
"sig_line": null
},
- "name": "getenv",
+ "name": "fopen",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "name",
+ "name": "filename",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "local_only",
+ "name": "mode",
"optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "use_include_path",
+ "optional": true,
"type": "bool"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "context",
+ "optional": true,
+ "type": "mixed"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "getenv",
- "sub_area": "Filesystem"
+ "slug": "fopen",
+ "sub_area": "IO"
},
{
- "area": "Type",
- "canonical_name": "gettype",
- "description": "Lowers `gettype(value)` for statically concrete PHP types.",
+ "area": "IO",
+ "canonical_name": "fpassthru",
+ "description": "Output all remaining data on a file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_gettype",
- "codegen_line": 612,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fpassthru",
+ "codegen_line": 3027,
"notes": [
- "Lowers `gettype(value)` for statically concrete PHP types."
+ "Lowers `fpassthru(stream)` through the remaining-bytes stream runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_feof",
+ "__rt_fpassthru"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fpassthru.rs",
"sig_line": null
},
- "name": "gettype",
+ "name": "fpassthru",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "stream",
"optional": false,
- "type": "mixed"
+ "type": "resource"
}
],
- "return_type": "string",
+ "return_type": "int",
"variadic": null
},
- "slug": "gettype",
- "sub_area": "Type"
+ "slug": "fpassthru",
+ "sub_area": "IO"
},
{
- "area": "Filesystem",
- "canonical_name": "glob",
- "description": "Lowers `glob(pattern)` through the target-aware runtime glob expansion helper.",
+ "area": "IO",
+ "canonical_name": "fprintf",
+ "description": "Write a formatted string to a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_glob",
- "codegen_line": 3820,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fprintf",
+ "codegen_line": 2862,
"notes": [
- "Lowers `glob(pattern)` through the target-aware runtime glob expansion helper."
+ "Lowers `fprintf(stream, format, values...)` as `sprintf()` plus stream write."
],
"runtime_helpers": [
- "__rt_glob"
+ "__rt_sprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fprintf.rs",
"sig_line": null
},
- "name": "glob",
+ "name": "fprintf",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "flags",
+ "name": "format",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "array",
- "variadic": null
+ "return_type": "int",
+ "variadic": "values"
},
- "slug": "glob",
- "sub_area": "Filesystem"
+ "slug": "fprintf",
+ "sub_area": "IO"
},
{
- "area": "Date",
- "canonical_name": "gmdate",
- "description": "Lowers `gmdate(format[, timestamp])`: the UTC counterpart of `date()`.",
+ "area": "IO",
+ "canonical_name": "fputcsv",
+ "description": "Format line as CSV and write to file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_gmdate",
- "codegen_line": 33,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fputcsv",
+ "codegen_line": 3005,
"notes": [
- "Lowers `gmdate(format[, timestamp])`: the UTC counterpart of `date()`.",
- "Identical argument marshalling to `date()`, but dispatches to `__rt_gmdate`, which formats",
- "the instant in UTC regardless of the active default timezone."
+ "Lowers `fputcsv(stream, fields, separator?, enclosure?)` for string arrays."
],
"runtime_helpers": [
- "__rt_date",
- "__rt_gmdate"
+ "__rt_fputcsv"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fputcsv.rs",
"sig_line": null
},
- "name": "gmdate",
+ "name": "fputcsv",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "format",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "timestamp",
+ "name": "fields",
+ "optional": false,
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": "','",
+ "name": "separator",
"optional": true,
- "type": "int"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "'\"'",
+ "name": "enclosure",
+ "optional": true,
+ "type": "string"
}
],
- "return_type": "string",
+ "return_type": "int",
"variadic": null
},
- "slug": "gmdate",
- "sub_area": "Date"
+ "slug": "fputcsv",
+ "sub_area": "IO"
},
{
- "area": "Date",
- "canonical_name": "gmmktime",
- "description": "Lowers `gmmktime(...)`: the UTC counterpart of `mktime()`.",
+ "area": "IO",
+ "canonical_name": "fread",
+ "description": "Binary-safe file read.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_gmmktime",
- "codegen_line": 151,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fread",
+ "codegen_line": 2816,
"notes": [
- "Lowers `gmmktime(...)`: the UTC counterpart of `mktime()`.",
- "Identical six-integer argument marshalling, but dispatches to `__rt_gmmktime`, which",
- "interprets the broken-down date/time as UTC (`timegm`) instead of local time."
+ "Lowers `fread(stream, length)` using the shared runtime file-read helper."
],
"runtime_helpers": [
- "__rt_checkdate",
- "__rt_getdate",
- "__rt_gmmktime"
+ "__rt_fread"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fread.rs",
"sig_line": null
},
- "name": "gmmktime",
+ "name": "fread",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "hour",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "minute",
+ "name": "stream",
"optional": false,
- "type": "int"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "second",
+ "name": "length",
"optional": false,
"type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "month",
- "optional": false,
- "type": "int"
- },
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "fread",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "fscanf",
+ "description": "Parses input from a file according to a format.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fscanf",
+ "codegen_line": 2938,
+ "notes": [
+ "Lowers `fscanf(stream, format)` through `__rt_fgets` and `__rt_sscanf`."
+ ],
+ "runtime_helpers": [
+ "__rt_fgets",
+ "__rt_sscanf"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/fscanf.rs",
+ "sig_line": null
+ },
+ "name": "fscanf",
+ "sig": {
+ "params": [
{
"by_ref": false,
"default": null,
- "name": "day",
+ "name": "stream",
"optional": false,
- "type": "int"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "year",
+ "name": "format",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "int",
- "variadic": null
+ "return_type": "array",
+ "variadic": "vars"
},
- "slug": "gmmktime",
- "sub_area": "Date"
+ "slug": "fscanf",
+ "sub_area": "IO"
},
{
- "area": "String",
- "canonical_name": "grapheme_strrev",
- "description": "Lowers `grapheme_strrev()` and boxes its `string|false` result as `Mixed`.",
+ "area": "IO",
+ "canonical_name": "fseek",
+ "description": "Seeks on a file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_grapheme_strrev",
- "codegen_line": 88,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fseek",
+ "codegen_line": 3172,
"notes": [
- "Lowers `grapheme_strrev()` and boxes its `string|false` result as `Mixed`."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "Lowers `fseek(stream, offset, whence?)` and clears EOF state on success."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fseek.rs",
"sig_line": null
},
- "name": "grapheme_strrev",
+ "name": "fseek",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "offset",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "whence",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "grapheme_strrev",
- "sub_area": "String"
+ "slug": "fseek",
+ "sub_area": "IO"
},
{
- "area": "String",
- "canonical_name": "hash",
- "description": "Lowers `hash(algo, data, binary?)` through the shared runtime digest dispatcher.",
+ "area": "Streams",
+ "canonical_name": "fsockopen",
+ "description": "Open Internet or Unix domain socket connection.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_hash",
- "codegen_line": 209,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fsockopen",
+ "codegen_line": 3644,
"notes": [
- "Lowers `hash(algo, data, binary?)` through the shared runtime digest dispatcher."
- ],
- "runtime_helpers": [
- "__rt_hash"
+ "Lowers `fsockopen(host, port, errno?, errstr?, timeout?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fsockopen.rs",
"sig_line": null
},
- "name": "hash",
+ "name": "fsockopen",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "algo",
+ "name": "hostname",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "data",
+ "name": "port",
"optional": false,
- "type": "string"
+ "type": "int"
},
{
- "by_ref": false,
- "default": "false",
- "name": "binary",
+ "by_ref": true,
+ "default": "null",
+ "name": "error_code",
"optional": true,
- "type": "bool"
+ "type": "int"
+ },
+ {
+ "by_ref": true,
+ "default": "null",
+ "name": "error_message",
+ "optional": true,
+ "type": "string"
},
{
"by_ref": false,
- "default": "[]",
- "name": "options",
+ "default": "null",
+ "name": "timeout",
"optional": true,
- "type": "array"
+ "type": "float"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "hash",
- "sub_area": "String"
+ "slug": "fsockopen",
+ "sub_area": "Streams"
},
{
- "area": "String",
- "canonical_name": "hash_algos",
- "description": "Lowers `hash_algos()` through the runtime algorithm-list builder.",
+ "area": "IO",
+ "canonical_name": "fstat",
+ "description": "Gets information about a file using an open file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_hash_algos",
- "codegen_line": 254,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fstat",
+ "codegen_line": 5539,
"notes": [
- "Lowers `hash_algos()` through the runtime algorithm-list builder."
+ "Lowers `fstat(stream)` and boxes the runtime stat array or PHP false result."
],
"runtime_helpers": [
- "__rt_hash_algos_list",
- "__rt_hash_init"
+ "__rt_fstat_array"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fstat.rs",
"sig_line": null
},
- "name": "hash_algos",
+ "name": "fstat",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "stream",
+ "optional": false,
+ "type": "resource"
+ }
+ ],
+ "return_type": "mixed",
"variadic": null
},
- "slug": "hash_algos",
- "sub_area": "String"
+ "slug": "fstat",
+ "sub_area": "IO"
},
{
- "area": "String",
- "canonical_name": "hash_copy",
- "description": "Lowers `hash_copy(context)` through the incremental hash clone helper.",
+ "area": "IO",
+ "canonical_name": "fsync",
+ "description": "Synchronizes changes to the file (including meta-data).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_hash_copy",
- "codegen_line": 333,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fsync",
+ "codegen_line": 3261,
"notes": [
- "Lowers `hash_copy(context)` through the incremental hash clone helper."
+ "Lowers `fsync(stream)` through the shared fd sync runtime helper."
],
"runtime_helpers": [
- "__rt_crc32",
- "__rt_hash_copy",
- "__rt_md5",
- "__rt_sha1"
+ "__rt_fflush",
+ "__rt_fsync"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/fsync.rs",
"sig_line": null
},
- "name": "hash_copy",
+ "name": "fsync",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "context",
+ "name": "stream",
"optional": false,
"type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "hash_copy",
- "sub_area": "String"
+ "slug": "fsync",
+ "sub_area": "IO"
},
{
- "area": "String",
- "canonical_name": "hash_equals",
- "description": "Lowers `hash_equals(known, user)` through the timing-safe runtime compare helper.",
+ "area": "IO",
+ "canonical_name": "ftell",
+ "description": "Returns the current position of the file read/write pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_hash_equals",
- "codegen_line": 247,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_ftell",
+ "codegen_line": 3133,
"notes": [
- "Lowers `hash_equals(known, user)` through the timing-safe runtime compare helper."
+ "Lowers `ftell(stream)` as `lseek(fd, 0, SEEK_CUR)`."
],
"runtime_helpers": [
- "__rt_hash_algos_list",
- "__rt_hash_equals",
- "__rt_hash_init"
+ "__rt_user_wrapper_ftell"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/ftell.rs",
"sig_line": null
},
- "name": "hash_equals",
+ "name": "ftell",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "known_string",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "user_string",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "hash_equals",
- "sub_area": "String"
+ "slug": "ftell",
+ "sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "hash_file",
- "description": "Lowers `hash_file(algo, filename, binary?)` by reading bytes then hashing them.",
+ "canonical_name": "ftruncate",
+ "description": "Truncates a file to a given length.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_hash_file",
- "codegen_line": 180,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_ftruncate",
+ "codegen_line": 3210,
"notes": [
- "Lowers `hash_file(algo, filename, binary?)` by reading bytes then hashing them."
+ "Lowers `ftruncate(stream, size)` through the shared fd truncate runtime helper."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/ftruncate.rs",
"sig_line": null
},
- "name": "hash_file",
+ "name": "ftruncate",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "algo",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "size",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": "false",
- "name": "binary",
- "optional": true,
- "type": "bool"
- },
- {
- "by_ref": false,
- "default": "[]",
- "name": "options",
- "optional": true,
- "type": "array"
+ "type": "int"
+ }
+ ],
+ "return_type": "bool",
+ "variadic": null
+ },
+ "slug": "ftruncate",
+ "sub_area": "IO"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "function_exists",
+ "description": "Returns true if the given function has been defined.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_function_exists",
+ "codegen_line": 276,
+ "notes": [
+ "Lowers `function_exists(\"name\")` for compile-time string names.",
+ "Recognizes user functions, externs, catalog builtins, and the date/time procedural aliases",
+ "that `name_resolver` desugars (including the injected timezone-introspection prelude",
+ "functions). The aliases are matched through `is_date_procedural_alias` rather than the catalog",
+ "because their call sites are rewritten before codegen, so they never reach the builtin catalog",
+ "yet must still report as existing to match PHP."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/function_exists.rs",
+ "sig_line": null
+ },
+ "name": "function_exists",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "function",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "bool",
+ "variadic": null
+ },
+ "slug": "function_exists",
+ "sub_area": "Class"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "fwrite",
+ "description": "Binary-safe file write.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fwrite",
+ "codegen_line": 2838,
+ "notes": [
+ "Lowers `fwrite(stream, data)` and returns the number of bytes written."
+ ],
+ "runtime_helpers": [
+ "__rt_fwrite"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/fwrite.rs",
+ "sig_line": null
+ },
+ "name": "fwrite",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "stream",
+ "optional": false,
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "int",
+ "variadic": null
+ },
+ "slug": "fwrite",
+ "sub_area": "IO"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "get_class",
+ "description": "Returns the name of the class of an object.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_class_name_lookup",
+ "codegen_line": 331,
+ "notes": [
+ "Lowers `get_class()` and `get_parent_class()` through static or dynamic class metadata."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/get_class.rs",
+ "sig_line": null
+ },
+ "name": "get_class",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "object",
+ "optional": true,
+ "type": "object"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "get_class",
+ "sub_area": "Class"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "get_declared_classes",
+ "description": "Returns an array of the names of the defined classes.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_get_declared_names",
+ "codegen_line": 388,
+ "notes": [
+ "Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/get_declared_classes.rs",
+ "sig_line": null
+ },
+ "name": "get_declared_classes",
+ "sig": {
+ "params": [],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "get_declared_classes",
+ "sub_area": "Class"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "get_declared_interfaces",
+ "description": "Returns an array of all declared interfaces.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_get_declared_names",
+ "codegen_line": 388,
+ "notes": [
+ "Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/get_declared_interfaces.rs",
+ "sig_line": null
+ },
+ "name": "get_declared_interfaces",
+ "sig": {
+ "params": [],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "get_declared_interfaces",
+ "sub_area": "Class"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "get_declared_traits",
+ "description": "Returns an array of all declared traits.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_get_declared_names",
+ "codegen_line": 388,
+ "notes": [
+ "Lowers `get_declared_classes/interfaces/traits()` using the shared declaration registry."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/get_declared_traits.rs",
+ "sig_line": null
+ },
+ "name": "get_declared_traits",
+ "sig": {
+ "params": [],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "get_declared_traits",
+ "sub_area": "Class"
+ },
+ {
+ "area": "Class",
+ "canonical_name": "get_parent_class",
+ "description": "Returns the name of the parent class of an object or class.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_class_name_lookup",
+ "codegen_line": 331,
+ "notes": [
+ "Lowers `get_class()` and `get_parent_class()` through static or dynamic class metadata."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/get_parent_class.rs",
+ "sig_line": null
+ },
+ "name": "get_parent_class",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "object_or_class",
+ "optional": true,
+ "type": "mixed"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "get_parent_class",
+ "sub_area": "Class"
+ },
+ {
+ "area": "Type",
+ "canonical_name": "get_resource_id",
+ "description": "Returns an integer identifier for the given resource.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_get_resource_id",
+ "codegen_line": 424,
+ "notes": [
+ "Lowers `get_resource_id(resource)` by unboxing the native handle and making it one-based."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/types/get_resource_id.rs",
+ "sig_line": null
+ },
+ "name": "get_resource_id",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "resource",
+ "optional": false,
+ "type": "resource"
+ }
+ ],
+ "return_type": "int",
+ "variadic": null
+ },
+ "slug": "get_resource_id",
+ "sub_area": "Type"
+ },
+ {
+ "area": "Type",
+ "canonical_name": "get_resource_type",
+ "description": "Returns the type of a resource.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_get_resource_type",
+ "codegen_line": 412,
+ "notes": [
+ "Lowers `get_resource_type(resource)` to elephc's current resource type label."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/types/get_resource_type.rs",
+ "sig_line": null
+ },
+ "name": "get_resource_type",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "resource",
+ "optional": false,
+ "type": "resource"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "get_resource_type",
+ "sub_area": "Type"
+ },
+ {
+ "area": "Filesystem",
+ "canonical_name": "getcwd",
+ "description": "Gets the current working directory.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_getcwd",
+ "codegen_line": 5396,
+ "notes": [
+ "Lowers `getcwd()` through the target-aware runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_getcwd",
+ "__rt_tmpfile"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/getcwd.rs",
+ "sig_line": null
+ },
+ "name": "getcwd",
+ "sig": {
+ "params": [],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "getcwd",
+ "sub_area": "Filesystem"
+ },
+ {
+ "area": "Date",
+ "canonical_name": "getdate",
+ "description": "Returns date/time information.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_getdate",
+ "codegen_line": 183,
+ "notes": [
+ "Lowers `getdate([$timestamp])` through the shared decomposition runtime helper.",
+ "Marshals the optional timestamp (the `-1` current-time sentinel when omitted; a boxed",
+ "`Mixed`/`Union` argument is unboxed) into the integer result register where `__rt_getdate`",
+ "reads it, then boxes the returned associative-array hash pointer into a `Mixed` cell \u2014 the same",
+ "representation `stat`/`getdate` use, so the checker types the result `Mixed`."
+ ],
+ "runtime_helpers": [
+ "__rt_getdate",
+ "__rt_mixed_from_value"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/getdate.rs",
+ "sig_line": null
+ },
+ "name": "getdate",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "timestamp",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "getdate",
+ "sub_area": "Date"
+ },
+ {
+ "area": "Filesystem",
+ "canonical_name": "getenv",
+ "description": "Gets the value of an environment variable.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_getenv",
+ "codegen_line": 645,
+ "notes": [
+ "Lowers `getenv(name)` through the target-aware environment lookup helper."
+ ],
+ "runtime_helpers": [
+ "__rt_getenv"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/getenv.rs",
+ "sig_line": null
+ },
+ "name": "getenv",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "name",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "getenv",
+ "sub_area": "Filesystem"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "gethostbyaddr",
+ "description": "Gets the Internet host name corresponding to a given IP address.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_gethostbyaddr",
+ "codegen_line": 3431,
+ "notes": [
+ "Lowers `gethostbyaddr(address)` and boxes malformed addresses as PHP `false`."
+ ],
+ "runtime_helpers": [
+ "__rt_gethostbyaddr",
+ "__rt_getprotobyname"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/gethostbyaddr.rs",
+ "sig_line": null
+ },
+ "name": "gethostbyaddr",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "ip",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "gethostbyaddr",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "gethostbyname",
+ "description": "Gets the IPv4 address corresponding to the given Internet host name.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_gethostbyname",
+ "codegen_line": 3419,
+ "notes": [
+ "Lowers `gethostbyname(hostname)` through the shared runtime resolver."
+ ],
+ "runtime_helpers": [
+ "__rt_gethostbyaddr",
+ "__rt_gethostbyname"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/gethostbyname.rs",
+ "sig_line": null
+ },
+ "name": "gethostbyname",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "hostname",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gethostbyname",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "gethostname",
+ "description": "Gets the standard host name for the local machine.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_gethostname",
+ "codegen_line": 3409,
+ "notes": [
+ "Lowers `gethostname()` through the shared runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_gethostbyaddr",
+ "__rt_gethostbyname",
+ "__rt_gethostname"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/gethostname.rs",
+ "sig_line": null
+ },
+ "name": "gethostname",
+ "sig": {
+ "params": [],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gethostname",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "getprotobyname",
+ "description": "Gets the protocol number associated with the given protocol name.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_getprotobyname",
+ "codegen_line": 3444,
+ "notes": [
+ "Lowers `getprotobyname(protocol)` and boxes a missing entry as PHP `false`."
+ ],
+ "runtime_helpers": [
+ "__rt_getprotobyname"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/getprotobyname.rs",
+ "sig_line": null
+ },
+ "name": "getprotobyname",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "protocol",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "getprotobyname",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "getprotobynumber",
+ "description": "Gets the protocol name associated with the given protocol number.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_getprotobynumber",
+ "codegen_line": 3467,
+ "notes": [
+ "Lowers `getprotobynumber(number)` and boxes a missing entry as PHP `false`."
+ ],
+ "runtime_helpers": [
+ "__rt_getprotobynumber"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/getprotobynumber.rs",
+ "sig_line": null
+ },
+ "name": "getprotobynumber",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "protocol",
+ "optional": false,
+ "type": "int"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "getprotobynumber",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "getservbyname",
+ "description": "Gets port number associated with an Internet service and protocol.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_getservbyname",
+ "codegen_line": 3486,
+ "notes": [
+ "Lowers `getservbyname(service, protocol)` and boxes a missing entry as PHP `false`."
+ ],
+ "runtime_helpers": [
+ "__rt_getservbyname"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/getservbyname.rs",
+ "sig_line": null
+ },
+ "name": "getservbyname",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "service",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "protocol",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "getservbyname",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "getservbyport",
+ "description": "Gets the Internet service that corresponds to a port and protocol.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_getservbyport",
+ "codegen_line": 3517,
+ "notes": [
+ "Lowers `getservbyport(port, protocol)` and boxes a missing entry as PHP `false`."
+ ],
+ "runtime_helpers": [
+ "__rt_getservbyport"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/getservbyport.rs",
+ "sig_line": null
+ },
+ "name": "getservbyport",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "port",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "protocol",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "getservbyport",
+ "sub_area": "IO"
+ },
+ {
+ "area": "Type",
+ "canonical_name": "gettype",
+ "description": "Returns the type of a variable as a string.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_gettype",
+ "codegen_line": 129,
+ "notes": [
+ "Lowers `gettype(value)` for statically concrete PHP types."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/types/gettype.rs",
+ "sig_line": null
+ },
+ "name": "gettype",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "mixed"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gettype",
+ "sub_area": "Type"
+ },
+ {
+ "area": "Filesystem",
+ "canonical_name": "glob",
+ "description": "Finds pathnames matching a pattern.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_glob",
+ "codegen_line": 4463,
+ "notes": [
+ "Lowers `glob(pattern)` through the target-aware runtime glob expansion helper."
+ ],
+ "runtime_helpers": [
+ "__rt_glob"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/glob.rs",
+ "sig_line": null
+ },
+ "name": "glob",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "pattern",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "glob",
+ "sub_area": "Filesystem"
+ },
+ {
+ "area": "Date",
+ "canonical_name": "gmdate",
+ "description": "Formats a GMT/UTC date and time.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_gmdate",
+ "codegen_line": 33,
+ "notes": [
+ "Lowers `gmdate(format[, timestamp])`: the UTC counterpart of `date()`.",
+ "Identical argument marshalling to `date()`, but dispatches to `__rt_gmdate`, which formats",
+ "the instant in UTC regardless of the active default timezone."
+ ],
+ "runtime_helpers": [
+ "__rt_date",
+ "__rt_gmdate"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/gmdate.rs",
+ "sig_line": null
+ },
+ "name": "gmdate",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "format",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "timestamp",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gmdate",
+ "sub_area": "Date"
+ },
+ {
+ "area": "Date",
+ "canonical_name": "gmmktime",
+ "description": "Returns the Unix timestamp for a GMT date.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_gmmktime",
+ "codegen_line": 151,
+ "notes": [
+ "Lowers `gmmktime(...)`: the UTC counterpart of `mktime()`.",
+ "Identical six-integer argument marshalling, but dispatches to `__rt_gmmktime`, which",
+ "interprets the broken-down date/time as UTC (`timegm`) instead of local time."
+ ],
+ "runtime_helpers": [
+ "__rt_checkdate",
+ "__rt_getdate",
+ "__rt_gmmktime"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/gmmktime.rs",
+ "sig_line": null
+ },
+ "name": "gmmktime",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "hour",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "minute",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "second",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "month",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "day",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "year",
+ "optional": false,
+ "type": "int"
+ }
+ ],
+ "return_type": "int",
+ "variadic": null
+ },
+ "slug": "gmmktime",
+ "sub_area": "Date"
+ },
+ {
+ "area": "String",
+ "canonical_name": "grapheme_strrev",
+ "description": "Reverses a string by grapheme cluster, returning false on failure.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_grapheme_strrev",
+ "codegen_line": 88,
+ "notes": [
+ "Lowers `grapheme_strrev()` and boxes its `string|false` result as `Mixed`."
+ ],
+ "runtime_helpers": [
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/grapheme_strrev.rs",
+ "sig_line": null
+ },
+ "name": "grapheme_strrev",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "string",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "grapheme_strrev",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "gzcompress",
+ "description": "Compress a string using the ZLIB data format.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_gzcompress",
+ "codegen_line": 402,
+ "notes": [
+ "Lowers `gzcompress(data, level?)` through inline zlib `compress2` calls."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/gzcompress.rs",
+ "sig_line": null
+ },
+ "name": "gzcompress",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "-1",
+ "name": "level",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gzcompress",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "gzdeflate",
+ "description": "Deflate a string using the DEFLATE data format.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_gzdeflate",
+ "codegen_line": 418,
+ "notes": [
+ "Lowers `gzdeflate(data, level?)` through inline raw-DEFLATE zlib calls."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/gzdeflate.rs",
+ "sig_line": null
+ },
+ "name": "gzdeflate",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "-1",
+ "name": "level",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "gzdeflate",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "gzinflate",
+ "description": "Inflate a deflated string.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_gzinflate",
+ "codegen_line": 436,
+ "notes": [
+ "Lowers `gzinflate(data, max_length?)` and boxes zlib failures as PHP false."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/gzinflate.rs",
+ "sig_line": null
+ },
+ "name": "gzinflate",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "max_length",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "gzinflate",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "gzuncompress",
+ "description": "Uncompress a compressed string.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_gzuncompress",
+ "codegen_line": 457,
+ "notes": [
+ "Lowers `gzuncompress(data, max_length?)` and boxes zlib failures as PHP false."
+ ],
+ "runtime_helpers": [
+ "__rt_long2ip"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/gzuncompress.rs",
+ "sig_line": null
+ },
+ "name": "gzuncompress",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "max_length",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "gzuncompress",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "hash",
+ "description": "Generates a hash value using the given algorithm.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_hash",
+ "codegen_line": 209,
+ "notes": [
+ "Lowers `hash(algo, data, binary?)` through the shared runtime digest dispatcher."
+ ],
+ "runtime_helpers": [
+ "__rt_hash"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/hash.rs",
+ "sig_line": null
+ },
+ "name": "hash",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "algo",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "binary",
+ "optional": true,
+ "type": "bool"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "hash",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "hash_algos",
+ "description": "Returns an array of supported hashing algorithm names.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_hash_algos",
+ "codegen_line": 254,
+ "notes": [
+ "Lowers `hash_algos()` through the runtime algorithm-list builder."
+ ],
+ "runtime_helpers": [
+ "__rt_hash_algos_list",
+ "__rt_hash_init"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/hash_algos.rs",
+ "sig_line": null
+ },
+ "name": "hash_algos",
+ "sig": {
+ "params": [],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "hash_algos",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "hash_copy",
+ "description": "Copies the state of an incremental hashing context.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_hash_copy",
+ "codegen_line": 333,
+ "notes": [
+ "Lowers `hash_copy(context)` through the incremental hash clone helper."
+ ],
+ "runtime_helpers": [
+ "__rt_crc32",
+ "__rt_hash_copy",
+ "__rt_md5",
+ "__rt_sha1"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/hash_copy.rs",
+ "sig_line": null
+ },
+ "name": "hash_copy",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "context",
+ "optional": false,
+ "type": "resource"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "hash_copy",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "hash_equals",
+ "description": "Compares two strings using a constant-time algorithm.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_hash_equals",
+ "codegen_line": 247,
+ "notes": [
+ "Lowers `hash_equals(known, user)` through the timing-safe runtime compare helper."
+ ],
+ "runtime_helpers": [
+ "__rt_hash_algos_list",
+ "__rt_hash_equals",
+ "__rt_hash_init"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/hash_equals.rs",
+ "sig_line": null
+ },
+ "name": "hash_equals",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "known_string",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "user_string",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "bool",
+ "variadic": null
+ },
+ "slug": "hash_equals",
+ "sub_area": "String"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "hash_file",
+ "description": "Generates a hash value using the contents of a given file.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_hash_file",
+ "codegen_line": 287,
+ "notes": [
+ "Lowers `hash_file(algo, filename, binary?)` by reading bytes then hashing them."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/hash_file.rs",
+ "sig_line": null
+ },
+ "name": "hash_file",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "algo",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "filename",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "binary",
+ "optional": true,
+ "type": "bool"
}
],
"return_type": "mixed",
@@ -7259,13 +8454,13 @@
{
"area": "String",
"canonical_name": "hash_final",
- "description": "Lowers `hash_final(context, binary?)` through the incremental hash finalizer.",
+ "description": "Finalizes an incremental hash and returns the digest string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_hash_final",
"codegen_line": 302,
"notes": [
@@ -7275,7 +8470,7 @@
"__rt_hash_final"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/hash_final.rs",
"sig_line": null
},
"name": "hash_final",
@@ -7290,7 +8485,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "binary",
"optional": true,
"type": "bool"
@@ -7305,13 +8500,13 @@
{
"area": "String",
"canonical_name": "hash_hmac",
- "description": "Lowers `hash_hmac(algo, data, key, binary?)` through the shared HMAC runtime dispatcher.",
+ "description": "Generates a keyed hash value using the HMAC method.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_hash_hmac",
"codegen_line": 228,
"notes": [
@@ -7322,7 +8517,7 @@
"__rt_hash_hmac"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/hash_hmac.rs",
"sig_line": null
},
"name": "hash_hmac",
@@ -7351,7 +8546,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "binary",
"optional": true,
"type": "bool"
@@ -7366,13 +8561,13 @@
{
"area": "String",
"canonical_name": "hash_init",
- "description": "Lowers `hash_init(algo)` and returns a boxed HashContext resource.",
+ "description": "Initialize an incremental hashing context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_hash_init",
"codegen_line": 266,
"notes": [
@@ -7382,7 +8577,7 @@
"__rt_hash_init"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/hash_init.rs",
"sig_line": null
},
"name": "hash_init",
@@ -7408,13 +8603,6 @@
"name": "key",
"optional": true,
"type": "string"
- },
- {
- "by_ref": false,
- "default": "[]",
- "name": "options",
- "optional": true,
- "type": "array"
}
],
"return_type": "mixed",
@@ -7426,13 +8614,13 @@
{
"area": "String",
"canonical_name": "hash_update",
- "description": "Lowers `hash_update(context, data)` through the incremental hash runtime helper.",
+ "description": "Pumps data into an active incremental hashing context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_hash_update",
"codegen_line": 277,
"notes": [
@@ -7442,7 +8630,7 @@
"__rt_hash_update"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/hash_update.rs",
"sig_line": null
},
"name": "hash_update",
@@ -7472,13 +8660,13 @@
{
"area": "Misc",
"canonical_name": "header",
- "description": "Lowers `header($line[, $replace[, $code]])` to `__rt_header`, materializing the",
+ "description": "Sends a raw HTTP header.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_header",
"codegen_line": 289,
"notes": [
@@ -7493,7 +8681,7 @@
"__rt_header"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/header.rs",
"sig_line": null
},
"name": "header",
@@ -7504,21 +8692,21 @@
"default": null,
"name": "header",
"optional": false,
- "type": "mixed"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
+ "default": "true",
"name": "replace",
"optional": true,
- "type": "mixed"
+ "type": "bool"
},
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "response_code",
"optional": true,
- "type": "mixed"
+ "type": "int"
}
],
"return_type": "void",
@@ -7530,13 +8718,13 @@
{
"area": "String",
"canonical_name": "hex2bin",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Decodes a hexadecimal string back into its binary representation.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -7547,7 +8735,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/hex2bin.rs",
"sig_line": null
},
"name": "hex2bin",
@@ -7570,13 +8758,13 @@
{
"area": "Date",
"canonical_name": "hrtime",
- "description": "Lowers `hrtime([$as_number])` through the monotonic-clock runtime helper.",
+ "description": "Returns the current high-resolution time.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_hrtime",
"codegen_line": 247,
"notes": [
@@ -7591,7 +8779,7 @@
"__rt_http_response_code"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/hrtime.rs",
"sig_line": null
},
"name": "hrtime",
@@ -7599,7 +8787,7 @@
"params": [
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "as_number",
"optional": true,
"type": "bool"
@@ -7614,13 +8802,13 @@
{
"area": "String",
"canonical_name": "html_entity_decode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Converts HTML entities in a string back into their corresponding characters.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -7631,7 +8819,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/html_entity_decode.rs",
"sig_line": null
},
"name": "html_entity_decode",
@@ -7643,20 +8831,6 @@
"name": "string",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "encoding",
- "optional": false,
- "type": "string"
}
],
"return_type": "string",
@@ -7668,13 +8842,13 @@
{
"area": "String",
"canonical_name": "htmlentities",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Converts all applicable characters in a string into their HTML entities.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -7685,7 +8859,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/htmlentities.rs",
"sig_line": null
},
"name": "htmlentities",
@@ -7697,27 +8871,6 @@
"name": "string",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "encoding",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "double_encode",
- "optional": false,
- "type": "bool"
}
],
"return_type": "string",
@@ -7729,13 +8882,13 @@
{
"area": "String",
"canonical_name": "htmlspecialchars",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Converts the HTML special characters in a string into their entities.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -7746,7 +8899,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/htmlspecialchars.rs",
"sig_line": null
},
"name": "htmlspecialchars",
@@ -7758,27 +8911,6 @@
"name": "string",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "encoding",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "double_encode",
- "optional": false,
- "type": "bool"
}
],
"return_type": "string",
@@ -7790,13 +8922,13 @@
{
"area": "Misc",
"canonical_name": "http_response_code",
- "description": "Lowers `http_response_code([$code])` to `__rt_http_response_code`. The code (or",
+ "description": "Gets or sets the HTTP response code.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_http_response_code",
"codegen_line": 264,
"notes": [
@@ -7810,7 +8942,7 @@
"__rt_http_response_code"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/http_response_code.rs",
"sig_line": null
},
"name": "http_response_code",
@@ -7818,10 +8950,10 @@
"params": [
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "response_code",
"optional": true,
- "type": "mixed"
+ "type": "int"
}
],
"return_type": "int",
@@ -7833,13 +8965,13 @@
{
"area": "Math",
"canonical_name": "hypot",
- "description": "Lowers `hypot()` using the C ABI argument order `x, y`.",
+ "description": "Calculates the length of the hypotenuse of a right-angle triangle.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/libm.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
"codegen_function": "lower_hypot",
"codegen_line": 43,
"notes": [
@@ -7847,7 +8979,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/hypot.rs",
"sig_line": null
},
"name": "hypot",
@@ -7877,13 +9009,13 @@
{
"area": "String",
"canonical_name": "implode",
- "description": "Lowers `implode(glue, array)` by selecting the string or integer array helper.",
+ "description": "Joins array elements into a single string using a separator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_implode",
"codegen_line": 192,
"notes": [
@@ -7891,7 +9023,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/implode.rs",
"sig_line": null
},
"name": "implode",
@@ -7906,7 +9038,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "null",
"name": "array",
"optional": true,
"type": "array"
@@ -7921,21 +9053,21 @@
{
"area": "Array",
"canonical_name": "in_array",
- "description": "Lowers `in_array()` for indexed arrays with scalar or string payloads.",
+ "description": "Checks if a value exists in an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_in_array",
- "codegen_line": 1160,
+ "codegen_line": 1786,
"notes": [
- "Lowers `in_array()` for indexed arrays with scalar or string payloads."
+ "Lowers `in_array()` for indexed and associative arrays with PHP loose or strict membership."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/in_array.rs",
"sig_line": null
},
"name": "in_array",
@@ -7957,36 +9089,114 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "strict",
"optional": true,
"type": "bool"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
"slug": "in_array",
"sub_area": "Array"
},
+ {
+ "area": "String",
+ "canonical_name": "inet_ntop",
+ "description": "Converts a packed internet address to a human-readable representation.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_inet",
+ "codegen_line": 497,
+ "notes": [
+ "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+ ],
+ "runtime_helpers": [
+ "__rt_sprintf"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/inet_ntop.rs",
+ "sig_line": null
+ },
+ "name": "inet_ntop",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "ip",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "inet_ntop",
+ "sub_area": "String"
+ },
+ {
+ "area": "String",
+ "canonical_name": "inet_pton",
+ "description": "Converts a human-readable IP address to its packed in_addr representation.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_inet",
+ "codegen_line": 497,
+ "notes": [
+ "Lowers `inet_ntop()` and `inet_pton()` and boxes invalid-address results as PHP false."
+ ],
+ "runtime_helpers": [
+ "__rt_sprintf"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/inet_pton.rs",
+ "sig_line": null
+ },
+ "name": "inet_pton",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "ip",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "inet_pton",
+ "sub_area": "String"
+ },
{
"area": "Math",
"canonical_name": "intdiv",
- "description": "Lowers `intdiv()` for concrete integer-like numeric operands.",
+ "description": "Integer division.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/binary.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/math/binary.rs",
"codegen_function": "lower_intdiv",
- "codegen_line": 21,
+ "codegen_line": 23,
"notes": [
"Lowers `intdiv()` for concrete integer-like numeric operands."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/intdiv.rs",
"sig_line": null
},
"name": "intdiv",
@@ -8013,18 +9223,62 @@
"slug": "intdiv",
"sub_area": "Math"
},
+ {
+ "area": "Class",
+ "canonical_name": "interface_exists",
+ "description": "Checks if the interface has been defined.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_class_like_exists",
+ "codegen_line": 293,
+ "notes": [
+ "Lowers AOT class/interface/enum existence checks for literal names."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/callables/interface_exists.rs",
+ "sig_line": null
+ },
+ "name": "interface_exists",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "interface",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
+ }
+ ],
+ "return_type": "bool",
+ "variadic": null
+ },
+ "slug": "interface_exists",
+ "sub_area": "Class"
+ },
{
"area": "Type",
"canonical_name": "intval",
- "description": "Lowers `intval()` for concrete scalar operands.",
+ "description": "Returns the integer value of a variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
"codegen_function": "lower_intval",
- "codegen_line": 1001,
+ "codegen_line": 523,
"notes": [
"Lowers `intval()` for concrete scalar operands."
],
@@ -8033,7 +9287,7 @@
"__rt_str_to_int"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/intval.rs",
"sig_line": null
},
"name": "intval",
@@ -8045,13 +9299,6 @@
"name": "value",
"optional": false,
"type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "base",
- "optional": false,
- "type": "int"
}
],
"return_type": "int",
@@ -8061,153 +9308,153 @@
"sub_area": "Casts"
},
{
- "area": "SPL",
- "canonical_name": "iterator_apply",
- "description": "Lowers `iterator_apply()` over supported Traversable sources and callback forms.",
+ "area": "String",
+ "canonical_name": "ip2long",
+ "description": "Converts a string containing an IPv4 address into a long integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_iterator_apply",
- "codegen_line": 289,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_ip2long",
+ "codegen_line": 488,
"notes": [
- "Lowers `iterator_apply()` over supported Traversable sources and callback forms."
+ "Lowers `ip2long(string)` and boxes invalid-address results as PHP false."
+ ],
+ "runtime_helpers": [
+ "__rt_ip2long",
+ "__rt_sprintf"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ip2long.rs",
"sig_line": null
},
- "name": "iterator_apply",
+ "name": "ip2long",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "iterator",
- "optional": false,
- "type": "traversable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "callback",
+ "name": "ip",
"optional": false,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "args",
- "optional": true,
- "type": "array"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "iterator_apply",
- "sub_area": "SPL"
+ "slug": "ip2long",
+ "sub_area": "String"
},
{
- "area": "SPL",
- "canonical_name": "iterator_count",
- "description": "Lowers `iterator_count()` over arrays, `iterable`, and Traversable objects.",
+ "area": "Class",
+ "canonical_name": "is_a",
+ "description": "Checks whether an object is of a given type or has it as one of its parents.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_iterator_count",
- "codegen_line": 236,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_is_a_relation",
+ "codegen_line": 369,
"notes": [
- "Lowers `iterator_count()` over arrays, `iterable`, and Traversable objects."
+ "Lowers `is_a()` and `is_subclass_of()` for object operands and literal targets."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/is_a.rs",
"sig_line": null
},
- "name": "iterator_count",
+ "name": "is_a",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "iterator",
+ "name": "object_or_class",
"optional": false,
- "type": "traversable"
+ "type": "object"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "class",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "allow_string",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "iterator_count",
- "sub_area": "SPL"
+ "slug": "is_a",
+ "sub_area": "Class"
},
{
- "area": "SPL",
- "canonical_name": "iterator_to_array",
- "description": "Lowers `iterator_to_array()` over arrays, `iterable`, and Traversable objects.",
+ "area": "Type",
+ "canonical_name": "is_array",
+ "description": "Checks whether a variable is an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_iterator_to_array",
- "codegen_line": 265,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_is_array",
+ "codegen_line": 1002,
"notes": [
- "Lowers `iterator_to_array()` over arrays, `iterable`, and Traversable objects."
+ "Lowers `is_array()`: true for statically-known arrays/hashes, or a boxed Mixed/Union value",
+ "whose runtime tag is an indexed (4) or associative (5) array. An `iterable`-typed value is",
+ "not treated as a definite array here (it may hold a Traversable); use `is_iterable` for that."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_array.rs",
"sig_line": null
},
- "name": "iterator_to_array",
+ "name": "is_array",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "iterator",
+ "name": "value",
"optional": false,
- "type": "traversable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "preserve_keys",
- "optional": true,
- "type": "bool"
+ "type": "mixed"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "iterator_to_array",
- "sub_area": "SPL"
+ "slug": "is_array",
+ "sub_area": "Type"
},
{
"area": "Type",
"canonical_name": "is_bool",
- "description": "",
+ "description": "Checks whether a variable is a boolean.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_static_type_predicate",
+ "codegen_line": 736,
+ "notes": [
+ "Lowers a static `is_*` predicate for concrete non-Mixed values."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_bool.rs",
"sig_line": null
},
"name": "is_bool",
@@ -8230,15 +9477,15 @@
{
"area": "Type",
"canonical_name": "is_callable",
- "description": "Lowers `is_callable(value)` through static lookup or runtime callable-shape helpers.",
+ "description": "Checks whether a variable can be called as a function.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
"codegen_function": "lower_is_callable",
- "codegen_line": 802,
+ "codegen_line": 319,
"notes": [
"Lowers `is_callable(value)` through static lookup or runtime callable-shape helpers."
],
@@ -8248,7 +9495,7 @@
"__rt_is_callable_object"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_callable.rs",
"sig_line": null
},
"name": "is_callable",
@@ -8260,20 +9507,6 @@
"name": "value",
"optional": false,
"type": "mixed"
- },
- {
- "by_ref": false,
- "default": "false",
- "name": "syntax_only",
- "optional": true,
- "type": "bool"
- },
- {
- "by_ref": true,
- "default": "null",
- "name": "callable_name",
- "optional": true,
- "type": "string"
}
],
"return_type": "bool",
@@ -8285,15 +9518,15 @@
{
"area": "Filesystem",
"canonical_name": "is_dir",
- "description": "Lowers `is_dir(path)` through the target-aware runtime stat helper.",
+ "description": "Tells whether the filename is a directory.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_is_dir",
- "codegen_line": 4957,
+ "codegen_line": 5600,
"notes": [
"Lowers `is_dir(path)` through the target-aware runtime stat helper."
],
@@ -8303,7 +9536,7 @@
"__rt_is_writable"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_dir.rs",
"sig_line": null
},
"name": "is_dir",
@@ -8326,15 +9559,15 @@
{
"area": "Filesystem",
"canonical_name": "is_executable",
- "description": "Lowers `is_executable(path)` through the target-aware runtime access helper.",
+ "description": "Tells whether the filename is executable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_is_executable",
- "codegen_line": 4989,
+ "codegen_line": 5632,
"notes": [
"Lowers `is_executable(path)` through the target-aware runtime access helper."
],
@@ -8345,7 +9578,7 @@
"__rt_readfile"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_executable.rs",
"sig_line": null
},
"name": "is_executable",
@@ -8368,15 +9601,15 @@
{
"area": "Filesystem",
"canonical_name": "is_file",
- "description": "Lowers `is_file(path)` through the target-aware runtime stat helper.",
+ "description": "Tells whether the filename is a regular file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_is_file",
- "codegen_line": 4949,
+ "codegen_line": 5592,
"notes": [
"Lowers `is_file(path)` through the target-aware runtime stat helper."
],
@@ -8386,7 +9619,7 @@
"__rt_is_writable"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_file.rs",
"sig_line": null
},
"name": "is_file",
@@ -8407,77 +9640,63 @@
"sub_area": "Filesystem"
},
{
- "area": "Class",
- "canonical_name": "is_a",
- "description": "",
+ "area": "Math",
+ "canonical_name": "is_finite",
+ "description": "Checks whether a float is finite.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_is_finite",
+ "codegen_line": 169,
+ "notes": [
+ "Lowers `is_finite()` by rejecting NaN and both infinities."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_finite.rs",
"sig_line": null
},
- "name": "is_a",
+ "name": "is_finite",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
- "optional": false,
- "type": "object"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "class",
+ "name": "num",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "allow_string",
- "optional": true,
- "type": "bool"
+ "type": "float"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_a",
- "sub_area": "Class"
+ "slug": "is_finite",
+ "sub_area": "Math"
},
{
"area": "Type",
- "canonical_name": "is_array",
- "description": "Lowers `is_array()`: true for statically-known arrays/hashes, or a boxed Mixed/Union value",
+ "canonical_name": "is_float",
+ "description": "Checks whether a variable is a floating-point number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_is_array",
- "codegen_line": 1480,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_static_type_predicate",
+ "codegen_line": 736,
"notes": [
- "Lowers `is_array()`: true for statically-known arrays/hashes, or a boxed Mixed/Union value",
- "whose runtime tag is an indexed (4) or associative (5) array. An `iterable`-typed value is",
- "not treated as a definite array here (it may hold a Traversable); use `is_iterable` for that."
+ "Lowers a static `is_*` predicate for concrete non-Mixed values."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_float.rs",
"sig_line": null
},
- "name": "is_array",
+ "name": "is_float",
"sig": {
"params": [
{
@@ -8491,31 +9710,67 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_array",
+ "slug": "is_float",
"sub_area": "Type"
},
+ {
+ "area": "Math",
+ "canonical_name": "is_infinite",
+ "description": "Checks whether a float is infinite.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_is_infinite",
+ "codegen_line": 132,
+ "notes": [
+ "Lowers `is_infinite()` by comparing the normalized float against +/- infinity."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/types/is_infinite.rs",
+ "sig_line": null
+ },
+ "name": "is_infinite",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "bool",
+ "variadic": null
+ },
+ "slug": "is_infinite",
+ "sub_area": "Math"
+ },
{
"area": "Type",
- "canonical_name": "is_object",
- "description": "Lowers `is_object()`: true for statically-known objects, or a boxed Mixed/Union value whose",
+ "canonical_name": "is_int",
+ "description": "Checks whether a variable is an integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_is_object",
- "codegen_line": 1495,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_static_type_predicate",
+ "codegen_line": 736,
"notes": [
- "Lowers `is_object()`: true for statically-known objects, or a boxed Mixed/Union value whose",
- "runtime tag is an object (6)."
+ "Lowers a static `is_*` predicate for concrete non-Mixed values."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_int.rs",
"sig_line": null
},
- "name": "is_object",
+ "name": "is_int",
"sig": {
"params": [
{
@@ -8529,32 +9784,30 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_object",
+ "slug": "is_int",
"sub_area": "Type"
},
{
"area": "Type",
- "canonical_name": "is_scalar",
- "description": "Lowers `is_scalar()`: true for int/float/string/bool, a non-null tagged scalar, or a boxed",
+ "canonical_name": "is_iterable",
+ "description": "Checks whether a variable is iterable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_is_scalar",
- "codegen_line": 1511,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_is_iterable",
+ "codegen_line": 794,
"notes": [
- "Lowers `is_scalar()`: true for int/float/string/bool, a non-null tagged scalar, or a boxed",
- "Mixed/Union value whose runtime tag is int (0), string (1), float (2), or bool (3). Null,",
- "arrays, objects, and resources are not scalars, matching PHP."
+ "Lowers `is_iterable()` for concrete values and boxed Mixed payloads."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_iterable.rs",
"sig_line": null
},
- "name": "is_scalar",
+ "name": "is_iterable",
"sig": {
"params": [
{
@@ -8568,186 +9821,184 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_scalar",
+ "slug": "is_iterable",
"sub_area": "Type"
},
{
- "area": "Math",
- "canonical_name": "is_finite",
- "description": "Lowers `is_finite()` by rejecting NaN and both infinities.",
+ "area": "Filesystem",
+ "canonical_name": "is_link",
+ "description": "Tells whether the filename is a symbolic link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_is_finite",
- "codegen_line": 169,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_is_link",
+ "codegen_line": 5640,
"notes": [
- "Lowers `is_finite()` by rejecting NaN and both infinities."
+ "Lowers `is_link(path)` through the target-aware runtime lstat helper."
+ ],
+ "runtime_helpers": [
+ "__rt_is_link",
+ "__rt_path_is_wrapper",
+ "__rt_readfile",
+ "__rt_readfile_wrapper"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_link.rs",
"sig_line": null
},
- "name": "is_finite",
+ "name": "is_link",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "filename",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_finite",
- "sub_area": "Math"
+ "slug": "is_link",
+ "sub_area": "Filesystem"
},
{
- "area": "Type",
- "canonical_name": "is_float",
- "description": "",
+ "area": "Math",
+ "canonical_name": "is_nan",
+ "description": "Checks whether a float is NAN.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_is_nan",
+ "codegen_line": 113,
+ "notes": [
+ "Lowers `is_nan()` by checking whether the normalized float is unordered with itself."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_nan.rs",
"sig_line": null
},
- "name": "is_float",
+ "name": "is_nan",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "num",
"optional": false,
- "type": "mixed"
+ "type": "float"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_float",
- "sub_area": "Type"
+ "slug": "is_nan",
+ "sub_area": "Math"
},
{
- "area": "Class",
- "canonical_name": "is_subclass_of",
- "description": "",
+ "area": "Type",
+ "canonical_name": "is_null",
+ "description": "Checks whether a variable is null.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_is_null_builtin",
+ "codegen_line": 992,
+ "notes": [
+ "Lowers `is_null()` for concrete scalar values and boxed Mixed payloads."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_null.rs",
"sig_line": null
},
- "name": "is_subclass_of",
+ "name": "is_null",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object_or_class",
+ "name": "value",
"optional": false,
"type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "class",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "allow_string",
- "optional": true,
- "type": "bool"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_subclass_of",
- "sub_area": "Class"
+ "slug": "is_null",
+ "sub_area": "Type"
},
{
- "area": "Math",
- "canonical_name": "is_infinite",
- "description": "Lowers `is_infinite()` by comparing the normalized float against +/- infinity.",
+ "area": "Type",
+ "canonical_name": "is_numeric",
+ "description": "Checks whether a variable is a number or a numeric string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_is_infinite",
- "codegen_line": 132,
+ "codegen_file": "src/codegen/lower_inst/builtins/is_numeric.rs",
+ "codegen_function": "lower_is_numeric",
+ "codegen_line": 22,
"notes": [
- "Lowers `is_infinite()` by comparing the normalized float against +/- infinity."
+ "Lowers `is_numeric()` for concrete scalar values."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_numeric.rs",
"sig_line": null
},
- "name": "is_infinite",
+ "name": "is_numeric",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "value",
"optional": false,
- "type": "float"
+ "type": "mixed"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_infinite",
- "sub_area": "Math"
+ "slug": "is_numeric",
+ "sub_area": "Type"
},
{
"area": "Type",
- "canonical_name": "is_int",
- "description": "",
+ "canonical_name": "is_object",
+ "description": "Checks whether a variable is an object.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_is_object",
+ "codegen_line": 1017,
+ "notes": [
+ "Lowers `is_object()`: true for statically-known objects, or a boxed Mixed/Union value whose",
+ "runtime tag is an object (6)."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_object.rs",
"sig_line": null
},
- "name": "is_int",
+ "name": "is_object",
"sig": {
"params": [
{
@@ -8761,102 +10012,110 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_int",
+ "slug": "is_object",
"sub_area": "Type"
},
{
- "area": "Type",
- "canonical_name": "is_iterable",
- "description": "Lowers `is_iterable()` for concrete values and boxed Mixed payloads.",
+ "area": "Filesystem",
+ "canonical_name": "is_readable",
+ "description": "Tells whether the filename is readable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_is_iterable",
- "codegen_line": 1272,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_is_readable",
+ "codegen_line": 5608,
"notes": [
- "Lowers `is_iterable()` for concrete values and boxed Mixed payloads."
+ "Lowers `is_readable(path)` through the target-aware runtime access helper."
+ ],
+ "runtime_helpers": [
+ "__rt_is_executable",
+ "__rt_is_readable",
+ "__rt_is_writable"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_readable.rs",
"sig_line": null
},
- "name": "is_iterable",
+ "name": "is_readable",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "filename",
"optional": false,
- "type": "mixed"
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_iterable",
- "sub_area": "Type"
+ "slug": "is_readable",
+ "sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "is_nan",
- "description": "Lowers `is_nan()` by checking whether the normalized float is unordered with itself.",
+ "area": "Type",
+ "canonical_name": "is_resource",
+ "description": "Checks whether a variable is a resource.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_is_nan",
- "codegen_line": 113,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_is_resource",
+ "codegen_line": 400,
"notes": [
- "Lowers `is_nan()` by checking whether the normalized float is unordered with itself."
+ "Lowers `is_resource(value)` for static resources and boxed Mixed resource cells."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_resource.rs",
"sig_line": null
},
- "name": "is_nan",
+ "name": "is_resource",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "value",
"optional": false,
- "type": "float"
+ "type": "mixed"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_nan",
- "sub_area": "Math"
+ "slug": "is_resource",
+ "sub_area": "Type"
},
{
"area": "Type",
- "canonical_name": "is_null",
- "description": "",
+ "canonical_name": "is_scalar",
+ "description": "Checks whether a variable is a scalar.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_is_scalar",
+ "codegen_line": 1033,
+ "notes": [
+ "Lowers `is_scalar()`: true for int/float/string/bool, a non-null tagged scalar, or a boxed",
+ "Mixed/Union value whose runtime tag is int (0), string (1), float (2), or bool (3). Null,",
+ "arrays, objects, and resources are not scalars, matching PHP."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/types/is_scalar.rs",
"sig_line": null
},
- "name": "is_null",
+ "name": "is_scalar",
"sig": {
"params": [
{
@@ -8870,30 +10129,30 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_null",
+ "slug": "is_scalar",
"sub_area": "Type"
},
{
"area": "Type",
- "canonical_name": "is_numeric",
- "description": "Lowers `is_numeric()` for concrete scalar values.",
+ "canonical_name": "is_string",
+ "description": "Checks whether a variable is a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/is_numeric.rs",
- "codegen_function": "lower_is_numeric",
- "codegen_line": 22,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_static_type_predicate",
+ "codegen_line": 736,
"notes": [
- "Lowers `is_numeric()` for concrete scalar values."
+ "Lowers a static `is_*` predicate for concrete non-Mixed values."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/is_string.rs",
"sig_line": null
},
- "name": "is_numeric",
+ "name": "is_string",
"sig": {
"params": [
{
@@ -8907,71 +10166,85 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_numeric",
+ "slug": "is_string",
"sub_area": "Type"
},
{
- "area": "Type",
- "canonical_name": "is_resource",
- "description": "Lowers `is_resource(value)` for static resources and boxed Mixed resource cells.",
+ "area": "Class",
+ "canonical_name": "is_subclass_of",
+ "description": "Checks if the object has a given class as one of its parents or implements it.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
- "codegen_function": "lower_is_resource",
- "codegen_line": 400,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_is_a_relation",
+ "codegen_line": 369,
"notes": [
- "Lowers `is_resource(value)` for static resources and boxed Mixed resource cells."
+ "Lowers `is_a()` and `is_subclass_of()` for object operands and literal targets."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/is_subclass_of.rs",
"sig_line": null
},
- "name": "is_resource",
+ "name": "is_subclass_of",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "object_or_class",
"optional": false,
"type": "mixed"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "class",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "allow_string",
+ "optional": true,
+ "type": "bool"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_resource",
- "sub_area": "Type"
+ "slug": "is_subclass_of",
+ "sub_area": "Class"
},
{
"area": "Filesystem",
- "canonical_name": "is_readable",
- "description": "Lowers `is_readable(path)` through the target-aware runtime access helper.",
+ "canonical_name": "is_writable",
+ "description": "Tells whether the filename is writable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_is_readable",
- "codegen_line": 4965,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_is_writable",
+ "codegen_line": 5616,
"notes": [
- "Lowers `is_readable(path)` through the target-aware runtime access helper."
+ "Lowers `is_writable(path)` through the target-aware runtime access helper."
],
"runtime_helpers": [
"__rt_is_executable",
- "__rt_is_readable",
+ "__rt_is_link",
"__rt_is_writable"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/is_writable.rs",
"sig_line": null
},
- "name": "is_readable",
+ "name": "is_writable",
"sig": {
"params": [
{
@@ -8985,215 +10258,229 @@
"return_type": "bool",
"variadic": null
},
- "slug": "is_readable",
+ "slug": "is_writable",
"sub_area": "Filesystem"
},
{
- "area": "Type",
- "canonical_name": "is_string",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "is_writeable",
+ "description": "Tells whether the filename is writable (alias of is_writable).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_is_writeable",
+ "codegen_line": 5624,
+ "notes": [
+ "Lowers `is_writeable(path)`, PHP's alias of `is_writable(path)`."
+ ],
+ "runtime_helpers": [
+ "__rt_is_executable",
+ "__rt_is_link",
+ "__rt_is_writable"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/is_writeable.rs",
"sig_line": null
},
- "name": "is_string",
+ "name": "is_writeable",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "filename",
"optional": false,
- "type": "mixed"
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "is_string",
- "sub_area": "Type"
+ "slug": "is_writeable",
+ "sub_area": "Filesystem"
},
{
- "area": "Filesystem",
- "canonical_name": "is_link",
- "description": "Lowers `is_link(path)` through the target-aware runtime lstat helper.",
+ "area": "Misc",
+ "canonical_name": "isset",
+ "description": "Determines whether a variable is set and is not null.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_is_link",
- "codegen_line": 4997,
+ "codegen_file": "src/codegen/lower_inst/builtins/isset.rs",
+ "codegen_function": "lower_isset",
+ "codegen_line": 24,
"notes": [
- "Lowers `is_link(path)` through the target-aware runtime lstat helper."
- ],
- "runtime_helpers": [
- "__rt_is_link",
- "__rt_path_is_wrapper",
- "__rt_readfile",
- "__rt_readfile_wrapper"
+ "Lowers `isset()` for values already evaluated by the EIR frontend."
],
+ "runtime_helpers": [],
"sig_arm": null,
"sig_file": null,
"sig_line": null
},
- "name": "is_link",
+ "name": "isset",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "var",
"optional": false,
- "type": "string"
+ "type": "mixed"
}
],
"return_type": "bool",
- "variadic": null
+ "variadic": "vars"
},
- "slug": "is_link",
- "sub_area": "Filesystem"
+ "slug": "isset",
+ "sub_area": "Variable"
},
{
- "area": "Filesystem",
- "canonical_name": "is_writeable",
- "description": "Lowers `is_writeable(path)`, PHP's alias of `is_writable(path)`.",
+ "area": "SPL",
+ "canonical_name": "iterator_apply",
+ "description": "Call a function for every element in an iterator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_is_writeable",
- "codegen_line": 4981,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_iterator_apply",
+ "codegen_line": 290,
"notes": [
- "Lowers `is_writeable(path)`, PHP's alias of `is_writable(path)`."
- ],
- "runtime_helpers": [
- "__rt_is_executable",
- "__rt_is_link",
- "__rt_is_writable"
+ "Lowers `iterator_apply()` over supported Traversable sources and callback forms."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/iterator_apply.rs",
"sig_line": null
},
- "name": "is_writeable",
+ "name": "iterator_apply",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "iterator",
"optional": false,
- "type": "string"
+ "type": "traversable"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "args",
+ "optional": true,
+ "type": "array"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "is_writeable",
- "sub_area": "Filesystem"
+ "slug": "iterator_apply",
+ "sub_area": "SPL"
},
{
- "area": "Filesystem",
- "canonical_name": "is_writable",
- "description": "Lowers `is_writable(path)` through the target-aware runtime access helper.",
+ "area": "SPL",
+ "canonical_name": "iterator_count",
+ "description": "Count the elements in an iterator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_is_writable",
- "codegen_line": 4973,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_iterator_count",
+ "codegen_line": 237,
"notes": [
- "Lowers `is_writable(path)` through the target-aware runtime access helper."
- ],
- "runtime_helpers": [
- "__rt_is_executable",
- "__rt_is_link",
- "__rt_is_writable"
+ "Lowers `iterator_count()` over arrays, `iterable`, and Traversable objects."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/iterator_count.rs",
"sig_line": null
},
- "name": "is_writable",
+ "name": "iterator_count",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "iterator",
"optional": false,
- "type": "string"
+ "type": "traversable"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "is_writable",
- "sub_area": "Filesystem"
+ "slug": "iterator_count",
+ "sub_area": "SPL"
},
{
- "area": "Misc",
- "canonical_name": "isset",
- "description": "Determines whether a variable is set and is not null.",
+ "area": "SPL",
+ "canonical_name": "iterator_to_array",
+ "description": "Copy the iterator into an array.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/isset.rs",
- "codegen_function": "lower_isset",
- "codegen_line": 24,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_iterator_to_array",
+ "codegen_line": 266,
"notes": [
- "Lowers `isset()` for values already evaluated by the EIR frontend."
+ "Lowers `iterator_to_array()` over arrays, `iterable`, and Traversable objects."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/iterator_to_array.rs",
"sig_line": null
},
- "name": "isset",
+ "name": "iterator_to_array",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "var",
- "optional": false,
- "type": "mixed"
+ "default": null,
+ "name": "iterator",
+ "optional": false,
+ "type": "traversable"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "preserve_keys",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "bool",
- "variadic": "vars"
+ "return_type": "array",
+ "variadic": null
},
- "slug": "isset",
- "sub_area": "Variable"
+ "slug": "iterator_to_array",
+ "sub_area": "SPL"
},
{
"area": "JSON",
"canonical_name": "json_decode",
- "description": "Lowers `json_decode(json, associative?, depth?, flags?)` through the shared JSON decoder runtime.",
+ "description": "Decodes a JSON string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/json.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/json.rs",
"codegen_function": "lower_json_decode",
"codegen_line": 30,
"notes": [
@@ -9203,7 +10490,7 @@
"__rt_json_decode_mixed"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/json_decode.rs",
"sig_line": null
},
"name": "json_decode",
@@ -9218,21 +10505,21 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "null",
"name": "associative",
"optional": true,
"type": "bool"
},
{
"by_ref": false,
- "default": null,
+ "default": "512",
"name": "depth",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "flags",
"optional": true,
"type": "int"
@@ -9247,13 +10534,13 @@
{
"area": "JSON",
"canonical_name": "json_encode",
- "description": "Lowers `json_encode(value, flags?, depth?)` through the shared JSON encoder runtime.",
+ "description": "Returns the JSON representation of a value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/json.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/json.rs",
"codegen_function": "lower_json_encode",
"codegen_line": 52,
"notes": [
@@ -9261,7 +10548,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/json_encode.rs",
"sig_line": null
},
"name": "json_encode",
@@ -9276,14 +10563,14 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "flags",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "512",
"name": "depth",
"optional": true,
"type": "int"
@@ -9298,13 +10585,13 @@
{
"area": "JSON",
"canonical_name": "json_last_error",
- "description": "Lowers `json_last_error()` by reading the shared runtime error-code symbol.",
+ "description": "Returns the last error (if any) occurred during the last JSON encoding/decoding.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/json.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/json.rs",
"codegen_function": "lower_json_last_error",
"codegen_line": 70,
"notes": [
@@ -9314,7 +10601,7 @@
"__rt_json_last_error_msg"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/json_last_error.rs",
"sig_line": null
},
"name": "json_last_error",
@@ -9329,13 +10616,13 @@
{
"area": "JSON",
"canonical_name": "json_last_error_msg",
- "description": "Lowers `json_last_error_msg()` through the runtime message lookup table.",
+ "description": "Returns the error string of the last json_encode() or json_decode() call.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/json.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/json.rs",
"codegen_function": "lower_json_last_error_msg",
"codegen_line": 85,
"notes": [
@@ -9346,7 +10633,7 @@
"__rt_json_validate"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/json_last_error_msg.rs",
"sig_line": null
},
"name": "json_last_error_msg",
@@ -9361,13 +10648,13 @@
{
"area": "JSON",
"canonical_name": "json_validate",
- "description": "Lowers `json_validate(json, depth?, flags?)` into the shared validator runtime.",
+ "description": "Checks if a string contains valid JSON.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/json.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/json.rs",
"codegen_function": "lower_json_validate",
"codegen_line": 95,
"notes": [
@@ -9377,7 +10664,7 @@
"__rt_json_validate"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/json_validate.rs",
"sig_line": null
},
"name": "json_validate",
@@ -9392,14 +10679,14 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "512",
"name": "depth",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "flags",
"optional": true,
"type": "int"
@@ -9414,17 +10701,17 @@
{
"area": "Array",
"canonical_name": "krsort",
- "description": "Lowers `krsort()` through the legacy reverse key-sort helper surface.",
+ "description": "Sorts an array by key in descending order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_krsort",
- "codegen_line": 1101,
+ "codegen_line": 1104,
"notes": [
- "Lowers `krsort()` through the legacy reverse key-sort helper surface."
+ "Lowers `krsort()` through the reverse key-sort helper surface."
],
"runtime_helpers": [
"__rt_krsort",
@@ -9432,7 +10719,7 @@
"__rt_natsort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/krsort.rs",
"sig_line": null
},
"name": "krsort",
@@ -9444,13 +10731,6 @@
"name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
}
],
"return_type": "bool",
@@ -9462,17 +10742,17 @@
{
"area": "Array",
"canonical_name": "ksort",
- "description": "Lowers `ksort()` through the legacy key-sort helper surface.",
+ "description": "Sorts an array by key in ascending order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_ksort",
- "codegen_line": 1096,
+ "codegen_line": 1099,
"notes": [
- "Lowers `ksort()` through the legacy key-sort helper surface."
+ "Lowers `ksort()` through the key-sort helper surface."
],
"runtime_helpers": [
"__rt_krsort",
@@ -9481,7 +10761,7 @@
"__rt_natsort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/ksort.rs",
"sig_line": null
},
"name": "ksort",
@@ -9493,13 +10773,6 @@
"name": "array",
"optional": false,
"type": "array"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
}
],
"return_type": "bool",
@@ -9511,13 +10784,13 @@
{
"area": "String",
"canonical_name": "lcfirst",
- "description": "Lowers `lcfirst()` by copying the string and lowercasing the first ASCII byte.",
+ "description": "Lowercases the first character of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_lcfirst",
"codegen_line": 104,
"notes": [
@@ -9527,7 +10800,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/lcfirst.rs",
"sig_line": null
},
"name": "lcfirst",
@@ -9549,43 +10822,40 @@
},
{
"area": "Filesystem",
- "canonical_name": "link",
- "description": "Lowers `link(oldpath, newpath)` through the target-aware libc wrapper.",
+ "canonical_name": "lchgrp",
+ "description": "Changes group ownership of a symlink.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_link",
- "codegen_line": 4810,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_lchgrp",
+ "codegen_line": 4488,
"notes": [
- "Lowers `link(oldpath, newpath)` through the target-aware libc wrapper."
+ "Lowers `lchgrp(path, group)` for integer GIDs and string group names without following symlinks."
],
"runtime_helpers": [
- "__rt_fileatime",
- "__rt_filectime",
- "__rt_link",
- "__rt_readlink"
+ "__rt_umask"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/lchgrp.rs",
"sig_line": null
},
- "name": "link",
+ "name": "lchgrp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "target",
+ "name": "filename",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "link",
+ "name": "group",
"optional": false,
"type": "string"
}
@@ -9593,153 +10863,156 @@
"return_type": "bool",
"variadic": null
},
- "slug": "link",
+ "slug": "lchgrp",
"sub_area": "Filesystem"
},
{
"area": "Filesystem",
- "canonical_name": "linkinfo",
- "description": "Lowers `linkinfo(path)` through the target-aware runtime lstat helper.",
+ "canonical_name": "lchown",
+ "description": "Changes user ownership of a symlink.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_linkinfo",
- "codegen_line": 4797,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_lchown",
+ "codegen_line": 4483,
"notes": [
- "Lowers `linkinfo(path)` through the target-aware runtime lstat helper."
+ "Lowers `lchown(path, owner)` for integer UIDs and string user names without following symlinks."
],
"runtime_helpers": [
- "__rt_link",
- "__rt_linkinfo",
- "__rt_readlink",
- "__rt_symlink"
+ "__rt_umask"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/lchown.rs",
"sig_line": null
},
- "name": "linkinfo",
+ "name": "lchown",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "path",
+ "name": "filename",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "user",
"optional": false,
"type": "string"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "linkinfo",
+ "slug": "lchown",
"sub_area": "Filesystem"
},
{
"area": "Filesystem",
- "canonical_name": "lchgrp",
- "description": "Lowers `lchgrp(path, group)` for integer GIDs and string group names without following symlinks.",
+ "canonical_name": "link",
+ "description": "Creates a hard link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_lchgrp",
- "codegen_line": 3845,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_link",
+ "codegen_line": 5453,
"notes": [
- "Lowers `lchgrp(path, group)` for integer GIDs and string group names without following symlinks."
+ "Lowers `link(oldpath, newpath)` through the target-aware libc wrapper."
],
"runtime_helpers": [
- "__rt_umask"
+ "__rt_fileatime",
+ "__rt_filectime",
+ "__rt_link",
+ "__rt_readlink"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/link.rs",
"sig_line": null
},
- "name": "lchgrp",
+ "name": "link",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "target",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "group",
+ "name": "link",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "lchgrp",
+ "slug": "link",
"sub_area": "Filesystem"
},
{
"area": "Filesystem",
- "canonical_name": "lchown",
- "description": "Lowers `lchown(path, owner)` for integer UIDs and string user names without following symlinks.",
+ "canonical_name": "linkinfo",
+ "description": "Gets information about a link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_lchown",
- "codegen_line": 3840,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_linkinfo",
+ "codegen_line": 5440,
"notes": [
- "Lowers `lchown(path, owner)` for integer UIDs and string user names without following symlinks."
+ "Lowers `linkinfo(path)` through the target-aware runtime lstat helper."
],
"runtime_helpers": [
- "__rt_umask"
+ "__rt_link",
+ "__rt_linkinfo",
+ "__rt_readlink",
+ "__rt_symlink"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/linkinfo.rs",
"sig_line": null
},
- "name": "lchown",
+ "name": "linkinfo",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "path",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "user",
- "optional": false,
- "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "lchown",
+ "slug": "linkinfo",
"sub_area": "Filesystem"
},
{
"area": "Date",
"canonical_name": "localtime",
- "description": "Lowers `localtime([$timestamp[, $associative]])` through the shared decomposition runtime helper.",
+ "description": "Returns the local time.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_localtime",
"codegen_line": 220,
"notes": [
@@ -9755,7 +11028,7 @@
"__rt_localtime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/localtime.rs",
"sig_line": null
},
"name": "localtime",
@@ -9763,14 +11036,14 @@
"params": [
{
"by_ref": false,
- "default": null,
+ "default": "-1",
"name": "timestamp",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "associative",
"optional": true,
"type": "bool"
@@ -9785,13 +11058,13 @@
{
"area": "Math",
"canonical_name": "log",
- "description": "Lowers `log()` in one-argument and base-changing two-argument forms.",
+ "description": "Natural logarithm.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/libm.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
"codegen_function": "lower_log",
"codegen_line": 51,
"notes": [
@@ -9799,7 +11072,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/log.rs",
"sig_line": null
},
"name": "log",
@@ -9814,7 +11087,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "2.718281828459045",
"name": "base",
"optional": true,
"type": "float"
@@ -9829,19 +11102,21 @@
{
"area": "Math",
"canonical_name": "log10",
- "description": "",
+ "description": "Returns the base-10 logarithm of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
+ "notes": [
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/math/log10.rs",
"sig_line": null
},
"name": "log10",
@@ -9864,19 +11139,21 @@
{
"area": "Math",
"canonical_name": "log2",
- "description": "",
+ "description": "Returns the base-2 logarithm of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
+ "notes": [
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/math/log2.rs",
"sig_line": null
},
"name": "log2",
@@ -9896,18 +11173,58 @@
"slug": "log2",
"sub_area": "Math"
},
+ {
+ "area": "String",
+ "canonical_name": "long2ip",
+ "description": "Converts an IPv4 address from long integer to dotted string notation.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_long2ip",
+ "codegen_line": 476,
+ "notes": [
+ "Lowers `long2ip(value)` through the IPv4 formatting runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_ip2long",
+ "__rt_long2ip"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/string/long2ip.rs",
+ "sig_line": null
+ },
+ "name": "long2ip",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "ip",
+ "optional": false,
+ "type": "int"
+ }
+ ],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "long2ip",
+ "sub_area": "String"
+ },
{
"area": "Filesystem",
"canonical_name": "lstat",
- "description": "Lowers `lstat(path)` and boxes the runtime lstat array or PHP false result.",
+ "description": "Gives information about a file or symbolic link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_lstat",
- "codegen_line": 4891,
+ "codegen_line": 5534,
"notes": [
"Lowers `lstat(path)` and boxes the runtime lstat array or PHP false result."
],
@@ -9916,7 +11233,7 @@
"__rt_lstat_array"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/lstat.rs",
"sig_line": null
},
"name": "lstat",
@@ -9939,13 +11256,13 @@
{
"area": "String",
"canonical_name": "ltrim",
- "description": "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.",
+ "description": "Strips whitespace (or other characters) from the beginning of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_trim_like",
"codegen_line": 112,
"notes": [
@@ -9953,7 +11270,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ltrim.rs",
"sig_line": null
},
"name": "ltrim",
@@ -9968,7 +11285,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "' \\n\\r\\t\\x0b\\x0c\\x00'",
"name": "characters",
"optional": true,
"type": "string"
@@ -9983,13 +11300,13 @@
{
"area": "Math",
"canonical_name": "max",
- "description": "Lowers numeric `min()` and `max()` over concrete integer-like or float operands.",
+ "description": "Find highest value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
"codegen_function": "lower_min_max",
"codegen_line": 204,
"notes": [
@@ -9997,7 +11314,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/max.rs",
"sig_line": null
},
"name": "max",
@@ -10011,7 +11328,7 @@
"type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": "values"
},
"slug": "max",
@@ -10020,13 +11337,13 @@
{
"area": "String",
"canonical_name": "md5",
- "description": "Lowers `md5(data, binary?)` through the shared crypto-backed runtime helper.",
+ "description": "Calculates the MD5 hash of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_md5",
"codegen_line": 355,
"notes": [
@@ -10038,7 +11355,7 @@
"__rt_sha1"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/md5.rs",
"sig_line": null
},
"name": "md5",
@@ -10053,7 +11370,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "binary",
"optional": true,
"type": "bool"
@@ -10068,13 +11385,13 @@
{
"area": "Date",
"canonical_name": "microtime",
- "description": "Lowers `microtime()` / `microtime(true)` / `microtime(false)` / `microtime($flag)`.",
+ "description": "Returns the current Unix timestamp with microseconds.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_microtime",
"codegen_line": 111,
"notes": [
@@ -10093,7 +11410,7 @@
"__rt_microtime_str"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/microtime.rs",
"sig_line": null
},
"name": "microtime",
@@ -10101,13 +11418,13 @@
"params": [
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "as_float",
"optional": true,
"type": "bool"
}
],
- "return_type": "int",
+ "return_type": "mixed",
"variadic": null
},
"slug": "microtime",
@@ -10116,13 +11433,13 @@
{
"area": "Math",
"canonical_name": "min",
- "description": "Lowers numeric `min()` and `max()` over concrete integer-like or float operands.",
+ "description": "Find lowest value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
"codegen_function": "lower_min_max",
"codegen_line": 204,
"notes": [
@@ -10130,7 +11447,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/min.rs",
"sig_line": null
},
"name": "min",
@@ -10144,7 +11461,7 @@
"type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "mixed",
"variadic": "values"
},
"slug": "min",
@@ -10153,59 +11470,38 @@
{
"area": "Filesystem",
"canonical_name": "mkdir",
- "description": "Lowers `mkdir(path)` through the target-aware runtime helper.",
+ "description": "Makes a directory.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_mkdir",
- "codegen_line": 3785,
+ "codegen_line": 4428,
"notes": [
"Lowers `mkdir(path)` through the target-aware runtime helper."
],
"runtime_helpers": [
- "__rt_chdir",
- "__rt_copy",
- "__rt_mkdir",
- "__rt_rmdir",
- "__rt_tempnam"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "mkdir",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "directory",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "permissions",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "recursive",
- "optional": false,
- "type": "bool"
- },
+ "__rt_chdir",
+ "__rt_copy",
+ "__rt_mkdir",
+ "__rt_rmdir",
+ "__rt_tempnam"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/mkdir.rs",
+ "sig_line": null
+ },
+ "name": "mkdir",
+ "sig": {
+ "params": [
{
"by_ref": false,
"default": null,
- "name": "context",
+ "name": "directory",
"optional": false,
- "type": "bool"
+ "type": "string"
}
],
"return_type": "bool",
@@ -10217,13 +11513,13 @@
{
"area": "Date",
"canonical_name": "mktime",
- "description": "Lowers `mktime(hour, minute, second, month, day, year)` through the runtime helper.",
+ "description": "Returns the Unix timestamp for a date.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_mktime",
"codegen_line": 140,
"notes": [
@@ -10235,7 +11531,7 @@
"__rt_mktime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/mktime.rs",
"sig_line": null
},
"name": "mktime",
@@ -10293,19 +11589,23 @@
{
"area": "Math",
"canonical_name": "mt_rand",
- "description": "",
+ "description": "Generate a random value via the Mersenne Twister Random Number Generator.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/math/random.rs",
+ "codegen_function": "lower_rand",
+ "codegen_line": 22,
+ "notes": [
+ "Lowers `rand()` and `mt_rand()` with either zero args or an inclusive range."
+ ],
+ "runtime_helpers": [
+ "__rt_random_u32"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/math/mt_rand.rs",
"sig_line": null
},
"name": "mt_rand",
@@ -10335,15 +11635,15 @@
{
"area": "Array",
"canonical_name": "natcasesort",
- "description": "Lowers `natcasesort()` for indexed integer arrays through the case-insensitive wrapper.",
+ "description": "Sorts an array using a case-insensitive natural order algorithm.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_natcasesort",
- "codegen_line": 1111,
+ "codegen_line": 1114,
"notes": [
"Lowers `natcasesort()` for indexed integer arrays through the case-insensitive wrapper."
],
@@ -10351,7 +11651,7 @@
"__rt_natcasesort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/natcasesort.rs",
"sig_line": null
},
"name": "natcasesort",
@@ -10374,15 +11674,15 @@
{
"area": "Array",
"canonical_name": "natsort",
- "description": "Lowers `natsort()` for indexed integer arrays through the natural-sort runtime wrapper.",
+ "description": "Sorts an array using a natural order algorithm.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_natsort",
- "codegen_line": 1106,
+ "codegen_line": 1109,
"notes": [
"Lowers `natsort()` for indexed integer arrays through the natural-sort runtime wrapper."
],
@@ -10391,7 +11691,7 @@
"__rt_natsort"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/natsort.rs",
"sig_line": null
},
"name": "natsort",
@@ -10414,306 +11714,93 @@
{
"area": "String",
"canonical_name": "nl2br",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Inserts HTML line breaks before newlines in a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "nl2br",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "string",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "use_xhtml",
- "optional": false,
- "type": "bool"
- }
- ],
- "return_type": "string",
- "variadic": null
- },
- "slug": "nl2br",
- "sub_area": "String"
- },
- {
- "area": "String",
- "canonical_name": "number_format",
- "description": "Lowers `number_format()` by arranging its runtime helper arguments.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_number_format",
- "codegen_line": 875,
- "notes": [
- "Lowers `number_format()` by arranging its runtime helper arguments."
- ],
- "runtime_helpers": [
- "__rt_number_format"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "number_format",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "num",
- "optional": false,
- "type": "float"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "decimals",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "decimal_separator",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "thousands_separator",
- "optional": true,
- "type": "string"
- }
- ],
- "return_type": "string",
- "variadic": null
- },
- "slug": "number_format",
- "sub_area": "String"
- },
- {
- "area": "String",
- "canonical_name": "ord",
- "description": "Lowers `ord()` by returning the first byte of a string or zero for empty input.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_ord",
- "codegen_line": 834,
- "notes": [
- "Lowers `ord()` by returning the first byte of a string or zero for empty input."
- ],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "ord",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "character",
- "optional": false,
- "type": "string"
- }
- ],
- "return_type": "int",
- "variadic": null
- },
- "slug": "ord",
- "sub_area": "String"
- },
- {
- "area": "Process",
- "canonical_name": "passthru",
- "description": "Lowers `passthru(command)` through libc `system()` for direct stdout passthrough.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_passthru",
- "codegen_line": 714,
- "notes": [
- "Lowers `passthru(command)` through libc `system()` for direct stdout passthrough."
- ],
- "runtime_helpers": [
- "__rt_cstr",
- "__rt_shell_exec"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "passthru",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "command",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "result_code",
- "optional": false,
- "type": "int"
- }
- ],
- "return_type": "void",
- "variadic": null
- },
- "slug": "passthru",
- "sub_area": "Process"
- },
- {
- "area": "Filesystem",
- "canonical_name": "pathinfo",
- "description": "Lowers `pathinfo(path, flags?)` through string, array, or boxed dynamic helpers.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_pathinfo",
- "codegen_line": 4001,
- "notes": [
- "Lowers `pathinfo(path, flags?)` through string, array, or boxed dynamic helpers."
- ],
- "runtime_helpers": [
- "__rt_pathinfo_array"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "pathinfo",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "path",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": true,
- "type": "int"
- }
- ],
- "return_type": "mixed",
- "variadic": null
- },
- "slug": "pathinfo",
- "sub_area": "Filesystem"
- },
- {
- "area": "Misc",
- "canonical_name": "php_uname",
- "description": "Returns information about the operating system PHP is running on.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_php_uname",
- "codegen_line": 672,
- "notes": [
- "Lowers `php_uname(mode?)` through the target-aware uname runtime helper."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_php_uname"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/nl2br.rs",
"sig_line": null
},
- "name": "php_uname",
+ "name": "nl2br",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "mode",
- "optional": true,
+ "name": "string",
+ "optional": false,
"type": "string"
}
],
"return_type": "string",
"variadic": null
},
- "slug": "php_uname",
- "sub_area": "Info"
+ "slug": "nl2br",
+ "sub_area": "String"
},
{
- "area": "Misc",
- "canonical_name": "phpversion",
- "description": "Returns the current PHP version information.",
+ "area": "String",
+ "canonical_name": "number_format",
+ "description": "Formats a number with grouped thousands.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_phpversion",
- "codegen_line": 734,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_number_format",
+ "codegen_line": 875,
"notes": [
- "Lowers `phpversion()` as the compiler package version string."
+ "Lowers `number_format()` by arranging its runtime helper arguments."
+ ],
+ "runtime_helpers": [
+ "__rt_number_format"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/number_format.rs",
"sig_line": null
},
- "name": "phpversion",
+ "name": "number_format",
"sig": {
"params": [
{
"by_ref": false,
- "default": "null",
- "name": "extension",
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "decimals",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "'.'",
+ "name": "decimal_separator",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "','",
+ "name": "thousands_separator",
"optional": true,
"type": "string"
}
@@ -10721,833 +11808,831 @@
"return_type": "string",
"variadic": null
},
- "slug": "phpversion",
- "sub_area": "Info"
+ "slug": "number_format",
+ "sub_area": "String"
},
{
- "area": "Math",
- "canonical_name": "pi",
- "description": "Lowers `pi()` as the same data-section float constant used by the legacy backend.",
+ "area": "IO",
+ "canonical_name": "opendir",
+ "description": "Open directory handle.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_pi",
- "codegen_line": 596,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_opendir",
+ "codegen_line": 3547,
"notes": [
- "Lowers `pi()` as the same data-section float constant used by the legacy backend."
+ "Lowers `opendir(path)` and boxes the directory stream as `resource|false`."
+ ],
+ "runtime_helpers": [
+ "__rt_opendir",
+ "__rt_readdir",
+ "__rt_user_wrapper_dir_readdir"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/opendir.rs",
"sig_line": null
},
- "name": "pi",
+ "name": "opendir",
"sig": {
- "params": [],
- "return_type": "float",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "directory",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "mixed",
"variadic": null
},
- "slug": "pi",
- "sub_area": "Math"
+ "slug": "opendir",
+ "sub_area": "IO"
},
{
- "area": "Math",
- "canonical_name": "pow",
- "description": "Lowers `pow()` for concrete integer-like and floating operands.",
+ "area": "String",
+ "canonical_name": "ord",
+ "description": "Returns the ASCII value of the first character of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/binary.rs",
- "codegen_function": "lower_pow",
- "codegen_line": 114,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_ord",
+ "codegen_line": 834,
"notes": [
- "Lowers `pow()` for concrete integer-like and floating operands."
+ "Lowers `ord()` by returning the first byte of a string or zero for empty input."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ord.rs",
"sig_line": null
},
- "name": "pow",
+ "name": "ord",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
- "optional": false,
- "type": "float"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "exponent",
+ "name": "character",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "int",
"variadic": null
},
- "slug": "pow",
- "sub_area": "Math"
+ "slug": "ord",
+ "sub_area": "String"
},
{
- "area": "Regex",
- "canonical_name": "preg_match",
- "description": "Lowers `preg_match(pattern, subject)` through the shared regex runtime helper.",
+ "area": "Process",
+ "canonical_name": "passthru",
+ "description": "Executes an external program and passes its output directly.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/regex.rs",
- "codegen_function": "lower_preg_match",
- "codegen_line": 28,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_passthru",
+ "codegen_line": 714,
"notes": [
- "Lowers `preg_match(pattern, subject)` through the shared regex runtime helper."
+ "Lowers `passthru(command)` through libc `system()` for direct stdout passthrough."
],
"runtime_helpers": [
- "__rt_preg_match",
- "__rt_preg_match_capture"
+ "__rt_cstr",
+ "__rt_shell_exec"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/passthru.rs",
"sig_line": null
},
- "name": "preg_match",
+ "name": "passthru",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "subject",
+ "name": "command",
"optional": false,
"type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "matches",
- "optional": true,
- "type": "array"
}
],
- "return_type": "int",
+ "return_type": "void",
"variadic": null
},
- "slug": "preg_match",
- "sub_area": "Regex"
+ "slug": "passthru",
+ "sub_area": "Process"
},
{
- "area": "Regex",
- "canonical_name": "preg_match_all",
- "description": "Lowers `preg_match_all(pattern, subject)` through the shared regex runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "pathinfo",
+ "description": "Returns information about a file path.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/regex.rs",
- "codegen_function": "lower_preg_match_all",
- "codegen_line": 52,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_pathinfo",
+ "codegen_line": 4644,
"notes": [
- "Lowers `preg_match_all(pattern, subject)` through the shared regex runtime helper."
+ "Lowers `pathinfo(path, flags?)` through string, array, or boxed dynamic helpers."
],
"runtime_helpers": [
- "__rt_preg_match_all"
+ "__rt_pathinfo_array"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/pathinfo.rs",
"sig_line": null
},
- "name": "preg_match_all",
+ "name": "pathinfo",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
+ "name": "path",
"optional": false,
"type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "subject",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "matches",
- "optional": false,
- "type": "array"
+ "default": "15",
+ "name": "flags",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "preg_match_all",
- "sub_area": "Regex"
+ "slug": "pathinfo",
+ "sub_area": "Filesystem"
},
{
- "area": "Regex",
- "canonical_name": "preg_replace_callback",
- "description": "Lowers `preg_replace_callback(pattern, callback, subject)` through supported direct callbacks.",
+ "area": "Process",
+ "canonical_name": "pclose",
+ "description": "Closes process file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/regex.rs",
- "codegen_function": "lower_preg_replace_callback",
- "codegen_line": 90,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_pclose",
+ "codegen_line": 3630,
"notes": [
- "Lowers `preg_replace_callback(pattern, callback, subject)` through supported direct callbacks."
+ "Lowers `pclose(handle)` and returns the child process status."
],
"runtime_helpers": [
- "__rt_preg_replace_callback"
+ "__rt_pclose"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/pclose.rs",
"sig_line": null
},
- "name": "preg_replace_callback",
+ "name": "pclose",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "callback",
- "optional": false,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "subject",
+ "name": "handle",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": "-1",
- "name": "limit",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": "null",
- "name": "count",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": "0",
- "name": "flags",
- "optional": true,
- "type": "int"
+ "type": "resource"
}
],
- "return_type": "array",
+ "return_type": "int",
"variadic": null
},
- "slug": "preg_replace_callback",
- "sub_area": "Regex"
+ "slug": "pclose",
+ "sub_area": "Process"
},
{
- "area": "Regex",
- "canonical_name": "preg_replace",
- "description": "Lowers `preg_replace(pattern, replacement, subject)` through the regex replacement helper.",
+ "area": "Streams",
+ "canonical_name": "pfsockopen",
+ "description": "Open persistent Internet or Unix domain socket connection.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/regex.rs",
- "codegen_function": "lower_preg_replace",
- "codegen_line": 65,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_fsockopen",
+ "codegen_line": 3644,
"notes": [
- "Lowers `preg_replace(pattern, replacement, subject)` through the regex replacement helper."
- ],
- "runtime_helpers": [
- "__rt_preg_replace"
+ "Lowers `fsockopen(host, port, errno?, errstr?, timeout?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/pfsockopen.rs",
"sig_line": null
},
- "name": "preg_replace",
+ "name": "pfsockopen",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pattern",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "replacement",
+ "name": "hostname",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "subject",
+ "name": "port",
"optional": false,
- "type": "string"
+ "type": "int"
},
{
- "by_ref": false,
- "default": "-1",
- "name": "limit",
+ "by_ref": true,
+ "default": "null",
+ "name": "error_code",
"optional": true,
"type": "int"
},
{
"by_ref": true,
"default": "null",
- "name": "count",
+ "name": "error_message",
"optional": true,
- "type": "int"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "timeout",
+ "optional": true,
+ "type": "float"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "preg_replace",
- "sub_area": "Regex"
+ "slug": "pfsockopen",
+ "sub_area": "Streams"
},
{
- "area": "Regex",
- "canonical_name": "preg_split",
- "description": "Lowers `preg_split(pattern, subject, limit?, flags?)` through the regex split helper.",
+ "area": "Misc",
+ "canonical_name": "php_uname",
+ "description": "Returns information about the operating system PHP is running on.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/regex.rs",
- "codegen_function": "lower_preg_split",
- "codegen_line": 374,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_php_uname",
+ "codegen_line": 672,
"notes": [
- "Lowers `preg_split(pattern, subject, limit?, flags?)` through the regex split helper."
+ "Lowers `php_uname(mode?)` through the target-aware uname runtime helper."
],
"runtime_helpers": [
- "__rt_preg_split"
+ "__rt_php_uname"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/php_uname.rs",
"sig_line": null
},
- "name": "preg_split",
+ "name": "php_uname",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "pattern",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "subject",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "limit",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
+ "default": "'a'",
+ "name": "mode",
"optional": true,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "string",
"variadic": null
},
- "slug": "preg_split",
- "sub_area": "Regex"
+ "slug": "php_uname",
+ "sub_area": "Info"
},
{
"area": "Misc",
- "canonical_name": "print_r",
- "description": "Prints human-readable information about a variable.",
+ "canonical_name": "phpversion",
+ "description": "Returns the current PHP version information.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/debug.rs",
- "codegen_function": "lower_print_r",
- "codegen_line": 24,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_phpversion",
+ "codegen_line": 251,
"notes": [
- "Lowers `print_r(value)` for concrete scalar/resource values and array/hash shells."
+ "Lowers `phpversion()` as the compiler package version string."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/phpversion.rs",
"sig_line": null
},
- "name": "print_r",
+ "name": "phpversion",
"sig": {
"params": [],
- "return_type": "void",
- "variadic": "values"
+ "return_type": "string",
+ "variadic": null
},
- "slug": "print_r",
- "sub_area": "Variable"
+ "slug": "phpversion",
+ "sub_area": "Info"
},
{
- "area": "String",
- "canonical_name": "printf",
- "description": "Lowers `printf(format, values...)` as `sprintf()` followed by stdout emission.",
+ "area": "Math",
+ "canonical_name": "pi",
+ "description": "Gets value of pi.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_printf",
- "codegen_line": 517,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_pi",
+ "codegen_line": 240,
"notes": [
- "Lowers `printf(format, values...)` as `sprintf()` followed by stdout emission."
- ],
- "runtime_helpers": [
- "__rt_sprintf"
+ "Lowers `pi()` as a data-section float constant."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/pi.rs",
"sig_line": null
},
- "name": "printf",
+ "name": "pi",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "format",
- "optional": false,
- "type": "string"
- }
- ],
- "return_type": "int",
- "variadic": "values"
+ "params": [],
+ "return_type": "float",
+ "variadic": null
},
- "slug": "printf",
- "sub_area": "String"
+ "slug": "pi",
+ "sub_area": "Math"
},
{
- "area": "Pointer",
- "canonical_name": "ptr",
- "description": "Lowers `ptr(value)` by materializing the address of addressable local/global storage.",
+ "area": "Process",
+ "canonical_name": "popen",
+ "description": "Opens process file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr",
- "codegen_line": 25,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_popen",
+ "codegen_line": 3602,
"notes": [
- "Lowers `ptr(value)` by materializing the address of addressable local/global storage."
+ "Lowers `popen(command, mode)` and boxes the process pipe as `resource|false`."
+ ],
+ "runtime_helpers": [
+ "__rt_popen"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/popen.rs",
"sig_line": null
},
- "name": "ptr",
+ "name": "popen",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "command",
"optional": false,
- "type": "mixed"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "mode",
+ "optional": false,
+ "type": "string"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "ptr",
- "sub_area": "Pointer"
+ "slug": "popen",
+ "sub_area": "Process"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_get",
- "description": "Lowers `ptr_get(pointer)` by reading one machine word through a checked pointer.",
+ "area": "Math",
+ "canonical_name": "pow",
+ "description": "Exponential expression.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_get",
- "codegen_line": 109,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/binary.rs",
+ "codegen_function": "lower_pow",
+ "codegen_line": 121,
"notes": [
- "Lowers `ptr_get(pointer)` by reading one machine word through a checked pointer."
+ "Lowers `pow()` for concrete integer-like and floating operands."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/pow.rs",
"sig_line": null
},
- "name": "ptr_get",
+ "name": "pow",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "num",
"optional": false,
- "type": "pointer"
+ "type": "float"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "exponent",
+ "optional": false,
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "ptr_get",
- "sub_area": "Pointer"
+ "slug": "pow",
+ "sub_area": "Math"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_is_null",
- "description": "Lowers `ptr_is_null(pointer)` by comparing the raw pointer address to zero.",
+ "area": "Regex",
+ "canonical_name": "preg_match",
+ "description": "Performs a regular expression match.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_is_null",
- "codegen_line": 56,
+ "codegen_file": "src/codegen/lower_inst/builtins/regex.rs",
+ "codegen_function": "lower_preg_match",
+ "codegen_line": 28,
"notes": [
- "Lowers `ptr_is_null(pointer)` by comparing the raw pointer address to zero."
+ "Lowers `preg_match(pattern, subject)` through the shared regex runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_preg_match",
+ "__rt_preg_match_all",
+ "__rt_preg_match_capture"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/preg_match.rs",
"sig_line": null
},
- "name": "ptr_is_null",
+ "name": "preg_match",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "pattern",
"optional": false,
- "type": "pointer"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "subject",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": true,
+ "default": "[]",
+ "name": "matches",
+ "optional": true,
+ "type": "array"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "ptr_is_null",
- "sub_area": "Pointer"
+ "slug": "preg_match",
+ "sub_area": "Regex"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_null",
- "description": "Lowers `ptr_null()` by materializing the raw null pointer sentinel.",
+ "area": "Regex",
+ "canonical_name": "preg_match_all",
+ "description": "Performs a global regular expression match and returns the number of matches.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_null",
+ "codegen_file": "src/codegen/lower_inst/builtins/regex.rs",
+ "codegen_function": "lower_preg_match_all",
"codegen_line": 49,
"notes": [
- "Lowers `ptr_null()` by materializing the raw null pointer sentinel."
+ "Lowers `preg_match_all(pattern, subject)` through the shared regex runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_preg_match_all",
+ "__rt_preg_replace"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/preg_match_all.rs",
"sig_line": null
},
- "name": "ptr_null",
+ "name": "preg_match_all",
"sig": {
- "params": [],
- "return_type": "mixed",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "pattern",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "subject",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "int",
"variadic": null
},
- "slug": "ptr_null",
- "sub_area": "Pointer"
+ "slug": "preg_match_all",
+ "sub_area": "Regex"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_offset",
- "description": "Lowers `ptr_offset(pointer, offset)` by adding a byte offset to a raw address.",
+ "area": "Regex",
+ "canonical_name": "preg_replace",
+ "description": "Performs a regular expression search and replace.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_offset",
- "codegen_line": 86,
+ "codegen_file": "src/codegen/lower_inst/builtins/regex.rs",
+ "codegen_function": "lower_preg_replace",
+ "codegen_line": 62,
"notes": [
- "Lowers `ptr_offset(pointer, offset)` by adding a byte offset to a raw address."
+ "Lowers `preg_replace(pattern, replacement, subject)` through the regex replacement helper."
+ ],
+ "runtime_helpers": [
+ "__rt_preg_replace"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/preg_replace.rs",
"sig_line": null
},
- "name": "ptr_offset",
+ "name": "preg_replace",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "pattern",
"optional": false,
- "type": "pointer"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "offset",
+ "name": "replacement",
"optional": false,
- "type": "int"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "subject",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "ptr_offset",
- "sub_area": "Pointer"
+ "slug": "preg_replace",
+ "sub_area": "Regex"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_read16",
- "description": "Lowers `ptr_read16(pointer)` by reading one unsigned 16-bit word through a checked pointer.",
+ "area": "Regex",
+ "canonical_name": "preg_replace_callback",
+ "description": "Performs a regular expression search and replace using a callback.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_read16",
- "codegen_line": 124,
+ "codegen_file": "src/codegen/lower_inst/builtins/regex.rs",
+ "codegen_function": "lower_preg_replace_callback",
+ "codegen_line": 84,
"notes": [
- "Lowers `ptr_read16(pointer)` by reading one unsigned 16-bit word through a checked pointer."
+ "Lowers `preg_replace_callback(pattern, callback, subject)` through supported direct callbacks."
],
"runtime_helpers": [
- "__rt_ptr_read_string"
+ "__rt_preg_replace_callback"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/preg_replace_callback.rs",
"sig_line": null
},
- "name": "ptr_read16",
+ "name": "preg_replace_callback",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "pattern",
"optional": false,
- "type": "pointer"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "callback",
+ "optional": false,
+ "type": "callable"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "subject",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "ptr_read16",
- "sub_area": "Pointer"
+ "slug": "preg_replace_callback",
+ "sub_area": "Regex"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_read32",
- "description": "Lowers `ptr_read32(pointer)` by reading one unsigned 32-bit word through a checked pointer.",
+ "area": "Regex",
+ "canonical_name": "preg_split",
+ "description": "Splits a string by a regular expression.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_read32",
- "codegen_line": 129,
+ "codegen_file": "src/codegen/lower_inst/builtins/regex.rs",
+ "codegen_function": "lower_preg_split",
+ "codegen_line": 388,
"notes": [
- "Lowers `ptr_read32(pointer)` by reading one unsigned 32-bit word through a checked pointer."
+ "Lowers `preg_split(pattern, subject, limit?, flags?)` through the regex split helper."
],
"runtime_helpers": [
- "__rt_ptr_read_string"
+ "__rt_preg_split"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/preg_split.rs",
"sig_line": null
},
- "name": "ptr_read32",
+ "name": "preg_split",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "pattern",
"optional": false,
- "type": "pointer"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "subject",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "-1",
+ "name": "limit",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "flags",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "ptr_read32",
- "sub_area": "Pointer"
+ "slug": "preg_split",
+ "sub_area": "Regex"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_read8",
- "description": "Lowers `ptr_read8(pointer)` by reading one unsigned byte through a checked pointer.",
+ "area": "Misc",
+ "canonical_name": "print_r",
+ "description": "Prints human-readable information about a variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_read8",
- "codegen_line": 119,
+ "codegen_file": "src/codegen/lower_inst/builtins/debug.rs",
+ "codegen_function": "lower_print_r",
+ "codegen_line": 24,
"notes": [
- "Lowers `ptr_read8(pointer)` by reading one unsigned byte through a checked pointer."
+ "Lowers `print_r(value)` for concrete scalar/resource values and array/hash shells."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/print_r.rs",
"sig_line": null
},
- "name": "ptr_read8",
+ "name": "print_r",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
+ "name": "value",
"optional": false,
- "type": "pointer"
+ "type": "mixed"
}
],
- "return_type": "int",
+ "return_type": "void",
"variadic": null
},
- "slug": "ptr_read8",
- "sub_area": "Pointer"
+ "slug": "print_r",
+ "sub_area": "Variable"
},
{
- "area": "Pointer",
- "canonical_name": "ptr_read_string",
- "description": "Lowers `ptr_read_string(pointer, length)` by copying raw bytes into an owned PHP string.",
+ "area": "String",
+ "canonical_name": "printf",
+ "description": "Outputs a formatted string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_read_string",
- "codegen_line": 134,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_printf",
+ "codegen_line": 517,
"notes": [
- "Lowers `ptr_read_string(pointer, length)` by copying raw bytes into an owned PHP string."
+ "Lowers `printf(format, values...)` as `sprintf()` followed by stdout emission."
],
"runtime_helpers": [
- "__rt_ptr_read_string"
+ "__rt_sprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/printf.rs",
"sig_line": null
},
- "name": "ptr_read_string",
+ "name": "printf",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "pointer",
- "optional": false,
- "type": "pointer"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
+ "name": "format",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "string",
- "variadic": null
+ "return_type": "int",
+ "variadic": "values"
},
- "slug": "ptr_read_string",
- "sub_area": "Pointer"
+ "slug": "printf",
+ "sub_area": "String"
},
{
"area": "Pointer",
- "canonical_name": "ptr_set",
- "description": "Lowers `ptr_set(pointer, value)` by writing one machine word through a checked pointer.",
+ "canonical_name": "ptr",
+ "description": "Returns a raw pointer to the given variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_set",
- "codegen_line": 114,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr",
+ "codegen_line": 25,
"notes": [
- "Lowers `ptr_set(pointer, value)` by writing one machine word through a checked pointer."
+ "Lowers `ptr(value)` by materializing the address of addressable local/global storage."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr.rs",
"sig_line": null
},
- "name": "ptr_set",
+ "name": "ptr",
"sig": {
"params": [
- {
- "by_ref": false,
- "default": null,
- "name": "pointer",
- "optional": false,
- "type": "pointer"
- },
{
"by_ref": false,
"default": null,
@@ -11556,72 +12641,70 @@
"type": "mixed"
}
],
- "return_type": "void",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ptr_set",
+ "slug": "ptr",
"sub_area": "Pointer"
},
{
"area": "Pointer",
- "canonical_name": "ptr_sizeof",
- "description": "Lowers `ptr_sizeof(\"type\")` by materializing the checked static byte size.",
+ "canonical_name": "ptr_get",
+ "description": "Reads one machine word through a raw pointer and returns it as an integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_sizeof",
- "codegen_line": 75,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_get",
+ "codegen_line": 109,
"notes": [
- "Lowers `ptr_sizeof(\"type\")` by materializing the checked static byte size."
+ "Lowers `ptr_get(pointer)` by reading one machine word through a checked pointer."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_get.rs",
"sig_line": null
},
- "name": "ptr_sizeof",
+ "name": "ptr_get",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "type",
+ "name": "pointer",
"optional": false,
- "type": "string"
+ "type": "pointer"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "ptr_sizeof",
+ "slug": "ptr_get",
"sub_area": "Pointer"
},
{
"area": "Pointer",
- "canonical_name": "ptr_write16",
- "description": "Lowers `ptr_write16(pointer, value)` by writing one 16-bit word through a checked pointer.",
+ "canonical_name": "ptr_is_null",
+ "description": "Returns true if the pointer is null.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_write16",
- "codegen_line": 161,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_is_null",
+ "codegen_line": 56,
"notes": [
- "Lowers `ptr_write16(pointer, value)` by writing one 16-bit word through a checked pointer."
- ],
- "runtime_helpers": [
- "__rt_ptr_write_string"
+ "Lowers `ptr_is_null(pointer)` by comparing the raw pointer address to zero."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_is_null.rs",
"sig_line": null
},
- "name": "ptr_write16",
+ "name": "ptr_is_null",
"sig": {
"params": [
{
@@ -11630,88 +12713,64 @@
"name": "pointer",
"optional": false,
"type": "pointer"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "value",
- "optional": false,
- "type": "int"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "ptr_write16",
+ "slug": "ptr_is_null",
"sub_area": "Pointer"
},
{
"area": "Pointer",
- "canonical_name": "ptr_write32",
- "description": "Lowers `ptr_write32(pointer, value)` by writing one 32-bit word through a checked pointer.",
+ "canonical_name": "ptr_null",
+ "description": "Returns a null raw pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_write32",
- "codegen_line": 166,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_null",
+ "codegen_line": 49,
"notes": [
- "Lowers `ptr_write32(pointer, value)` by writing one 32-bit word through a checked pointer."
- ],
- "runtime_helpers": [
- "__rt_ptr_write_string"
+ "Lowers `ptr_null()` by materializing the raw null pointer sentinel."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_null.rs",
"sig_line": null
},
- "name": "ptr_write32",
+ "name": "ptr_null",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "pointer",
- "optional": false,
- "type": "pointer"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "value",
- "optional": false,
- "type": "int"
- }
- ],
- "return_type": "void",
+ "params": [],
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ptr_write32",
+ "slug": "ptr_null",
"sub_area": "Pointer"
},
{
"area": "Pointer",
- "canonical_name": "ptr_write8",
- "description": "Lowers `ptr_write8(pointer, value)` by writing one byte through a checked pointer.",
+ "canonical_name": "ptr_offset",
+ "description": "Returns a new pointer offset from the given pointer by the given byte count.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_write8",
- "codegen_line": 156,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_offset",
+ "codegen_line": 86,
"notes": [
- "Lowers `ptr_write8(pointer, value)` by writing one byte through a checked pointer."
+ "Lowers `ptr_offset(pointer, offset)` by adding a byte offset to a raw address."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_offset.rs",
"sig_line": null
},
- "name": "ptr_write8",
+ "name": "ptr_offset",
"sig": {
"params": [
{
@@ -11724,40 +12783,40 @@
{
"by_ref": false,
"default": null,
- "name": "value",
+ "name": "offset",
"optional": false,
"type": "int"
}
],
- "return_type": "void",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "ptr_write8",
+ "slug": "ptr_offset",
"sub_area": "Pointer"
},
{
"area": "Pointer",
- "canonical_name": "ptr_write_string",
- "description": "Lowers `ptr_write_string(pointer, string)` by copying PHP string bytes into raw memory.",
+ "canonical_name": "ptr_read16",
+ "description": "Reads one unsigned 16-bit word through a raw pointer and returns it as an integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/pointers.rs",
- "codegen_function": "lower_ptr_write_string",
- "codegen_line": 171,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_read16",
+ "codegen_line": 124,
"notes": [
- "Lowers `ptr_write_string(pointer, string)` by copying PHP string bytes into raw memory."
+ "Lowers `ptr_read16(pointer)` by reading one unsigned 16-bit word through a checked pointer."
],
"runtime_helpers": [
- "__rt_ptr_write_string"
+ "__rt_ptr_read_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_read16.rs",
"sig_line": null
},
- "name": "ptr_write_string",
+ "name": "ptr_read16",
"sig": {
"params": [
{
@@ -11766,2347 +12825,2238 @@
"name": "pointer",
"optional": false,
"type": "pointer"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "string",
- "optional": false,
- "type": "string"
}
],
"return_type": "int",
"variadic": null
},
- "slug": "ptr_write_string",
+ "slug": "ptr_read16",
"sub_area": "Pointer"
},
{
- "area": "Filesystem",
- "canonical_name": "putenv",
- "description": "Lowers `putenv(assignment)` by copying the environment string into persistent heap storage.",
+ "area": "Pointer",
+ "canonical_name": "ptr_read32",
+ "description": "Reads one unsigned 32-bit word through a raw pointer and returns it as an integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_putenv",
- "codegen_line": 657,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_read32",
+ "codegen_line": 129,
"notes": [
- "Lowers `putenv(assignment)` by copying the environment string into persistent heap storage."
+ "Lowers `ptr_read32(pointer)` by reading one unsigned 32-bit word through a checked pointer."
],
"runtime_helpers": [
- "__rt_php_uname"
+ "__rt_ptr_read_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_read32.rs",
"sig_line": null
},
- "name": "putenv",
+ "name": "ptr_read32",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "assignment",
+ "name": "pointer",
"optional": false,
- "type": "string"
+ "type": "pointer"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "putenv",
- "sub_area": "Filesystem"
+ "slug": "ptr_read32",
+ "sub_area": "Pointer"
},
{
- "area": "Math",
- "canonical_name": "rad2deg",
- "description": "Lowers `rad2deg()` by multiplying with `180 / PI`.",
+ "area": "Pointer",
+ "canonical_name": "ptr_read8",
+ "description": "Reads one unsigned byte through a raw pointer and returns it as an integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/libm.rs",
- "codegen_function": "lower_rad2deg",
- "codegen_line": 83,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_read8",
+ "codegen_line": 119,
"notes": [
- "Lowers `rad2deg()` by multiplying with `180 / PI`."
+ "Lowers `ptr_read8(pointer)` by reading one unsigned byte through a checked pointer."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_read8.rs",
"sig_line": null
},
- "name": "rad2deg",
+ "name": "ptr_read8",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "pointer",
"optional": false,
- "type": "float"
+ "type": "pointer"
}
],
- "return_type": "float",
+ "return_type": "int",
"variadic": null
},
- "slug": "rad2deg",
- "sub_area": "Math"
+ "slug": "ptr_read8",
+ "sub_area": "Pointer"
},
{
- "area": "Math",
- "canonical_name": "rand",
- "description": "",
+ "area": "Pointer",
+ "canonical_name": "ptr_read_string",
+ "description": "Copies raw bytes from a pointer into a PHP string of the given length.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_read_string",
+ "codegen_line": 134,
+ "notes": [
+ "Lowers `ptr_read_string(pointer, length)` by copying raw bytes into an owned PHP string."
+ ],
+ "runtime_helpers": [
+ "__rt_ptr_read_string"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/pointers/ptr_read_string.rs",
"sig_line": null
},
- "name": "rand",
+ "name": "ptr_read_string",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "min",
+ "name": "pointer",
"optional": false,
- "type": "int"
+ "type": "pointer"
},
{
"by_ref": false,
"default": null,
- "name": "max",
+ "name": "length",
"optional": false,
"type": "int"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "rand",
- "sub_area": "Math"
+ "slug": "ptr_read_string",
+ "sub_area": "Pointer"
},
{
- "area": "Math",
- "canonical_name": "random_int",
- "description": "Lowers `random_int()` over an inclusive integer range.",
+ "area": "Pointer",
+ "canonical_name": "ptr_set",
+ "description": "Writes one machine word through a raw pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math/random.rs",
- "codegen_function": "lower_random_int",
- "codegen_line": 40,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_set",
+ "codegen_line": 114,
"notes": [
- "Lowers `random_int()` over an inclusive integer range."
+ "Lowers `ptr_set(pointer, value)` by writing one machine word through a checked pointer."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_set.rs",
"sig_line": null
},
- "name": "random_int",
+ "name": "ptr_set",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "min",
+ "name": "pointer",
"optional": false,
- "type": "int"
+ "type": "pointer"
},
{
"by_ref": false,
"default": null,
- "name": "max",
+ "name": "value",
"optional": false,
- "type": "int"
+ "type": "mixed"
}
],
- "return_type": "int",
+ "return_type": "void",
"variadic": null
},
- "slug": "random_int",
- "sub_area": "Math"
+ "slug": "ptr_set",
+ "sub_area": "Pointer"
},
{
- "area": "Array",
- "canonical_name": "range",
- "description": "Lowers `range()` for integer endpoints through the shared runtime constructor.",
+ "area": "Pointer",
+ "canonical_name": "ptr_sizeof",
+ "description": "Returns the byte size of the named pointer target type.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_range",
- "codegen_line": 1020,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_sizeof",
+ "codegen_line": 75,
"notes": [
- "Lowers `range()` for integer endpoints through the shared runtime constructor."
- ],
- "runtime_helpers": [
- "__rt_mixed_cast_int",
- "__rt_range"
+ "Lowers `ptr_sizeof(\"type\")` by materializing the checked static byte size."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_sizeof.rs",
"sig_line": null
},
- "name": "range",
+ "name": "ptr_sizeof",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "start",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "end",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "step",
+ "name": "type",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "int",
"variadic": null
},
- "slug": "range",
- "sub_area": "Array"
+ "slug": "ptr_sizeof",
+ "sub_area": "Pointer"
},
{
- "area": "String",
- "canonical_name": "rawurldecode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Pointer",
+ "canonical_name": "ptr_write16",
+ "description": "Writes one 16-bit word through a raw pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_write16",
+ "codegen_line": 161,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `ptr_write16(pointer, value)` by writing one 16-bit word through a checked pointer."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_ptr_write_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_write16.rs",
"sig_line": null
},
- "name": "rawurldecode",
+ "name": "ptr_write16",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "pointer",
"optional": false,
- "type": "string"
+ "type": "pointer"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "int"
}
],
- "return_type": "string",
+ "return_type": "void",
"variadic": null
},
- "slug": "rawurldecode",
- "sub_area": "String"
+ "slug": "ptr_write16",
+ "sub_area": "Pointer"
},
{
- "area": "String",
- "canonical_name": "rawurlencode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Pointer",
+ "canonical_name": "ptr_write32",
+ "description": "Writes one 32-bit word through a raw pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_write32",
+ "codegen_line": 166,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `ptr_write32(pointer, value)` by writing one 32-bit word through a checked pointer."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_ptr_write_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_write32.rs",
"sig_line": null
},
- "name": "rawurlencode",
+ "name": "ptr_write32",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "pointer",
"optional": false,
- "type": "string"
+ "type": "pointer"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "int"
}
],
- "return_type": "string",
+ "return_type": "void",
"variadic": null
},
- "slug": "rawurlencode",
- "sub_area": "String"
+ "slug": "ptr_write32",
+ "sub_area": "Pointer"
},
{
- "area": "Process",
- "canonical_name": "readline",
- "description": "Lowers `readline(prompt?)` by optionally writing a prompt and reading stdin.",
+ "area": "Pointer",
+ "canonical_name": "ptr_write8",
+ "description": "Writes one byte through a raw pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_readline",
- "codegen_line": 203,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_write8",
+ "codegen_line": 156,
"notes": [
- "Lowers `readline(prompt?)` by optionally writing a prompt and reading stdin."
- ],
- "runtime_helpers": [
- "__rt_fgets"
+ "Lowers `ptr_write8(pointer, value)` by writing one byte through a checked pointer."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_write8.rs",
"sig_line": null
},
- "name": "readline",
+ "name": "ptr_write8",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "prompt",
- "optional": true,
- "type": "string"
+ "name": "pointer",
+ "optional": false,
+ "type": "pointer"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "void",
"variadic": null
},
- "slug": "readline",
- "sub_area": "Process"
+ "slug": "ptr_write8",
+ "sub_area": "Pointer"
},
{
- "area": "Filesystem",
- "canonical_name": "readlink",
- "description": "Lowers `readlink(path)` and boxes the owned runtime string-or-false result.",
+ "area": "Pointer",
+ "canonical_name": "ptr_write_string",
+ "description": "Copies PHP string bytes into raw memory at the given pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_readlink",
- "codegen_line": 4815,
+ "codegen_file": "src/codegen/lower_inst/builtins/pointers.rs",
+ "codegen_function": "lower_ptr_write_string",
+ "codegen_line": 171,
"notes": [
- "Lowers `readlink(path)` and boxes the owned runtime string-or-false result."
+ "Lowers `ptr_write_string(pointer, string)` by copying PHP string bytes into raw memory."
],
"runtime_helpers": [
- "__rt_fileatime",
- "__rt_filectime",
- "__rt_fileperms",
- "__rt_readlink"
+ "__rt_ptr_write_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/pointers/ptr_write_string.rs",
"sig_line": null
},
- "name": "readlink",
+ "name": "ptr_write_string",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "path",
+ "name": "pointer",
+ "optional": false,
+ "type": "pointer"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "string",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "readlink",
- "sub_area": "Filesystem"
+ "slug": "ptr_write_string",
+ "sub_area": "Pointer"
},
{
"area": "Filesystem",
- "canonical_name": "realpath",
- "description": "Lowers `realpath(path)` and boxes the owned runtime string-or-false result.",
+ "canonical_name": "putenv",
+ "description": "Sets an environment variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_realpath",
- "codegen_line": 3465,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_putenv",
+ "codegen_line": 657,
"notes": [
- "Lowers `realpath(path)` and boxes the owned runtime string-or-false result."
+ "Lowers `putenv(assignment)` by copying the environment string into persistent heap storage."
],
"runtime_helpers": [
- "__rt_realpath"
+ "__rt_php_uname"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/putenv.rs",
"sig_line": null
},
- "name": "realpath",
+ "name": "putenv",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "path",
+ "name": "assignment",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "realpath",
+ "slug": "putenv",
"sub_area": "Filesystem"
},
{
- "area": "Filesystem",
- "canonical_name": "realpath_cache_get",
- "description": "Lowers `realpath_cache_get()` to elephc's empty realpath-cache view.",
+ "area": "Math",
+ "canonical_name": "rad2deg",
+ "description": "Converts a radian value to degrees.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_realpath_cache_get",
- "codegen_line": 3475,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_rad2deg",
+ "codegen_line": 83,
"notes": [
- "Lowers `realpath_cache_get()` to elephc's empty realpath-cache view."
+ "Lowers `rad2deg()` by multiplying with `180 / PI`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/rad2deg.rs",
"sig_line": null
},
- "name": "realpath_cache_get",
+ "name": "rad2deg",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "float",
"variadic": null
},
- "slug": "realpath_cache_get",
- "sub_area": "Filesystem"
+ "slug": "rad2deg",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "realpath_cache_size",
- "description": "Lowers `realpath_cache_size()` to zero because elephc has no realpath cache.",
+ "area": "Math",
+ "canonical_name": "rand",
+ "description": "Generate a random integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_realpath_cache_size",
- "codegen_line": 3485,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/random.rs",
+ "codegen_function": "lower_rand",
+ "codegen_line": 22,
"notes": [
- "Lowers `realpath_cache_size()` to zero because elephc has no realpath cache."
+ "Lowers `rand()` and `mt_rand()` with either zero args or an inclusive range."
+ ],
+ "runtime_helpers": [
+ "__rt_random_u32"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/rand.rs",
"sig_line": null
},
- "name": "realpath_cache_size",
+ "name": "rand",
"sig": {
- "params": [],
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "min",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "max",
+ "optional": false,
+ "type": "int"
+ }
+ ],
"return_type": "int",
"variadic": null
},
- "slug": "realpath_cache_size",
- "sub_area": "Filesystem"
+ "slug": "rand",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "rename",
- "description": "Lowers `rename(from, to)` through the target-aware runtime helper.",
+ "area": "Math",
+ "canonical_name": "random_bytes",
+ "description": "Get a cryptographically secure random string of the given length.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_rename",
- "codegen_line": 3805,
+ "codegen_file": "src/codegen_ir/lower_inst/builtins/math/random.rs",
+ "codegen_function": "lower_random_bytes",
+ "codegen_line": 58,
"notes": [
- "Lowers `rename(from, to)` through the target-aware runtime helper."
+ "Lowers `random_bytes()` into an owned CSPRNG binary string of the given length.",
+ "Materializes the single length operand as an integer, passes it to the",
+ "`__rt_random_bytes` runtime helper (length in `x0` on AArch64, `rdi` on",
+ "x86_64), and stores the returned owned string result (`x1`/`x2` on AArch64,",
+ "`rax`/`rdx` on x86_64) into the instruction's result slot. The runtime helper",
+ "owns allocation, the cryptographic fill, and the fatal paths for a length",
+ "below 1 or an unavailable entropy source."
],
"runtime_helpers": [
- "__rt_glob",
- "__rt_scandir",
- "__rt_tempnam"
+ "__rt_random_bytes"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/random_bytes.rs",
"sig_line": null
},
- "name": "rename",
+ "name": "random_bytes",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "from",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "to",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
+ "name": "length",
"optional": false,
- "type": "mixed"
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "string",
"variadic": null
},
- "slug": "rename",
- "sub_area": "Filesystem"
+ "slug": "random_bytes",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "rewind",
- "description": "Lowers `rewind(stream)` as `lseek(fd, 0, SEEK_SET)` and clears EOF state on success.",
+ "area": "Math",
+ "canonical_name": "random_int",
+ "description": "Get a cryptographically secure, uniformly selected integer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_rewind",
- "codegen_line": 2975,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/random.rs",
+ "codegen_function": "lower_random_int",
+ "codegen_line": 41,
"notes": [
- "Lowers `rewind(stream)` as `lseek(fd, 0, SEEK_SET)` and clears EOF state on success."
+ "Lowers `random_int()` over an inclusive integer range."
+ ],
+ "runtime_helpers": [
+ "__rt_random_bytes"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/random_int.rs",
"sig_line": null
},
- "name": "rewind",
+ "name": "random_int",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "min",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "max",
"optional": false,
- "type": "resource"
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "rewind",
- "sub_area": "IO"
+ "slug": "random_int",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "rmdir",
- "description": "Lowers `rmdir(path)` through the target-aware runtime helper.",
+ "area": "Array",
+ "canonical_name": "range",
+ "description": "Create an array containing a range of elements.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_rmdir",
- "codegen_line": 3790,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_range",
+ "codegen_line": 1023,
"notes": [
- "Lowers `rmdir(path)` through the target-aware runtime helper."
+ "Lowers `range()` for integer endpoints through the shared runtime constructor."
],
"runtime_helpers": [
- "__rt_chdir",
- "__rt_copy",
- "__rt_rmdir",
- "__rt_scandir",
- "__rt_tempnam"
+ "__rt_mixed_cast_int",
+ "__rt_range"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/range.rs",
"sig_line": null
},
- "name": "rmdir",
+ "name": "range",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "start",
"optional": false,
- "type": "string"
+ "type": "mixed"
},
{
"by_ref": false,
- "default": "null",
- "name": "context",
- "optional": true,
+ "default": null,
+ "name": "end",
+ "optional": false,
"type": "mixed"
}
],
- "return_type": "bool",
+ "return_type": "array",
"variadic": null
},
- "slug": "rmdir",
- "sub_area": "Filesystem"
+ "slug": "range",
+ "sub_area": "Array"
},
{
- "area": "Math",
- "canonical_name": "round",
- "description": "Lowers `round()` for concrete integer-like and floating operands.",
+ "area": "String",
+ "canonical_name": "rawurldecode",
+ "description": "Decodes an RFC 3986 percent-encoded string without treating '+' as a space.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_round",
- "codegen_line": 186,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `round()` for concrete integer-like and floating operands."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/rawurldecode.rs",
"sig_line": null
},
- "name": "round",
+ "name": "rawurldecode",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "string",
"optional": false,
- "type": "float"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "precision",
- "optional": true,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "round",
- "sub_area": "Math"
+ "slug": "rawurldecode",
+ "sub_area": "String"
},
{
- "area": "Array",
- "canonical_name": "rsort",
- "description": "Lowers `rsort()` for indexed integer arrays by mutating the source array in place.",
+ "area": "String",
+ "canonical_name": "rawurlencode",
+ "description": "URL-encodes a string using RFC 3986 percent-encoding (no '+' for spaces).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_rsort",
- "codegen_line": 1081,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `rsort()` for indexed integer arrays by mutating the source array in place."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_arsort",
- "__rt_asort",
- "__rt_krsort",
- "__rt_ksort",
- "__rt_natsort",
- "__rt_rsort_int",
- "__rt_rsort_str"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/rawurlencode.rs",
"sig_line": null
},
- "name": "rsort",
+ "name": "rawurlencode",
"sig": {
"params": [
- {
- "by_ref": true,
- "default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- },
{
"by_ref": false,
"default": null,
- "name": "flags",
+ "name": "string",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "bool",
+ "return_type": "string",
"variadic": null
},
- "slug": "rsort",
- "sub_area": "Array"
+ "slug": "rawurlencode",
+ "sub_area": "String"
},
{
- "area": "String",
- "canonical_name": "rtrim",
- "description": "",
+ "area": "IO",
+ "canonical_name": "readdir",
+ "description": "Read entry from directory handle.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_readdir",
+ "codegen_line": 3557,
+ "notes": [
+ "Lowers `readdir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+ ],
+ "runtime_helpers": [
+ "__rt_closedir",
+ "__rt_readdir",
+ "__rt_user_wrapper_dir_closedir",
+ "__rt_user_wrapper_dir_readdir"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/readdir.rs",
"sig_line": null
},
- "name": "rtrim",
+ "name": "readdir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "dir_handle",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "characters",
- "optional": true,
- "type": "string"
+ "type": "resource"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "rtrim",
- "sub_area": "String"
+ "slug": "readdir",
+ "sub_area": "IO"
},
{
"area": "Filesystem",
- "canonical_name": "scandir",
- "description": "Lowers `scandir(path)` through the target-aware runtime directory listing helper.",
+ "canonical_name": "readfile",
+ "description": "Outputs a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_scandir",
- "codegen_line": 3815,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_readfile",
+ "codegen_line": 300,
"notes": [
- "Lowers `scandir(path)` through the target-aware runtime directory listing helper."
- ],
- "runtime_helpers": [
- "__rt_glob",
- "__rt_scandir"
+ "Lowers `readfile(path)` and boxes the runtime byte-count-or-false result."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/readfile.rs",
"sig_line": null
},
- "name": "scandir",
+ "name": "readfile",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "filename",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "sorting_order",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": false,
- "type": "mixed"
}
],
- "return_type": "array",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "scandir",
+ "slug": "readfile",
"sub_area": "Filesystem"
},
{
- "area": "Type",
- "canonical_name": "settype",
- "description": "Lowers `settype($local, \"type\")` by mutating the resolved local slot and returning true.",
+ "area": "Process",
+ "canonical_name": "readline",
+ "description": "Reads a line from the user's terminal.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
- "codegen_function": "lower_settype",
- "codegen_line": 25,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_readline",
+ "codegen_line": 310,
"notes": [
- "Lowers `settype($local, \"type\")` by mutating the resolved local slot and returning true."
+ "Lowers `readline(prompt?)` by optionally writing a prompt and reading stdin."
+ ],
+ "runtime_helpers": [
+ "__rt_fgets"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/readline.rs",
"sig_line": null
},
- "name": "settype",
+ "name": "readline",
"sig": {
"params": [
- {
- "by_ref": true,
- "default": null,
- "name": "var",
- "optional": false,
- "type": "mixed"
- },
{
"by_ref": false,
- "default": null,
- "name": "type",
- "optional": false,
+ "default": "null",
+ "name": "prompt",
+ "optional": true,
"type": "string"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "settype",
- "sub_area": "Casts"
+ "slug": "readline",
+ "sub_area": "Process"
},
{
- "area": "String",
- "canonical_name": "sha1",
- "description": "Lowers `sha1(data, binary?)` through the shared crypto-backed runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "readlink",
+ "description": "Returns the target of a symbolic link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_sha1",
- "codegen_line": 360,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_readlink",
+ "codegen_line": 5458,
"notes": [
- "Lowers `sha1(data, binary?)` through the shared crypto-backed runtime helper."
+ "Lowers `readlink(path)` and boxes the owned runtime string-or-false result."
],
"runtime_helpers": [
- "__rt_hash",
- "__rt_sha1"
+ "__rt_fileatime",
+ "__rt_filectime",
+ "__rt_fileperms",
+ "__rt_readlink"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/readlink.rs",
"sig_line": null
},
- "name": "sha1",
+ "name": "readlink",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "path",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "binary",
- "optional": true,
- "type": "bool"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "sha1",
- "sub_area": "String"
+ "slug": "readlink",
+ "sub_area": "Filesystem"
},
{
- "area": "Process",
- "canonical_name": "shell_exec",
- "description": "Lowers `shell_exec(command)` by capturing shell stdout through the shared runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "realpath",
+ "description": "Returns canonicalized absolute pathname.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_shell_exec",
- "codegen_line": 698,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_realpath",
+ "codegen_line": 3690,
"notes": [
- "Lowers `shell_exec(command)` by capturing shell stdout through the shared runtime helper."
+ "Lowers `realpath(path)` and boxes the owned runtime string-or-false result."
+ ],
+ "runtime_helpers": [
+ "__rt_realpath"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/realpath.rs",
"sig_line": null
},
- "name": "shell_exec",
+ "name": "realpath",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "command",
+ "name": "path",
"optional": false,
"type": "string"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "shell_exec",
- "sub_area": "Process"
+ "slug": "realpath",
+ "sub_area": "Filesystem"
},
{
- "area": "Array",
- "canonical_name": "shuffle",
- "description": "Lowers `shuffle()` for indexed arrays with 8-byte slots by mutating the source array in place.",
+ "area": "Filesystem",
+ "canonical_name": "realpath_cache_get",
+ "description": "Returns realpath cache entries.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_shuffle",
- "codegen_line": 1116,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_realpath_cache_get",
+ "codegen_line": 3700,
"notes": [
- "Lowers `shuffle()` for indexed arrays with 8-byte slots by mutating the source array in place."
+ "Lowers `realpath_cache_get()` to elephc's empty realpath-cache view."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/realpath_cache_get.rs",
"sig_line": null
},
- "name": "shuffle",
+ "name": "realpath_cache_get",
"sig": {
- "params": [
- {
- "by_ref": true,
- "default": null,
- "name": "array",
- "optional": false,
- "type": "array"
- }
- ],
- "return_type": "bool",
+ "params": [],
+ "return_type": "array",
"variadic": null
},
- "slug": "shuffle",
- "sub_area": "Array"
+ "slug": "realpath_cache_get",
+ "sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "sin",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "realpath_cache_size",
+ "description": "Returns the amount of memory used by the realpath cache.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_realpath_cache_size",
+ "codegen_line": 3710,
+ "notes": [
+ "Lowers `realpath_cache_size()` to zero because elephc has no realpath cache."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/realpath_cache_size.rs",
"sig_line": null
},
- "name": "sin",
+ "name": "realpath_cache_size",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "num",
- "optional": false,
- "type": "float"
- }
- ],
- "return_type": "float",
+ "params": [],
+ "return_type": "int",
"variadic": null
},
- "slug": "sin",
- "sub_area": "Math"
+ "slug": "realpath_cache_size",
+ "sub_area": "Filesystem"
},
{
- "area": "SPL",
- "canonical_name": "spl_autoload",
- "description": "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used.",
+ "area": "Filesystem",
+ "canonical_name": "rename",
+ "description": "Renames a file or directory.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_void",
- "codegen_line": 150,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_rename",
+ "codegen_line": 4448,
"notes": [
- "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
+ "Lowers `rename(from, to)` through the target-aware runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_glob",
+ "__rt_scandir",
+ "__rt_tempnam"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/rename.rs",
"sig_line": null
},
- "name": "spl_autoload",
+ "name": "rename",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class",
+ "name": "from",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "file_extensions",
- "optional": true,
+ "name": "to",
+ "optional": false,
"type": "string"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "spl_autoload",
- "sub_area": "SPL"
+ "slug": "rename",
+ "sub_area": "Filesystem"
},
{
- "area": "SPL",
- "canonical_name": "spl_autoload_call",
- "description": "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used.",
+ "area": "IO",
+ "canonical_name": "rewind",
+ "description": "Rewind the position of a file pointer.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_void",
- "codegen_line": 150,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_rewind",
+ "codegen_line": 3196,
"notes": [
- "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
+ "Lowers `rewind(stream)` as `lseek(fd, 0, SEEK_SET)` and clears EOF state on success."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/rewind.rs",
"sig_line": null
},
- "name": "spl_autoload_call",
+ "name": "rewind",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "class",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "spl_autoload_call",
- "sub_area": "SPL"
+ "slug": "rewind",
+ "sub_area": "IO"
},
{
- "area": "SPL",
- "canonical_name": "spl_autoload_extensions",
- "description": "Lowers `spl_autoload_extensions()` against the legacy mutable extension globals.",
+ "area": "IO",
+ "canonical_name": "rewinddir",
+ "description": "Rewind directory handle.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_extensions",
- "codegen_line": 177,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_rewinddir",
+ "codegen_line": 3588,
"notes": [
- "Lowers `spl_autoload_extensions()` against the legacy mutable extension globals."
+ "Lowers `rewinddir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+ ],
+ "runtime_helpers": [
+ "__rt_rewinddir",
+ "__rt_user_wrapper_dir_rewinddir"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/rewinddir.rs",
"sig_line": null
},
- "name": "spl_autoload_extensions",
+ "name": "rewinddir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "file_extensions",
- "optional": true,
- "type": "string"
+ "name": "dir_handle",
+ "optional": false,
+ "type": "resource"
}
],
- "return_type": "string",
+ "return_type": "void",
"variadic": null
},
- "slug": "spl_autoload_extensions",
- "sub_area": "SPL"
+ "slug": "rewinddir",
+ "sub_area": "IO"
},
{
- "area": "SPL",
- "canonical_name": "spl_autoload_functions",
- "description": "Lowers `spl_autoload_functions()` to an indexed array of AOT rule placeholders.",
+ "area": "Filesystem",
+ "canonical_name": "rmdir",
+ "description": "Removes a directory.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_functions",
- "codegen_line": 166,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_rmdir",
+ "codegen_line": 4433,
"notes": [
- "Lowers `spl_autoload_functions()` to an indexed array of AOT rule placeholders."
+ "Lowers `rmdir(path)` through the target-aware runtime helper."
],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "spl_autoload_functions",
- "sig": {
- "params": [],
- "return_type": "array",
- "variadic": null
- },
- "slug": "spl_autoload_functions",
- "sub_area": "SPL"
- },
- {
- "area": "SPL",
- "canonical_name": "spl_autoload_register",
- "description": "Lowers autoload registration stubs by preserving arg effects and returning true.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_bool",
- "codegen_line": 134,
- "notes": [
- "Lowers autoload registration stubs by preserving arg effects and returning true."
+ "runtime_helpers": [
+ "__rt_chdir",
+ "__rt_copy",
+ "__rt_rmdir",
+ "__rt_scandir",
+ "__rt_tempnam"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/rmdir.rs",
"sig_line": null
},
- "name": "spl_autoload_register",
+ "name": "rmdir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "callback",
- "optional": true,
- "type": "callable"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "throw",
- "optional": true,
- "type": "bool"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "prepend",
- "optional": true,
- "type": "bool"
+ "name": "directory",
+ "optional": false,
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "spl_autoload_register",
- "sub_area": "SPL"
+ "slug": "rmdir",
+ "sub_area": "Filesystem"
},
{
- "area": "SPL",
- "canonical_name": "spl_autoload_unregister",
- "description": "Lowers autoload registration stubs by preserving arg effects and returning true.",
+ "area": "Math",
+ "canonical_name": "round",
+ "description": "Rounds a float.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_autoload_bool",
- "codegen_line": 134,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_round",
+ "codegen_line": 186,
"notes": [
- "Lowers autoload registration stubs by preserving arg effects and returning true."
+ "Lowers `round()` for concrete integer-like and floating operands."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/round.rs",
"sig_line": null
},
- "name": "spl_autoload_unregister",
+ "name": "round",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "callback",
+ "name": "num",
"optional": false,
- "type": "callable"
+ "type": "float"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "precision",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "float",
"variadic": null
},
- "slug": "spl_autoload_unregister",
- "sub_area": "SPL"
+ "slug": "round",
+ "sub_area": "Math"
},
{
- "area": "SPL",
- "canonical_name": "spl_classes",
- "description": "Lowers `spl_classes()` to the static compiler-shipped SPL/core type snapshot.",
+ "area": "Array",
+ "canonical_name": "rsort",
+ "description": "Sorts an array in descending order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_classes",
- "codegen_line": 205,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_rsort",
+ "codegen_line": 1084,
"notes": [
- "Lowers `spl_classes()` to the static compiler-shipped SPL/core type snapshot."
+ "Lowers `rsort()` for indexed integer arrays by mutating the source array in place."
],
"runtime_helpers": [
- "__rt_itoa"
+ "__rt_arsort",
+ "__rt_asort",
+ "__rt_krsort",
+ "__rt_ksort",
+ "__rt_natsort",
+ "__rt_rsort_int",
+ "__rt_rsort_str"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/rsort.rs",
"sig_line": null
},
- "name": "spl_classes",
+ "name": "rsort",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": true,
+ "default": null,
+ "name": "array",
+ "optional": false,
+ "type": "array"
+ }
+ ],
+ "return_type": "bool",
"variadic": null
},
- "slug": "spl_classes",
- "sub_area": "SPL"
+ "slug": "rsort",
+ "sub_area": "Array"
},
{
- "area": "SPL",
- "canonical_name": "spl_object_hash",
- "description": "Lowers `spl_object_hash(object)` by formatting the loaded object pointer as a string.",
+ "area": "String",
+ "canonical_name": "rtrim",
+ "description": "Strips whitespace (or other characters) from the end of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_object_hash",
- "codegen_line": 225,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_trim_like",
+ "codegen_line": 112,
"notes": [
- "Lowers `spl_object_hash(object)` by formatting the loaded object pointer as a string."
- ],
- "runtime_helpers": [
- "__rt_itoa"
+ "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/rtrim.rs",
"sig_line": null
},
- "name": "spl_object_hash",
+ "name": "rtrim",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object",
+ "name": "string",
"optional": false,
- "type": "object"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "' \\n\\r\\t\\x0b\\x0c\\x00'",
+ "name": "characters",
+ "optional": true,
+ "type": "string"
}
],
"return_type": "string",
"variadic": null
},
- "slug": "spl_object_hash",
- "sub_area": "SPL"
+ "slug": "rtrim",
+ "sub_area": "String"
},
{
- "area": "SPL",
- "canonical_name": "spl_object_id",
- "description": "Lowers `spl_object_id(object)` by returning the loaded object pointer as an integer.",
+ "area": "Filesystem",
+ "canonical_name": "scandir",
+ "description": "Lists files and directories inside the specified path.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/spl.rs",
- "codegen_function": "lower_spl_object_id",
- "codegen_line": 215,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_scandir",
+ "codegen_line": 4458,
"notes": [
- "Lowers `spl_object_id(object)` by returning the loaded object pointer as an integer."
+ "Lowers `scandir(path)` through the target-aware runtime directory listing helper."
],
"runtime_helpers": [
- "__rt_itoa"
+ "__rt_glob",
+ "__rt_scandir"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/scandir.rs",
"sig_line": null
},
- "name": "spl_object_id",
+ "name": "scandir",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "object",
+ "name": "directory",
"optional": false,
- "type": "object"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "array",
"variadic": null
},
- "slug": "spl_object_id",
- "sub_area": "SPL"
+ "slug": "scandir",
+ "sub_area": "Filesystem"
},
{
- "area": "Math",
- "canonical_name": "sinh",
- "description": "",
+ "area": "Misc",
+ "canonical_name": "serialize",
+ "description": "Generates a storable representation of a value.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/serialize.rs",
+ "codegen_function": "lower_serialize",
+ "codegen_line": 33,
+ "notes": [
+ "Lowers `serialize($value)` into the shared serialize runtime helper.",
+ "Scalar static types are formatted directly through `__rt_serialize_value`; a",
+ "Mixed/Union argument is unboxed and dispatched by `__rt_serialize_mixed`.",
+ "Non-scalar static types (arrays/objects) are not yet supported and are rejected."
+ ],
+ "runtime_helpers": [
+ "__rt_serialize_begin",
+ "__rt_serialize_mixed",
+ "__rt_serialize_value"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/system/serialize.rs",
"sig_line": null
},
- "name": "sinh",
+ "name": "serialize",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "value",
"optional": false,
- "type": "float"
+ "type": "mixed"
}
],
- "return_type": "float",
+ "return_type": "string",
"variadic": null
},
- "slug": "sinh",
- "sub_area": "Math"
+ "slug": "serialize",
+ "sub_area": "Misc"
},
{
- "area": "Process",
- "canonical_name": "sleep",
- "description": "Lowers `sleep(seconds)` through the target's C library symbol.",
+ "area": "Type",
+ "canonical_name": "settype",
+ "description": "Sets the type of a variable.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_sleep",
- "codegen_line": 473,
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
+ "codegen_function": "lower_settype",
+ "codegen_line": 25,
"notes": [
- "Lowers `sleep(seconds)` through the target's C library symbol."
- ],
- "runtime_helpers": [
- "__rt_strtotime"
+ "Lowers `settype($local, \"type\")` by mutating the resolved local slot and returning true."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/types/settype.rs",
"sig_line": null
},
- "name": "sleep",
+ "name": "settype",
"sig": {
"params": [
+ {
+ "by_ref": true,
+ "default": null,
+ "name": "var",
+ "optional": false,
+ "type": "mixed"
+ },
{
"by_ref": false,
"default": null,
- "name": "seconds",
+ "name": "type",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "sleep",
- "sub_area": "Process"
+ "slug": "settype",
+ "sub_area": "Casts"
},
{
- "area": "Array",
- "canonical_name": "sort",
- "description": "Lowers `sort()` for indexed integer arrays by mutating the source array in place.",
+ "area": "String",
+ "canonical_name": "sha1",
+ "description": "Calculates the SHA-1 hash of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
- "codegen_function": "lower_sort",
- "codegen_line": 1076,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_sha1",
+ "codegen_line": 360,
"notes": [
- "Lowers `sort()` for indexed integer arrays by mutating the source array in place."
+ "Lowers `sha1(data, binary?)` through the shared crypto-backed runtime helper."
],
"runtime_helpers": [
- "__rt_arsort",
- "__rt_asort",
- "__rt_krsort",
- "__rt_ksort",
- "__rt_rsort_int",
- "__rt_rsort_str",
- "__rt_sort_int",
- "__rt_sort_str"
+ "__rt_hash",
+ "__rt_sha1"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/sha1.rs",
"sig_line": null
},
- "name": "sort",
+ "name": "sha1",
"sig": {
"params": [
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "array",
+ "name": "string",
"optional": false,
- "type": "array"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "flags",
- "optional": false,
- "type": "int"
+ "default": "false",
+ "name": "binary",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "string",
"variadic": null
},
- "slug": "sort",
- "sub_area": "Array"
+ "slug": "sha1",
+ "sub_area": "String"
},
{
- "area": "String",
- "canonical_name": "sprintf",
- "description": "Lowers `sprintf(format, values...)` by packing variadic records for `__rt_sprintf`.",
+ "area": "Process",
+ "canonical_name": "shell_exec",
+ "description": "Executes a command via the shell and returns the complete output as a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_sprintf",
- "codegen_line": 511,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_shell_exec",
+ "codegen_line": 698,
"notes": [
- "Lowers `sprintf(format, values...)` by packing variadic records for `__rt_sprintf`."
- ],
- "runtime_helpers": [
- "__rt_sprintf"
+ "Lowers `shell_exec(command)` by capturing shell stdout through the shared runtime helper."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/shell_exec.rs",
"sig_line": null
},
- "name": "sprintf",
+ "name": "shell_exec",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "format",
+ "name": "command",
"optional": false,
"type": "string"
}
],
"return_type": "string",
- "variadic": "values"
+ "variadic": null
},
- "slug": "sprintf",
- "sub_area": "String"
+ "slug": "shell_exec",
+ "sub_area": "Process"
},
{
- "area": "Math",
- "canonical_name": "sqrt",
- "description": "Lowers `sqrt()` for concrete integer-like and floating operands.",
+ "area": "Array",
+ "canonical_name": "shuffle",
+ "description": "Shuffles an array into random order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/math.rs",
- "codegen_function": "lower_sqrt",
- "codegen_line": 97,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_shuffle",
+ "codegen_line": 1119,
"notes": [
- "Lowers `sqrt()` for concrete integer-like and floating operands."
+ "Lowers `shuffle()` for indexed arrays with 8-byte slots by mutating the source array in place."
+ ],
+ "runtime_helpers": [
+ "__rt_array_is_list"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/shuffle.rs",
"sig_line": null
},
- "name": "sqrt",
+ "name": "shuffle",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "num",
+ "name": "array",
"optional": false,
- "type": "float"
+ "type": "array"
}
],
- "return_type": "float",
+ "return_type": "bool",
"variadic": null
},
- "slug": "sqrt",
- "sub_area": "Math"
+ "slug": "shuffle",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "sscanf",
- "description": "Lowers `sscanf(string, format)` into the shared scanner helper.",
+ "area": "Math",
+ "canonical_name": "sin",
+ "description": "Returns the sine of a number (radians).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_sscanf",
- "codegen_line": 163,
- "notes": [
- "Lowers `sscanf(string, format)` into the shared scanner helper."
- ],
- "runtime_helpers": [
- "__rt_sscanf",
- "__rt_str_split"
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
+ "notes": [
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/sin.rs",
"sig_line": null
},
- "name": "sscanf",
+ "name": "sin",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "format",
+ "name": "num",
"optional": false,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "array",
- "variadic": "vars"
+ "return_type": "float",
+ "variadic": null
},
- "slug": "sscanf",
- "sub_area": "String"
+ "slug": "sin",
+ "sub_area": "Math"
},
{
- "area": "Filesystem",
- "canonical_name": "stat",
- "description": "Lowers `stat(path)` and boxes the runtime stat array or PHP false result.",
+ "area": "Math",
+ "canonical_name": "sinh",
+ "description": "Returns the hyperbolic sine of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stat",
- "codegen_line": 4886,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `stat(path)` and boxes the runtime stat array or PHP false result."
- ],
- "runtime_helpers": [
- "__rt_fstat_array",
- "__rt_lstat_array",
- "__rt_stat_array"
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/sinh.rs",
"sig_line": null
},
- "name": "stat",
+ "name": "sinh",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "num",
"optional": false,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "mixed",
+ "return_type": "float",
"variadic": null
},
- "slug": "stat",
- "sub_area": "Filesystem"
+ "slug": "sinh",
+ "sub_area": "Math"
},
{
- "area": "String",
- "canonical_name": "str_contains",
- "description": "Lowers `str_contains()` through `strpos()` and converts found positions to bool.",
+ "area": "Process",
+ "canonical_name": "sleep",
+ "description": "Delays execution for a number of seconds.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_str_contains",
- "codegen_line": 682,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_sleep",
+ "codegen_line": 473,
"notes": [
- "Lowers `str_contains()` through `strpos()` and converts found positions to bool."
+ "Lowers `sleep(seconds)` through the target's C library symbol."
],
"runtime_helpers": [
- "__rt_strpos"
+ "__rt_strtotime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/sleep.rs",
"sig_line": null
},
- "name": "str_contains",
+ "name": "sleep",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "haystack",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "needle",
+ "name": "seconds",
"optional": false,
- "type": "string"
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "int",
"variadic": null
},
- "slug": "str_contains",
- "sub_area": "String"
+ "slug": "sleep",
+ "sub_area": "Process"
},
{
- "area": "String",
- "canonical_name": "str_ends_with",
- "description": "Lowers a two-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Array",
+ "canonical_name": "sort",
+ "description": "Sorts an array in ascending order.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_binary_string_runtime",
- "codegen_line": 139,
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
+ "codegen_function": "lower_sort",
+ "codegen_line": 1079,
"notes": [
- "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `sort()` for indexed integer arrays by mutating the source array in place."
],
"runtime_helpers": [
- "__rt_explode"
+ "__rt_arsort",
+ "__rt_asort",
+ "__rt_krsort",
+ "__rt_ksort",
+ "__rt_rsort_int",
+ "__rt_rsort_str",
+ "__rt_sort_int",
+ "__rt_sort_str"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/sort.rs",
"sig_line": null
},
- "name": "str_ends_with",
+ "name": "sort",
"sig": {
"params": [
{
- "by_ref": false,
- "default": null,
- "name": "haystack",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "needle",
+ "name": "array",
"optional": false,
- "type": "string"
+ "type": "array"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "str_ends_with",
- "sub_area": "String"
+ "slug": "sort",
+ "sub_area": "Array"
},
{
- "area": "String",
- "canonical_name": "str_ireplace",
- "description": "",
+ "area": "SPL",
+ "canonical_name": "spl_autoload",
+ "description": "Default implementation for __autoload().",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_void",
+ "codegen_line": 151,
+ "notes": [
+ "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/spl/spl_autoload.rs",
"sig_line": null
},
- "name": "str_ireplace",
+ "name": "spl_autoload",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "search",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "replace",
+ "name": "class",
"optional": false,
- "type": "mixed"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "subject",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "count",
+ "default": "null",
+ "name": "file_extensions",
"optional": true,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "void",
"variadic": null
},
- "slug": "str_ireplace",
- "sub_area": "String"
+ "slug": "spl_autoload",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "str_pad",
- "description": "Lowers `str_pad(string, length, pad_string?, pad_type?)` through the shared runtime helper.",
+ "area": "SPL",
+ "canonical_name": "spl_autoload_call",
+ "description": "Try all registered __autoload() functions to load the requested class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_str_pad",
- "codegen_line": 818,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_void",
+ "codegen_line": 151,
"notes": [
- "Lowers `str_pad(string, length, pad_string?, pad_type?)` through the shared runtime helper."
- ],
- "runtime_helpers": [
- "__rt_str_pad"
+ "Lowers no-op autoload calls by preserving arg effects and returning PHP null if used."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_autoload_call.rs",
"sig_line": null
},
- "name": "str_pad",
+ "name": "spl_autoload_call",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
+ "name": "class",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "pad_string",
- "optional": true,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "pad_type",
- "optional": true,
- "type": "int"
}
],
- "return_type": "string",
+ "return_type": "void",
"variadic": null
},
- "slug": "str_pad",
- "sub_area": "String"
+ "slug": "spl_autoload_call",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "str_repeat",
- "description": "Lowers `str_repeat(string, times)` through the shared runtime helper.",
+ "area": "SPL",
+ "canonical_name": "spl_autoload_extensions",
+ "description": "Register and return default file extensions for spl_autoload.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_str_repeat",
- "codegen_line": 746,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_extensions",
+ "codegen_line": 178,
"notes": [
- "Lowers `str_repeat(string, times)` through the shared runtime helper."
- ],
- "runtime_helpers": [
- "__rt_str_repeat"
+ "Lowers `spl_autoload_extensions()` against the mutable extension globals."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_autoload_extensions.rs",
"sig_line": null
},
- "name": "str_repeat",
+ "name": "spl_autoload_extensions",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "string",
- "optional": false,
+ "default": "null",
+ "name": "file_extensions",
+ "optional": true,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "times",
- "optional": false,
- "type": "int"
}
],
- "return_type": "string",
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "spl_autoload_extensions",
+ "sub_area": "SPL"
+ },
+ {
+ "area": "SPL",
+ "canonical_name": "spl_autoload_functions",
+ "description": "Return all registered __autoload() functions.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_functions",
+ "codegen_line": 167,
+ "notes": [
+ "Lowers `spl_autoload_functions()` to an indexed array of AOT rule placeholders."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/spl/spl_autoload_functions.rs",
+ "sig_line": null
+ },
+ "name": "spl_autoload_functions",
+ "sig": {
+ "params": [],
+ "return_type": "array",
"variadic": null
},
- "slug": "str_repeat",
- "sub_area": "String"
+ "slug": "spl_autoload_functions",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "str_replace",
- "description": "Lowers `str_replace()`/`str_ireplace()` with three string operands.",
+ "area": "SPL",
+ "canonical_name": "spl_autoload_register",
+ "description": "Register given function as __autoload() implementation.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_string_replace",
- "codegen_line": 780,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_bool",
+ "codegen_line": 135,
"notes": [
- "Lowers `str_replace()`/`str_ireplace()` with three string operands."
+ "Lowers autoload registration stubs by preserving arg effects and returning true."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_autoload_register.rs",
"sig_line": null
},
- "name": "str_replace",
+ "name": "spl_autoload_register",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "search",
- "optional": false,
- "type": "string"
+ "default": "null",
+ "name": "callback",
+ "optional": true,
+ "type": "callable"
},
{
"by_ref": false,
- "default": null,
- "name": "replace",
- "optional": false,
- "type": "string"
+ "default": "true",
+ "name": "throw",
+ "optional": true,
+ "type": "bool"
},
{
"by_ref": false,
- "default": null,
- "name": "subject",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "count",
+ "default": "false",
+ "name": "prepend",
"optional": true,
- "type": "int"
+ "type": "bool"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "str_replace",
- "sub_area": "String"
+ "slug": "spl_autoload_register",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "str_split",
- "description": "Lowers `str_split(string, length?)` into the fixed-width string-array splitter.",
+ "area": "SPL",
+ "canonical_name": "spl_autoload_unregister",
+ "description": "Unregister given function as __autoload() implementation.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_str_split",
- "codegen_line": 176,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_autoload_bool",
+ "codegen_line": 135,
"notes": [
- "Lowers `str_split(string, length?)` into the fixed-width string-array splitter."
- ],
- "runtime_helpers": [
- "__rt_str_split"
+ "Lowers autoload registration stubs by preserving arg effects and returning true."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_autoload_unregister.rs",
"sig_line": null
},
- "name": "str_split",
+ "name": "spl_autoload_unregister",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "callback",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": true,
- "type": "int"
+ "type": "callable"
}
],
- "return_type": "array",
+ "return_type": "bool",
"variadic": null
},
- "slug": "str_split",
- "sub_area": "String"
+ "slug": "spl_autoload_unregister",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "str_starts_with",
- "description": "Lowers a two-argument string builtin that directly delegates to a runtime helper.",
+ "area": "SPL",
+ "canonical_name": "spl_classes",
+ "description": "Return available SPL classes.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_binary_string_runtime",
- "codegen_line": 139,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_classes",
+ "codegen_line": 206,
"notes": [
- "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `spl_classes()` to the static compiler-shipped SPL/core type snapshot."
],
"runtime_helpers": [
- "__rt_explode"
+ "__rt_itoa"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_classes.rs",
"sig_line": null
},
- "name": "str_starts_with",
+ "name": "spl_classes",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "haystack",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "needle",
- "optional": false,
- "type": "string"
- }
- ],
- "return_type": "bool",
+ "params": [],
+ "return_type": "array",
"variadic": null
},
- "slug": "str_starts_with",
- "sub_area": "String"
+ "slug": "spl_classes",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "strcasecmp",
- "description": "",
+ "area": "SPL",
+ "canonical_name": "spl_object_hash",
+ "description": "Return hash id for given object.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_object_hash",
+ "codegen_line": 226,
+ "notes": [
+ "Lowers `spl_object_hash(object)` by formatting the loaded object pointer as a string."
+ ],
+ "runtime_helpers": [
+ "__rt_itoa"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/spl/spl_object_hash.rs",
"sig_line": null
},
- "name": "strcasecmp",
+ "name": "spl_object_hash",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string1",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "string2",
+ "name": "object",
"optional": false,
- "type": "string"
+ "type": "object"
}
],
- "return_type": "int",
+ "return_type": "string",
"variadic": null
},
- "slug": "strcasecmp",
- "sub_area": "String"
+ "slug": "spl_object_hash",
+ "sub_area": "SPL"
},
{
- "area": "String",
- "canonical_name": "strcmp",
- "description": "Lowers a two-argument string builtin that directly delegates to a runtime helper.",
+ "area": "SPL",
+ "canonical_name": "spl_object_id",
+ "description": "Return the integer object handle for given object.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_binary_string_runtime",
- "codegen_line": 139,
+ "codegen_file": "src/codegen/lower_inst/builtins/spl.rs",
+ "codegen_function": "lower_spl_object_id",
+ "codegen_line": 216,
"notes": [
- "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `spl_object_id(object)` by returning the loaded object pointer as an integer."
],
"runtime_helpers": [
- "__rt_explode"
+ "__rt_itoa"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/spl/spl_object_id.rs",
"sig_line": null
},
- "name": "strcmp",
+ "name": "spl_object_id",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string1",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "string2",
+ "name": "object",
"optional": false,
- "type": "string"
+ "type": "object"
}
],
"return_type": "int",
"variadic": null
},
- "slug": "strcmp",
- "sub_area": "String"
+ "slug": "spl_object_id",
+ "sub_area": "SPL"
},
{
"area": "String",
- "canonical_name": "stripslashes",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "canonical_name": "sprintf",
+ "description": "Returns a formatted string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_sprintf",
+ "codegen_line": 511,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `sprintf(format, values...)` by packing variadic records for `__rt_sprintf`."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_sprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/sprintf.rs",
"sig_line": null
},
- "name": "stripslashes",
+ "name": "sprintf",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "format",
"optional": false,
"type": "string"
}
],
"return_type": "string",
- "variadic": null
+ "variadic": "values"
},
- "slug": "stripslashes",
+ "slug": "sprintf",
"sub_area": "String"
},
{
- "area": "String",
- "canonical_name": "strlen",
- "description": "Lowers `strlen()` by coercing string-like values and returning the byte length.",
+ "area": "Math",
+ "canonical_name": "sqrt",
+ "description": "Returns the square root of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins.rs",
- "codegen_function": "lower_strlen",
- "codegen_line": 971,
+ "codegen_file": "src/codegen/lower_inst/builtins/math.rs",
+ "codegen_function": "lower_sqrt",
+ "codegen_line": 97,
"notes": [
- "Lowers `strlen()` by coercing string-like values and returning the byte length."
- ],
- "runtime_helpers": [
- "__rt_mixed_cast_string"
+ "Lowers `sqrt()` for concrete integer-like and floating operands."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/sqrt.rs",
"sig_line": null
},
- "name": "strlen",
+ "name": "sqrt",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "num",
"optional": false,
- "type": "string"
+ "type": "float"
}
],
- "return_type": "int",
+ "return_type": "float",
"variadic": null
},
- "slug": "strlen",
- "sub_area": "String"
+ "slug": "sqrt",
+ "sub_area": "Math"
},
{
"area": "String",
- "canonical_name": "strpos",
- "description": "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed.",
+ "canonical_name": "sscanf",
+ "description": "Parses a string according to a format.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_string_position",
- "codegen_line": 700,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_sscanf",
+ "codegen_line": 163,
"notes": [
- "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
+ "Lowers `sscanf(string, format)` into the shared scanner helper."
+ ],
+ "runtime_helpers": [
+ "__rt_sscanf",
+ "__rt_str_split"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/sscanf.rs",
"sig_line": null
},
- "name": "strpos",
+ "name": "sscanf",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "haystack",
+ "name": "string",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "needle",
+ "name": "format",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "offset",
- "optional": true,
- "type": "int"
}
],
- "return_type": "mixed",
- "variadic": null
+ "return_type": "array",
+ "variadic": "vars"
},
- "slug": "strpos",
+ "slug": "sscanf",
"sub_area": "String"
},
{
- "area": "String",
- "canonical_name": "strrev",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "area": "Filesystem",
+ "canonical_name": "stat",
+ "description": "Gives information about a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stat",
+ "codegen_line": 5529,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `stat(path)` and boxes the runtime stat array or PHP false result."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_fstat_array",
+ "__rt_lstat_array",
+ "__rt_stat_array"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stat.rs",
"sig_line": null
},
- "name": "strrev",
+ "name": "stat",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "filename",
"optional": false,
"type": "string"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "strrev",
- "sub_area": "String"
+ "slug": "stat",
+ "sub_area": "Filesystem"
},
{
"area": "String",
- "canonical_name": "strrpos",
- "description": "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed.",
+ "canonical_name": "str_contains",
+ "description": "Determines if a string contains a given substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_string_position",
- "codegen_line": 700,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_str_contains",
+ "codegen_line": 682,
"notes": [
- "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
+ "Lowers `str_contains()` through `strpos()` and converts found positions to bool."
+ ],
+ "runtime_helpers": [
+ "__rt_strpos"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_contains.rs",
"sig_line": null
},
- "name": "strrpos",
+ "name": "str_contains",
"sig": {
"params": [
{
@@ -14122,42 +15072,37 @@
"name": "needle",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "offset",
- "optional": true,
- "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "strrpos",
+ "slug": "str_contains",
"sub_area": "String"
},
{
"area": "String",
- "canonical_name": "strstr",
- "description": "Lowers `strstr(haystack, needle)` by searching and returning the matching suffix.",
+ "canonical_name": "str_ends_with",
+ "description": "Checks if a string ends with a given substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_strstr",
- "codegen_line": 762,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_binary_string_runtime",
+ "codegen_line": 139,
"notes": [
- "Lowers `strstr(haystack, needle)` by searching and returning the matching suffix."
+ "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_explode"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_ends_with.rs",
"sig_line": null
},
- "name": "strstr",
+ "name": "str_ends_with",
"sig": {
"params": [
{
@@ -14173,137 +15118,95 @@
"name": "needle",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "before_needle",
- "optional": true,
- "type": "bool"
}
],
- "return_type": "string",
+ "return_type": "bool",
"variadic": null
},
- "slug": "strstr",
+ "slug": "str_ends_with",
"sub_area": "String"
},
{
"area": "String",
- "canonical_name": "strtolower",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "canonical_name": "str_ireplace",
+ "description": "Case-insensitive version of str_replace().",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_string_replace",
+ "codegen_line": 780,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
- ],
- "runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "Lowers `str_replace()`/`str_ireplace()` with three string operands."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_ireplace.rs",
"sig_line": null
},
- "name": "strtolower",
+ "name": "str_ireplace",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "search",
"optional": false,
"type": "string"
- }
- ],
- "return_type": "string",
- "variadic": null
- },
- "slug": "strtolower",
- "sub_area": "String"
- },
- {
- "area": "Date",
- "canonical_name": "strtotime",
- "description": "Lowers `strtotime(datetime[, baseTimestamp])` through the shared parser runtime helper.",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_strtotime",
- "codegen_line": 487,
- "notes": [
- "Lowers `strtotime(datetime[, baseTimestamp])` through the shared parser runtime helper.",
- "Returns PHP's `int|false`: the `__rt_strtotime` `i64::MIN` parse-failure sentinel is boxed as",
- "`Mixed` `false`, and every other value (including a real `-1` pre-epoch timestamp) is boxed as",
- "a `Mixed` integer, so `=== false`, `=== -1`, and `echo` all observe the distinct results.",
- "Supports PHP's optional `$baseTimestamp`. (The `__elephc_strtotime_raw` alias keeps the plain",
- "`-1` integer shape for the synthetic `DateTime` internals.)"
- ],
- "runtime_helpers": [
- "__rt_mixed_from_value",
- "__rt_strtotime"
- ],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "strtotime",
- "sig": {
- "params": [
+ },
{
"by_ref": false,
"default": null,
- "name": "datetime",
+ "name": "replace",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "baseTimestamp",
+ "name": "subject",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "count",
"optional": true,
"type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "strtotime",
- "sub_area": "Date"
+ "slug": "str_ireplace",
+ "sub_area": "String"
},
{
"area": "String",
- "canonical_name": "strtoupper",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "canonical_name": "str_pad",
+ "description": "Pads a string to a certain length with another string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_unary_string_runtime",
- "codegen_line": 76,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_str_pad",
+ "codegen_line": 818,
"notes": [
- "Lowers a one-argument string builtin that directly delegates to a runtime helper."
+ "Lowers `str_pad(string, length, pad_string?, pad_type?)` through the shared runtime helper."
],
"runtime_helpers": [
- "__rt_grapheme_strrev",
- "__rt_strcopy"
+ "__rt_str_pad"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_pad.rs",
"sig_line": null
},
- "name": "strtoupper",
+ "name": "str_pad",
"sig": {
"params": [
{
@@ -14312,37 +15215,58 @@
"name": "string",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "length",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "' '",
+ "name": "pad_string",
+ "optional": true,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "1",
+ "name": "pad_type",
+ "optional": true,
+ "type": "int"
}
],
"return_type": "string",
"variadic": null
},
- "slug": "strtoupper",
+ "slug": "str_pad",
"sub_area": "String"
},
{
"area": "String",
- "canonical_name": "substr",
- "description": "Lowers `substr(string, offset, length?)` with target-local pointer arithmetic.",
+ "canonical_name": "str_repeat",
+ "description": "Repeats a string a given number of times.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_substr",
- "codegen_line": 713,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_str_repeat",
+ "codegen_line": 746,
"notes": [
- "Lowers `substr(string, offset, length?)` with target-local pointer arithmetic."
+ "Lowers `str_repeat(string, times)` through the shared runtime helper."
],
"runtime_helpers": [
- "__rt_substr_replace"
+ "__rt_str_repeat"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_repeat.rs",
"sig_line": null
},
- "name": "substr",
+ "name": "str_repeat",
"sig": {
"params": [
{
@@ -14355,54 +15279,44 @@
{
"by_ref": false,
"default": null,
- "name": "offset",
+ "name": "times",
"optional": false,
"type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": true,
- "type": "int"
}
],
"return_type": "string",
"variadic": null
},
- "slug": "substr",
+ "slug": "str_repeat",
"sub_area": "String"
},
{
"area": "String",
- "canonical_name": "substr_replace",
- "description": "Lowers `substr_replace(string, replacement, start, length?)`.",
+ "canonical_name": "str_replace",
+ "description": "Replaces all occurrences of a search string with a replacement string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
- "codegen_function": "lower_substr_replace",
- "codegen_line": 730,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_string_replace",
+ "codegen_line": 780,
"notes": [
- "Lowers `substr_replace(string, replacement, start, length?)`."
- ],
- "runtime_helpers": [
- "__rt_str_repeat",
- "__rt_substr_replace"
+ "Lowers `str_replace()`/`str_ireplace()` with three string operands."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_replace.rs",
"sig_line": null
},
- "name": "substr_replace",
+ "name": "str_replace",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "string",
+ "name": "search",
"optional": false,
"type": "string"
},
@@ -14416,14 +15330,14 @@
{
"by_ref": false,
"default": null,
- "name": "offset",
+ "name": "subject",
"optional": false,
- "type": "int"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "length",
+ "default": "null",
+ "name": "count",
"optional": true,
"type": "int"
}
@@ -14431,79 +15345,91 @@
"return_type": "string",
"variadic": null
},
- "slug": "substr_replace",
+ "slug": "str_replace",
"sub_area": "String"
},
{
- "area": "Filesystem",
- "canonical_name": "sys_get_temp_dir",
- "description": "Lowers `sys_get_temp_dir()` as the project's hardcoded `/tmp` string.",
+ "area": "String",
+ "canonical_name": "str_split",
+ "description": "Converts a string into an array of chunks of the given length.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_sys_get_temp_dir",
- "codegen_line": 4760,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_str_split",
+ "codegen_line": 176,
"notes": [
- "Lowers `sys_get_temp_dir()` as the project's hardcoded `/tmp` string."
+ "Lowers `str_split(string, length?)` into the fixed-width string-array splitter."
],
"runtime_helpers": [
- "__rt_tmpfile"
+ "__rt_str_split"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_split.rs",
"sig_line": null
},
- "name": "sys_get_temp_dir",
+ "name": "str_split",
"sig": {
- "params": [],
- "return_type": "string",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "string",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "1",
+ "name": "length",
+ "optional": true,
+ "type": "int"
+ }
+ ],
+ "return_type": "array",
"variadic": null
},
- "slug": "sys_get_temp_dir",
- "sub_area": "Filesystem"
+ "slug": "str_split",
+ "sub_area": "String"
},
{
- "area": "Filesystem",
- "canonical_name": "symlink",
- "description": "Lowers `symlink(target, link)` through the target-aware libc wrapper.",
+ "area": "String",
+ "canonical_name": "str_starts_with",
+ "description": "Checks if a string starts with a given substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_symlink",
- "codegen_line": 4805,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_binary_string_runtime",
+ "codegen_line": 139,
"notes": [
- "Lowers `symlink(target, link)` through the target-aware libc wrapper."
+ "Lowers a two-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_fileatime",
- "__rt_link",
- "__rt_readlink",
- "__rt_symlink"
+ "__rt_explode"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/str_starts_with.rs",
"sig_line": null
},
- "name": "symlink",
+ "name": "str_starts_with",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "target",
+ "name": "haystack",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "link",
+ "name": "needle",
"optional": false,
"type": "string"
}
@@ -14511,777 +15437,706 @@
"return_type": "bool",
"variadic": null
},
- "slug": "symlink",
- "sub_area": "Filesystem"
+ "slug": "str_starts_with",
+ "sub_area": "String"
},
{
- "area": "Process",
- "canonical_name": "system",
- "description": "Lowers `system(command)` through libc `system()` and returns the legacy empty string result.",
+ "area": "String",
+ "canonical_name": "strcasecmp",
+ "description": "Binary safe case-insensitive string comparison. Returns negative, zero, or positive.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_system",
- "codegen_line": 706,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_binary_string_runtime",
+ "codegen_line": 139,
"notes": [
- "Lowers `system(command)` through libc `system()` and returns the legacy empty string result."
+ "Lowers a two-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_shell_exec"
+ "__rt_explode"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strcasecmp.rs",
"sig_line": null
},
- "name": "system",
+ "name": "strcasecmp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "command",
+ "name": "string1",
"optional": false,
"type": "string"
},
{
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "result_code",
+ "name": "string2",
"optional": false,
- "type": "int"
+ "type": "string"
}
],
- "return_type": "string",
+ "return_type": "int",
"variadic": null
},
- "slug": "system",
- "sub_area": "Process"
+ "slug": "strcasecmp",
+ "sub_area": "String"
},
{
- "area": "Math",
- "canonical_name": "tan",
- "description": "",
+ "area": "String",
+ "canonical_name": "strcmp",
+ "description": "Binary safe string comparison. Returns negative, zero, or positive.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_binary_string_runtime",
+ "codegen_line": 139,
+ "notes": [
+ "Lowers a two-argument string builtin that directly delegates to a runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_explode"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/string/strcmp.rs",
"sig_line": null
},
- "name": "tan",
+ "name": "strcmp",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "string1",
"optional": false,
- "type": "float"
- }
- ],
- "return_type": "float",
- "variadic": null
- },
- "slug": "tan",
- "sub_area": "Math"
- },
- {
- "area": "Math",
- "canonical_name": "tanh",
- "description": "",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": "src/types/signatures.rs",
- "sig_line": null
- },
- "name": "tanh",
- "sig": {
- "params": [
+ "type": "string"
+ },
{
"by_ref": false,
"default": null,
- "name": "num",
+ "name": "string2",
"optional": false,
- "type": "float"
+ "type": "string"
}
],
- "return_type": "float",
+ "return_type": "int",
"variadic": null
},
- "slug": "tanh",
- "sub_area": "Math"
+ "slug": "strcmp",
+ "sub_area": "String"
},
{
- "area": "Filesystem",
- "canonical_name": "tempnam",
- "description": "Lowers `tempnam(directory, prefix)` through the target-aware runtime helper.",
+ "area": "Streams",
+ "canonical_name": "stream_bucket_append",
+ "description": "Appends a bucket to the brigade.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_tempnam",
- "codegen_line": 3810,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_bucket_append_or_prepend",
+ "codegen_line": 2064,
"notes": [
- "Lowers `tempnam(directory, prefix)` through the target-aware runtime helper."
- ],
- "runtime_helpers": [
- "__rt_glob",
- "__rt_scandir",
- "__rt_tempnam"
+ "Lowers `stream_bucket_append` and `stream_bucket_prepend` over the `_buckets` array."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_bucket_append.rs",
"sig_line": null
},
- "name": "tempnam",
+ "name": "stream_bucket_append",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "brigade",
"optional": false,
- "type": "string"
+ "type": "mixed"
},
{
"by_ref": false,
"default": null,
- "name": "prefix",
+ "name": "bucket",
"optional": false,
- "type": "string"
+ "type": "mixed"
}
],
- "return_type": "string",
+ "return_type": "void",
"variadic": null
},
- "slug": "tempnam",
- "sub_area": "Filesystem"
+ "slug": "stream_bucket_append",
+ "sub_area": "Streams"
},
{
- "area": "Date",
- "canonical_name": "time",
- "description": "Lowers `time()` through the shared wall-clock runtime helper.",
+ "area": "IO",
+ "canonical_name": "stream_bucket_make_writeable",
+ "description": "Returns a bucket object from the brigade for use in a stream filter.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
- "codegen_function": "lower_time",
- "codegen_line": 615,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_bucket_make_writeable",
+ "codegen_line": 1987,
"notes": [
- "Lowers `time()` through the shared wall-clock runtime helper."
+ "Lowers `stream_bucket_make_writeable(brigade)` by popping the brigade head."
],
"runtime_helpers": [
- "__rt_time"
+ "__rt_stdclass_get"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_bucket_make_writeable.rs",
"sig_line": null
},
- "name": "time",
+ "name": "stream_bucket_make_writeable",
"sig": {
- "params": [],
- "return_type": "int",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "brigade",
+ "optional": false,
+ "type": "mixed"
+ }
+ ],
+ "return_type": "mixed",
"variadic": null
},
- "slug": "time",
- "sub_area": "Date"
+ "slug": "stream_bucket_make_writeable",
+ "sub_area": "IO"
},
{
- "area": "Filesystem",
- "canonical_name": "tmpfile",
- "description": "Lowers `tmpfile()` and boxes the anonymous stream descriptor or PHP false.",
+ "area": "IO",
+ "canonical_name": "stream_bucket_new",
+ "description": "Creates a new bucket for use in a stream filter.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_tmpfile",
- "codegen_line": 4773,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_bucket_new",
+ "codegen_line": 1970,
"notes": [
- "Lowers `tmpfile()` and boxes the anonymous stream descriptor or PHP false."
- ],
- "runtime_helpers": [
- "__rt_filemtime",
- "__rt_linkinfo",
- "__rt_tmpfile"
+ "Lowers `stream_bucket_new(stream, data)` into a stdClass-backed bucket object."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_bucket_new.rs",
"sig_line": null
},
- "name": "tmpfile",
+ "name": "stream_bucket_new",
"sig": {
- "params": [],
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "stream",
+ "optional": false,
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "buffer",
+ "optional": false,
+ "type": "string"
+ }
+ ],
"return_type": "mixed",
"variadic": null
},
- "slug": "tmpfile",
- "sub_area": "Filesystem"
+ "slug": "stream_bucket_new",
+ "sub_area": "IO"
},
{
- "area": "Filesystem",
- "canonical_name": "touch",
- "description": "Lowers `touch(path, mtime?, atime?)` through the target-aware runtime helper.",
+ "area": "Streams",
+ "canonical_name": "stream_bucket_prepend",
+ "description": "Prepends a bucket to the brigade.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_touch",
- "codegen_line": 3880,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_bucket_append_or_prepend",
+ "codegen_line": 2064,
"notes": [
- "Lowers `touch(path, mtime?, atime?)` through the target-aware runtime helper."
+ "Lowers `stream_bucket_append` and `stream_bucket_prepend` over the `_buckets` array."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_bucket_prepend.rs",
"sig_line": null
},
- "name": "touch",
+ "name": "stream_bucket_prepend",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "brigade",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "mtime",
- "optional": true,
- "type": "int"
+ "type": "mixed"
},
{
"by_ref": false,
"default": null,
- "name": "atime",
- "optional": true,
- "type": "int"
+ "name": "bucket",
+ "optional": false,
+ "type": "mixed"
}
],
- "return_type": "bool",
+ "return_type": "void",
"variadic": null
},
- "slug": "touch",
- "sub_area": "Filesystem"
+ "slug": "stream_bucket_prepend",
+ "sub_area": "Streams"
},
{
"area": "IO",
- "canonical_name": "stream_isatty",
- "description": "Lowers `stream_isatty(stream)`.",
+ "canonical_name": "stream_context_create",
+ "description": "Creates a stream context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_isatty",
- "codegen_line": 1908,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_create",
+ "codegen_line": 1064,
"notes": [
- "Lowers `stream_isatty(stream)`."
- ],
- "runtime_helpers": [
- "__rt_stream_isatty"
+ "Lowers `stream_context_create(options?, params?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_context_create.rs",
"sig_line": null
},
- "name": "stream_isatty",
+ "name": "stream_context_create",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
+ "default": "null",
+ "name": "options",
+ "optional": true,
+ "type": "array"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "params",
+ "optional": true,
+ "type": "array"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_isatty",
+ "slug": "stream_context_create",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_server",
- "description": "Lowers `stream_socket_server(address)` and boxes `resource|false`.",
+ "canonical_name": "stream_context_get_default",
+ "description": "Retrieves the default stream context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_server",
- "codegen_line": 2155,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_get_default",
+ "codegen_line": 1078,
"notes": [
- "Lowers `stream_socket_server(address)` and boxes `resource|false`."
- ],
- "runtime_helpers": [
- "__rt_stream_socket_server"
+ "Lowers `stream_context_get_default(options?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_context_get_default.rs",
"sig_line": null
},
- "name": "stream_socket_server",
+ "name": "stream_context_get_default",
"sig": {
"params": [
{
"by_ref": false,
- "default": null,
- "name": "address",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_code",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_message",
- "optional": false,
- "type": "int"
+ "default": "null",
+ "name": "options",
+ "optional": true,
+ "type": "array"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "stream_socket_server",
+ "slug": "stream_context_get_default",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_client",
- "description": "Lowers `stream_socket_client(address)` and records the connected host for TLS defaults.",
+ "canonical_name": "stream_context_get_options",
+ "description": "Retrieves options for the specified stream context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_client",
- "codegen_line": 2178,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_get_options",
+ "codegen_line": 1252,
"notes": [
- "Lowers `stream_socket_client(address)` and records the connected host for TLS defaults."
+ "Lowers `stream_context_get_options(context)`."
],
"runtime_helpers": [
- "__rt_stash_connect_host",
- "__rt_stream_socket_client"
+ "__rt_hash_new",
+ "__rt_incref"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_context_get_options.rs",
"sig_line": null
},
- "name": "stream_socket_client",
+ "name": "stream_context_get_options",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "address",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_code",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_message",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "timeout",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
+ "name": "context",
"optional": false,
- "type": "float"
+ "type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "array",
"variadic": null
},
- "slug": "stream_socket_client",
+ "slug": "stream_context_get_options",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_accept",
- "description": "Lowers `stream_socket_accept(server, timeout?, peer_name?)`.",
+ "canonical_name": "stream_context_get_params",
+ "description": "Retrieves parameters from the specified stream context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_accept",
- "codegen_line": 2217,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_get_params",
+ "codegen_line": 1291,
"notes": [
- "Lowers `stream_socket_accept(server, timeout?, peer_name?)`."
- ],
- "runtime_helpers": [
- "__rt_stream_socket_accept"
+ "Lowers `stream_context_get_params(context)` to an empty associative hash."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_context_get_params.rs",
"sig_line": null
},
- "name": "stream_socket_accept",
+ "name": "stream_context_get_params",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "socket",
+ "name": "context",
"optional": false,
"type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "timeout",
- "optional": true,
- "type": "float"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "peer_name",
- "optional": true,
- "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "array",
"variadic": null
},
- "slug": "stream_socket_accept",
+ "slug": "stream_context_get_params",
"sub_area": "IO"
},
{
- "area": "Streams",
- "canonical_name": "fsockopen",
- "description": "",
+ "area": "IO",
+ "canonical_name": "stream_context_set_default",
+ "description": "Sets the default stream context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_set_default",
+ "codegen_line": 1088,
+ "notes": [
+ "Lowers `stream_context_set_default(options)`."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/stream_context_set_default.rs",
"sig_line": null
},
- "name": "fsockopen",
+ "name": "stream_context_set_default",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "hostname",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "port",
+ "name": "options",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_code",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_message",
- "optional": true,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "timeout",
- "optional": true,
- "type": "float"
+ "type": "array"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "fsockopen",
- "sub_area": "Streams"
+ "slug": "stream_context_set_default",
+ "sub_area": "IO"
},
{
- "area": "Streams",
- "canonical_name": "pfsockopen",
- "description": "",
+ "area": "IO",
+ "canonical_name": "stream_context_set_option",
+ "description": "Sets an option on the specified context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_set_option",
+ "codegen_line": 1098,
+ "notes": [
+ "Lowers `stream_context_set_option(context, options)` and the four-argument form."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/stream_context_set_option.rs",
"sig_line": null
},
- "name": "pfsockopen",
+ "name": "stream_context_set_option",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "hostname",
+ "name": "context",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "port",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "error_code",
- "optional": true,
- "type": "int"
+ "name": "wrapper_or_options",
+ "optional": false,
+ "type": "string"
},
{
- "by_ref": true,
- "default": null,
- "name": "error_message",
+ "by_ref": false,
+ "default": "null",
+ "name": "option_name",
"optional": true,
"type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "timeout",
+ "default": "null",
+ "name": "value",
"optional": true,
- "type": "float"
+ "type": "mixed"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "pfsockopen",
- "sub_area": "Streams"
+ "slug": "stream_context_set_option",
+ "sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_wrapper_register",
- "description": "Lowers `stream_wrapper_register(protocol, class, flags?)`.",
+ "canonical_name": "stream_context_set_params",
+ "description": "Sets parameters on the specified context.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_wrapper_register",
- "codegen_line": 887,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_context_set_params",
+ "codegen_line": 1118,
"notes": [
- "Lowers `stream_wrapper_register(protocol, class, flags?)`."
- ],
- "runtime_helpers": [
- "__rt_stream_wrapper_register"
+ "Lowers `stream_context_set_params(context, params)` as an accepted parameter update."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_context_set_params.rs",
"sig_line": null
},
- "name": "stream_wrapper_register",
+ "name": "stream_context_set_params",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "context",
"optional": false,
- "type": "string"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "class",
+ "name": "params",
"optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": true,
- "type": "int"
+ "type": "array"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "stream_wrapper_register",
+ "slug": "stream_context_set_params",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_wrapper_unregister",
- "description": "Lowers `stream_wrapper_unregister(protocol)`.",
+ "canonical_name": "stream_copy_to_stream",
+ "description": "Copies data from one stream to another.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_wrapper_unregister",
- "codegen_line": 917,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_copy_to_stream",
+ "codegen_line": 1360,
"notes": [
- "Lowers `stream_wrapper_unregister(protocol)`."
- ],
- "runtime_helpers": [
- "__rt_stream_wrapper_unregister"
+ "Lowers `stream_copy_to_stream(from, to, length?, offset?)` through wrapper-aware read/write loops."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_copy_to_stream.rs",
"sig_line": null
},
- "name": "stream_wrapper_unregister",
+ "name": "stream_copy_to_stream",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "from",
"optional": false,
- "type": "string"
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "to",
+ "optional": false,
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "-1",
+ "name": "offset",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_wrapper_unregister",
+ "slug": "stream_copy_to_stream",
"sub_area": "IO"
},
{
- "area": "IO",
- "canonical_name": "stream_wrapper_restore",
- "description": "Lowers `stream_wrapper_restore(protocol)` as a successful no-op.",
+ "area": "Streams",
+ "canonical_name": "stream_filter_append",
+ "description": "Attaches a filter to a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_wrapper_restore",
- "codegen_line": 939,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_filter_attach",
+ "codegen_line": 1550,
"notes": [
- "Lowers `stream_wrapper_restore(protocol)` as a successful no-op."
+ "Lowers `stream_filter_append` and `stream_filter_prepend`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_filter_append.rs",
"sig_line": null
},
- "name": "stream_wrapper_restore",
+ "name": "stream_filter_append",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "stream",
+ "optional": false,
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "filtername",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "3",
+ "name": "read_write",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "params",
+ "optional": true,
+ "type": "mixed"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_wrapper_restore",
- "sub_area": "IO"
+ "slug": "stream_filter_append",
+ "sub_area": "Streams"
},
{
- "area": "IO",
- "canonical_name": "stream_socket_enable_crypto",
- "description": "Lowers `stream_socket_enable_crypto(stream, enable, method?, session_stream?)`.",
+ "area": "Streams",
+ "canonical_name": "stream_filter_prepend",
+ "description": "Attaches a filter to a stream (prepend).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_enable_crypto",
- "codegen_line": 2328,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_filter_attach",
+ "codegen_line": 1550,
"notes": [
- "Lowers `stream_socket_enable_crypto(stream, enable, method?, session_stream?)`."
+ "Lowers `stream_filter_append` and `stream_filter_prepend`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_filter_prepend.rs",
"sig_line": null
},
- "name": "stream_socket_enable_crypto",
+ "name": "stream_filter_prepend",
"sig": {
"params": [
{
@@ -15294,599 +16149,590 @@
{
"by_ref": false,
"default": null,
- "name": "enable",
+ "name": "filtername",
"optional": false,
- "type": "bool"
+ "type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "crypto_method",
+ "default": "3",
+ "name": "read_write",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
- "name": "session_stream",
+ "default": "null",
+ "name": "params",
"optional": true,
- "type": "resource"
+ "type": "mixed"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_socket_enable_crypto",
- "sub_area": "IO"
+ "slug": "stream_filter_prepend",
+ "sub_area": "Streams"
},
{
"area": "IO",
- "canonical_name": "stream_context_create",
- "description": "Lowers `stream_context_create(options?, params?)`.",
+ "canonical_name": "stream_filter_register",
+ "description": "Registers a user-defined stream filter.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_create",
- "codegen_line": 951,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_filter_register",
+ "codegen_line": 1521,
"notes": [
- "Lowers `stream_context_create(options?, params?)`."
+ "Lowers `stream_filter_register(filter_name, class)` into the user-filter registry helper."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_filter_register"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_filter_register.rs",
"sig_line": null
},
- "name": "stream_context_create",
+ "name": "stream_filter_register",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "options",
- "optional": true,
- "type": "array"
+ "name": "filter_name",
+ "optional": false,
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "params",
- "optional": true,
- "type": "array"
+ "name": "class",
+ "optional": false,
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_context_create",
+ "slug": "stream_filter_register",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_get_default",
- "description": "Lowers `stream_context_get_default(options?)`.",
+ "canonical_name": "stream_filter_remove",
+ "description": "Removes a filter from a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_get_default",
- "codegen_line": 965,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_filter_remove",
+ "codegen_line": 1939,
"notes": [
- "Lowers `stream_context_get_default(options?)`."
+ "Lowers `stream_filter_remove(filter)` and clears both direction tables for the fd."
+ ],
+ "runtime_helpers": [
+ "__rt_user_filter_release_fd"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_filter_remove.rs",
"sig_line": null
},
- "name": "stream_context_get_default",
+ "name": "stream_filter_remove",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "options",
- "optional": true,
- "type": "array"
+ "name": "stream_filter",
+ "optional": false,
+ "type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_context_get_default",
+ "slug": "stream_filter_remove",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_set_default",
- "description": "Lowers `stream_context_set_default(options)`.",
+ "canonical_name": "stream_get_contents",
+ "description": "Reads remainder of a stream into a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_set_default",
- "codegen_line": 975,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_contents",
+ "codegen_line": 1301,
"notes": [
- "Lowers `stream_context_set_default(options)`."
+ "Lowers `stream_get_contents(stream, length?, offset?)` to `string|false`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_get_contents.rs",
"sig_line": null
},
- "name": "stream_context_set_default",
+ "name": "stream_get_contents",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "options",
+ "name": "stream",
"optional": false,
- "type": "array"
+ "type": "resource"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "-1",
+ "name": "offset",
+ "optional": true,
+ "type": "int"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "stream_context_set_default",
+ "slug": "stream_get_contents",
+ "sub_area": "IO"
+ },
+ {
+ "area": "IO",
+ "canonical_name": "stream_get_filters",
+ "description": "Retrieves list of registered filters.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_filters",
+ "codegen_line": 1493,
+ "notes": [
+ "Lowers `stream_get_filters()` to the static built-in filter list."
+ ],
+ "runtime_helpers": [],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/stream_get_filters.rs",
+ "sig_line": null
+ },
+ "name": "stream_get_filters",
+ "sig": {
+ "params": [],
+ "return_type": "array",
+ "variadic": null
+ },
+ "slug": "stream_get_filters",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_set_option",
- "description": "Lowers `stream_context_set_option(context, options)` and the four-argument form.",
+ "canonical_name": "stream_get_line",
+ "description": "Gets line from stream resource up to a given delimiter.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_set_option",
- "codegen_line": 985,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_line",
+ "codegen_line": 1393,
"notes": [
- "Lowers `stream_context_set_option(context, options)` and the four-argument form."
+ "Lowers `stream_get_line(stream, length, ending?)`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_get_line.rs",
"sig_line": null
},
- "name": "stream_context_set_option",
+ "name": "stream_get_line",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "context",
+ "name": "stream",
"optional": false,
"type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "wrapper_or_options",
+ "name": "length",
"optional": false,
- "type": "string"
+ "type": "int"
},
{
"by_ref": false,
- "default": null,
- "name": "option_name",
+ "default": "''",
+ "name": "ending",
"optional": true,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "value",
- "optional": true,
- "type": "mixed"
}
],
- "return_type": "bool",
+ "return_type": "string",
"variadic": null
},
- "slug": "stream_context_set_option",
+ "slug": "stream_get_line",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_set_params",
- "description": "Lowers `stream_context_set_params(context, params)` as an accepted parameter update.",
+ "canonical_name": "stream_get_meta_data",
+ "description": "Retrieves metadata from streams/file pointers.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_set_params",
- "codegen_line": 1005,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_meta_data",
+ "codegen_line": 1446,
"notes": [
- "Lowers `stream_context_set_params(context, params)` as an accepted parameter update."
+ "Lowers `stream_get_meta_data(stream)` through the metadata runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_get_meta_data"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_get_meta_data.rs",
"sig_line": null
},
- "name": "stream_context_set_params",
+ "name": "stream_get_meta_data",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "context",
+ "name": "stream",
"optional": false,
"type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "params",
- "optional": false,
- "type": "array"
}
],
- "return_type": "bool",
+ "return_type": "array",
"variadic": null
},
- "slug": "stream_context_set_params",
+ "slug": "stream_get_meta_data",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_get_options",
- "description": "Lowers `stream_context_get_options(context)`.",
+ "canonical_name": "stream_get_transports",
+ "description": "Retrieves list of registered socket transports.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_get_options",
- "codegen_line": 1139,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_transports",
+ "codegen_line": 1477,
"notes": [
- "Lowers `stream_context_get_options(context)`."
- ],
- "runtime_helpers": [
- "__rt_hash_new",
- "__rt_incref"
+ "Lowers `stream_get_transports()` to the static transport list."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_get_transports.rs",
"sig_line": null
},
- "name": "stream_context_get_options",
+ "name": "stream_get_transports",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "stream_or_context",
- "optional": false,
- "type": "resource"
- }
- ],
+ "params": [],
"return_type": "array",
"variadic": null
},
- "slug": "stream_context_get_options",
+ "slug": "stream_get_transports",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_context_get_params",
- "description": "Lowers `stream_context_get_params(context)` to an empty associative hash.",
+ "canonical_name": "stream_get_wrappers",
+ "description": "Retrieves list of registered streams.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_context_get_params",
- "codegen_line": 1178,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_get_wrappers",
+ "codegen_line": 1461,
"notes": [
- "Lowers `stream_context_get_params(context)` to an empty associative hash."
+ "Lowers `stream_get_wrappers()` to the static built-in wrapper list."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_get_wrappers.rs",
"sig_line": null
},
- "name": "stream_context_get_params",
+ "name": "stream_get_wrappers",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "context",
- "optional": false,
- "type": "resource"
- }
- ],
+ "params": [],
"return_type": "array",
"variadic": null
},
- "slug": "stream_context_get_params",
+ "slug": "stream_get_wrappers",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_resolve_include_path",
- "description": "Lowers `stream_resolve_include_path(filename)` as realpath-backed `string|false`.",
+ "canonical_name": "stream_is_local",
+ "description": "Checks if a stream is a local stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_resolve_include_path",
- "codegen_line": 2142,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_is_local",
+ "codegen_line": 2103,
"notes": [
- "Lowers `stream_resolve_include_path(filename)` as realpath-backed `string|false`."
- ],
- "runtime_helpers": [
- "__rt_realpath",
- "__rt_stream_socket_server"
+ "Lowers `stream_is_local(stream)` as a true predicate after evaluating its argument."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_is_local.rs",
"sig_line": null
},
- "name": "stream_resolve_include_path",
+ "name": "stream_is_local",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filename",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_resolve_include_path",
+ "slug": "stream_is_local",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_filter_register",
- "description": "Lowers `stream_filter_register(filter_name, class)` into the user-filter registry helper.",
+ "canonical_name": "stream_isatty",
+ "description": "Checks if a stream is a TTY.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_filter_register",
- "codegen_line": 1408,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_isatty",
+ "codegen_line": 2127,
"notes": [
- "Lowers `stream_filter_register(filter_name, class)` into the user-filter registry helper."
+ "Lowers `stream_isatty(stream)`."
],
"runtime_helpers": [
- "__rt_stream_filter_register"
+ "__rt_stream_isatty"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_isatty.rs",
"sig_line": null
},
- "name": "stream_filter_register",
+ "name": "stream_isatty",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "filter_name",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "class",
+ "name": "stream",
"optional": false,
- "type": "string"
+ "type": "resource"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "stream_filter_register",
+ "slug": "stream_isatty",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_bucket_make_writeable",
- "description": "Lowers `stream_bucket_make_writeable(brigade)` by popping the brigade head.",
+ "canonical_name": "stream_resolve_include_path",
+ "description": "Resolves filename against the include path.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_bucket_make_writeable",
- "codegen_line": 1768,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_resolve_include_path",
+ "codegen_line": 2361,
"notes": [
- "Lowers `stream_bucket_make_writeable(brigade)` by popping the brigade head."
+ "Lowers `stream_resolve_include_path(filename)` as realpath-backed `string|false`."
],
"runtime_helpers": [
- "__rt_stdclass_get"
+ "__rt_realpath",
+ "__rt_stream_socket_server"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_resolve_include_path.rs",
"sig_line": null
},
- "name": "stream_bucket_make_writeable",
+ "name": "stream_resolve_include_path",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "brigade",
+ "name": "filename",
"optional": false,
- "type": "mixed"
+ "type": "string"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "stream_bucket_make_writeable",
+ "slug": "stream_resolve_include_path",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_bucket_new",
- "description": "Lowers `stream_bucket_new(stream, data)` into a stdClass-backed bucket object.",
+ "canonical_name": "stream_select",
+ "description": "Runs the equivalent of the select() system call on the given arrays of streams.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_bucket_new",
- "codegen_line": 1751,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_select",
+ "codegen_line": 2316,
"notes": [
- "Lowers `stream_bucket_new(stream, data)` into a stdClass-backed bucket object."
+ "Lowers `stream_select(read, write, except, seconds, microseconds?)`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_select.rs",
"sig_line": null
},
- "name": "stream_bucket_new",
+ "name": "stream_select",
"sig": {
"params": [
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "stream",
+ "name": "read",
"optional": false,
- "type": "resource"
+ "type": "array"
},
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "buffer",
+ "name": "write",
"optional": false,
- "type": "string"
- }
- ],
- "return_type": "mixed",
- "variadic": null
- },
- "slug": "stream_bucket_new",
- "sub_area": "IO"
- },
- {
- "area": "Streams",
- "canonical_name": "stream_bucket_append",
- "description": "",
- "in_catalog": true,
- "is_internal": false,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": "src/types/signatures.rs",
- "sig_line": null
- },
- "name": "stream_bucket_append",
- "sig": {
- "params": [
+ "type": "array"
+ },
{
- "by_ref": false,
+ "by_ref": true,
"default": null,
- "name": "brigade",
+ "name": "except",
"optional": false,
- "type": "mixed"
+ "type": "array"
},
{
"by_ref": false,
"default": null,
- "name": "bucket",
+ "name": "seconds",
"optional": false,
- "type": "mixed"
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "microseconds",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "void",
+ "return_type": "int",
"variadic": null
},
- "slug": "stream_bucket_append",
- "sub_area": "Streams"
+ "slug": "stream_select",
+ "sub_area": "IO"
},
{
- "area": "Streams",
- "canonical_name": "stream_bucket_prepend",
- "description": "",
+ "area": "IO",
+ "canonical_name": "stream_set_blocking",
+ "description": "Sets blocking/non-blocking mode on a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_set_blocking",
+ "codegen_line": 2142,
+ "notes": [
+ "Lowers `stream_set_blocking(stream, enable)`."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_set_blocking",
+ "__rt_user_wrapper_set_option"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/stream_set_blocking.rs",
"sig_line": null
},
- "name": "stream_bucket_prepend",
+ "name": "stream_set_blocking",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "brigade",
+ "name": "stream",
"optional": false,
- "type": "mixed"
+ "type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "bucket",
+ "name": "enable",
"optional": false,
- "type": "mixed"
+ "type": "bool"
}
],
- "return_type": "void",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_bucket_prepend",
- "sub_area": "Streams"
+ "slug": "stream_set_blocking",
+ "sub_area": "IO"
},
{
"area": "IO",
"canonical_name": "stream_set_chunk_size",
- "description": "Lowers `stream_set_chunk_size(stream, size)` and returns the previous size.",
+ "description": "Sets the read chunk size on a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_stream_set_chunk_size",
- "codegen_line": 1975,
+ "codegen_line": 2194,
"notes": [
"Lowers `stream_set_chunk_size(stream, size)` and returns the previous size."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_set_chunk_size.rs",
"sig_line": null
},
"name": "stream_set_chunk_size",
@@ -15916,21 +16762,21 @@
{
"area": "IO",
"canonical_name": "stream_set_read_buffer",
- "description": "Lowers stream read/write buffer setters as successful no-ops.",
+ "description": "Sets the read file buffering on a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_stream_set_buffer",
- "codegen_line": 2035,
+ "codegen_line": 2254,
"notes": [
"Lowers stream read/write buffer setters as successful no-ops."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_set_read_buffer.rs",
"sig_line": null
},
"name": "stream_set_read_buffer",
@@ -15959,25 +16805,25 @@
},
{
"area": "IO",
- "canonical_name": "stream_set_write_buffer",
- "description": "Lowers stream read/write buffer setters as successful no-ops.",
+ "canonical_name": "stream_set_timeout",
+ "description": "Sets timeout period on a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_set_buffer",
- "codegen_line": 2035,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_set_timeout",
+ "codegen_line": 2267,
"notes": [
- "Lowers stream read/write buffer setters as successful no-ops."
+ "Lowers `stream_set_timeout(stream, seconds, microseconds?)`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_set_timeout.rs",
"sig_line": null
},
- "name": "stream_set_write_buffer",
+ "name": "stream_set_timeout",
"sig": {
"params": [
{
@@ -15990,38 +16836,45 @@
{
"by_ref": false,
"default": null,
- "name": "size",
+ "name": "seconds",
"optional": false,
"type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "microseconds",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_set_write_buffer",
+ "slug": "stream_set_timeout",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_get_contents",
- "description": "Lowers `stream_get_contents(stream, length?, offset?)` to `string|false`.",
+ "canonical_name": "stream_set_write_buffer",
+ "description": "Sets the write file buffering on a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_contents",
- "codegen_line": 1188,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_set_buffer",
+ "codegen_line": 2254,
"notes": [
- "Lowers `stream_get_contents(stream, length?, offset?)` to `string|false`."
+ "Lowers stream read/write buffer setters as successful no-ops."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_set_write_buffer.rs",
"sig_line": null
},
- "name": "stream_get_contents",
+ "name": "stream_set_write_buffer",
"sig": {
"params": [
{
@@ -16034,138 +16887,131 @@
{
"by_ref": false,
"default": null,
- "name": "length",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "offset",
- "optional": true,
+ "name": "size",
+ "optional": false,
"type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "int",
"variadic": null
},
- "slug": "stream_get_contents",
+ "slug": "stream_set_write_buffer",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_get_line",
- "description": "Lowers `stream_get_line(stream, length, ending?)`.",
+ "canonical_name": "stream_socket_accept",
+ "description": "Accept a connection on a socket created by stream_socket_server().",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_line",
- "codegen_line": 1280,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_accept",
+ "codegen_line": 2436,
"notes": [
- "Lowers `stream_get_line(stream, length, ending?)`."
+ "Lowers `stream_socket_accept(server, timeout?, peer_name?)`."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_socket_accept"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_accept.rs",
"sig_line": null
},
- "name": "stream_get_line",
+ "name": "stream_socket_accept",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "socket",
"optional": false,
"type": "resource"
},
{
"by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
+ "default": "null",
+ "name": "timeout",
+ "optional": true,
+ "type": "float"
},
{
- "by_ref": false,
- "default": null,
- "name": "ending",
+ "by_ref": true,
+ "default": "null",
+ "name": "peer_name",
"optional": true,
"type": "string"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_get_line",
+ "slug": "stream_socket_accept",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_get_meta_data",
- "description": "Lowers `stream_get_meta_data(stream)` through the metadata runtime helper.",
+ "canonical_name": "stream_socket_client",
+ "description": "Open Internet or Unix domain socket connection.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_meta_data",
- "codegen_line": 1333,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_client",
+ "codegen_line": 2397,
"notes": [
- "Lowers `stream_get_meta_data(stream)` through the metadata runtime helper."
+ "Lowers `stream_socket_client(address)` and records the connected host for TLS defaults."
],
"runtime_helpers": [
- "__rt_stream_get_meta_data"
+ "__rt_stash_connect_host",
+ "__rt_stream_socket_client"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_client.rs",
"sig_line": null
},
- "name": "stream_get_meta_data",
+ "name": "stream_socket_client",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "address",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "array",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_get_meta_data",
+ "slug": "stream_socket_client",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_set_blocking",
- "description": "Lowers `stream_set_blocking(stream, enable)`.",
+ "canonical_name": "stream_socket_enable_crypto",
+ "description": "Turns encryption on/off on an already connected socket.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_set_blocking",
- "codegen_line": 1923,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_enable_crypto",
+ "codegen_line": 2547,
"notes": [
- "Lowers `stream_set_blocking(stream, enable)`."
- ],
- "runtime_helpers": [
- "__rt_stream_set_blocking",
- "__rt_user_wrapper_set_option"
+ "Lowers `stream_socket_enable_crypto(stream, enable, method?, session_stream?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_enable_crypto.rs",
"sig_line": null
},
- "name": "stream_set_blocking",
+ "name": "stream_socket_enable_crypto",
"sig": {
"params": [
{
@@ -16181,194 +17027,203 @@
"name": "enable",
"optional": false,
"type": "bool"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "crypto_method",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "session_stream",
+ "optional": true,
+ "type": "resource"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "stream_set_blocking",
+ "slug": "stream_socket_enable_crypto",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_set_timeout",
- "description": "Lowers `stream_set_timeout(stream, seconds, microseconds?)`.",
+ "canonical_name": "stream_socket_get_name",
+ "description": "Retrieve the name of the local or remote sockets.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_set_timeout",
- "codegen_line": 2048,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_get_name",
+ "codegen_line": 2496,
"notes": [
- "Lowers `stream_set_timeout(stream, seconds, microseconds?)`."
+ "Lowers `stream_socket_get_name(socket, remote)` and boxes `string|false`."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_socket_get_name"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_get_name.rs",
"sig_line": null
},
- "name": "stream_set_timeout",
+ "name": "stream_socket_get_name",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "socket",
"optional": false,
"type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "seconds",
+ "name": "remote",
"optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "microseconds",
- "optional": true,
- "type": "int"
+ "type": "bool"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_set_timeout",
+ "slug": "stream_socket_get_name",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_select",
- "description": "Lowers `stream_select(read, write, except, seconds, microseconds?)`.",
+ "canonical_name": "stream_socket_pair",
+ "description": "Creates a pair of connected, indistinguishable socket streams.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_select",
- "codegen_line": 2097,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_pair",
+ "codegen_line": 2465,
"notes": [
- "Lowers `stream_select(read, write, except, seconds, microseconds?)`."
+ "Lowers `stream_socket_pair(domain, type, protocol)` and boxes `array|false`."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_socket_pair"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_pair.rs",
"sig_line": null
},
- "name": "stream_select",
+ "name": "stream_socket_pair",
"sig": {
"params": [
{
- "by_ref": true,
- "default": null,
- "name": "read",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": true,
- "default": null,
- "name": "write",
- "optional": false,
- "type": "array"
- },
- {
- "by_ref": true,
+ "by_ref": false,
"default": null,
- "name": "except",
+ "name": "domain",
"optional": false,
- "type": "array"
+ "type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "seconds",
+ "name": "type",
"optional": false,
"type": "int"
},
{
"by_ref": false,
"default": null,
- "name": "microseconds",
- "optional": true,
+ "name": "protocol",
+ "optional": false,
"type": "int"
}
],
- "return_type": "int",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_select",
+ "slug": "stream_socket_pair",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_shutdown",
- "description": "Lowers `stream_socket_shutdown(stream, mode)`.",
+ "canonical_name": "stream_socket_recvfrom",
+ "description": "Receives data from a socket, connected or not.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_shutdown",
- "codegen_line": 2303,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_recvfrom",
+ "codegen_line": 2599,
"notes": [
- "Lowers `stream_socket_shutdown(stream, mode)`."
- ],
- "runtime_helpers": [
- "__rt_stream_socket_shutdown"
+ "Lowers `stream_socket_recvfrom(socket, length, flags?, address?)`."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_recvfrom.rs",
"sig_line": null
},
- "name": "stream_socket_shutdown",
+ "name": "stream_socket_recvfrom",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "socket",
"optional": false,
"type": "resource"
},
{
- "by_ref": false,
- "default": null,
- "name": "mode",
- "optional": false,
- "type": "int"
+ "by_ref": false,
+ "default": null,
+ "name": "length",
+ "optional": false,
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "flags",
+ "optional": true,
+ "type": "int"
+ },
+ {
+ "by_ref": true,
+ "default": "''",
+ "name": "address",
+ "optional": true,
+ "type": "string"
}
],
- "return_type": "bool",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "stream_socket_shutdown",
+ "slug": "stream_socket_recvfrom",
"sub_area": "IO"
},
{
"area": "IO",
"canonical_name": "stream_socket_sendto",
- "description": "Lowers `stream_socket_sendto(socket, data, flags?, address?)` and boxes `int|false`.",
+ "description": "Sends a message to a socket, whether it is connected or not.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_stream_socket_sendto",
- "codegen_line": 2422,
+ "codegen_line": 2641,
"notes": [
"Lowers `stream_socket_sendto(socket, data, flags?, address?)` and boxes `int|false`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_sendto.rs",
"sig_line": null
},
"name": "stream_socket_sendto",
@@ -16390,14 +17245,14 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "0",
"name": "flags",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "''",
"name": "address",
"optional": true,
"type": "string"
@@ -16411,1076 +17266,1185 @@
},
{
"area": "IO",
- "canonical_name": "stream_socket_recvfrom",
- "description": "Lowers `stream_socket_recvfrom(socket, length, flags?, address?)`.",
+ "canonical_name": "stream_socket_server",
+ "description": "Create an Internet or Unix domain server socket.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_recvfrom",
- "codegen_line": 2380,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_server",
+ "codegen_line": 2374,
"notes": [
- "Lowers `stream_socket_recvfrom(socket, length, flags?, address?)`."
+ "Lowers `stream_socket_server(address)` and boxes `resource|false`."
+ ],
+ "runtime_helpers": [
+ "__rt_stream_socket_server"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_server.rs",
"sig_line": null
},
- "name": "stream_socket_recvfrom",
+ "name": "stream_socket_server",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "socket",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "length",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "flags",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": true,
- "default": null,
"name": "address",
- "optional": true,
+ "optional": false,
"type": "string"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "stream_socket_recvfrom",
+ "slug": "stream_socket_server",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_get_name",
- "description": "Lowers `stream_socket_get_name(socket, remote)` and boxes `string|false`.",
+ "canonical_name": "stream_socket_shutdown",
+ "description": "Shutdown a full-duplex connection.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_get_name",
- "codegen_line": 2277,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_socket_shutdown",
+ "codegen_line": 2522,
"notes": [
- "Lowers `stream_socket_get_name(socket, remote)` and boxes `string|false`."
+ "Lowers `stream_socket_shutdown(stream, mode)`."
],
"runtime_helpers": [
- "__rt_stream_socket_get_name"
+ "__rt_stream_socket_shutdown"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_socket_shutdown.rs",
"sig_line": null
},
- "name": "stream_socket_get_name",
+ "name": "stream_socket_shutdown",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "socket",
+ "name": "stream",
"optional": false,
"type": "resource"
},
{
"by_ref": false,
"default": null,
- "name": "remote",
+ "name": "mode",
"optional": false,
- "type": "bool"
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_socket_get_name",
+ "slug": "stream_socket_shutdown",
"sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "stream_socket_pair",
- "description": "Lowers `stream_socket_pair(domain, type, protocol)` and boxes `array|false`.",
+ "canonical_name": "stream_supports_lock",
+ "description": "Tells whether the stream supports locking.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_socket_pair",
- "codegen_line": 2246,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_supports_lock",
+ "codegen_line": 2115,
"notes": [
- "Lowers `stream_socket_pair(domain, type, protocol)` and boxes `array|false`."
+ "Lowers `stream_supports_lock(stream)` as true after resource unboxing."
],
"runtime_helpers": [
- "__rt_stream_socket_pair"
+ "__rt_stream_isatty"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_supports_lock.rs",
"sig_line": null
},
- "name": "stream_socket_pair",
+ "name": "stream_supports_lock",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "domain",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "type",
- "optional": false,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "protocol",
+ "name": "stream",
"optional": false,
- "type": "int"
+ "type": "resource"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_socket_pair",
+ "slug": "stream_supports_lock",
"sub_area": "IO"
},
{
- "area": "Process",
- "canonical_name": "popen",
- "description": "Lowers `popen(command, mode)` and boxes the process pipe as `resource|false`.",
+ "area": "IO",
+ "canonical_name": "stream_wrapper_register",
+ "description": "Registers a URL wrapper implemented as a PHP class.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_popen",
- "codegen_line": 3379,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_wrapper_register",
+ "codegen_line": 1000,
"notes": [
- "Lowers `popen(command, mode)` and boxes the process pipe as `resource|false`."
+ "Lowers `stream_wrapper_register(protocol, class, flags?)`."
],
"runtime_helpers": [
- "__rt_popen"
+ "__rt_stream_wrapper_register"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_wrapper_register.rs",
"sig_line": null
},
- "name": "popen",
+ "name": "stream_wrapper_register",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "command",
+ "name": "protocol",
"optional": false,
"type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "mode",
+ "name": "class",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "flags",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "popen",
- "sub_area": "Process"
+ "slug": "stream_wrapper_register",
+ "sub_area": "IO"
},
{
- "area": "Process",
- "canonical_name": "pclose",
- "description": "Lowers `pclose(handle)` and returns the child process status.",
+ "area": "IO",
+ "canonical_name": "stream_wrapper_restore",
+ "description": "Restores a previously unregistered built-in wrapper.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_pclose",
- "codegen_line": 3407,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_wrapper_restore",
+ "codegen_line": 1052,
"notes": [
- "Lowers `pclose(handle)` and returns the child process status."
- ],
- "runtime_helpers": [
- "__rt_pclose"
+ "Lowers `stream_wrapper_restore(protocol)` as a successful no-op."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_wrapper_restore.rs",
"sig_line": null
},
- "name": "pclose",
+ "name": "stream_wrapper_restore",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "handle",
+ "name": "protocol",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "int",
+ "return_type": "bool",
"variadic": null
},
- "slug": "pclose",
- "sub_area": "Process"
+ "slug": "stream_wrapper_restore",
+ "sub_area": "IO"
},
{
"area": "IO",
- "canonical_name": "opendir",
- "description": "Lowers `opendir(path)` and boxes the directory stream as `resource|false`.",
+ "canonical_name": "stream_wrapper_unregister",
+ "description": "Unregisters a previously registered URL wrapper.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_opendir",
- "codegen_line": 3326,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_stream_wrapper_unregister",
+ "codegen_line": 1030,
"notes": [
- "Lowers `opendir(path)` and boxes the directory stream as `resource|false`."
+ "Lowers `stream_wrapper_unregister(protocol)`."
],
"runtime_helpers": [
- "__rt_opendir",
- "__rt_readdir",
- "__rt_user_wrapper_dir_readdir"
+ "__rt_stream_wrapper_unregister"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/stream_wrapper_unregister.rs",
"sig_line": null
},
- "name": "opendir",
+ "name": "stream_wrapper_unregister",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "directory",
+ "name": "protocol",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "opendir",
+ "slug": "stream_wrapper_unregister",
"sub_area": "IO"
},
{
- "area": "IO",
- "canonical_name": "readdir",
- "description": "Lowers `readdir(dir_handle)` for libc, glob, and userspace-wrapper handles.",
+ "area": "String",
+ "canonical_name": "stripslashes",
+ "description": "Removes backslashes from a string previously escaped by addslashes.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_readdir",
- "codegen_line": 3336,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `readdir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_closedir",
- "__rt_readdir",
- "__rt_user_wrapper_dir_closedir",
- "__rt_user_wrapper_dir_readdir"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/stripslashes.rs",
"sig_line": null
},
- "name": "readdir",
+ "name": "stripslashes",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "dir_handle",
+ "name": "string",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "readdir",
- "sub_area": "IO"
+ "slug": "stripslashes",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "closedir",
- "description": "Lowers `closedir(dir_handle)` for libc, glob, and userspace-wrapper handles.",
+ "area": "String",
+ "canonical_name": "strlen",
+ "description": "Returns the length of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_closedir",
- "codegen_line": 3351,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_strlen",
+ "codegen_line": 493,
"notes": [
- "Lowers `closedir(dir_handle)` for libc, glob, and userspace-wrapper handles."
+ "Lowers `strlen()` by coercing string-like values and returning the byte length."
],
"runtime_helpers": [
- "__rt_closedir",
- "__rt_rewinddir",
- "__rt_user_wrapper_dir_closedir",
- "__rt_user_wrapper_dir_rewinddir"
+ "__rt_mixed_cast_string"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strlen.rs",
"sig_line": null
},
- "name": "closedir",
+ "name": "strlen",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "dir_handle",
+ "name": "string",
"optional": false,
- "type": "resource"
+ "type": "string"
}
],
- "return_type": "void",
+ "return_type": "int",
"variadic": null
},
- "slug": "closedir",
- "sub_area": "IO"
+ "slug": "strlen",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "rewinddir",
- "description": "Lowers `rewinddir(dir_handle)` for libc, glob, and userspace-wrapper handles.",
+ "area": "String",
+ "canonical_name": "strpos",
+ "description": "Finds the numeric position of the first occurrence of a substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_rewinddir",
- "codegen_line": 3365,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_string_position",
+ "codegen_line": 700,
"notes": [
- "Lowers `rewinddir(dir_handle)` for libc, glob, and userspace-wrapper handles."
- ],
- "runtime_helpers": [
- "__rt_rewinddir",
- "__rt_user_wrapper_dir_rewinddir"
+ "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strpos.rs",
"sig_line": null
},
- "name": "rewinddir",
+ "name": "strpos",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "dir_handle",
+ "name": "haystack",
"optional": false,
- "type": "resource"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "needle",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "offset",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "void",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "rewinddir",
- "sub_area": "IO"
+ "slug": "strpos",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "gethostname",
- "description": "Lowers `gethostname()` through the shared runtime helper.",
+ "area": "String",
+ "canonical_name": "strrev",
+ "description": "Reverses a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_gethostname",
- "codegen_line": 3188,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `gethostname()` through the shared runtime helper."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_gethostbyaddr",
- "__rt_gethostbyname",
- "__rt_gethostname"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strrev.rs",
"sig_line": null
},
- "name": "gethostname",
+ "name": "strrev",
"sig": {
- "params": [],
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "string",
+ "optional": false,
+ "type": "string"
+ }
+ ],
"return_type": "string",
"variadic": null
},
- "slug": "gethostname",
- "sub_area": "IO"
+ "slug": "strrev",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "gethostbyname",
- "description": "Lowers `gethostbyname(hostname)` through the shared runtime resolver.",
+ "area": "String",
+ "canonical_name": "strrpos",
+ "description": "Finds the numeric position of the last occurrence of a substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_gethostbyname",
- "codegen_line": 3198,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_string_position",
+ "codegen_line": 700,
"notes": [
- "Lowers `gethostbyname(hostname)` through the shared runtime resolver."
- ],
- "runtime_helpers": [
- "__rt_gethostbyaddr",
- "__rt_gethostbyname"
+ "Lowers `strpos()`/`strrpos()` and boxes position-or-false results as Mixed."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strrpos.rs",
"sig_line": null
},
- "name": "gethostbyname",
+ "name": "strrpos",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "hostname",
+ "name": "haystack",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "needle",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "0",
+ "name": "offset",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "string",
+ "return_type": "mixed",
"variadic": null
},
- "slug": "gethostbyname",
- "sub_area": "IO"
+ "slug": "strrpos",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "gethostbyaddr",
- "description": "Lowers `gethostbyaddr(address)` and boxes malformed addresses as PHP `false`.",
+ "area": "String",
+ "canonical_name": "strstr",
+ "description": "Returns the portion of a string starting at the first occurrence of a substring.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_gethostbyaddr",
- "codegen_line": 3210,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_strstr",
+ "codegen_line": 762,
"notes": [
- "Lowers `gethostbyaddr(address)` and boxes malformed addresses as PHP `false`."
- ],
- "runtime_helpers": [
- "__rt_gethostbyaddr",
- "__rt_getprotobyname"
+ "Lowers `strstr(haystack, needle)` by searching and returning the matching suffix."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strstr.rs",
"sig_line": null
},
- "name": "gethostbyaddr",
+ "name": "strstr",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "ip",
+ "name": "haystack",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "needle",
"optional": false,
"type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "false",
+ "name": "before_needle",
+ "optional": true,
+ "type": "bool"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "gethostbyaddr",
- "sub_area": "IO"
+ "slug": "strstr",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "getprotobyname",
- "description": "Lowers `getprotobyname(protocol)` and boxes a missing entry as PHP `false`.",
+ "area": "String",
+ "canonical_name": "strtolower",
+ "description": "Converts a string to lowercase.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_getprotobyname",
- "codegen_line": 3223,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `getprotobyname(protocol)` and boxes a missing entry as PHP `false`."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_getprotobyname"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strtolower.rs",
"sig_line": null
},
- "name": "getprotobyname",
+ "name": "strtolower",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "string",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "getprotobyname",
- "sub_area": "IO"
+ "slug": "strtolower",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "getprotobynumber",
- "description": "Lowers `getprotobynumber(number)` and boxes a missing entry as PHP `false`.",
+ "area": "Date",
+ "canonical_name": "strtotime",
+ "description": "Parses an English textual datetime description into a Unix timestamp.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_getprotobynumber",
- "codegen_line": 3246,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_strtotime",
+ "codegen_line": 487,
"notes": [
- "Lowers `getprotobynumber(number)` and boxes a missing entry as PHP `false`."
+ "Lowers `strtotime(datetime[, baseTimestamp])` through the shared parser runtime helper.",
+ "Returns PHP's `int|false`: the `__rt_strtotime` `i64::MIN` parse-failure sentinel is boxed as",
+ "`Mixed` `false`, and every other value (including a real `-1` pre-epoch timestamp) is boxed as",
+ "a `Mixed` integer, so `=== false`, `=== -1`, and `echo` all observe the distinct results.",
+ "Supports PHP's optional `$baseTimestamp`. (The `__elephc_strtotime_raw` alias keeps the plain",
+ "`-1` integer shape for the synthetic `DateTime` internals.)"
],
"runtime_helpers": [
- "__rt_getprotobynumber"
+ "__rt_mixed_from_value",
+ "__rt_strtotime"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/strtotime.rs",
"sig_line": null
},
- "name": "getprotobynumber",
+ "name": "strtotime",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "datetime",
"optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "baseTimestamp",
+ "optional": true,
"type": "int"
}
],
"return_type": "mixed",
"variadic": null
},
- "slug": "getprotobynumber",
- "sub_area": "IO"
+ "slug": "strtotime",
+ "sub_area": "Date"
},
{
- "area": "IO",
- "canonical_name": "getservbyname",
- "description": "Lowers `getservbyname(service, protocol)` and boxes a missing entry as PHP `false`.",
+ "area": "String",
+ "canonical_name": "strtoupper",
+ "description": "Converts a string to uppercase.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_getservbyname",
- "codegen_line": 3265,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_unary_string_runtime",
+ "codegen_line": 76,
"notes": [
- "Lowers `getservbyname(service, protocol)` and boxes a missing entry as PHP `false`."
+ "Lowers a one-argument string builtin that directly delegates to a runtime helper."
],
"runtime_helpers": [
- "__rt_getservbyname"
+ "__rt_grapheme_strrev",
+ "__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/strtoupper.rs",
"sig_line": null
},
- "name": "getservbyname",
+ "name": "strtoupper",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "service",
- "optional": false,
- "type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "protocol",
+ "name": "string",
"optional": false,
"type": "string"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "getservbyname",
- "sub_area": "IO"
+ "slug": "strtoupper",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "getservbyport",
- "description": "Lowers `getservbyport(port, protocol)` and boxes a missing entry as PHP `false`.",
+ "area": "String",
+ "canonical_name": "substr",
+ "description": "Returns a portion of a string specified by the offset and length.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_getservbyport",
- "codegen_line": 3296,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_substr",
+ "codegen_line": 713,
"notes": [
- "Lowers `getservbyport(port, protocol)` and boxes a missing entry as PHP `false`."
+ "Lowers `substr(string, offset, length?)` with target-local pointer arithmetic."
],
"runtime_helpers": [
- "__rt_getservbyport"
+ "__rt_substr_replace"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/substr.rs",
"sig_line": null
},
- "name": "getservbyport",
+ "name": "substr",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "port",
+ "name": "string",
"optional": false,
- "type": "int"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "protocol",
+ "name": "offset",
"optional": false,
- "type": "string"
+ "type": "int"
+ },
+ {
+ "by_ref": false,
+ "default": "null",
+ "name": "length",
+ "optional": true,
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "getservbyport",
- "sub_area": "IO"
+ "slug": "substr",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "stream_copy_to_stream",
- "description": "Lowers `stream_copy_to_stream(from, to, length?, offset?)` through wrapper-aware read/write loops.",
+ "area": "String",
+ "canonical_name": "substr_replace",
+ "description": "Replaces text within a portion of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_copy_to_stream",
- "codegen_line": 1247,
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
+ "codegen_function": "lower_substr_replace",
+ "codegen_line": 730,
"notes": [
- "Lowers `stream_copy_to_stream(from, to, length?, offset?)` through wrapper-aware read/write loops."
+ "Lowers `substr_replace(string, replacement, start, length?)`."
+ ],
+ "runtime_helpers": [
+ "__rt_str_repeat",
+ "__rt_substr_replace"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/substr_replace.rs",
"sig_line": null
},
- "name": "stream_copy_to_stream",
+ "name": "substr_replace",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "from",
+ "name": "string",
"optional": false,
- "type": "resource"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "to",
+ "name": "replace",
"optional": false,
- "type": "resource"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "length",
- "optional": true,
+ "name": "offset",
+ "optional": false,
"type": "int"
},
{
"by_ref": false,
- "default": null,
- "name": "offset",
+ "default": "null",
+ "name": "length",
"optional": true,
"type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "string",
"variadic": null
},
- "slug": "stream_copy_to_stream",
- "sub_area": "IO"
+ "slug": "substr_replace",
+ "sub_area": "String"
},
{
- "area": "IO",
- "canonical_name": "stream_is_local",
- "description": "Lowers `stream_is_local(stream)` as a true predicate after evaluating its argument.",
+ "area": "Filesystem",
+ "canonical_name": "symlink",
+ "description": "Creates a symbolic link.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_is_local",
- "codegen_line": 1884,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_symlink",
+ "codegen_line": 5448,
"notes": [
- "Lowers `stream_is_local(stream)` as a true predicate after evaluating its argument."
+ "Lowers `symlink(target, link)` through the target-aware libc wrapper."
+ ],
+ "runtime_helpers": [
+ "__rt_fileatime",
+ "__rt_link",
+ "__rt_readlink",
+ "__rt_symlink"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/symlink.rs",
"sig_line": null
},
- "name": "stream_is_local",
+ "name": "symlink",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "target",
"optional": false,
- "type": "resource"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "link",
+ "optional": false,
+ "type": "string"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "stream_is_local",
- "sub_area": "IO"
+ "slug": "symlink",
+ "sub_area": "Filesystem"
},
{
- "area": "IO",
- "canonical_name": "stream_supports_lock",
- "description": "Lowers `stream_supports_lock(stream)` as true after resource unboxing.",
+ "area": "Filesystem",
+ "canonical_name": "sys_get_temp_dir",
+ "description": "Returns the directory path used for temporary files.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_supports_lock",
- "codegen_line": 1896,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_sys_get_temp_dir",
+ "codegen_line": 5403,
"notes": [
- "Lowers `stream_supports_lock(stream)` as true after resource unboxing."
+ "Lowers `sys_get_temp_dir()` as the project's hardcoded `/tmp` string."
],
"runtime_helpers": [
- "__rt_stream_isatty"
+ "__rt_tmpfile"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/sys_get_temp_dir.rs",
"sig_line": null
},
- "name": "stream_supports_lock",
+ "name": "sys_get_temp_dir",
"sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- }
- ],
- "return_type": "bool",
+ "params": [],
+ "return_type": "string",
"variadic": null
},
- "slug": "stream_supports_lock",
- "sub_area": "IO"
+ "slug": "sys_get_temp_dir",
+ "sub_area": "Filesystem"
},
{
- "area": "IO",
- "canonical_name": "stream_get_transports",
- "description": "Lowers `stream_get_transports()` to the static transport list.",
+ "area": "Process",
+ "canonical_name": "system",
+ "description": "Executes an external program and displays the output.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_transports",
- "codegen_line": 1364,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_system",
+ "codegen_line": 706,
"notes": [
- "Lowers `stream_get_transports()` to the static transport list."
+ "Lowers `system(command)` through libc `system()` and returns the compiler's empty string result."
+ ],
+ "runtime_helpers": [
+ "__rt_shell_exec"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/system.rs",
"sig_line": null
},
- "name": "stream_get_transports",
+ "name": "system",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "command",
+ "optional": false,
+ "type": "string"
+ }
+ ],
+ "return_type": "string",
"variadic": null
},
- "slug": "stream_get_transports",
- "sub_area": "IO"
+ "slug": "system",
+ "sub_area": "Process"
},
{
- "area": "IO",
- "canonical_name": "stream_get_wrappers",
- "description": "Lowers `stream_get_wrappers()` to the static built-in wrapper list.",
+ "area": "Math",
+ "canonical_name": "tan",
+ "description": "Returns the tangent of a number (radians).",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_wrappers",
- "codegen_line": 1348,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `stream_get_wrappers()` to the static built-in wrapper list."
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/tan.rs",
"sig_line": null
},
- "name": "stream_get_wrappers",
+ "name": "tan",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "float",
"variadic": null
},
- "slug": "stream_get_wrappers",
- "sub_area": "IO"
+ "slug": "tan",
+ "sub_area": "Math"
},
{
- "area": "IO",
- "canonical_name": "stream_get_filters",
- "description": "Lowers `stream_get_filters()` to the static built-in filter list.",
+ "area": "Math",
+ "canonical_name": "tanh",
+ "description": "Returns the hyperbolic tangent of a number.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_get_filters",
- "codegen_line": 1380,
+ "codegen_file": "src/codegen/lower_inst/builtins/math/libm.rs",
+ "codegen_function": "lower_unary_libm",
+ "codegen_line": 22,
"notes": [
- "Lowers `stream_get_filters()` to the static built-in filter list."
+ "Lowers a one-argument libm builtin such as `sin()`, `cos()`, or `exp()`."
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/math/tanh.rs",
"sig_line": null
},
- "name": "stream_get_filters",
+ "name": "tanh",
"sig": {
- "params": [],
- "return_type": "array",
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "num",
+ "optional": false,
+ "type": "float"
+ }
+ ],
+ "return_type": "float",
"variadic": null
},
- "slug": "stream_get_filters",
- "sub_area": "IO"
+ "slug": "tanh",
+ "sub_area": "Math"
},
{
- "area": "Streams",
- "canonical_name": "stream_filter_append",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "tempnam",
+ "description": "Creates a file with a unique filename.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
- "runtime_helpers": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_tempnam",
+ "codegen_line": 4453,
+ "notes": [
+ "Lowers `tempnam(directory, prefix)` through the target-aware runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_glob",
+ "__rt_scandir",
+ "__rt_tempnam"
+ ],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/tempnam.rs",
"sig_line": null
},
- "name": "stream_filter_append",
+ "name": "tempnam",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
+ "name": "directory",
"optional": false,
- "type": "resource"
+ "type": "string"
},
{
"by_ref": false,
"default": null,
- "name": "filter_name",
+ "name": "prefix",
"optional": false,
"type": "string"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "mode",
- "optional": true,
- "type": "int"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "params",
- "optional": true,
- "type": "mixed"
}
],
+ "return_type": "string",
+ "variadic": null
+ },
+ "slug": "tempnam",
+ "sub_area": "Filesystem"
+ },
+ {
+ "area": "Date",
+ "canonical_name": "time",
+ "description": "Returns the current Unix timestamp.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
+ "codegen_function": "lower_time",
+ "codegen_line": 615,
+ "notes": [
+ "Lowers `time()` through the shared wall-clock runtime helper."
+ ],
+ "runtime_helpers": [
+ "__rt_time"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/time.rs",
+ "sig_line": null
+ },
+ "name": "time",
+ "sig": {
+ "params": [],
+ "return_type": "int",
+ "variadic": null
+ },
+ "slug": "time",
+ "sub_area": "Date"
+ },
+ {
+ "area": "Filesystem",
+ "canonical_name": "tmpfile",
+ "description": "Creates a temporary file.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_tmpfile",
+ "codegen_line": 5416,
+ "notes": [
+ "Lowers `tmpfile()` and boxes the anonymous stream descriptor or PHP false."
+ ],
+ "runtime_helpers": [
+ "__rt_filemtime",
+ "__rt_linkinfo",
+ "__rt_tmpfile"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/io/tmpfile.rs",
+ "sig_line": null
+ },
+ "name": "tmpfile",
+ "sig": {
+ "params": [],
"return_type": "mixed",
"variadic": null
},
- "slug": "stream_filter_append",
- "sub_area": "Streams"
+ "slug": "tmpfile",
+ "sub_area": "Filesystem"
},
{
- "area": "Streams",
- "canonical_name": "stream_filter_prepend",
- "description": "",
+ "area": "Filesystem",
+ "canonical_name": "touch",
+ "description": "Sets access and modification time of a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": null,
- "codegen_function": null,
- "codegen_line": null,
- "notes": [],
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
+ "codegen_function": "lower_touch",
+ "codegen_line": 4523,
+ "notes": [
+ "Lowers `touch(path, mtime?, atime?)` through the target-aware runtime helper."
+ ],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": "src/types/signatures.rs",
+ "sig_file": "src/builtins/io/touch.rs",
"sig_line": null
},
- "name": "stream_filter_prepend",
+ "name": "touch",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream",
- "optional": false,
- "type": "resource"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "filter_name",
+ "name": "filename",
"optional": false,
"type": "string"
},
{
"by_ref": false,
- "default": null,
- "name": "mode",
+ "default": "null",
+ "name": "mtime",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
- "name": "params",
+ "default": "null",
+ "name": "atime",
"optional": true,
- "type": "mixed"
+ "type": "int"
}
],
- "return_type": "mixed",
+ "return_type": "bool",
"variadic": null
},
- "slug": "stream_filter_prepend",
- "sub_area": "Streams"
+ "slug": "touch",
+ "sub_area": "Filesystem"
},
{
- "area": "IO",
- "canonical_name": "stream_filter_remove",
- "description": "Lowers `stream_filter_remove(filter)` and clears both direction tables for the fd.",
+ "area": "Class",
+ "canonical_name": "trait_exists",
+ "description": "Checks whether the trait exists.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_stream_filter_remove",
- "codegen_line": 1720,
+ "codegen_file": "src/codegen/lower_inst/builtins.rs",
+ "codegen_function": "lower_class_like_exists",
+ "codegen_line": 293,
"notes": [
- "Lowers `stream_filter_remove(filter)` and clears both direction tables for the fd."
- ],
- "runtime_helpers": [
- "__rt_user_filter_release_fd"
+ "Lowers AOT class/interface/enum existence checks for literal names."
],
+ "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/callables/trait_exists.rs",
"sig_line": null
},
- "name": "stream_filter_remove",
+ "name": "trait_exists",
"sig": {
"params": [
{
"by_ref": false,
"default": null,
- "name": "stream_filter",
+ "name": "trait",
"optional": false,
- "type": "resource"
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "true",
+ "name": "autoload",
+ "optional": true,
+ "type": "bool"
}
],
"return_type": "bool",
"variadic": null
},
- "slug": "stream_filter_remove",
- "sub_area": "IO"
+ "slug": "trait_exists",
+ "sub_area": "Class"
},
{
"area": "String",
"canonical_name": "trim",
- "description": "Lowers `trim()`/`ltrim()`/`rtrim()`/`chop()` for default and explicit masks.",
+ "description": "Strips whitespace (or other characters) from the beginning and end of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_trim_like",
"codegen_line": 112,
"notes": [
@@ -17488,7 +18452,7 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/trim.rs",
"sig_line": null
},
"name": "trim",
@@ -17503,7 +18467,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "' \\n\\r\\t\\x0b\\x0c\\x00'",
"name": "characters",
"optional": true,
"type": "string"
@@ -17518,21 +18482,23 @@
{
"area": "Array",
"canonical_name": "uasort",
- "description": "Lowers `uasort()` through the legacy user-sort helper for static comparators.",
+ "description": "Sorts an array with a user-defined comparison function and maintains index association.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_uasort",
- "codegen_line": 1131,
+ "codegen_line": 1134,
"notes": [
- "Lowers `uasort()` through the legacy user-sort helper for static comparators."
+ "Lowers `uasort()` through the user-sort helper for static comparators."
+ ],
+ "runtime_helpers": [
+ "__rt_array_is_list"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/uasort.rs",
"sig_line": null
},
"name": "uasort",
@@ -17562,13 +18528,13 @@
{
"area": "String",
"canonical_name": "ucfirst",
- "description": "Lowers `ucfirst()` by copying the string and uppercasing the first ASCII byte.",
+ "description": "Uppercases the first character of a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_ucfirst",
"codegen_line": 96,
"notes": [
@@ -17578,7 +18544,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ucfirst.rs",
"sig_line": null
},
"name": "ucfirst",
@@ -17601,13 +18567,13 @@
{
"area": "String",
"canonical_name": "ucwords",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Uppercases the first character of each word in a string.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -17618,7 +18584,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/ucwords.rs",
"sig_line": null
},
"name": "ucwords",
@@ -17633,7 +18599,7 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "' \\t\\r\\n\\x0c\\x0b'",
"name": "separators",
"optional": true,
"type": "string"
@@ -17648,21 +18614,23 @@
{
"area": "Array",
"canonical_name": "uksort",
- "description": "Lowers `uksort()` through the legacy user-sort helper for static comparators.",
+ "description": "Sorts an array by keys using a user-defined comparison function.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_uksort",
- "codegen_line": 1126,
+ "codegen_line": 1129,
"notes": [
- "Lowers `uksort()` through the legacy user-sort helper for static comparators."
+ "Lowers `uksort()` through the user-sort helper for static comparators."
+ ],
+ "runtime_helpers": [
+ "__rt_array_is_list"
],
- "runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/uksort.rs",
"sig_line": null
},
"name": "uksort",
@@ -17692,15 +18660,15 @@
{
"area": "Filesystem",
"canonical_name": "umask",
- "description": "Lowers `umask(mask?)` through the target-aware runtime helper.",
+ "description": "Changes the current umask.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_umask",
- "codegen_line": 3850,
+ "codegen_line": 4493,
"notes": [
"Lowers `umask(mask?)` through the target-aware runtime helper."
],
@@ -17708,7 +18676,7 @@
"__rt_umask"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/umask.rs",
"sig_line": null
},
"name": "umask",
@@ -17716,7 +18684,7 @@
"params": [
{
"by_ref": false,
- "default": null,
+ "default": "null",
"name": "mask",
"optional": true,
"type": "int"
@@ -17731,15 +18699,15 @@
{
"area": "Filesystem",
"canonical_name": "unlink",
- "description": "Lowers `unlink(path)` through the target-aware runtime helper.",
+ "description": "Deletes a file.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_unlink",
- "codegen_line": 3764,
+ "codegen_line": 4407,
"notes": [
"Lowers `unlink(path)` through the target-aware runtime helper."
],
@@ -17749,7 +18717,7 @@
"__rt_unlink"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/unlink.rs",
"sig_line": null
},
"name": "unlink",
@@ -17769,6 +18737,57 @@
"slug": "unlink",
"sub_area": "Filesystem"
},
+ {
+ "area": "Misc",
+ "canonical_name": "unserialize",
+ "description": "Creates a PHP value from a stored representation.",
+ "in_catalog": true,
+ "is_internal": false,
+ "lowering": {
+ "checker_file": null,
+ "checker_line": null,
+ "codegen_file": "src/codegen/lower_inst/builtins/serialize.rs",
+ "codegen_function": "lower_unserialize",
+ "codegen_line": 164,
+ "notes": [
+ "Lowers `unserialize($data, $options?)` into the shared unserialize runtime helper.",
+ "The source string is parsed by `__rt_unserialize_mixed`; a null result pointer",
+ "(parse error or unsupported wire form) is boxed as PHP `false`. The optional",
+ "`$options` argument is accepted but currently ignored."
+ ],
+ "runtime_helpers": [
+ "__rt_mixed_cast_string",
+ "__rt_unserialize_begin",
+ "__rt_unserialize_mixed"
+ ],
+ "sig_arm": null,
+ "sig_file": "src/builtins/system/unserialize.rs",
+ "sig_line": null
+ },
+ "name": "unserialize",
+ "sig": {
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "data",
+ "optional": false,
+ "type": "string"
+ },
+ {
+ "by_ref": false,
+ "default": "[]",
+ "name": "options",
+ "optional": true,
+ "type": "mixed"
+ }
+ ],
+ "return_type": "mixed",
+ "variadic": null
+ },
+ "slug": "unserialize",
+ "sub_area": "Misc"
+ },
{
"area": "Misc",
"canonical_name": "unset",
@@ -17778,7 +18797,7 @@
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/types.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/types.rs",
"codegen_function": "lower_unset_builtin",
"codegen_line": 48,
"notes": [
@@ -17809,13 +18828,13 @@
{
"area": "String",
"canonical_name": "urldecode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "Decodes a URL-encoded string, including '+' as a space.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -17826,7 +18845,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/urldecode.rs",
"sig_line": null
},
"name": "urldecode",
@@ -17849,13 +18868,13 @@
{
"area": "String",
"canonical_name": "urlencode",
- "description": "Lowers a one-argument string builtin that directly delegates to a runtime helper.",
+ "description": "URL-encodes a string using application/x-www-form-urlencoded rules.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_unary_string_runtime",
"codegen_line": 76,
"notes": [
@@ -17866,7 +18885,7 @@
"__rt_strcopy"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/urlencode.rs",
"sig_line": null
},
"name": "urlencode",
@@ -17889,13 +18908,13 @@
{
"area": "Process",
"canonical_name": "usleep",
- "description": "Lowers `usleep(microseconds)` through the target's C library symbol.",
+ "description": "Delays execution for a number of microseconds.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/system.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/system.rs",
"codegen_function": "lower_usleep",
"codegen_line": 625,
"notes": [
@@ -17905,7 +18924,7 @@
"__rt_getenv"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/system/usleep.rs",
"sig_line": null
},
"name": "usleep",
@@ -17928,21 +18947,23 @@
{
"area": "Array",
"canonical_name": "usort",
- "description": "Lowers `usort()` for indexed integer arrays with a static user comparator.",
+ "description": "Sorts an array by values using a user-defined comparison function.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/arrays.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/arrays.rs",
"codegen_function": "lower_usort",
- "codegen_line": 1121,
+ "codegen_line": 1124,
"notes": [
"Lowers `usort()` for indexed integer arrays with a static user comparator."
],
- "runtime_helpers": [],
+ "runtime_helpers": [
+ "__rt_array_is_list"
+ ],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/array/usort.rs",
"sig_line": null
},
"name": "usort",
@@ -17978,7 +18999,7 @@
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/debug.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/debug.rs",
"codegen_function": "lower_var_dump",
"codegen_line": 35,
"notes": [
@@ -17986,14 +19007,22 @@
],
"runtime_helpers": [],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/var_dump.rs",
"sig_line": null
},
"name": "var_dump",
"sig": {
- "params": [],
+ "params": [
+ {
+ "by_ref": false,
+ "default": null,
+ "name": "value",
+ "optional": false,
+ "type": "mixed"
+ }
+ ],
"return_type": "void",
- "variadic": "values"
+ "variadic": null
},
"slug": "var_dump",
"sub_area": "Variable"
@@ -18001,15 +19030,15 @@
{
"area": "IO",
"canonical_name": "vfprintf",
- "description": "Lowers `vfprintf(stream, format, values)` through `__rt_vsprintf` then fwrite.",
+ "description": "Write a formatted string to a stream.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/io.rs",
"codegen_function": "lower_vfprintf",
- "codegen_line": 2677,
+ "codegen_line": 2898,
"notes": [
"Lowers `vfprintf(stream, format, values)` through `__rt_vsprintf` then fwrite."
],
@@ -18018,7 +19047,7 @@
"__rt_vsprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/io/vfprintf.rs",
"sig_line": null
},
"name": "vfprintf",
@@ -18055,13 +19084,13 @@
{
"area": "String",
"canonical_name": "vprintf",
- "description": "Lowers `vprintf(format, values)` as `vsprintf()` followed by stdout emission.",
+ "description": "Outputs a formatted string using an array of values.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_vprintf",
"codegen_line": 530,
"notes": [
@@ -18071,7 +19100,7 @@
"__rt_sprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/vprintf.rs",
"sig_line": null
},
"name": "vprintf",
@@ -18101,13 +19130,13 @@
{
"area": "String",
"canonical_name": "vsprintf",
- "description": "Lowers `vsprintf(format, values)` through the array-to-sprintf runtime bridge.",
+ "description": "Returns a formatted string using an array of values.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_vsprintf",
"codegen_line": 524,
"notes": [
@@ -18117,7 +19146,7 @@
"__rt_sprintf"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/vsprintf.rs",
"sig_line": null
},
"name": "vsprintf",
@@ -18147,13 +19176,13 @@
{
"area": "String",
"canonical_name": "wordwrap",
- "description": "Lowers `wordwrap(string, width?, break?, cut?)` through the shared runtime helper.",
+ "description": "Wraps a string to a given number of characters.",
"in_catalog": true,
"is_internal": false,
"lowering": {
"checker_file": null,
"checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/strings.rs",
+ "codegen_file": "src/codegen/lower_inst/builtins/strings.rs",
"codegen_function": "lower_wordwrap",
"codegen_line": 802,
"notes": [
@@ -18164,7 +19193,7 @@
"__rt_wordwrap"
],
"sig_arm": null,
- "sig_file": null,
+ "sig_file": "src/builtins/string/wordwrap.rs",
"sig_line": null
},
"name": "wordwrap",
@@ -18179,21 +19208,21 @@
},
{
"by_ref": false,
- "default": null,
+ "default": "75",
"name": "width",
"optional": true,
"type": "int"
},
{
"by_ref": false,
- "default": null,
+ "default": "'\\n'",
"name": "break",
"optional": true,
"type": "string"
},
{
"by_ref": false,
- "default": null,
+ "default": "false",
"name": "cut_long_words",
"optional": true,
"type": "bool"
@@ -18204,88 +19233,5 @@
},
"slug": "wordwrap",
"sub_area": "String"
- },
- {
- "area": "IO",
- "canonical_name": "__elephc_phar_list_entries",
- "description": "Internal helper used by the built-in Phar / PharData support to enumerate archive entries.",
- "in_catalog": false,
- "is_internal": true,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_elephc_phar_list_entries",
- "codegen_line": 3634,
- "notes": [
- "Internal helper used by the built-in Phar / PharData support to enumerate archive entries.",
- "Calls the native PHAR listing bridge and returns the entries as an array."
- ],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "__elephc_phar_list_entries",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "filename",
- "optional": false,
- "type": "mixed"
- }
- ],
- "return_type": "array",
- "variadic": null
- },
- "slug": "__elephc_phar_list_entries",
- "sub_area": "IO"
- },
- {
- "area": "IO",
- "canonical_name": "__elephc_phar_set_compression",
- "description": "Internal helper used by the built-in Phar / PharData support to change archive compression.",
- "in_catalog": false,
- "is_internal": true,
- "lowering": {
- "checker_file": null,
- "checker_line": null,
- "codegen_file": "src/codegen_ir/lower_inst/builtins/io.rs",
- "codegen_function": "lower_elephc_phar_set_compression",
- "codegen_line": 3576,
- "notes": [
- "Internal helper used by the built-in Phar / PharData support to change archive compression.",
- "Calls the native PHAR compression-control bridge and returns whether the update succeeded."
- ],
- "runtime_helpers": [],
- "sig_arm": null,
- "sig_file": null,
- "sig_line": null
- },
- "name": "__elephc_phar_set_compression",
- "sig": {
- "params": [
- {
- "by_ref": false,
- "default": null,
- "name": "filename",
- "optional": false,
- "type": "mixed"
- },
- {
- "by_ref": false,
- "default": null,
- "name": "compression",
- "optional": false,
- "type": "mixed"
- }
- ],
- "return_type": "bool",
- "variadic": null
- },
- "slug": "__elephc_phar_set_compression",
- "sub_area": "IO"
}
]
\ No newline at end of file
diff --git a/scripts/docs/elephc_builtins/extract.py b/scripts/docs/elephc_builtins/extract.py
index 2c03ef8875..3f5dc2535e 100644
--- a/scripts/docs/elephc_builtins/extract.py
+++ b/scripts/docs/elephc_builtins/extract.py
@@ -1,17 +1,27 @@
-"""Extract builtin metadata from the Elephc source tree.
-
-We parse three layers:
-
-1. ``src/types/checker/builtins/catalog.rs`` — the canonical list of
- PHP-visible builtins. This is our *set* of builtins.
-2. ``src/types/signatures.rs`` — per-builtin canonical call signatures
- (param names, variadic, by-ref, first-class return type).
-3. ``src/codegen_ir/lower_inst/builtins.rs`` and the per-area submodules
- — for each builtin we capture the lowering function name, the runtime
- helpers it calls, and the leading /// doc comment of that function.
-
-The output is a list of :class:`registry.Builtin` written to a JSON file
-in ``scripts/docs/builtin_registry.json``.
+"""Extract builtin metadata from the Elephc `builtin!` registry.
+
+Since the single-source builtin registry migration, every PHP builtin is declared
+once via `builtin!` in ``src/builtins/ /.rs`` and collected through the
+`inventory` crate. The authoritative data is therefore read from the registry
+itself, via the ``gen_builtins`` binary (``cargo run --bin gen_builtins
+--include-internal``), NOT by regex-scraping ``catalog.rs`` / ``signatures.rs``
+(which the migration emptied).
+
+For each builtin we enrich the registry data with:
+
+1. its lowering location — the emitter its home-file ``lower`` hook dispatches to,
+ plus that emitter's ``__rt_*`` runtime helpers and leading ``///`` doc notes,
+2. its documentation area (derived from the lowering file path, as before),
+3. optional type-precision refinements for non-scalar params/returns that the
+ registry represents coarsely as ``Mixed`` (``PARAM_TYPES`` / ``RETURN_TYPE_OVERRIDES``).
+
+The 8 PHP language constructs that intentionally stay checker-resident
+(``isset``/``unset``/``empty``/``exit``/``die``/``buffer_len``/``buffer_free``/
+``buffer_new``) are not in the registry; they are added from a small hand-curated
+table so their documentation pages are preserved.
+
+The output is a list of :class:`registry.Builtin` written to a JSON file in
+``scripts/docs/builtin_registry.json``.
"""
from __future__ import annotations
@@ -19,6 +29,7 @@
import argparse
import json
import re
+import subprocess
import sys
from pathlib import Path
from typing import Optional
@@ -31,437 +42,127 @@
AREA_BY_LOWERING_FN,
AREA_BY_MODULE,
AREA_BY_NAME,
- AREAS,
Builtin,
BuiltinSig,
DESCRIPTION_OVERRIDES,
INTERNAL_NOTES,
LoweringInfo,
- OPTIONAL_PARAM_OVERRIDES,
- PARAM_NAME_OVERRIDES,
PARAM_TYPES,
- REF_PARAM_OVERRIDES,
Parameter,
RETURN_TYPE_OVERRIDES,
- VARIADIC_OVERRIDES,
slug,
)
# ---------------------------------------------------------------------------
-# catalog.rs
-# ---------------------------------------------------------------------------
-
-# We pull SUPPORTED_BUILTIN_FUNCTIONS and INTERNAL_BUILTIN_FUNCTIONS straight
-# out of catalog.rs. The file is a simple list-of-string-literals, so a small
-# state machine is enough — no need for a full Rust parser.
-
-def parse_catalog(path: Path) -> tuple[list[str], list[str]]:
- """Return (supported_names, internal_names) from catalog.rs."""
- src = path.read_text(encoding="utf-8")
- return _extract_string_list(src, "SUPPORTED_BUILTIN_FUNCTIONS"), _extract_string_list(
- src, "INTERNAL_BUILTIN_FUNCTIONS"
- )
-
-
-def _extract_string_list(src: str, const_name: str) -> list[str]:
- pattern = re.compile(
- r"const\s+" + re.escape(const_name) + r"\s*:\s*&?\[?&?str\]\s*=\s*&?\[(.*?)\];",
- re.DOTALL,
- )
- match = pattern.search(src)
- if not match:
- return []
- body = match.group(1)
- return re.findall(r'"([^"]+)"', body)
-
-
-# ---------------------------------------------------------------------------
-# signatures.rs
+# Registry source of truth: the `gen_builtins` binary
# ---------------------------------------------------------------------------
-# Each arm of `builtin_call_sig` looks like:
-# "name" | "alias" | ... => Some(fixed(&["a", "b"])),
-# or
-# "name" => Some(optional(&["a", "b"], 2, vec![int_lit(0)])),
-# or
-# "name" => Some(variadic(&["a"], "rest")),
-# or
-# "name" => {
-# let mut sig = first_param_ref(fixed(&["a", "b"]));
-# sig.ref_params[2] = true;
-# Some(sig)
-# }
-#
-# And `first_class_callable_builtin_sig` / `general_first_class_callable_builtin_sig`
-# carry the canonical return type:
-# "name" => Some(typed_first_class_builtin_sig(name, &[PhpType::Str], PhpType::Str))
-# or
-# "name" => Some(FunctionSig { return_type: PhpType::Int, ... })
-
-_FN_CALL_SIG_RE = re.compile(
- r'"([^"]+)"\s*(?:\|\s*"[^"]+"\s*)*=>\s*Some\(([a-z_]+)\(([^)]*)\)\)',
- re.DOTALL,
-)
-
-
-def _split_name_list(arm: str) -> list[str]:
- return re.findall(r'"([^"]+)"', arm)
-
-
-def _split_args(args: str) -> list[str]:
- # split top-level commas only — args of variadic(&["a"], "b") must stay grouped
- depth = 0
- parts: list[str] = []
- buf: list[str] = []
- for ch in args:
- if ch in "([{":
- depth += 1
- elif ch in ")]}":
- depth -= 1
- if ch == "," and depth == 0:
- parts.append("".join(buf).strip())
- buf = []
- else:
- buf.append(ch)
- if buf:
- parts.append("".join(buf).strip())
- return parts
-
-
-def _parse_param_list(s: str) -> list[str]:
- """`&["a", "b"]` → ['a', 'b']"""
- return re.findall(r'"([^"]+)"', s)
-
-
-def _parse_optional_defaults(s: str) -> dict[int, str]:
- """`vec![int_lit(0), string_lit(" ")]` → {0: '0', 1: "' '"}"""
- out: dict[int, str] = {}
- for idx, expr in enumerate(_split_args(s)):
- m = re.match(r"(int|bool|string|null)_lit\((.*)\)$", expr.strip())
- if m:
- kind, raw = m.group(1), m.group(2).strip()
- if kind == "int":
- out[idx] = raw
- elif kind == "bool":
- out[idx] = raw
- elif kind == "string":
- out[idx] = repr(raw) # render as PHP string
- elif kind == "null":
- out[idx] = "null"
- return out
-
-
-def _default_expr_renderer(expr_kind: str, raw: str) -> str:
- """Render a few special default expressions from signatures.rs."""
- expr_kind = expr_kind.strip()
- raw = raw.strip()
- if expr_kind == "FloatLiteral":
- # default for log() base is e — render as M_E
- return "M_E"
- if expr_kind == "ArrayLiteral":
- return "[]"
- if expr_kind in ("int", "Int"):
- return raw
- if expr_kind in ("bool", "Bool"):
- return raw
- if expr_kind in ("string", "Str"):
- return repr(raw)
- if expr_kind in ("null", "Null"):
- return "null"
- return raw
-
-
-def _extract_function_body(src: str, fn_name: str) -> str:
- """Return the body of `pub(crate) fn (...)` (between matching braces), or ''."""
- for prefix in ("pub(crate) ", "pub(super) ", "pub ", ""):
- marker = f"{prefix}fn {fn_name}("
- start = src.find(marker)
- if start >= 0:
- break
- else:
- return ""
- brace = src.find("{", start)
- if brace < 0:
- return ""
- depth = 0
- for i in range(brace, len(src)):
- if src[i] == "{":
- depth += 1
- elif src[i] == "}":
- depth -= 1
- if depth == 0:
- return src[brace : i + 1]
- return ""
-
-
-def _split_match_arms(body: str) -> list[str]:
- """Split a `match` body into individual arms.
+def run_gen_builtins(repo: Path) -> list[dict]:
+ """Return the registry as a list of dicts by invoking the `gen_builtins` binary.
- Each arm ends either at a top-level `,` (direct-expression arm) or at the
- closing `}` of an arm-block. The final closing `}` of the match itself
- (which has no leading `,`) terminates the walk.
+ Includes `internal` builtins (the docs pipeline renders compiler-internals
+ pages for the `__elephc_*` helpers). Prefers a prebuilt binary under
+ ``target/{release,debug}/`` when present (fast path for CI, which builds it
+ first); otherwise falls back to ``cargo run``.
"""
- arms: list[str] = []
- i = 0
- while i < len(body):
- m = re.search(r'"([^"]+)"(?:\s*\|\s*"([^"]+)")*\s*=>\s*', body[i:])
- if not m:
+ cmd: list[str]
+ for profile in ("release", "debug"):
+ exe = repo / "target" / profile / "gen_builtins"
+ if exe.exists():
+ cmd = [str(exe), "--include-internal"]
break
- arm_start = i + m.start()
- rhs_start = i + m.end()
- # If the arm body starts with `{` (after optional whitespace/newlines),
- # the matching `}` ends the arm.
- scan = rhs_start
- while scan < len(body) and body[scan] in " \t\n\r":
- scan += 1
- is_block = scan < len(body) and body[scan] == "{"
- depth = 0
- j = rhs_start
- in_str = False
- ended_on = None # "," or "}" once we terminate
- while j < len(body):
- ch = body[j]
- if ch == "\\" and j + 1 < len(body):
- j += 2
- continue
- if ch == '"':
- in_str = not in_str
- elif not in_str:
- if ch in "([{":
- depth += 1
- elif ch in ")]}":
- if depth == 0:
- # Closing `}` of the whole match (not inside any arm).
- ended_on = "}"
- break
- depth -= 1
- # After decrement, depth may have hit 0: a block arm's
- # closing `}` ends the arm here.
- if is_block and depth == 0 and ch == "}":
- ended_on = "}"
- break
- elif ch == "," and depth == 0:
- ended_on = ","
- j += 1
- break
- j += 1
- # Include the closing `}` for block arms so `_parse_rhs` sees a
- # balanced `{ ... }` (it relies on rhs.endswith("}")).
- end = j + 1 if (is_block and ended_on == "}") else j
- arm_text = body[arm_start:end].rstrip().rstrip(",").rstrip()
- arms.append(arm_text)
- if ended_on == "}" and not is_block:
- # closing of the whole match
- break
- i = j
- return arms
-
-
-def parse_builtin_call_sigs(path: Path) -> dict[str, dict]:
- """Return {name: {params, variadic, ref_params, required}} from signatures.rs::builtin_call_sig."""
- src = path.read_text(encoding="utf-8")
- body = _extract_function_body(src, "builtin_call_sig")
- if not body:
- return {}
- out: dict[str, dict] = {}
- for arm in _split_match_arms(body):
- # names: everything before " =>"
- head, _, rhs = arm.partition("=>")
- names = _split_name_list(head)
- info = _parse_rhs(rhs)
- for n in names:
- out[n] = info
- return out
-
-
-def _parse_rhs(rhs: str) -> dict:
- """Parse a single arm RHS — either a direct `Some(builder(...))` or a block with `let mut sig = ...`."""
- rhs = rhs.strip().rstrip(",")
- # Strip leading/trailing braces if it's a block.
- block: Optional[str] = None
- if rhs.startswith("{") and rhs.endswith("}"):
- block = rhs[1:-1]
- # Case A: `let mut sig = ...` (custom builder with overrides).
- m_let = re.search(r"let\s+mut\s+sig\s*=\s*([^;]+);", block)
- if m_let:
- builder_expr = m_let.group(1).strip()
- ref_overrides = {
- int(m.group(1)): m.group(2).strip() == "true"
- for m in re.finditer(r"sig\.ref_params\[(\d+)\]\s*=\s*(true|false)", block)
- }
- else:
- # Case B: block with a single `Some(builder(...))` expression.
- m_some = re.search(r"Some\s*\(\s*(.+?)\s*\)\s*(?:;|$)", block, re.DOTALL)
- if not m_some:
- return {}
- builder_expr = m_some.group(1).strip()
- ref_overrides = {
- int(m.group(1)): m.group(2).strip() == "true"
- for m in re.finditer(r"sig\.ref_params\[(\d+)\]\s*=\s*(true|false)", block)
- }
else:
- # form: Some(builder(args))
- m = re.match(r"Some\s*\(\s*(.+?)\s*\)\s*$", rhs, re.DOTALL)
- if not m:
- return {}
- builder_expr = m.group(1).strip()
- ref_overrides = {}
-
- return _parse_builder(builder_expr, ref_overrides)
-
-
-def _parse_builder(expr: str, ref_overrides: dict[int, bool]) -> dict:
- """Parse `fixed(&["a", "b"])` / `optional(...)` / `variadic(...)` / `first_param_ref(...)`.
-
- All regexes use `re.DOTALL` and tolerate whitespace between tokens, because
- the Rust source often spans multiple lines and inserts newlines + indentation.
- """
- # unwrap first_param_ref(...) if present
- m = re.match(r"first_param_ref\s*\(\s*(.+?)\s*\)\s*$", expr, re.DOTALL)
- if m:
- inner = m.group(1).strip()
- ref_overrides = {0: True, **ref_overrides}
- return _parse_builder(inner, ref_overrides)
-
- m = re.match(r"fixed\s*\(\s*&\[(.*?)\]\s*\)", expr, re.DOTALL)
- if m:
- params = _parse_param_list(m.group(1))
- return {
- "params": [{"name": p, "by_ref": ref_overrides.get(i, False), "default": None, "optional": False} for i, p in enumerate(params)],
- "variadic": None,
- "required": len(params),
- }
- m = re.match(r"optional\s*\(\s*&\[(.*?)\]\s*,\s*(\d+)\s*,\s*(.+?)\s*\)\s*$", expr, re.DOTALL)
- if m:
- params = _parse_param_list(m.group(1))
- required = int(m.group(2))
- defaults = _parse_optional_defaults(m.group(3))
- result = []
- for i, p in enumerate(params):
- opt = i >= required
- default = defaults.get(i - required)
- result.append({"name": p, "by_ref": ref_overrides.get(i, False), "default": default, "optional": opt})
- return {"params": result, "variadic": None, "required": required}
- m = re.match(r"variadic\s*\(\s*&\[(.*?)\]\s*,\s*\"([^\"]+)\"\s*\)\s*$", expr, re.DOTALL)
- if m:
- params = _parse_param_list(m.group(1))
- variadic = m.group(2)
- result = [{"name": p, "by_ref": ref_overrides.get(i, False), "default": None, "optional": False} for i, p in enumerate(params)]
- return {"params": result, "variadic": variadic, "required": len(params)}
- return {"params": [], "variadic": None, "required": 0}
-
-
-def parse_first_class_return_types(path: Path) -> dict[str, str]:
- """Return {name: return_type_string} from signatures.rs::general_first_class_callable_builtin_sig."""
- src = path.read_text(encoding="utf-8")
- out: dict[str, str] = {}
-
- # explicit FunctionSig blocks (strlen, count, buffer_len, ...)
- for m in re.finditer(
- r'"([^"]+)"\s*=>\s*Some\(FunctionSig\s*\{[^}]*?return_type:\s*(PhpType::[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)?(?:\([^)]*\))?)[^}]*?\}',
- src,
- re.DOTALL,
- ):
- out[m.group(1)] = _render_phptype(m.group(2))
-
- # typed_first_class_builtin_sig(name, &[PhpType::X, ...], PhpType::Y)
- for m in re.finditer(
- r'typed_first_class_builtin_sig\(\s*name\s*,\s*&\[([^\]]*)\]\s*,\s*(PhpType::[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)?(?:\([^)]*\))?)\s*\)',
- src,
- ):
- # we don't know the names here without re-parsing the match arm; resolve by walking backward to find the most recent arm header
- # simpler: just use the previous match in src order
- pass
-
- # General "name | "name" => Some(typed_first_class_builtin_sig(...))" pattern
- arm_re = re.compile(
- r'"([^"]+)"(?:\s*\|\s*"[^"]+")*\s*=>\s*Some\(typed_first_class_builtin_sig\(\s*name\s*,\s*&\[([^\]]*)\]\s*,\s*(PhpType::[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)?(?:\([^)]*\))?)\)\)',
- )
- for m in arm_re.finditer(src):
- for name in _split_name_list(m.group(0).split("=>", 1)[0]):
- out[name] = _render_phptype(m.group(3))
-
- # return_typed_first_class_builtin_sig(name, PhpType::X)
- arm_re2 = re.compile(
- r'"([^"]+)"(?:\s*\|\s*"[^"]+")*\s*=>\s*return_typed_first_class_builtin_sig\(\s*name\s*,\s*(PhpType::[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)?(?:\([^)]*\))?)\s*\)',
- )
- for m in arm_re2.finditer(src):
- for name in _split_name_list(m.group(0).split("=>", 1)[0]):
- out[name] = _render_phptype(m.group(2))
-
- return out
-
-
-def _render_phptype(ty: str) -> str:
- """Render a PhpType expression as a short user-facing string."""
- ty = ty.strip()
- mapping = {
- "PhpType::Int": "int",
- "PhpType::Float": "float",
- "PhpType::Bool": "bool",
- "PhpType::Str": "string",
- "PhpType::Void": "void",
- "PhpType::Null": "null",
- "PhpType::Mixed": "mixed",
- "PhpType::Never": "never",
- }
- if ty in mapping:
- return mapping[ty]
- if ty.startswith("PhpType::Array"):
- return "array"
- if ty.startswith("PhpType::Buffer"):
- return "buffer"
- if ty.startswith("PhpType::Union"):
- return "mixed"
- if ty.startswith("PhpType::AssocArray"):
- return "array"
- return "mixed"
+ cmd = ["cargo", "run", "--quiet", "--bin", "gen_builtins", "--", "--include-internal"]
+ proc = subprocess.run(cmd, cwd=repo, capture_output=True, text=True)
+ if proc.returncode != 0:
+ sys.exit(
+ "gen_builtins failed (build it with `cargo build --bin gen_builtins`):\n"
+ + proc.stderr
+ )
+ try:
+ return json.loads(proc.stdout)
+ except json.JSONDecodeError as exc: # pragma: no cover - defensive
+ sys.exit(f"gen_builtins produced invalid JSON: {exc}")
# ---------------------------------------------------------------------------
-# lowering: builtins.rs and per-area submodules
+# Home-file lowering map: name -> the emitter its `lower` hook dispatches to
# ---------------------------------------------------------------------------
-# Each dispatch arm in `lower_builtin_call` matches either a single name
-# or a `|`-separated list. e.g. `"strlen" => lower_strlen(ctx, inst),`
-
-DISPATCH_ARM_RE = re.compile(
- r'"([^"]+)"(?:\s*\|\s*"[^"]+")*\s*=>\s*([A-Za-z_][A-Za-z0-9_]*)::([A-Za-z_][A-Za-z0-9_]*|lower_[A-Za-z0-9_]+|lower_unary_libm)\(',
-)
-
-
-def parse_lowering_dispatch(path: Path) -> dict[str, tuple[str, str, int]]:
- """Return {name: (module, codegen_function, line)} for arms in builtins.rs
- that dispatch a *single* builtin name to a dedicated lowering function.
-
- Multi-name arms (e.g. catch-all dispatchers that handle a whole family) are
- skipped, so the per-name entry wins when both exist.
+# Core registry-machinery files under src/builtins/ that are NOT builtin homes.
+_NON_HOME_FILES = {
+ "spec.rs",
+ "registry.rs",
+ "macros.rs",
+ "convert.rs",
+ "docs.rs",
+ "mod.rs",
+ "parity_tests.rs",
+}
+
+_NAME_RE = re.compile(r'name:\s*"([^"]+)"')
+# The `lower` hook dispatches to the real emitter via a fully-qualified path,
+# e.g. `crate::codegen::lower_inst::builtins::math::lower_abs(ctx, inst)`
+# (the `(ctx` may be on the following line — `\s*` spans newlines).
+_EMITTER_RE = re.compile(r"lower_inst::builtins::([A-Za-z0-9_:]+)\s*\(\s*ctx\b")
+
+
+def build_home_lowering_map(repo: Path) -> dict[str, tuple[str, str, str]]:
+ """Map each registry builtin name (lowercased) to ``(emitter_fn, module, home_rel)``.
+
+ Scans every builtin home file under ``src/builtins/`` (skipping the registry
+ machinery files), reads its ``builtin!`` name and the emitter path its
+ ``lower`` hook dispatches to. ``module`` is the last path segment before the
+ emitter function (used for the AREA_BY_MODULE area fallback); ``home_rel`` is
+ the home file path relative to the repo root.
"""
- src_lines = path.read_text(encoding="utf-8").splitlines()
- out: dict[str, tuple[str, str, int]] = {}
- for lineno, line in enumerate(src_lines, start=1):
- m = DISPATCH_ARM_RE.search(line)
- if not m:
+ out: dict[str, tuple[str, str, str]] = {}
+ builtins_root = repo / "src" / "builtins"
+ for path in builtins_root.rglob("*.rs"):
+ if path.name in _NON_HOME_FILES:
+ continue
+ text = path.read_text(encoding="utf-8")
+ if "builtin!" not in text:
continue
- names_in_arm = re.findall(r'"([^"]+)"', line.split("=>", 1)[0])
- if len(names_in_arm) != 1:
+ name_match = _NAME_RE.search(text)
+ if not name_match:
continue
- n = names_in_arm[0]
- out[n] = (m.group(2), m.group(3), lineno)
+ canonical = name_match.group(1).lower()
+ emitter_fn = ""
+ module = ""
+ emit_match = _EMITTER_RE.search(text)
+ if emit_match:
+ segments = emit_match.group(1).split("::")
+ emitter_fn = segments[-1]
+ module = segments[-2] if len(segments) >= 2 else ""
+ out[canonical] = (emitter_fn, module, str(path.relative_to(repo)))
return out
+# ---------------------------------------------------------------------------
+# Emitter resolution: find the emitter fn definition, its doc + runtime helpers
+# ---------------------------------------------------------------------------
+
DOC_COMMENT_RE = re.compile(r"^///\s?(.*)$")
+def find_lowering_function_def(src: str, fn_name: str) -> Optional[tuple[str, int]]:
+ """Find the (line_text, line_number) of ``fn (`` in ``src``."""
+ lines = src.splitlines()
+ for i, line in enumerate(lines, start=1):
+ if re.match(rf"\s*(pub(?:\([^)]*\))?\s+)?fn\s+{re.escape(fn_name)}\s*\(", line):
+ return (line, i)
+ return None
+
+
def _leading_doc_comment(src: str, line: int) -> str:
- """Return the /// doc comment block immediately above a function definition at `line`."""
+ """Return the ``///`` doc-comment block immediately above the function at ``line``."""
lines = src.splitlines()
- i = line - 2 # 1-based
+ i = line - 2 # 1-based → index above the def
out: list[str] = []
while i >= 0 and lines[i].lstrip().startswith("///"):
- m = DOC_COMMENT_RE.match(lines[i])
+ m = DOC_COMMENT_RE.match(lines[i].lstrip())
if m:
out.append(m.group(1).strip())
i -= 1
@@ -469,19 +170,8 @@ def _leading_doc_comment(src: str, line: int) -> str:
return "\n".join(out)
-def find_lowering_function_def(
- src: str, fn_name: str
-) -> Optional[tuple[str, int]]:
- """Find the (path, line) of `fn (` in `src`."""
- lines = src.splitlines()
- for i, line in enumerate(lines, start=1):
- if re.match(rf"\s*(pub(?:\([^)]*\))?\s+)?fn\s+{re.escape(fn_name)}\s*\(", line):
- return (line, i)
- return None
-
-
def collect_runtime_helpers(notes: str, body: str) -> list[str]:
- """Find `__rt_*` symbols in the doc comment and the lowering body."""
+ """Return the sorted set of ``__rt_*`` symbols mentioned in the doc + lowering body."""
found = set(re.findall(r"\b__rt_[A-Za-z0-9_]+", notes)) | set(
re.findall(r"\b__rt_[A-Za-z0-9_]+", body)
)
@@ -489,195 +179,292 @@ def collect_runtime_helpers(notes: str, body: str) -> list[str]:
def parse_area_for_file(rel_path: str) -> tuple[Optional[str], str]:
- """Look up the (area, sub_area) for a given relative file path.
+ """Look up the ``(area, sub_area)`` for a lowering file path under ``builtins/``.
- Returns (None, "") as a sentinel when the file is the root dispatcher
- (builtins/builtins.rs) and the area should be inferred from the
- dispatch module/function instead.
+ Returns ``(None, "")`` as a sentinel when the file is the root dispatcher and
+ the area should be inferred from the module/function instead.
"""
key = rel_path.replace("builtins/", "").replace("builtins\\", "")
if key in AREA_BY_FILE:
val = AREA_BY_FILE[key]
- if val is None:
- return (None, "") # sentinel
- return val
- # try the basename if no submodule match
+ return (None, "") if val is None else val
base = Path(key).name
if base in AREA_BY_FILE:
val = AREA_BY_FILE[base]
- if val is None:
- return (None, "")
- return val
+ return (None, "") if val is None else val
return ("Misc", "Misc")
-def parse_check_builtin_returns(path: Path) -> dict[str, str]:
- """Return {name: return_type} extracted from check_builtin() in a checker file.
+def resolve_lowering(
+ repo: Path,
+ read,
+ dispatch: Path,
+ lowering_dir: Path,
+ emitter_fn: str,
+ sig_file: Optional[str],
+) -> LoweringInfo:
+ """Resolve an emitter function name to its definition, doc notes, and helpers.
+
+ Searches ``builtins.rs`` (root dispatcher) and every per-area submodule for
+ ``fn (``. Returns a populated :class:`LoweringInfo` (with
+ ``codegen_file``/``codegen_line``/``notes``/``runtime_helpers``) when found, or
+ a bare one carrying only ``sig_file`` when not.
+ """
+ lowering = LoweringInfo(sig_file=sig_file)
+ if not emitter_fn:
+ return lowering
+ for candidate in [dispatch, *sorted(lowering_dir.rglob("*.rs"))]:
+ src_text = read(candidate)
+ defn = find_lowering_function_def(src_text, emitter_fn)
+ if defn is None:
+ continue
+ _, def_line = defn
+ doc = _leading_doc_comment(src_text, def_line)
+ body = "\n".join(src_text.splitlines()[def_line - 1 : def_line + 30])
+ helpers = collect_runtime_helpers(doc, body)
+ notes = [line for line in doc.splitlines() if line.strip()]
+ return LoweringInfo(
+ sig_file=sig_file,
+ codegen_file=str(candidate.relative_to(repo)),
+ codegen_line=def_line,
+ codegen_function=emitter_fn,
+ runtime_helpers=helpers,
+ notes=notes,
+ )
+ return lowering
+
- We scan the body line-by-line, tracking:
- - the set of names that make up the current arm (multi-name and multi-line
- headers are handled by accumulating names until we see `=>` or a `}`),
- - and the return type as the *last* `Ok(Some(PhpType::))` we observe
- before the arm closes.
+def resolve_area(
+ canonical: str, lowering: LoweringInfo, emitter_fn: str, module: str
+) -> tuple[str, str]:
+ """Resolve a builtin's documentation ``(area, sub_area)``.
- Conditional arms like `min`/`max` keep the *last* such return, which is the
- more specific (Float) branch.
+ Priority (most specific first): per-name override → the lowering file's path →
+ the generic libm/lowering-fn mapping → the dispatch module → ``Misc``.
"""
- text = path.read_text(encoding="utf-8")
- out: dict[str, str] = {}
- re_phptype = re.compile(
- r"Ok\s*\(\s*Some\s*\(\s*(PhpType::[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)?(?:\([^)]*\))?)\s*\)\s*\)"
- )
- re_name = re.compile(r'"([^"]+)"')
- in_match = False
- current_names: list[str] = []
- pending_names: list[str] = [] # names found on a line that didn't have `=>` yet
- arm_indent: int | None = None
- for line in text.splitlines():
- # Skip Rust string literals when scanning for tokens.
- # We do this by re-tokenising the line on quotes.
- if not in_match:
- if "match name" in line or re.search(r"match\s+\w+\s*\{", line):
- in_match = True
- continue
- # Detect a "names" line: a line with one or more `"name"` segments.
- names = re_name.findall(line)
- if "=>" in line:
- # commit pending names + this line's names
- current_names = list(dict.fromkeys(current_names + pending_names + names))
- pending_names = []
- arm_indent = len(line) - len(line.lstrip())
- continue
- if names:
- pending_names.extend(names)
- # Look for the return type on any line in the arm
- if current_names:
- m = re_phptype.search(line)
- if m:
- rt = _render_phptype(m.group(1))
- for n in current_names:
- out[n] = rt
- current_names = []
- # arm ends at a `}` at the same indent as the header
- if arm_indent is not None and line.strip() == "}":
- cur_indent = len(line) - len(line.lstrip())
- if cur_indent == arm_indent:
- current_names = []
- pending_names = []
- arm_indent = None
- return out
+ area = AREA_BY_NAME.get(canonical, ("Misc", "Misc"))
+ if area == ("Misc", "Misc") and lowering.codegen_file:
+ cf = lowering.codegen_file
+ prefix = "src/codegen/lower_inst/builtins"
+ rel_under = cf[len(prefix) + 1 :] if cf.startswith(prefix + "/") else cf
+ file_area = parse_area_for_file(rel_under)
+ if file_area[0] is not None and (file_area[0] != "Misc" or file_area[1] != "Misc"):
+ area = file_area
+ if area == ("Misc", "Misc"):
+ fn_area = AREA_BY_LOWERING_FN.get(emitter_fn) if emitter_fn else None
+ if fn_area is not None:
+ area = fn_area
+ elif module:
+ mod_area = AREA_BY_MODULE.get(module)
+ if mod_area is not None:
+ area = mod_area
+ return area
-def parse_check_builtin_param_types(path: Path) -> dict[str, list[tuple[int, str]]]:
- """Return {name: [(arg_index, type_str), ...]} extracted from check_builtin().
+# ---------------------------------------------------------------------------
+# Type + default rendering (registry data → doc vocabulary)
+# ---------------------------------------------------------------------------
- We scan the arm body and watch for the pattern:
- let = checker.infer_type(&args[N], env)?;
- if !matches!(, PhpType::X | PhpType::Y) { ... } # X|Y
- if != PhpType::X { ... } # X
- match { PhpType::X => ..., _ => Err } # X
+def _normalize_type(reg_type: str) -> str:
+ """Map a registry type string to the doc's simple type vocabulary.
- For each such pair we record (N, type). For the common "must be array" /
- "must be string" / "must be int" patterns this gives us precise param
- types. The renderer picks the *first* declared type per arg index.
+ The registry renders `TypeSpec::ArrayOf`/`AssocOf` as ``array<...>`` and
+ unions as ``a|b``; the docs collapse those to ``array`` / ``mixed``. Scalars
+ (``int``/``float``/``string``/``bool``/``mixed``/``null``/``void``) pass through.
"""
- text = path.read_text(encoding="utf-8")
- out: dict[str, list[tuple[int, str]]] = {}
- in_match = False
- current_names: list[str] = []
- pending_names: list[str] = []
- arm_indent: int | None = None
- # Map from inferred-var name -> (arg_index, type_str) — collected while
- # scanning the current arm.
- inferred: dict[str, tuple[int, str]] = {}
- for line in text.splitlines():
- if not in_match:
- if "match name" in line or re.search(r"match\s+\w+\s*\{", line):
- in_match = True
- continue
- names = re.findall(r'"([^"]+)"', line)
- if "=>" in line:
- current_names = list(dict.fromkeys(current_names + pending_names + names))
- pending_names = []
- arm_indent = len(line) - len(line.lstrip())
- inferred = {} # reset for new arm
- continue
- if names:
- pending_names.extend(names)
- # Pattern 1: `let = checker.infer_type(&args[N], env)?;`
- m_infer = re.search(
- r"let\s+(\w+)\s*=\s*checker\.infer_type\(&args\[(\d+)\]\s*,\s*env\)?\s*;?",
- line,
- )
- if m_infer:
- var_name = m_infer.group(1)
- arg_idx = int(m_infer.group(2))
- # If we already saw a constraint on this var, use it now.
- if var_name in inferred:
- idx, ty = inferred[var_name]
- inferred[var_name] = (idx, ty)
- else:
- inferred[var_name] = (arg_idx, "mixed")
- continue
- # Pattern 2: `if !matches!(, PhpType::X | PhpType::Y) {`
- m_match = re.search(
- r"if\s+!matches!\(\s*(\w+)\s*,\s*(PhpType::[A-Za-z0-9_]+(?:\([^)]*\))?(?:\s*\|\s*PhpType::[A-Za-z0-9_]+(?:\([^)]*\))?)*)\s*\)",
- line,
- )
- if m_match:
- var_name = m_match.group(1)
- type_pattern = m_match.group(2)
- # Take the FIRST PhpType from the alternation.
- first = re.match(r"(PhpType::[A-Za-z0-9_]+)", type_pattern)
- if first and var_name in inferred:
- idx, _ = inferred[var_name]
- inferred[var_name] = (idx, _render_phptype(first.group(1)))
- continue
- # Pattern 3: `if != PhpType::X {`
- m_neq = re.search(
- r"if\s+(\w+)\s*!=\s*(PhpType::[A-Za-z0-9_]+(?:\([^)]*\))?)\s*\{",
- line,
- )
- if m_neq:
- var_name = m_neq.group(1)
- type_str = _render_phptype(m_neq.group(2))
- if var_name in inferred:
- idx, _ = inferred[var_name]
- inferred[var_name] = (idx, type_str)
- continue
- # Pattern 4: `match { PhpType::X => ..., _ => Err }` — the first
- # match arm header gives the type. We look for lines that start with
- # `PhpType::X =>` and pair them with the var that was just matched.
- m_match_arm = re.match(
- r"^\s*(PhpType::[A-Za-z0-9_]+(?:\([^)]*\))?)\s*=>",
- line,
- )
- if m_match_arm and inferred:
- # We can't easily tell which var this `match` belongs to without
- # a brace counter. Cheap heuristic: take the most recently assigned
- # inferred var. The patterns above usually appear right after
- # `let = ...`.
- type_str = _render_phptype(m_match_arm.group(1))
- for var, (idx, _) in list(inferred.items()):
- if inferred[var][1] == "mixed":
- inferred[var] = (idx, type_str)
- break
- # When the arm closes, commit the inferred types to the current names.
- if arm_indent is not None and line.strip() == "}":
- cur_indent = len(line) - len(line.lstrip())
- if cur_indent == arm_indent and current_names:
- # Group by arg index, keep the first non-mixed type per index.
- by_idx: dict[int, str] = {}
- for _, (idx, ty) in inferred.items():
- if idx not in by_idx or ty != "mixed":
- by_idx[idx] = ty
- for n in current_names:
- out.setdefault(n, []).extend([(i, t) for i, t in sorted(by_idx.items())])
- current_names = []
- pending_names = []
- inferred = {}
- arm_indent = None
- return out
+ reg_type = reg_type.strip()
+ if "|" in reg_type:
+ return "mixed"
+ if reg_type.startswith("array"):
+ return "array"
+ return reg_type
+
+
+def _param_refine_type(entry) -> Optional[str]:
+ """Extract the display type from a `PARAM_TYPES` entry (``str`` or ``(type, name)``)."""
+ if entry is None:
+ return None
+ if isinstance(entry, str):
+ return entry or None
+ ty = entry[0]
+ return ty or None
+
+
+# Maps a Rust `PhpType::` to the doc's display type.
+_PHPTYPE_DISPLAY = {
+ "Str": "string",
+ "Int": "int",
+ "Bool": "bool",
+ "Float": "float",
+ "Void": "void",
+ "Null": "null",
+ "Mixed": "mixed",
+ "Never": "never",
+ "Array": "array",
+ "AssocArray": "array",
+ "Union": "mixed",
+ "Buffer": "buffer",
+}
+
+
+def _extract_fn_body(text: str, fn_name: str) -> str:
+ """Return the brace-matched body of ``fn (`` in ``text`` (or '')."""
+ for prefix in ("pub(crate) ", "pub(super) ", "pub ", ""):
+ start = text.find(f"{prefix}fn {fn_name}(")
+ if start >= 0:
+ break
+ else:
+ return ""
+ brace = text.find("{", start)
+ if brace < 0:
+ return ""
+ depth = 0
+ for i in range(brace, len(text)):
+ if text[i] == "{":
+ depth += 1
+ elif text[i] == "}":
+ depth -= 1
+ if depth == 0:
+ return text[brace : i + 1]
+ return ""
+
+
+def parse_home_check_return(home_text: str, resolve_body) -> Optional[str]:
+ """Recover a precise return type from a home file's ``check`` hook, or ``None``.
+
+ The registry types non-scalar returns coarsely as ``Mixed`` (arrays are
+ declared ``Mixed`` + a check hook that returns the precise type). We locate the
+ hook's body — a local ``fn check`` or, when ``check:`` points to a distinctively
+ named shared fn (e.g. ``support::check_declared_names``), that fn resolved via
+ ``resolve_body`` — then scan its ``Ok(PhpType::)`` returns. When they
+ agree on a single non-``mixed`` display type (or an array type dominates), we
+ return it; otherwise ``None``.
+ """
+ m = re.search(r"\bcheck:\s*([A-Za-z0-9_:]+)", home_text)
+ if not m:
+ return None
+ target = m.group(1)
+ fn_name = target.split("::")[-1]
+ if "::" in target and fn_name != "check":
+ body = resolve_body(fn_name) or _extract_fn_body(home_text, fn_name)
+ else:
+ body = _extract_fn_body(home_text, "check")
+ if not body:
+ return None
+ variants = re.findall(r"Ok\(\s*PhpType::([A-Za-z0-9_]+)", body)
+ displays = {_PHPTYPE_DISPLAY.get(v, "mixed") for v in variants}
+ # Array-passthrough pattern: the hook validates the argument is an array and
+ # returns it unchanged (`Ok(ty)`), so the literal PhpType is never written.
+ if re.search(r"Ok\(\s*[a-z_]\w*\s*\)", body) and "PhpType::Array" in body:
+ displays.add("array")
+ non_mixed = displays - {"mixed"}
+ if len(non_mixed) == 1:
+ return next(iter(non_mixed))
+ if "array" in non_mixed:
+ return "array"
+ return None
+
+
+def _render_default(value, optional: bool) -> Optional[str]:
+ """Render a registry default value as a PHP-literal display string.
+
+ Required params (``optional`` false) have no default (``None``). Optional
+ params render their default: ``null``, ``true``/``false``, integers/floats
+ verbatim, strings single-quoted, the ``PHP_INT_MAX``/``PHP_INT_MIN`` sentinels
+ as constants, and the empty-array sentinel as ``[]``.
+ """
+ if not optional:
+ return None
+ if value is None:
+ return "null"
+ # bool must precede int: bool is a subclass of int in Python.
+ if isinstance(value, bool):
+ return "true" if value else "false"
+ if isinstance(value, (int, float)):
+ return str(value)
+ if isinstance(value, list):
+ return "[]"
+ if isinstance(value, str):
+ if value in ("PHP_INT_MAX", "PHP_INT_MIN"):
+ return value
+ return repr(value)
+ return str(value)
+
+
+# ---------------------------------------------------------------------------
+# PHP language constructs (checker-resident, NOT in the registry)
+# ---------------------------------------------------------------------------
+
+# These stay in the type checker (they operate on l-values / are lazy constructs)
+# and are absent from the `builtin!` registry. We add them by hand so their doc
+# pages are preserved. Each: params [(name, type, by_ref, default, optional)],
+# variadic, return_type, (area, sub_area), description, emitter_fn (or None).
+LANGUAGE_CONSTRUCTS: dict[str, dict] = {
+ "isset": {
+ "params": [("var", "mixed", False, None, False)],
+ "variadic": "vars",
+ "return_type": "bool",
+ "area": ("Misc", "Variable"),
+ "description": "Determines whether a variable is set and is not null.",
+ "emitter_fn": "lower_isset",
+ },
+ "unset": {
+ "params": [("var", "mixed", False, None, False)],
+ "variadic": "vars",
+ "return_type": "void",
+ "area": ("Misc", "Variable"),
+ "description": "Unsets the given variables.",
+ "emitter_fn": "lower_unset_builtin",
+ },
+ "empty": {
+ "params": [("value", "mixed", False, None, False)],
+ "variadic": None,
+ "return_type": "bool",
+ "area": ("Misc", "Variable"),
+ "description": "Determines whether a variable is considered empty.",
+ "emitter_fn": "lower_empty",
+ },
+ "exit": {
+ "params": [("status", "int", False, None, True)],
+ "variadic": None,
+ "return_type": "void",
+ "area": ("Process", "Process"),
+ "description": "",
+ "emitter_fn": None,
+ },
+ "die": {
+ "params": [("status", "int", False, None, True)],
+ "variadic": None,
+ "return_type": "void",
+ "area": ("Process", "Process"),
+ "description": "",
+ "emitter_fn": None,
+ },
+ "buffer_len": {
+ "params": [("buffer", "buffer", False, None, False)],
+ "variadic": None,
+ "return_type": "int",
+ "area": ("Buffer", "Buffer"),
+ "description": "Lowers `buffer_len()` through the direct buffer opcode helper.",
+ "emitter_fn": "lower_buffer_len",
+ },
+ "buffer_free": {
+ "params": [("buffer", "buffer", False, None, False)],
+ "variadic": None,
+ "return_type": "mixed",
+ "area": ("Buffer", "Buffer"),
+ "description": "Lowers `buffer_free()` through the direct buffer opcode helper.",
+ "emitter_fn": "lower_buffer_free",
+ },
+ "buffer_new": {
+ "params": [("length", "int", False, None, False)],
+ "variadic": None,
+ "return_type": "mixed",
+ "area": ("Misc", "Misc"),
+ "description": "",
+ "emitter_fn": None,
+ },
+}
# ---------------------------------------------------------------------------
@@ -685,29 +472,14 @@ def parse_check_builtin_param_types(path: Path) -> dict[str, list[tuple[int, str
# ---------------------------------------------------------------------------
def build_registry(repo: Path) -> list[Builtin]:
- """Build the full list of builtins from the given repo root."""
+ """Build the full list of builtins from the registry + language constructs."""
src = repo / "src"
+ dispatch = src / "codegen" / "lower_inst" / "builtins.rs"
+ lowering_dir = src / "codegen" / "lower_inst" / "builtins"
+
+ gen = run_gen_builtins(repo)
+ home_map = build_home_lowering_map(repo)
- catalog = src / "types" / "checker" / "builtins" / "catalog.rs"
- sigs = src / "types" / "signatures.rs"
- dispatch = src / "codegen_ir" / "lower_inst" / "builtins.rs"
- lowering_dir = src / "codegen_ir" / "lower_inst" / "builtins"
-
- if not catalog.exists():
- sys.exit(f"catalog.rs not found: {catalog}")
- if not sigs.exists():
- sys.exit(f"signatures.rs not found: {sigs}")
- if not dispatch.exists():
- sys.exit(f"builtins.rs not found: {dispatch}")
-
- supported, internal = parse_catalog(catalog)
- internal_names = {name.lower() for name in internal}
- catalog_names = list(dict.fromkeys([*supported, *internal]))
- call_sigs = parse_builtin_call_sigs(sigs)
- first_class_returns = parse_first_class_return_types(sigs)
- dispatch_map = parse_lowering_dispatch(dispatch)
-
- # Cache file contents to avoid re-reading.
file_cache: dict[Path, str] = {}
def read(p: Path) -> str:
@@ -715,269 +487,129 @@ def read(p: Path) -> str:
file_cache[p] = p.read_text(encoding="utf-8")
return file_cache[p]
+ builtins_root = src / "builtins"
+
+ def resolve_check_body(fn_name: str) -> str:
+ """Return the body of a shared check fn ``fn (`` defined under src/builtins/."""
+ for path in sorted(builtins_root.rglob("*.rs")):
+ text = read(path)
+ if f"fn {fn_name}(" in text:
+ body = _extract_fn_body(text, fn_name)
+ if body:
+ return body
+ return ""
+
builtins: list[Builtin] = []
- # Pre-parse checker return/param types once. Re-parsing every checker file
- # inside the per-builtin loop is O(N*M); doing it once keeps extraction fast.
- checker_dir = src / "types" / "checker" / "builtins"
- check_builtin_returns: dict[str, str] = {}
- check_builtin_params: dict[str, list[tuple[int, str]]] = {}
- for cb_path in checker_dir.rglob("*.rs"):
- for n, rt in parse_check_builtin_returns(cb_path).items():
- check_builtin_returns[n] = rt
- for n, inferred_params in parse_check_builtin_param_types(cb_path).items():
- check_builtin_params.setdefault(n, []).extend(inferred_params)
-
- for name in catalog_names:
+ # --- registry builtins (PHP-visible + internal helpers) ---
+ for entry in gen:
+ name = entry["name"]
canonical = name.lower()
- in_catalog = canonical not in internal_names
- # Any function whose canonical name starts with __elephc_ is a compiler
- # internal helper; it gets an internals page but no user-facing page.
- is_internal = canonical.startswith("__elephc_") or canonical in internal_names
-
- # Fallback: if signatures.rs didn't yield a result, use the call_sigs
- # entry directly (may be empty) so the builtin still renders.
- sig_info = call_sigs.get(canonical)
- if sig_info is None:
- # No match at all in signatures.rs — use an empty stub.
- sig_info = {"params": [], "variadic": None, "required": 0}
- params = [
- Parameter(
- name=p["name"],
- php_type="mixed",
- by_ref=p["by_ref"],
- default=p["default"],
- optional=p["optional"],
+ is_internal = bool(entry.get("internal"))
+ in_catalog = not is_internal
+
+ refine = PARAM_TYPES.get(canonical)
+ params: list[Parameter] = []
+ for i, p in enumerate(entry.get("params", [])):
+ php_type = _normalize_type(p["type"])
+ if php_type == "mixed" and refine and i < len(refine):
+ better = _param_refine_type(refine[i])
+ if better:
+ php_type = better
+ params.append(
+ Parameter(
+ name=p["name"],
+ php_type=php_type,
+ by_ref=bool(p.get("by_ref")),
+ default=_render_default(p.get("default"), bool(p.get("optional"))),
+ optional=bool(p.get("optional")),
+ )
)
- for p in sig_info.get("params", [])
- ]
- return_type = first_class_returns.get(canonical, "mixed")
- # Second-pass return type from check_builtin() in src/types/checker/builtins/*.rs.
- # This gives precise types for things like `floor -> float`, `is_array -> bool`, etc.
- # It overrides the first_class_returns value when more specific.
- if canonical in check_builtin_returns and check_builtin_returns[canonical] != "mixed":
- return_type = check_builtin_returns[canonical]
- # Apply hand-curated return-type and variadic overrides. These win
- # over anything parsed from check_builtin() / signatures.rs.
+
+ emitter_fn, module, home_rel = home_map.get(canonical, ("", "", None))
+
+ return_type = _normalize_type(entry.get("returns", "mixed"))
+ # The registry types non-scalar returns as `Mixed`; recover the precise
+ # type from the home file's `check` hook when possible.
+ if return_type == "mixed" and home_rel:
+ precise = parse_home_check_return(read(repo / home_rel), resolve_check_body)
+ if precise:
+ return_type = precise
if canonical in RETURN_TYPE_OVERRIDES:
return_type = RETURN_TYPE_OVERRIDES[canonical]
- if canonical in VARIADIC_OVERRIDES:
- # Force variadic even when signatures.rs says fixed.
- # Re-shape params: drop the fixed stub, switch to variadic.
- vname = VARIADIC_OVERRIDES[canonical]
- params = []
- sig_info["variadic"] = vname
- sig_info.pop("__stub", None)
- # Apply PARAM_TYPES to refine parameter names/types. For each known
- # (arg_index, type) pair, override the parameter's `php_type` when it
- # is currently `mixed` and the inferred type is more specific.
- if canonical in PARAM_TYPES:
- table = PARAM_TYPES[canonical]
- # Normalize entries: accept either `str` or `(type, name)` tuple.
- norm: list[tuple[str, str]] = []
- for entry in table:
- if entry is None:
- continue
- if isinstance(entry, str):
- norm.append((entry or "mixed", "value"))
- else:
- # tuple (type, name)
- ty, entry_name = entry
- norm.append((ty or "mixed", entry_name or "value"))
-
- # Apply name overrides on top of PARAM_TYPES.
- name_overrides = PARAM_NAME_OVERRIDES.get(canonical, [])
- for idx, override_name in enumerate(name_overrides):
- if override_name is not None and idx < len(norm):
- norm[idx] = (norm[idx][0], override_name)
-
- # Case A: signatures.rs gave us nothing — materialize all params
- # from PARAM_TYPES (skip when signatures.rs says variadic, the
- # variadic info is the authoritative shape).
- if not params and not sig_info.get("variadic"):
- for ty, entry_name in norm:
- params.append(Parameter(name=entry_name, php_type=ty))
- else:
- is_single_stub = (
- len(params) == 1 and params[0].name == "…"
- )
- is_named = all(p.name not in (None, "…") for p in params)
- if is_single_stub or is_named:
- # Refine existing params in place; PARAM_TYPES can
- # override the name when it's a tuple.
- target_count = len(params) if is_named else len(norm)
- for idx, (ty, entry_name) in enumerate(norm[:target_count]):
- if 0 <= idx < len(params) and ty and params[idx].php_type == "mixed":
- new_name = params[idx].name if is_named else entry_name
- if is_named and entry_name != "value":
- new_name = entry_name # explicit override
- params[idx] = Parameter(
- name=new_name,
- php_type=ty,
- by_ref=params[idx].by_ref,
- default=params[idx].default,
- optional=params[idx].optional,
- )
- # Append any extra params that signatures.rs missed (e.g.
- # `preg_match_all` declares 2 params in signatures.rs but
- # PHP takes a 3rd `&$matches` array). Skip when variadic.
- if not sig_info.get("variadic") and len(norm) > len(params):
- for ty, entry_name in norm[len(params):]:
- params.append(Parameter(name=entry_name, php_type=ty))
- # Apply by-reference overrides after PARAM_TYPES/checker refinement.
- if canonical in REF_PARAM_OVERRIDES:
- for idx, by_ref in enumerate(REF_PARAM_OVERRIDES[canonical]):
- if 0 <= idx < len(params):
- params[idx] = Parameter(
- name=params[idx].name,
- php_type=params[idx].php_type,
- by_ref=by_ref,
- default=params[idx].default,
- optional=params[idx].optional,
- )
- # Apply optional/default overrides.
- if canonical in OPTIONAL_PARAM_OVERRIDES:
- for idx, default in enumerate(OPTIONAL_PARAM_OVERRIDES[canonical]):
- if default is not None and 0 <= idx < len(params):
- params[idx] = Parameter(
- name=params[idx].name,
- php_type=params[idx].php_type,
- by_ref=params[idx].by_ref,
- default=default,
- optional=True,
- )
- # Then refine further with what the check_builtin() arms tell us.
- # We only apply this when the entry is unambiguous (one type per
- # arg_index, and the inferred type is not `mixed`).
- if canonical in check_builtin_params and any(
- p.name not in (None, "…") for p in params
- ):
- # Group by arg index, keep the first non-mixed type per index.
- by_idx: dict[int, str] = {}
- for idx, ty in check_builtin_params[canonical]:
- if ty == "mixed":
- continue
- if idx not in by_idx:
- by_idx[idx] = ty
- for idx, ty in by_idx.items():
- if 0 <= idx < len(params) and params[idx].php_type == "mixed":
- params[idx] = Parameter(
- name=params[idx].name,
- php_type=ty,
- by_ref=params[idx].by_ref,
- default=params[idx].default,
- optional=params[idx].optional,
- )
- # If still no signature at all (signatures.rs had no entry AND
- # check_builtin() didn't yield anything useful), mark the builtin
- # with a TODO stub so the renderer can show a placeholder.
- # Skip when signatures.rs already gave us info (even an empty list
- # means "the builtin takes no arguments", which is a real signature).
- if (
- not params
- and not sig_info.get("variadic")
- and canonical not in call_sigs
- ):
- params = [Parameter(name="…", php_type="mixed", optional=False)]
- sig_info["__stub"] = True
-
- lowering = LoweringInfo(
- sig_file=str(sigs.relative_to(repo)),
- sig_line=None,
- sig_arm=None,
+ lowering = resolve_lowering(
+ repo, read, dispatch, lowering_dir, emitter_fn, home_rel
)
- fn_name = ""
- if canonical in dispatch_map:
- module, fn_name, line = dispatch_map[canonical]
- else:
- # No dedicated dispatch arm. Try a heuristic: if the builtin name
- # matches a `lower_` function defined in builtins.rs root, use it.
- guessed_fn = f"lower_{canonical}"
- guessed = find_lowering_function_def(read(dispatch), guessed_fn)
- if guessed is not None:
- module, fn_name, line = "", guessed_fn, guessed[1]
- else:
- module, fn_name, line = "", "", 0
- if fn_name:
- # search in builtins.rs first, then submodules
- for candidate in [dispatch, *sorted(lowering_dir.rglob("*.rs"))]:
- src_text = read(candidate)
- defn = find_lowering_function_def(src_text, fn_name)
- if defn is None:
- continue
- _, def_line = defn
- doc = _leading_doc_comment(src_text, def_line)
- # pull the next 30 lines to look for runtime helpers
- body = "\n".join(src_text.splitlines()[def_line - 1 : def_line + 30])
- helpers = collect_runtime_helpers(doc, body)
- notes = [l for l in doc.splitlines() if l.strip()]
- lowering = LoweringInfo(
- codegen_file=str(candidate.relative_to(repo)),
- codegen_line=def_line,
- codegen_function=fn_name,
- runtime_helpers=helpers,
- notes=notes,
- )
- break
+ description = DESCRIPTION_OVERRIDES.get(canonical, "")
+ if not description:
+ description = entry.get("summary", "") or ""
+ if not description and lowering.notes:
+ description = lowering.notes[0]
- # Attach hand-curated notes for compiler-internal helpers.
if is_internal and canonical in INTERNAL_NOTES:
lowering.notes = INTERNAL_NOTES[canonical]
- # Area resolution priority (most specific first):
- # 1. AREA_BY_NAME — hand-curated per-name overrides (e.g. sin→Math).
- # 2. Lowering-fn location (the file the lowering lives in).
- # 3. AREA_BY_LOWERING_FN — generic libm dispatcher mapping.
- # 4. AREA_BY_MODULE — based on the dispatch arm's module prefix.
- # 5. Misc (last resort).
- area = AREA_BY_NAME.get(canonical, ("Misc", "Misc"))
- if area == ("Misc", "Misc") and lowering.codegen_file:
- cf = lowering.codegen_file
- prefix = "src/codegen_ir/lower_inst/builtins"
- if cf.startswith(prefix + "/"):
- rel_under = cf[len(prefix) + 1:]
- else:
- rel_under = cf # legacy safety
- file_area = parse_area_for_file(rel_under)
- if file_area[0] is not None and (file_area[0] != "Misc" or file_area[1] != "Misc"):
- area = file_area
- if area == ("Misc", "Misc"):
- fn_area = AREA_BY_LOWERING_FN.get(fn_name) if fn_name else None
- if fn_area is not None:
- area = fn_area
- elif module:
- mod_area = AREA_BY_MODULE.get(module)
- if mod_area is not None:
- area = mod_area
-
- # Derive a one-line description from hand-curated overrides or from
- # the first line of the lowering function's doc comment.
- description = DESCRIPTION_OVERRIDES.get(canonical, "")
- if not description and lowering.notes:
- description = lowering.notes[0]
+ area = resolve_area(canonical, lowering, emitter_fn, module)
+
+ builtins.append(
+ Builtin(
+ name=name,
+ canonical_name=canonical,
+ in_catalog=in_catalog,
+ is_internal=is_internal,
+ area=area[0],
+ sub_area=area[1],
+ sig=BuiltinSig(
+ params=params,
+ variadic=entry.get("variadic"),
+ return_type=return_type,
+ ),
+ lowering=lowering,
+ description=description,
+ )
+ )
- b = Builtin(
- name=name,
- canonical_name=canonical,
- in_catalog=in_catalog,
- is_internal=is_internal,
- area=area[0],
- sub_area=area[1],
- sig=BuiltinSig(
- params=params,
- variadic=sig_info.get("variadic"),
- return_type=return_type,
- ),
- lowering=lowering,
- description=description,
+ # --- language constructs (checker-resident, hand-curated) ---
+ for canonical, spec in LANGUAGE_CONSTRUCTS.items():
+ params = [
+ Parameter(
+ name=pname,
+ php_type=ptype,
+ by_ref=by_ref,
+ default=default,
+ optional=optional,
+ )
+ for (pname, ptype, by_ref, default, optional) in spec["params"]
+ ]
+ emitter_fn = spec.get("emitter_fn") or ""
+ lowering = resolve_lowering(repo, read, dispatch, lowering_dir, emitter_fn, None)
+ description = DESCRIPTION_OVERRIDES.get(canonical, spec.get("description", ""))
+ builtins.append(
+ Builtin(
+ name=canonical,
+ canonical_name=canonical,
+ in_catalog=True,
+ is_internal=False,
+ area=spec["area"][0],
+ sub_area=spec["area"][1],
+ sig=BuiltinSig(
+ params=params,
+ variadic=spec.get("variadic"),
+ return_type=spec["return_type"],
+ ),
+ lowering=lowering,
+ description=description,
+ )
)
- builtins.append(b)
+ # Deterministic order for reproducible JSON.
+ builtins.sort(key=lambda b: b.canonical_name)
return builtins
def main_with(repo_root: Path, out: Path) -> int:
+ """Build the registry from ``repo_root`` and write the JSON registry to ``out``."""
builtins = build_registry(repo_root)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(
@@ -989,6 +621,7 @@ def main_with(repo_root: Path, out: Path) -> int:
def main() -> int:
+ """CLI entry point: parse the registry and write ``builtin_registry.json``."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[3])
parser.add_argument("--out", type=Path, default=None)
@@ -999,6 +632,7 @@ def main() -> int:
def _builtin_to_dict(b: Builtin) -> dict:
+ """Serialize a :class:`Builtin` to the JSON schema consumed by the renderer."""
return {
"name": b.name,
"canonical_name": b.canonical_name,
@@ -1039,5 +673,3 @@ def _builtin_to_dict(b: Builtin) -> dict:
if __name__ == "__main__":
sys.exit(main())
-
-
diff --git a/scripts/docs/elephc_builtins/registry.py b/scripts/docs/elephc_builtins/registry.py
index fe3ca78e6c..d95e11156c 100644
--- a/scripts/docs/elephc_builtins/registry.py
+++ b/scripts/docs/elephc_builtins/registry.py
@@ -35,7 +35,7 @@
]
-# Sub-area mapping: file path under src/codegen_ir/lower_inst/builtins/ or
+# Sub-area mapping: file path under src/codegen/lower_inst/builtins/ or
# src/types/checker/builtins/ → (area, sub_area). When multiple files match
# the same key, the first one wins.
AREA_BY_FILE: Dict[str, Optional[Tuple[str, str]]] = {
@@ -508,6 +508,7 @@
"rand": ["int", "int"],
"mt_rand": ["int", "int"],
"random_int": ["int", "int"],
+ "random_bytes": ["int"],
"is_nan": ["float"],
"is_finite": ["float"],
"is_infinite": ["float"],
@@ -1213,8 +1214,6 @@ def slug(name: str) -> str:
"next": "mixed",
"prev": "mixed",
"reset": "mixed",
- # in_array returns the value if found, false otherwise → mixed.
- "in_array": "mixed",
# String functions with a concrete string return type.
"addslashes": "string",
"bin2hex": "string",
@@ -1268,98 +1267,6 @@ def slug(name: str) -> str:
}
-# Hand-curated by-reference parameter overrides. signatures.rs does not always
-# mark parameters that PHP passes by reference; these overrides make the
-# generated docs match the actual PHP calling convention.
-REF_PARAM_OVERRIDES: Dict[str, List[bool]] = {
- "exec": [False, True, True],
- "is_callable": [False, False, True],
- "passthru": [False, True],
- "preg_match_all": [False, False, True],
- "preg_replace": [False, False, False, False, True],
- "preg_replace_callback": [False, False, False, False, True, False],
- "str_ireplace": [False, False, False, True],
- "str_replace": [False, False, False, True],
- "system": [False, True],
- "stream_socket_client": [False, True, True, False, False],
- "stream_socket_server": [False, True, True],
-}
-
-
-# Hand-curated optional-parameter overrides. Each entry is a list parallel to
-# the parameter list: None = required, string = optional with that PHP default.
-OPTIONAL_PARAM_OVERRIDES: Dict[str, List[Optional[str]]] = {
- "file_put_contents": [None, None, "0", "null"],
- "fputcsv": [None, None, "','", "'\"'", "'\\\\'", "'\\n'"],
- "hash": [None, None, "false", "[]"],
- "hash_file": [None, None, "false", "[]"],
- "hash_init": [None, "0", "''", "[]"],
- "is_callable": [None, "false", "null"],
- "phpversion": ["null"],
- "preg_replace": [None, None, None, "-1", "null"],
- "preg_replace_callback": [None, None, None, "-1", "null", "0"],
- "print_r": [None, "false"],
- "rmdir": [None, "null"],
-}
-
-
-# Hand-curated parameter-name overrides. signatures.rs sometimes uses generic
-# names like `value`; these match the PHP manual names.
-PARAM_NAME_OVERRIDES: Dict[str, List[Optional[str]]] = {
- "array_chunk": [None, None, "preserve_keys"],
- "array_column": [None, None, "index_key"],
- "array_keys": [None, "filter_value", "strict"],
- "array_rand": [None, "num"],
- "array_reverse": [None, "preserve_keys"],
- "array_slice": [None, None, None, "preserve_keys"],
- "array_splice": [None, None, None, "replacement"],
- "array_unique": [None, "flags"],
- "array_walk": [None, None, "arg"],
- "arsort": [None, "flags"],
- "asort": [None, "flags"],
- "base64_decode": [None, "strict"],
- "exec": [None, "output", "result_code"],
- "fgetcsv": [None, None, None, "enclosure", "escape"],
- "fgets": [None, "length"],
- "fwrite": [None, None, "length"],
- "getenv": [None, "local_only"],
- "glob": [None, "flags"],
- "html_entity_decode": [None, "flags", "encoding"],
- "htmlentities": [None, "flags", "encoding", "double_encode"],
- "htmlspecialchars": [None, "flags", "encoding", "double_encode"],
- "is_callable": [None, "syntax_only"],
- "krsort": [None, "flags"],
- "ksort": [None, "flags"],
- "mkdir": [None, "permissions", "recursive", "context"],
- "nl2br": [None, "use_xhtml"],
- "passthru": [None, "result_code"],
- "preg_replace": [None, None, None, "limit"],
- "preg_replace_callback": [None, None, None, "limit"],
- "range": [None, None, "step"],
- "rename": [None, None, "context"],
- "rsort": [None, "flags"],
- "scandir": [None, "sorting_order", "context"],
- "sort": [None, "flags"],
- "stream_context_get_options": ["stream_or_context"],
- "stream_filter_append": [None, "filter_name", "mode"],
- "stream_filter_prepend": [None, "filter_name", "mode"],
- "stream_socket_client": [None, "error_code", "error_message", "timeout", "flags"],
- "stream_socket_server": [None, "error_code", "error_message"],
- "system": [None, "result_code"],
- "fputcsv": [None, None, None, None, "escape", "eol"],
- "gzcompress": [None, None, "encoding"],
- "gzdeflate": [None, None, "encoding"],
- "hash": [None, None, None, "options"],
- "hash_file": [None, None, None, "options"],
- "hash_init": [None, None, None, "options"],
- "is_callable": [None, "syntax_only", "callable_name"],
- "phpversion": ["extension"],
- "preg_replace": [None, None, None, "limit", "count"],
- "preg_replace_callback": [None, None, None, "limit", "count", "flags"],
- "rmdir": [None, "context"],
-}
-
-
# Hand-curated one-line descriptions for the user-facing pages. When a
# builtin has no override here, the renderer falls back to the first line of
# the lowering function's `///` doc comment, if available.
@@ -1402,13 +1309,3 @@ def slug(name: str) -> str:
"Calls the native PHAR compression-control bridge and returns whether the update succeeded.",
],
}
-
-
-# Hand-curated variadic overrides. signatures.rs says `fixed(&["value"])`
-# for these but PHP actually accepts any number of arguments.
-# Note: printf/sprintf/fprintf are already variadic in signatures.rs,
-# so they do not need an override.
-VARIADIC_OVERRIDES: Dict[str, str] = {
- "var_dump": "values",
- "print_r": "values",
-}
diff --git a/scripts/docs/elephc_builtins/render.py b/scripts/docs/elephc_builtins/render.py
index 8677b7b06b..71792fe8be 100644
--- a/scripts/docs/elephc_builtins/render.py
+++ b/scripts/docs/elephc_builtins/render.py
@@ -521,7 +521,8 @@ def area_dir(base: Path, b: dict) -> Path:
if internals_path.exists() and not force:
skipped += 1
else:
- internals_path.write_text(render_internals(b, idx, repo), encoding="utf-8")
+ content = render_internals(b, idx, repo).rstrip() + "\n"
+ internals_path.write_text(content, encoding="utf-8")
written += 1
# Compiler-only builtin entries are tracked outside the PHP-visible catalog.
diff --git a/scripts/gen_windows_codegen_allowlist.py b/scripts/gen_windows_codegen_allowlist.py
new file mode 100755
index 0000000000..33e8b7ea4c
--- /dev/null
+++ b/scripts/gen_windows_codegen_allowlist.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python3
+"""Manage the Windows (windows-x86_64) codegen no-regression parity gate.
+
+This single tool owns every piece of the Windows codegen parity gate that is
+expressed in data rather than YAML:
+
+ * ``generate`` — (re)builds the two in-repo source-of-truth lists from a
+ ``cargo nextest list`` JSON dump (the *runnable* set) and a set of failing
+ test names (JUnit reports and/or plain-text lists):
+
+ allow_list = runnable_ci_tests - known_windows_failures
+ known_failures = the failing set (sorted)
+
+ * ``gate`` — the CI post-step. Given the allow-list and the *actual* failing
+ tests of a Windows-under-wine run (per-shard JUnit reports), it computes
+ ``regressions = actual_failures ∩ allow_list`` and exits non-zero iff that
+ intersection is non-empty. Tests that are NOT in the allow-list (the known
+ failures and any brand-new / native-only tests) can never fail the gate.
+
+Both subcommands share the same JUnit / plain-text failure parsing, so the
+"what counts as a failing test name" definition lives in exactly one place.
+
+Determinism: every emitted list is sorted with Python's default (Unicode
+code-point) ordering, independent of the host locale, so regenerating from the
+same inputs always produces byte-identical files.
+
+Usage examples::
+
+ # Regenerate both lists from a fresh nextest list + the failures dump.
+ python3 scripts/gen_windows_codegen_allowlist.py generate \\
+ --list-json nextest_list.json \\
+ --failures win_failing.txt
+
+ # Regenerate from the 16 per-shard JUnit artifacts of a parity CI run.
+ python3 scripts/gen_windows_codegen_allowlist.py generate \\
+ --list-json nextest_list.json \\
+ --junit artifacts/*/junit.xml
+
+ # CI gate step for one shard.
+ python3 scripts/gen_windows_codegen_allowlist.py gate \\
+ --allowlist tests/codegen/support/windows_codegen_allowlist.txt \\
+ --junit target/nextest/ci/junit.xml \\
+ --shard 3/16
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import os
+import sys
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+# Default in-repo locations, resolved relative to the repository root (this
+# script lives in `/scripts/`).
+REPO_ROOT = Path(__file__).resolve().parent.parent
+SUPPORT_DIR = REPO_ROOT / "tests" / "codegen" / "support"
+DEFAULT_ALLOWLIST = SUPPORT_DIR / "windows_codegen_allowlist.txt"
+DEFAULT_KNOWN_FAILURES = SUPPORT_DIR / "windows_codegen_known_failures.txt"
+
+# The nextest test binary whose fixtures make up the parity suite.
+CODEGEN_SUITE_ID = "elephc::codegen_tests"
+
+
+def _local_tag(tag: str) -> str:
+ """Return an XML element tag without its ``{namespace}`` prefix, if any."""
+ return tag.rsplit("}", 1)[-1]
+
+
+def parse_junit_failures(path: Path) -> tuple[set[str], int]:
+ """Parse a nextest JUnit report into (failing_test_names, tests_run).
+
+ A ```` counts as failing iff it has a direct child element named
+ ``failure`` or ``error`` (nextest emits ``