# TODO - phpef-runtime-embed

## Current state

- Vala library builds and installs header/VAPI/deps/pkg-config metadata.
- Public runtime name is stable: `phpef-runtime-embed`.
- Private pwphpbytecode dependency is selected via `-Dphpslot` and `-Dabiver`.
- No dependency on `valaphp-runtime-*`.
- Current regressions start/stop the pwphpbytecode engine, evaluate a tiny PHP snippet, and compare core zval arithmetic against PHP/Zend eval.

## Near-term API work

- Keep `get_defined_constants(true)` usable for native-runtime build generators. Embed now preserves nested Zend arrays as `PhpefRuntimeCore.Zval` arrays and exposes `get_defined_constants` as a normal generated `phpfunc_get_defined_constants(PhpefRuntimeCore.Zval? categorize = null)` wrapper, not as zendcall-only. Add regression coverage if this conversion path changes.
- Keep direct pwphpbytecode/Zend calls behind this package boundary.
- Add explicit result-capturing helpers for reference tests instead of relying on stdout capture forever.
- Add bridge functions between `PhpefRuntimeCore.Zval` and Zend/zval-backed calls once the conversion boundary is designed.
- Clarify lifecycle constraints: multiple starts/stops, process-global PHP state, thread-safety, and error handling.

## Later

- Embed-backed call helpers for standard PHP functions.
- Diagnostics that name the final implementation path: embed vs native helper.
- Slot/ABI compatibility tests across supported pwphpbytecode packages.

## Test conventions

- Prefer Vala tests for runtime semantics and cross-runtime checks, because the runtimes currently expose a public VAPI and are implemented in Vala.
- Use C tests only for explicit C ABI/header/pkg-config smoke coverage.
- Keep PHP/Zend reference comparisons in this package, not in `phpefc/tests`.

## PHP call wrapper convention

Use `phpfunc_*` for generated-code-facing wrappers of PHP functions/behaviors on the embed side.

- Keep Zend/libphp-dependent behavior here or in future `phpef-runtime-embed-*` packages.
- Keep pure language/core helpers in `phpef-runtime-core`.
- Avoid generated code calling raw pwphpbytecode/Zend APIs directly.

Typed fast-path wrappers are part of the public VAPI/ABI surface, so their names must be stable across builds and versions:

- Base dynamic/Zval-compatible wrappers keep the canonical name, e.g. `phpfunc_echo`.
- Do not use sequence suffixes such as `_s1`, `_s2`, `_s3`; they are not stable enough for compiled artifacts.
- Use signature-based typed names instead:

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

  Example: `phpfunc_echo_typed_void_arg1_string`.
- PHPEFC groups the base wrapper (`phpfunc_echo`) with typed candidates (`phpfunc_echo_typed_...`); later optimizer passes choose by argument/return types while `valacodegen` stays dumb.

## Developer diagnostic: function_exists for missing wrappers

When phpefc resolves a generic PHP function call to a `phpfunc_*` wrapper and the wrapper is missing from the VAPI, the embed runtime should eventually provide a diagnostic helper backed by PHP/Zend / `pwphpbytecode` to answer:

```php
function_exists($functionName)
```

This lets the compiler/developer tooling distinguish:

- PHP function exists but phpef has no wrapper yet → implementation TODO;
- PHP function does not exist in the selected PHP runtime/extension set → unavailable function/source diagnostic.

This belongs on the embed/dev-diagnostic side because it depends on the selected PHP runtime and loaded extensions.

<!-- phpef-runtime-embed-next-2026-06-10 -->
## Current next steps snapshot (2026-06-10)

Function-wrapper generation is in place. Next work:

1. Decide how to share generated function metadata with `phpefc` resolver diagnostics.
   - Current resolver reads VAPI wrappers and derives min/max arity from Vala signatures.
   - A later metadata file could avoid reparsing VAPI and preserve extension/source details.
2. Decide extension gating model.
   - Current default inventory is `Core,standard`.
   - Debug inventories already split included vs not-included functions by extension.
3. Keep helper install mass out of the package.
   - `src/phphelpers/meson.build.pw` should stay minimal.
   - Do not reintroduce helper libraries unless there is a real runtime/API reason.
4. Keep CLI-only/runtime-difference functions as observed behavior, not hard-coded filters.
   - If pwphpbytecode/libphp does not expose a function that PHP CLI exposes, record it in diagnostics/inventory comparison instead of filtering it manually.
5. Improve generated wrapper shape only after tests cover it.
   - Current wrappers pass `PhpefRuntimeCore.Zval[]` into the generic embed `call_user_function()` bridge.
   - Optional/variadic signatures are readability and arity metadata; detailed PHP by-ref/default semantics still need future modeling.

## Zendcall-only policy follow-up

- Keep the zendcall-only policy visible to compiler diagnostics.
  - `SPECIAL_ZENDCALL_FUNCTIONS` is a single zendcall-only policy list: no automatic normal PHP-name resolver match, but explicit `zendcall_*` remains available.
  - Examples: `get_included_files`, `function_exists`, `sort`, `array_pop`, `compact`, `current`, `array_map`.
  - `phpefc` should eventually explain this distinction when source calls the normal PHP name and only a `phpfunc_zendcall_*` wrapper exists.
- Replace the temporary conservative policy with richer generated metadata.
  - Desired flags include `by_ref`, `has_array`, `has_callable`, `has_resource_like`, `needs_scope`, and `wrapper_status`.
  - Keep ordinary `phpfunc_*` exposure limited to functions whose current bridge semantics are faithful.

## Typed call follow-up

- Do not reintroduce generated per-function `_typed_` wrappers that only pack native values into Zvals and call `call_php_function()`.
- Extend the current static typed C-call boundary beyond the initial arity-1 `string -> string` / `string -> int` slice when return/argument semantics are modeled. Metadata should remain `typed_fast_path static_php_call`, not `typed_fast_path native`.
- Add per-function native fast paths behind stable `phpfunc_<name>_typed_<return>_argN_<type>` names only when their implementation is actually native and metadata says `typed_fast_path native`.
- Keep skipping unions/nullables, mixed/object/resource/callable, by-reference args, variadics, arrays, zendcall-only cases, and optional/default variants until semantics are modeled explicitly.
