# PHPEF embedded resource/assets concept

Status: design note from 2026-06-19 discussion. Not implemented yet.

## Problem

PHP code often accesses local files through many different APIs, for example:

```php
file_get_contents("bild.jpg");
fopen("data/config.json", "rb");
readfile("asset.bin");
getimagesize("image.jpg");
```

For compiled PHPEF binaries we want selected compile-time-known local files to be embedded into the binary/package, so the resulting program can read them without requiring the original loose files at runtime.

## Preferred direction: PHP stream wrapper / scheme

Instead of special-casing every file function, PHPEF should expose an internal stream wrapper/scheme, for example:

```text
phpef-resource:///
```

Then PHPEFC can rewrite safe, compile-time-known local file literals:

```php
file_get_contents("bild.jpg")
```

into something like:

```php
file_get_contents("phpef-resource:///bild.jpg")
```

or, preferably, a canonical source-root-relative asset path:

```php
file_get_contents("phpef-resource:///assets/bild.jpg")
```

The runtime/libphp stream layer handles `phpef-resource://...`. Any PHP API that goes through streams can then work without a separate implementation:

- `file_get_contents()`
- `fopen()` / `fread()` / `fclose()`
- `readfile()`
- `file()`
- possibly `getimagesize()` and other APIs, if they use PHP streams

Functions that bypass streams and require real filesystem paths will not automatically work; PHPEF should prefer routing PHP file APIs through RuntimeCore/PHP stream abstraction.

## Avoiding `pw-build-meson` changes

Using GLib `GResource` normally needs Meson integration such as `gnome.compile_resources()`. That would likely require changing `pw-build-meson`.

To avoid that, implement the first version entirely in the PHPEF/PHPEFC build pipeline:

1. PHPEFC/build pipeline discovers embeddable assets.
2. It generates an ordinary source file already consumed by the existing compile flow, e.g. generated Vala or C.
3. That source file contains a static resource table.
4. RuntimeCore/libphp registers a `phpef-resource://` stream wrapper backed by that table.

This keeps Meson mostly unaware of resources: they are just another generated source file.

## Generated resource table idea

C is probably the simplest/most efficient representation:

```c
static const unsigned char asset_bild_jpg[] = {
  0xff, 0xd8, 0xff, /* ... */
};

typedef struct {
  const char *path;
  const unsigned char *data;
  size_t len;
} PhpefResourceEntry;

const PhpefResourceEntry phpef_embedded_resources[] = {
  { "/assets/bild.jpg", asset_bild_jpg, sizeof(asset_bild_jpg) },
};

const size_t phpef_embedded_resources_count =
  sizeof(phpef_embedded_resources) / sizeof(phpef_embedded_resources[0]);
```

Equivalent Vala is possible, but large binary arrays may be less pleasant there. C generated source with a small Vala/C API boundary is likely cleaner.

## Runtime lookup / stream wrapper

RuntimeCore/libphp should provide something conceptually like:

```c
const PhpefResourceEntry *phpef_resource_lookup(const char *path);
```

The `phpef-resource://` stream object then holds:

```text
entry pointer
current offset
length
```

Reads copy only the requested slice into the caller's buffer, similar to reading from a memory-backed file.

## Copy / zero-copy considerations

### Streaming APIs

For `fopen("phpef-resource:///...")` + `fread()`, no whole-file copy is required. The stream can keep `const uint8_t *data + len + offset` and copy only read chunks into the caller's buffer.

### `file_get_contents()`

A normal PHP string result may require a copy if PHPEF strings are currently always owned mutable buffers.

Zero-copy is possible if PHPEF `ZvalString` supports a read-only/static bytes backend:

```text
ZvalString
- pointer
- length
- owner/backend: owned buffer | static embedded bytes | GBytes-like owner
- readonly/static flag
- copy-on-write on mutation
```

Then `file_get_contents("phpef-resource:///bild.jpg")` can return a PHP string that references embedded static data directly. If code later mutates the string, RuntimeCore performs copy-on-write.

### Compression

If resources are compressed, lookup must decompress, which forces allocation/copy. For zero-copy, embed uncompressed static byte arrays.

## PHPEFC rewrite rules

Only rewrite safe cases initially:

- literal string path
- local relative path
- file exists at compile/build time
- path is inside the allowed project/source root or configured asset roots
- no URL schemes (`http://`, `data:`, etc.)
- no absolute host paths unless explicitly configured
- no dynamic variables/concatenations initially

Examples to rewrite:

```php
file_get_contents("bild.jpg");
fopen("assets/logo.png", "rb");
readfile("data/schema.json");
```

Examples not to rewrite at first:

```php
file_get_contents($path);
file_get_contents("../outside.jpg");
file_get_contents("/etc/passwd");
file_get_contents("https://example.com/x");
```

The rewrite should only happen if the asset was actually embedded. Otherwise preserve original PHP semantics.

## Path identity

Runtime resource paths should be deterministic and source-root-relative, not absolute host paths. Absolute paths may appear in debug/source maps, but not in user-facing resource URLs.

Potential canonical form:

```text
phpef-resource:///relative/path/from/source-root
```

For multiple source roots/packages, add a namespace/build-id if needed later:

```text
phpef-resource://app/relative/path
```

or:

```text
phpef-resource:///__main__/relative/path
```

## Open questions

- Exact integration point for asset discovery: PHPIR builder, ValaIR lowering, or final build manifest generation?
- Whether to rewrite only selected known functions, or all string literals passed to stream-opening APIs through a central resolver.
- How to represent embedded assets in generated source without bloating compile times too badly for large files.
- Size threshold and opt-in/opt-out configuration.
- Whether includes/templates should use the same resource wrapper or remain a separate include/class registry path.
- How to expose debugging/introspection, e.g. `phpef_resource_list()` for tests.

## Recommended implementation slice

1. Implement generated C resource table for a small fixture asset.
2. Add RuntimeCore lookup by canonical path.
3. Add a minimal read-only `phpef-resource://` stream wrapper.
4. Make `file_get_contents("phpef-resource:///asset.txt")` work first.
5. Teach PHPEFC to rewrite `file_get_contents("asset.txt")` only when the file exists and was embedded.
6. Add compare tests for binary-safe content, missing resource fallback/error, and non-rewritten dynamic path.
7. Later extend to `fopen`/`fread`, `readfile`, and other stream consumers.

## Main conclusion

Use a PHP-compatible stream wrapper (`phpef-resource://`) plus a generated embedded resource table. This avoids touching every PHP file function and can avoid changing `pw-build-meson` by keeping resources as ordinary generated source in the existing PHPEF build pipeline.
