# PHP Exception Object Model Recommendation

Recommendation: model PHP `Throwable`/`Exception` as normal PHP objects carried in
`PhpefRuntimeCore.Zval`, while continuing to use the host/Vala exception mechanism
only as the transport for unwinding.

## Target shape

- `Zval` gets an object variant that can hold a `PhpObject` reference.
- `PhpObject` stores:
  - PHP class name and class metadata;
  - properties / internal slots;
  - object identity;
  - optional native payload for runtime-owned objects.
- `PhpThrowableObject` is a runtime-owned object shape with at least:
  - class name (`Exception`, `UnexpectedValueException`, ...);
  - message;
  - code;
  - file/line when available;
  - previous;
  - later: PHP-style trace array.
- The technical thrown value should be a small transport exception, e.g.
  `PhpefRuntimeCore.PhpThrown`, that references the Throwable Zval/object.

## Lowering rule

`throw new Exception($message, $code)` should become:

1. construct a normal PHP object/Zval for the requested exception class;
2. initialize constructor-visible state (`message`, `code`, later `previous`);
3. throw the transport exception carrying that object.

`catch (SomeException $e)` should:

1. inspect the carried PHP object with normal class hierarchy / `instanceof`
   semantics;
2. bind `$e` to the PHP object Zval;
3. run the catch block.

## Runtime/ABI boundary

- Do not expose GLib/Vala exceptions as PHP-visible values. They are only the
  unwinding mechanism.
- The thrown payload must always be a PHP `Throwable` object in a `Zval`, so PHP
  code can catch it, inspect it, pass it around, and later reflect on it.
- Native/runtime helpers may allocate specialized internal slots, but property and
  method behavior must go through the same object/class machinery as user objects.

## Migration path

1. Keep existing helper-based fixture behavior green.
2. Introduce `PhpObject` / `PhpThrowableObject` in runtime-core behind the current
   helper API.
3. Change helper internals to create/carry Throwable objects instead of ad-hoc
   metadata strings.
4. Move `$e->getMessage()` / `$e->getCode()` from special helpers to ordinary
   method dispatch once object/class dispatch can cover internal runtime classes.
5. Replace `exception_is_a()` with the shared class hierarchy / `instanceof`
   implementation.

## Why this direction

- It preserves PHP semantics: caught values are PHP objects, not host errors.
- It keeps Vala/GLib exceptions as an implementation detail for control flow.
- It avoids inventing a parallel exception model that later blocks object,
  property, method, and `Throwable` compatibility.
- It lets stacktrace/debug metadata attach to PHP objects without coupling the
  compiler to generated symbol-name heuristics.
