# valaphp-runtime-embed Static ABI Rules

## Core rule

`*_static` functions are the php2vala optimizer ABI.

They must satisfy both constraints:

- Always execute PHP behavior through libphp / Zend.
- Never construct or pass `Php.zval` in the static Vala call path.

This keeps PHP semantics while preserving optimizer performance.

## ABI split

- `*_static`: native Vala parameters and native Vala return types only (`string`, `int`, `double`, `bool`, `void`, typed collections).
- `*_zval`: PHP-compatible mixed ABI using `Php.zval`.
- Functions with mixed or input-dependent PHP values belong in `*_zval`, unless a concrete typed static variant exists.

Do not add a `*_static` function that accepts `Php.zval` or internally wraps native values with `Php.zval.string_value()`, `Php.zval.integer()`, `string_zval()`, `int_zval()`, etc.

## zval public ABI

The embed runtime should expose named `*_zval` wrappers for PHP functions, even when the implementation is mechanically generated and delegates to the generic zval call helper.

This is intentional: manual Vala users should be able to call stable, discoverable symbols such as `PhpEmbed.md5_zval(...)` and `PhpEmbed.strtoupper_zval(...)` instead of passing function names as strings.

php2vala should inspect the loaded `.vapi` dynamically and emit a direct named `PhpEmbed.<function>_zval(...)` call only when the wrapper exists. It should not generate stringly calls such as `PhpEmbed.call_zval_params("function_name", ...)`.

`embed_generated_zval.vala` is a Meson build artifact, not a checked source file. Meson builds `tools/generate_embed_zval_wrappers.c`, runs it during the compile, and the generator calls `get_defined_functions()` through embedded libphp to create the named wrappers for the active PHP slot.

## Embed implementation

Static embed functions should call small C bridge helpers that convert native C/Vala values directly to Zend `zval` parameters and invoke libphp:

- Use `call_user_function()` for regular PHP functions.
- Use Zend output/write APIs for constructs like `echo`.
- Use Zend eval/engine handling for constructs like `exit` / `die`.
- If a helper captures PHP output with an output buffer, print the captured buffer to the console after the function call unless PHP semantics explicitly requested a returned string instead.

The Vala static wrapper may perform error handling and type-safe return handling, but it must not create `Php.zval` values.

## Optimizer mapping

php2vala may map optimized calls to `PhpEmbed.*_static` only when the static function has a native ABI.

Mixed functions such as `json_encode`, `serialize`, `print_r`, `var_dump`, `var_export`, `count`, `is_*`, `gettype`, `strval`, `intval`, and `boolval` must stay on the `*_zval` path unless a separate typed static variant is deliberately introduced.

## Performance intent

The static path is intended to avoid:

- `Php.zval` allocation/conversion in generated optimized Vala.
- `Php.zval` array packing before calling libphp.
- Static wrappers that only delegate to zval wrappers.

The only conversion for static calls should happen in the C bridge at the libphp boundary.

## Development TODO

- Add `*_static` wrappers for generated `*_zval` PHP function wrappers by priority.
  Start with functions used by benchmarks and common translated code paths.
- Each new `*_static` wrapper must still call libphp / Zend, but must keep the
  static Vala call path free of `Php.zval`.
- Native runtime modules (`valaphp-runtime-*`) should eventually expose native
  equivalents for every PHP function that is available through the embed
  runtime, so php2vala can choose native implementations explicitly when
  requested.
