Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .claude/agents/code-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ Review against the checklist below, in this order of severity.

## 2. Version pins (blocking)

- `composer.json` must keep `"php": "~8.4.0"` and `"lisachenko/z-engine": "dev-master"`,
with root `"minimum-stability": "dev"` and `"prefer-stable": true`.
- Any diff that widens the PHP constraint (`^8.4`, `>=8.4`), drops the z-engine dev
requirement, or bypasses `Core::init()`'s version guard is rejected outright. Offsets into
engine structures are minor-version specific; loosening the pin trades a clear error for
memory corruption.
- CI must run on PHP 8.4 with `ffi.enable=1` and `opcache.jit=off`.
- `composer.json` must keep `"php": "^8.4"` and `"lisachenko/z-engine": "~8.4.2 || ~8.5.0"`
— one stable z-engine line per supported PHP minor, resolved by Composer against the
running PHP.
- Any diff that widens either constraint beyond the minors z-engine has definitions for
(`>=8.4`, a `^8.4` z-engine constraint that would span future lines), or bypasses
`Core::init()`'s version guard, is rejected outright. Offsets into engine structures are
minor-version specific; loosening the pin trades a clear error for memory corruption.
- CI must run on PHP 8.4 and 8.5 with `ffi.enable=1` and `opcache.jit=off`.

## 3. PHPStan generics honesty (blocking)

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ on:
permissions:
contents: read

# z-engine tracks one PHP minor per release line; the "8.4.x-dev || 8.5.x-dev"
# constraint lets Composer resolve the line matching each runner's PHP, so the
# suite runs on PHP 8.4 and 8.5 in parallel.
# z-engine tracks one PHP minor per release line; the "~8.4.2 || ~8.5.0" constraint
# lets Composer resolve the stable line matching each runner's PHP, so the suite
# runs on PHP 8.4 and 8.5 in parallel.
jobs:
tests:
name: Tests (PHP ${{ matrix.php }})
Expand Down
38 changes: 21 additions & 17 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ structures (`zend_class_entry`, `zval`, `zend_object_handlers`) by byte offset,
those offsets change on every PHP minor release. Running against the wrong minor
does not throw a nice exception; it reads and writes the wrong memory.

That is why z-engine is required as **`8.4.x-dev || 8.5.x-dev`**: Composer resolves
the line matching the running PHP (the `8.4` branch on PHP 8.4, `master` — aliased
`8.5.x-dev` — on PHP 8.5). `ZEngine\Core::init()` (called from `bootstrap.php`)
That is why z-engine is required as **`~8.4.2 || ~8.5.0`**: Composer resolves the
release line matching the running PHP (`8.4.x` on PHP 8.4, `8.5.x` on PHP 8.5 — each
tag declares its own `~8.4.0`/`~8.5.0` platform requirement, so only one line can
ever satisfy a given runtime). `ZEngine\Core::init()` (called from `bootstrap.php`)
enforces the exact match and aborts with a clear message. **Never "fix" an
initialization failure by loosening the constraints past the minors z-engine has
definitions for, skipping `Core::init()`, or defeating the guard.** If the
Expand All @@ -36,13 +37,15 @@ process it spawns:

- `ffi.enable=1` — FFI cannot be turned on at runtime.
- `opcache.jit=off` — the JIT rewrites the very executor internals z-engine hooks.
- `error_reporting=E_ALL & ~E_DEPRECATED` — z-engine `dev-master` still declares
implicitly nullable parameters (e.g. `ZEngine\Type\OpLine::__construct()`), which
PHP 8.4 reports as a deprecation. PHPUnit's `.phpt` runner forces
`display_errors=1`, so without this the dependency's deprecation is prepended to
the captured output of **every** test and each `--EXPECT--` block fails on noise
that has nothing to do with this library. Drop the suppression once z-engine
declares those parameters `?Type`.
- `error_reporting=E_ALL & ~E_DEPRECATED` — a guard against dependency deprecations
leaking into captured output. PHPUnit's `.phpt` runner forces `display_errors=1`,
so a deprecation raised by a dependency is prepended to the captured output of
**every** test and each `--EXPECT--` block fails on noise that has nothing to do
with this library. The specific offender that motivated it — z-engine declaring
implicitly nullable parameters (e.g. `ZEngine\Type\OpLine::__construct()`) — is
fixed as of the stable releases (`~8.4.2 || ~8.5.0`), and the suite passes with
`error_reporting=E_ALL` forced on both minors, so the line is now belt-and-braces
rather than a requirement.

CI supplies the FFI and JIT pair as `ini-values` on the PHP setup step, and **every
`.phpt` file carries its own `--INI--` section** — all three lines — so the child
Expand Down Expand Up @@ -190,13 +193,14 @@ proposing a change rather than hand-formatting.

## Dependency policy

- `lisachenko/z-engine` is required as **`8.4.x-dev || 8.5.x-dev`** — one dev line
per supported PHP minor, resolved by Composer to match the running PHP. Those are
development branches, so the root `composer.json` also carries
`"minimum-stability": "dev"` with `"prefer-stable": true` — Composer resolves
development stability only at the root level, so consumers need the same pair.
- `lisachenko/z-engine` is required as **`~8.4.2 || ~8.5.0`** — one **stable** release
line per supported PHP minor, resolved by Composer to match the running PHP. The
tilde is deliberate: it admits patch releases within a line (`8.4.3`, `8.5.1`) but
never the next minor line, which would be built for a PHP this package does not
claim to support.
- The root `composer.json` no longer carries `"minimum-stability": "dev"` /
`"prefer-stable": true` — nothing in `require` is a development branch any more,
and consumers no longer need those flags to install this package's dependencies.
- PHP stays at `^8.4`, in lockstep with the set of z-engine lines this package
tracks: a new PHP minor is added here only together with the z-engine line built
for it, and never one without the other.
- When z-engine ships stable releases for the supported minors, the constraint and
the root stability flags should be tightened in a single change.
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,17 @@ The maths is plain PHP. The magic is only in getting the engine to call it.
- **PHP `^8.4`** — 8.4 and 8.5 are supported in parallel. Z-Engine reads engine structures by byte offset and those offsets change on every PHP minor release, so each minor rides its own Z-Engine line; `Core::init()` refuses to boot on a mismatch rather than corrupting memory.
- **`ext-ffi` enabled**, with `ffi.enable=1` for CLI usage.
- **x64, non-thread-safe (NTS)** build — the same platform limitations as [Z-Engine](https://github.com/lisachenko/z-engine#requirements--support-matrix).
- The **matching Z-Engine minor branch**. Composer resolves it for you from the `8.4.x-dev || 8.5.x-dev` constraint; mixing a Z-Engine built for another minor is not a configuration choice, it is undefined behaviour.
- The **matching Z-Engine minor release line**. Composer resolves it for you from the `~8.4.2 || ~8.5.0` constraint; mixing a Z-Engine built for another minor is not a configuration choice, it is undefined behaviour.

## Installation

```bash
composer require lisachenko/native-php-matrix:dev-master
```

This package requires Z-Engine as `8.4.x-dev || 8.5.x-dev` — z-engine minors track PHP minors and are not interchangeable, so Composer resolves the line matching your PHP automatically (the `8.4` branch on PHP 8.4, `master` on PHP 8.5). Those are development branches, and the package itself is consumed from `dev-master`; Composer only resolves development stability at the **root** level, so your `composer.json` needs:
This package requires Z-Engine as `~8.4.2 || ~8.5.0` — z-engine minors track PHP minors and are not interchangeable, so Composer resolves the release line matching your PHP automatically (`8.4.x` on PHP 8.4, `8.5.x` on PHP 8.5). Both are **stable tags**, so no `minimum-stability` tweak is needed for them.

```json
{
"minimum-stability": "dev",
"prefer-stable": true
}
```

Once a native-php-matrix release covering the current code is tagged, this collapses to a plain `composer require lisachenko/native-php-matrix`.
native-php-matrix itself is not tagged yet, which is why the command above asks for `dev-master`; that one dev constraint is explicit, so Composer accepts it without a root stability change. Once a native-php-matrix release is tagged, this collapses to a plain `composer require lisachenko/native-php-matrix`.

No initialization call is needed: `bootstrap.php` ships in the package's `files` autoload and sets everything up behind `require vendor/autoload.php`.

Expand Down
4 changes: 1 addition & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
"require": {
"php": "^8.4",
"ext-ffi": "*",
"lisachenko/z-engine": "8.4.x-dev || 8.5.x-dev"
"lisachenko/z-engine": "~8.4.2 || ~8.5.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.75",
"phpstan/phpstan": "^2.1",
Expand Down