# Native Runtime Migration Notes - 2026-05-24

These notes capture the php2vala/runtime decisions from the Pylon native-runtime work.

## Optimizer Levels

The Meson helper may expose php2vala optimizer levels as `opt=`:

- `opt=1`: zval-first / no local static optimization. This must call `*_zval` runtime functions.
- `opt=2`: local variable optimization inside one PHP function/method. It may replace safe zval temporaries with native Vala types.
- `opt=3`: file/function level optimization. It may cross local function/method boundaries inside the generated file when php2vala can prove the static shape.
- `opt=4`: cross-file/library optimization. It may consume installed PHP stub metadata and runtime VAPIs for static ABI calls across generated library boundaries.

`opt=1` must pass php2vala's no-optimize switch. It is intentionally useful as the compatibility baseline and must not require static-only runtime functions.

## Runtime ABI Rule

Every native PHP function that php2vala can call should provide both ABI shapes when practical:

- `<php_function>_zval`: PHP-compatible dynamic ABI for opt=1 and fallback paths.
- `<php_function>_static`: typed optimizer ABI for opt=2..4.
- `<php_function>_<shape>_static`: typed ABI for a specific safe shape, for example `preg_match_bool_static`, `preg_match_all_set_order_static`, `preg_split_string_list_static`, `rtrim_chars_static`, `pack_string_static`, or `sprintf_string_int_static`.

The zval ABI remains the compatibility contract. Static ABI is additional and must not be the only callable form for a normal PHP function.

## Native Runtime Package Placement

Functions used by Pylon and similar CLI tools should live in native packages by PHP extension/domain:

- `valaphp-runtime-std`
  - general standard functions: `date`, `sleep`, `usleep`, `count`, `is_numeric`, `ctype_digit`
  - strings: `trim`, `ltrim`, `rtrim`, `strlen`, `stripos`, `str_replace`, `str_starts_with`, `substr`
  - process/shell: `exec`, `shell_exec`, `escapeshellarg`, `sprintf`, `implode`
  - arrays/scalars where no better extension exists: `array_slice`, `min`, `max`
  - streams/resources: `fopen`, `fclose`, `fread`, `fwrite`, `fflush`, `is_resource`, `stream_socket_client`, `stream_set_blocking`, `stream_select`
  - regex through GLib.Regex for now: `preg_match`, `preg_match_all`, `preg_replace`, `preg_split`
  - binary helper for current Pylon use: `pack` with `H*`
- `valaphp-runtime-json`
  - `json_encode`, `json_last_error_msg`
- `valaphp-runtime-math`
  - numeric/math functions such as `round`, `floor`
- `valaphp-runtime-embed`
  - PHP/libphp-only fallback and functions that should not be natively reimplemented, for example tokenizer APIs `token_get_all` and `token_name`.

Regex currently does not need a separate runtime package. Keep it in `std` while the implementation is GLib.Regex based. A separate regex package only makes sense if a PCRE dependency or broader PHP-compatible regex surface becomes its own concern.

## Resource Values

Embed `StreamResource` stays in `valaphp-runtime-embed` because it wraps libphp resources.

Native stream resources belong in `valaphp-runtime-std`. For zval ABI compatibility, `valaphp-runtime-core` may carry a generic resource/object slot in `Php.zval`; the concrete native stream type remains owned by `std`. This lets opt=1 keep `Php.zval` variables while native stream functions can unwrap their own resource type.

## php2vala Resolution

php2vala should keep using runtime VAPIs as the source of truth. It should not hard-code long PHP function mapping tables where a runtime VAPI symbol can describe the call.

Resolution order:

- native runtime: use native `*_zval` for zval mode; use matching native `*_static` or shaped static symbol for optimized mode.
- embed runtime: use embed `*_zval` for zval mode; use embed static only when present and shape-compatible.
- both runtimes: prefer native; fall back to embed only when native does not provide the requested ABI.

Diagnostics should name the missing final ABI clearly, not just an intermediate attempt.

## php2vala Function Special-Case Audit

The remaining PHP-name checks in php2vala should be split into these categories:

- PHP syntax/constructs that may stay in php2vala: `array`, `isset`, `empty`, `exit`, `die`, `echo`, constants such as `STREAM_CLIENT_CONNECT`, `PREG_SET_ORDER`, `JSON_PRETTY_PRINT`, and PHP scalar type names used for casts/type mapping.
- Call-shape/default-argument logic that should move into runtime VAPI metadata or runtime overload/shaped symbols: `print_r`, `token_get_all`, `json_decode`, `round`, `microtime`, `substr`, `number_format`, `class_exists`, and similar functions where php2vala currently fills defaults or rewrites arguments.
- Optimizer shortcuts that are acceptable only when they are generic by VAPI type, not by PHP name: static return wrapping, `out` parameter emission, source-location argument order, resource/object parameters, and `Gee` collection `.size`.
- Project-specific inference should be removed or replaced by metadata. The historic `get_total_mem_zram`/`ret` and generic `ret`/`ret2` array-shape guesses are not runtime ABI and should not remain as permanent php2vala behavior.

Specific rule from the Pylon work: no php2vala special handling for `rtrim()` or `preg_match()`. Their zval/static/out-parameter call forms must come entirely from the runtime VAPI signatures, e.g. `rtrim_chars_zval`, `preg_match_zval`, `preg_match_static`, and shaped static variants.

## Pylon Build Lessons

Pylon native builds exposed two separate issues:

- If php2vala is newer than the installed runtime VAPIs/headers, generated `PhpNative.*` calls fail later as implicit C declarations. Install/update the matching runtime packages before testing generated consumers.
- `json_encode` requires `valaphp-runtime-json`. Pylon's `meson.build.pw` must include `json` when native or both-runtime output can call JSON helpers.

