# phpef-runtime-embed

Vala implementation of the embed-backed runtime bridge for `phpef` / `phpefc`.

This package is intentionally independent from `valaphp-runtime-*`. It talks directly to `pwphpbytecode`, which in turn links the selected PHP/Zend runtime.

## Public naming

`phpef-runtime-embed` has a stable public runtime name. PHP slot / pwphpbytecode ABI details are private build/link details and do not appear in produced phpef runtime filenames.

## Selecting pwphpbytecode

```bash
meson setup build -Dphpslot=8.3 -Dabiver=3
```

Meson derives private dependency `pwphpbytecode-<phpslot>-<abiver>`; for example `pwphpbytecode-8.3-3`.

## Current API surface

Current Vala/C ABI includes `engine_start`, `engine_stop`, `ensure_initialized`, `print_zend_identity`, `eval_code(debugname, code)`, embed-backed PHP functions such as `phpfunc_substr`, and language constructs such as `phpfunc_echo` / `phpfunc_print`.

`eval_code()` is used by runtime tests as a PHP/Zend semantic reference path.

## Dependencies

- private `pwphpbytecode-<phpslot>-<abiver>` bridge library
- GLib/GObject because the runtime is currently implemented in Vala
- `phpef-runtime-core` for explicit zval bridge conversion

No dependency on `valaphp-runtime-core`, `valaphp-runtime-embed`, or `valaphp-runtime-embed-pwphpbytecode`.

## Build and test

```bash
meson setup build -Dphpslot=8.3 -Dabiver=3
CCACHE_DISABLE=1 meson test -C build --print-errorlogs
```

## Install metadata

The Meson build installs shared library, generated C header, VAPI, `.deps`, and pkg-config `.pc` metadata. Consumers should prefer `dependency('phpef-runtime-embed')` or `pkg-config --cflags --libs phpef-runtime-embed`.

## Tests

Runtime-embed owns tests that exercise the PHP/Zend embed/reference path, including the zval arithmetic comparison against `phpef-runtime-core`. Those tests intentionally do not live in `phpefc/tests`, because `phpefc/tests` is reserved for compiler-pipeline PHP inputs.

Current runtime-embed test split:

- `embed_smoke.vala`: Vala smoke test for direct pwphpbytecode-backed lifecycle/eval.
- `zval_retyping_compare.vala`: Vala semantic comparison test; compares `phpef-runtime-core` arithmetic with PHP/Zend eval via this embed runtime.
- `language_constructs_smoke.vala`: verifies embed-backed `echo`/`print` language construct wrappers.

Use C tests only when checking the installed C ABI/header surface specifically. Runtime semantic tests should prefer Vala while the runtime implementation and public VAPI are Vala-first.

The zval comparison test includes arithmetic and concat cases, comparing `phpef-runtime-core` helpers such as `add()` and `concat()` against PHP/Zend eval through the embed runtime.

## PHP call wrapper convention

Embed-side runtime packages use the shared `phpef-runtime-*` API below for PHP-level function/language wrappers. Normal PHP functions that require Zend/libphp use the generated `functions.vala` + `call_user_function()` path. PHP language constructs such as `echo` and `print` are not functions; they live in the language-construct branch and use Zend output helpers such as `zend_print_zval()`.

## Unified `phpef-runtime-*` function API

All `phpef-runtime-*` packages (`embed`, `native-*`, and future domain runtimes) use the same public Vala/VAPI naming and resolving scheme. PHPEFC must be able to resolve embed and native candidates with the same `Php2ValaFuncResolver` code path; runtime-specific packages only differ by VAPI metadata such as `typed_fast_path native` vs `typed_fast_path static_php_call`.

### Zval-compatible PHP wrappers

```text
phpfunc_<phpname>(Zval...)
```

- Return type is `PhpefRuntimeCore.Zval`.
- Arguments are `PhpefRuntimeCore.Zval` / `Zval?` as needed for PHP default/nullability behavior.
- Semantics should match PHP/Zend for the selected runtime. Native wrappers must be covered against the embed wrapper before they are treated as interchangeable.
- Public `phpfunc_*` wrappers are resolver/ABI endpoints and should not call each other. Shared implementation code belongs in internal, non-VAPI helper functions/files.
- Example: `phpfunc_microtime(Zval? as_float = null)`.

### Typed fast paths

```text
phpfunc_<phpname>_typed_<returntype>_arg1_<type>_arg2_<type>...
```

- Return and argument types are scalar Vala types (`string`, `int`, `double`, `bool`, `void`, ...), normalized by PHPEFC for matching.
- Multiple typed variants for the same PHP function are allowed when each variant has a distinct signature suffix.
- The VAPI must carry an adjacent metadata comment before PHPEFC may optimize to the wrapper, for example:

```vala
// PHPEF typed_fast_path native; source php_function; PHP function: foo; typed_signature _typed_int_arg1_string
public static int phpfunc_foo_typed_int_arg1_string(string value);
```

For embed-backed static typed calls, use `typed_fast_path static_php_call` and include the boundary name when relevant. For native direct implementations, use `typed_fast_path native`.

### Special typed fast paths with argument-dependent return type

When the PHP return type depends on a literal argument value, encode that literal in the typed suffix:

```text
phpfunc_<phpname>_typed_<returntype>_argN_<type>_<literal>
```

Example:

```vala
// PHPEF typed_fast_path native; source php_function; PHP function: microtime; typed_signature _typed_double_arg1_bool_true; role current_time_seconds
public static double phpfunc_microtime_typed_double_arg1_bool_true();
```

Here `arg1_bool_true` describes the PHP call shape (`microtime(true)`). The native Vala wrapper does not have to receive that argument if the literal is fully represented by the selected specialization. PHPEFC derives the PHP name and typed signature from the `phpfunc_*_typed_*` name and uses the VAPI comment only as optimizer policy/metadata.

Zval/call_php_function bridges are compatibility/debug wrappers, not optimizer targets unless a generated static typed wrapper is explicitly marked with `typed_fast_path static_php_call` metadata.

<!-- phpef-runtime-embed-status-2026-06-10 -->
## Status snapshot 2026-06-10

Embed-backed ordinary PHP function wrappers are now generated at build time.

The build discovers PHP internal functions through the selected pwphpbytecode/libphp environment, writes readable debug inventories, and generates the Vala wrapper source into the build directory:

```text
build/phpef-runtime-embed-functions-included.txt
build/phpef-runtime-embed-functions-not-included.txt
build/phpef_runtime_embed_functions.vala
```

`src/phpef_runtime_embed_functions.vala` is no longer the source of truth. Generated code is included in the runtime library sources as a Meson `custom_target` output.

The generated wrappers use readable `phpfunc_*` names and parameter names derived from PHP reflection where available, for example:

```vala
phpfunc_substr(string_arg, offset, length = null)
phpfunc_trim(string_arg, characters = null)
phpfunc_printf(format, params values)
phpfunc_array_merge(params arrays)
```

Vala reserved words are sanitized, e.g. PHP `$class` becomes `class_arg`.

The helper that creates inventories and generated Vala is a build helper only:

- self-contained pwphpbytecode binary source under `src/phphelpers/`
- no helper shared library
- no helper raw library
- no installed helper headers/stubs/manpages
- helper binary is marked `install:false` after `pw-build-meson` generation

Installed runtime payload should remain only the public runtime artifacts:

```text
libphpef-runtime-embed.so
phpef-runtime-embed.h
phpef-runtime-embed.vapi
phpef-runtime-embed.deps
phpef-runtime-embed.pc
```

The default function inventory extension selection is currently controlled by Meson option:

```text
-Dphp_function_extensions=Core,standard
```

## Zendcall-only function inventory policy

Not every discovered PHP internal function is safe to expose as a normal generated `phpfunc_*` wrapper.
The generic embed bridge currently converts PHPEF `Zval` arguments into fresh Zend parameters and converts only the return value back. That is not enough for PHP functions whose semantics depend on by-reference mutation, caller scope, Zend frame/include state, PHP array identity/internal pointer state, or callbacks into PHPEF code.

Such functions are intentionally not generated under their normal PHP source name. Instead, the inventory marks them as explicit Zend escape hatches:

```text
sort                  -> phpfunc_zendcall_sort
get_included_files    -> phpfunc_zendcall_get_included_files
function_exists       -> phpfunc_zendcall_function_exists
array_map             -> phpfunc_zendcall_array_map
```

The source-level meaning is: `sort()` should not be auto-resolved by `phpefc` to the embed wrapper, but an explicit `zendcall_sort()` may still be used when the caller deliberately wants Zend/libphp behavior and accepts the semantic boundary.

The central policy list lives in `src/phphelpers/bin_phpef_runtime_embed_function_inventory.php` as `SPECIAL_ZENDCALL_FUNCTIONS`. Its `functions` entries are plain string lists, grouped by reason. The same zendcall-only path is also used for auto-detected functions with by-reference parameters or callable parameters until PHPEF has faithful inout/callback bridging.

## Typed PHP function metadata and fast paths

Keep three typed-call categories separate:

1. **Native per-function typed fast paths** use stable ABI names such as `phpfunc_<phpname>_typed_<returntype>_arg1_<type>...`, are implemented for one concrete PHP function/language construct, and are optimizer candidates only with explicit VAPI metadata such as `typed_fast_path native; source language_construct`. Current native examples are `phpfunc_echo_typed_void_arg1_string` and `phpfunc_print_typed_int_arg1_string`; their typed path avoids both Zval conversion and `call_php_function()`.
2. **Generated static PHP-call typed fast paths** use `phpfunc_<phpname>_typed_<return>_arg1_<type>` wrappers that call controlled C boundaries such as `call_php_function_typed_string_arg1_string` / `call_php_function_typed_int_arg1_string`. These still cross Zend/libphp dynamically, but Vala passes/receives primitives directly and does not build `Zval[]` or call generic `call_php_function(...)`. Metadata is explicit: `typed_fast_path static_php_call; source php_function`.
3. **Normal dynamic calls** stay on `call_php_function(...)` with `PhpefRuntimeCore.Zval` arguments/results.

The build-time helper `src/phphelpers/bin_phpef_runtime_embed_function_inventory.php` may emit a narrow scalar arity-1 `_typed_` slice (`string -> string`, `string -> int`) only when the generated Vala wrapper routes through the static typed C-call boundary. Wrappers that pack `PhpefRuntimeCore.Zval.from_*`, allocate `Zval[]`, or call generic `call_php_function(...)` are generic bridges and must not be marked as typed fast paths.
