Skip to content

docs/api: README showcases Core::$executor / Core::$compiler as consumer API while AGENTS.md declares them off-limits; AbstractModule::getGlobals() returns raw CData #192

Description

@lisachenko

Contradiction 1: the engine-global wrappers

AGENTS.md ("Public APIs never leak CData") states that Core::$executor / Core::$compiler / Core::$modules are "core-layer state and not a consumer API". The README, however, documents them as the public way to do things:

  • "The object store" → Core::$executor->objectStore[spl_object_id($instance)]
  • "Abstract Syntax Tree" / "Debugger-grade introspection" → Core::$compiler->parseString(...), Core::$compiler->setOptions(...)

One of the two is wrong. Either:

  • (a) the README routes through named public methods (Core::parseString(), Core::objectStore() or similar facades), and the statics get de-emphasized, or
  • (b) AGENTS.md carves out the specific read paths it blesses for consumers (parse, object-store read) while keeping mutation core-only.

A related structural note: PHP 8.4 asymmetric visibility does not apply to static properties, so public private(set) static is not an available fix — hardening the statics means accessor methods (BC-affecting) or documentation.

Contradiction 2: module globals

src/EngineExtension/AbstractModule.php:233final public function getGlobals(): ?object with @return \FFI\CData|null. AGENTS.md: modules "must expose pure PHP-native interfaces: no public method of a module may return CData". The README's extension example ($globals = $module->getGlobals(); // FFI-backed) is the documented entry point for the whole subsystem, so this is the contract's flagship counter-example. Resolution options: a typed globals-wrapper object, or a documented, explicit exception in AGENTS.md.

Also in scope

@internal marking on the CData escape hatches is inconsistent (tracked separately, but the resolution here should set the rule): StringEntry::getRawValue(), ObjectEntry::getRawValue(), ReflectionValue::getRawValue(), ReflectionClassConstant::getRawValue(), ClosureEntry::getRawFunction(), ResourceEntry::getRawData(), OpLine::getHandler() and ReflectionExtension::getGlobals() all hand out raw CData/pointers without the @internal marker that HashTable::getRawValue() / ReflectionClass::getRawValue() carry.

This is a design decision for the maintainer, not a mechanical fix — filing for discussion. Found during the 2026-08 modernization review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions