# phpef-runtime-core

Engine-independent runtime primitives for the `phpef` / `phpefc` userspace target family.

`phpef-runtime-core` owns the PHP-like value container, generated-code registries, dispatch helpers, exception helpers, and PHPEF stacktrace normalization that do **not** require Zend/libphp state. Embed-backed wrappers and direct PHP/Zend calls belong in `phpef-runtime-embed`; native standard-library fast paths belong in packages such as `phpef-runtime-native-standard`.

## Scope

- `Zval` fallback value container for generated PHPEF code.
- Scalar constructors/conversions for null, bool, int, float, and string.
- Bootstrap array/object support used by generated code and runtime bridges.
- PHP-style stringification and boolean conversion.
- Engine-independent arithmetic/comparison helpers currently used by compiler tests.
- User-function, class, static-method, and instance-method registries.
- Dynamic dispatch helpers for registered functions/methods/constructors.
- Runtime argument access helpers for generated handlers.
- PHP-style exception metadata helpers and uncaught-exception handling.
- PHPEF stacktrace normalization, including registered PHP frame names and embedded `.phpef_stacktrace` maps.
- Deliberate crash helper for debugger/stacktrace/strip/debug-file tests.
- Shared ABI-facing Vala/C types that can be used without Zend headers.

## Non-scope

This package must not depend on `libphp`, `libphp-embed.so`, `pwphpbytecode`, or Zend headers. Those belong in `phpef-runtime-embed`.

Ordinary PHP functions that need Zend/libphp request state should not be implemented here. Keep them in `phpef-runtime-embed` or move them to a native runtime package only when they are truly engine-independent.

## Current API surface

### Value model

The public value tag is `PhpefRuntimeCore.ValueKind` with these enum values:

- `UNDEF`
- `NULL`
- `BOOL`
- `INT`
- `FLOAT`
- `STRING`
- `ARRAY`
- `OBJECT`
- `RESOURCE`

Implemented constructors/helpers include:

```vala
Zval.undefined()
Zval.from_null()
Zval.from_bool(bool value)
Zval.from_int(int64 value)
Zval.from_float(double value)
Zval.from_string(string value)
Zval.from_array(ZvalArrayEntry[] entries)
Zval.from_object(PhpObject value)
```

Implemented conversions/accessors include:

```vala
z.is_undefined()
z.as_bool()
z.as_int()
z.as_float()
z.as_string()
z.as_object()
z.as_array_entries()
z.to_bool()
z.to_float_zval()
z.to_php_string()
```

`Zval` is currently a Vala `class`, not a `struct`. That is intentional for the bootstrap/fallback container; optimized generated code should avoid unnecessary `Zval` use when a native Vala type is statically known.

### Arrays and objects

Array support is intentionally small but real:

```vala
new ZvalArrayEntry(Zval? key, Zval value)
Zval.from_array(...)
z.array_append(value)
z.array_get_key(key)
z.array_unset_key(key)
```

Numeric auto-indexing is tracked so append after unset follows PHP-like next-index behavior for current compiler/runtime smoke cases.

Object support is currently registry-backed and minimal: `PhpObject` wraps `ClassMetadata`; `new_object()` creates registered runtime objects without constructor arguments.

### Operators/helpers

Current engine-independent helpers include:

```vala
z.concat(other)
z.add(other)
z.subtract(other)
z.multiply(other)
z.divide(other)
z.equal(other)
z.not_equal(other)
z.identical(other)
z.not_identical(other)
z.smaller(other)
z.smaller_or_equal(other)
z.greater(other)
```

The numeric parsing path is still a bootstrap implementation. Do not treat it as fully PHP-compatible yet; expand it only with PHP-vs-PHPEF comparison tests.

### Registries and dispatch

Generated PHPEF code can register and call functions/classes/methods through runtime-core:

```vala
register_function(...)
has_registered_function(...)
lookup_registered_function(...)
lookup_registered_function_by_handler(...)
call_function(...)

register_class(...)
has_registered_class(...)
lookup_registered_class(...)
new_object(...)

register_static_method(...)
has_registered_static_method(...)
lookup_registered_static_method(...)
lookup_registered_static_method_by_handler(...)
call_static_method(...)

register_instance_method(...)
has_registered_instance_method(...)
lookup_registered_instance_method(...)
lookup_registered_instance_method_by_handler(...)
call_instance_method(...)

argument(args, index)
argv(index)
```

Visibility checks for public/protected/private method metadata are implemented for the current generated-code paths.

### Exceptions and stacktraces

Runtime-core provides:

```vala
init(args)
throw_exception(class_name, message, code = null)
handle_uncaught(error)
exception_is_a(error, class_name)
exception_get_throwable(error)
exception_get_message(error)
exception_get_code(error)
create_throwable_zval(class_name, message, code, file_name = "", line = 0, previous = null)
register_stacktrace_frame(frame_symbol, display_name)
lookup_stacktrace_frame(frame_symbol)
```

Thrown PHP exceptions are represented as PHP Throwable objects carried in Zval object payloads. The Vala/GLib Error is only the unwinding transport; catch helpers inspect the carried Throwable object and keep the old helper API stable while object/class dispatch matures.

Stacktrace normalization uses explicit registered metadata. It normalizes registered PHP function/method handlers to PHP display names, hides runtime dispatcher frames, supports compiler/optimizer symbol aliases, and can read versioned binary `.phpef_stacktrace` ELF sections from the current executable. The section starts with `PHPEFSTK`; current maps use version `3` with fixed-size little-endian entries pointing to phrase records; phrase records reference `(uint16 position, uint16 length)` slices in one non-NUL-terminated endless string pool. Version `2` glossary maps, version `1` string-table maps and the old line-based `phpef-stacktrace-map-v0` reader remain compatibility fallbacks.

If no meaningful PHP frame can be recovered from a stripped/unknown stacktrace, runtime-core falls back honestly to:

```text
no stacktrace available, involved libraries:
```

### Test-only crash helper

```vala
phpfunc_phpef_test_segfault()
```

This deliberately raises `SIGSEGV`. It exists only for native crash, stacktrace, debugger, DWARF/source-line, strip/debug-file, and PHPEF debug-metadata tests. Normal generated PHP programs should not call it except in explicit tests.

## Build and test

```bash
meson setup build -Dprefix=/usr
meson compile -C build
meson test -C build --print-errorlogs
```

Build dependencies currently include:

- C compiler
- Vala compiler
- `glib-2.0`
- `gobject-2.0`
- `vala-stacktrace`
- Meson/Ninja

## Install metadata

The Meson build installs:

- shared library under `${libdir}`
- generated C header under `${includedir}/phpef-runtime-core/`
- generated Vala `.vapi` under `${datadir}/vala/vapi/`
- generated Vala `.deps` under `${datadir}/vala/vapi/`
- pkg-config `.pc` under `${libdir}/pkgconfig/`

Consumers should prefer:

```meson
dependency('phpef-runtime-core')
```

or:

```bash
pkg-config --cflags --libs phpef-runtime-core
```

Semantic comparisons that need PHP/Zend eval live outside this package, mostly in `phpef-runtime-embed` and compiler compare tests, because this package intentionally has no Zend/libphp dependency.

## Packaging notes

For the initial normal-SVN/ebuild import:

- keep the package name `phpef-runtime-core`;
- keep the Meson project version in sync with the ebuild versioning policy;
- install library/header/VAPI/deps/pkg-config metadata from Meson, not by hand;
- include `vala-stacktrace` as a runtime/build dependency as required by the linked library;
- do not add any Zend/libphp/pwphpbytecode dependency to this package.

## Architecture note

See [`docs/zval-architecture.md`](docs/zval-architecture.md) for the current `Zval` class-vs-struct decision and fallback-container rationale.
