spoova/ghost

A GhostProxy class for anonymous objects.

Maintainers

Package info

github.com/teymzz/Ghost

pkg:composer/spoova/ghost

Transparency log

Statistics

Installs: 7

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.0 2026-07-17 16:11 UTC

This package is auto-updated.

Last update: 2026-07-17 16:52:47 UTC


README

Ghost is a small PHP utility for creating dynamic, proxy-backed objects without needing a concrete class upfront. The package is centered around four components:

  • GhostFunction: defines dynamic methods and properties using closures and simple array definitions.
  • GhostDraft: a lightweight bridge that exposes the proxy id and the underlying GhostFunction payload.
  • GhostProxy: creates a proxy object and maps it to a concrete class or anonymous object.
  • GhostClass: a convenience base class that automatically performs the GhostProxy mapping and exposes a protected proxy object for initialization.

Installation

composer require spoova/ghost
<?php
require 'vendor/autoload.php';

GhostFunction

GhostFunction lets you create callable members dynamically. It accepts an array of names and optional property values. Methods are registered by calling their name as a method and passing a closure. Properties are defined by passing an array entry with a single key/value pair.

<?php
require 'vendor/autoload.php';

use Ghost\GhostFunction;

$ghost = new GhostFunction(['greet', ['name' => 'Ghost']]);
$ghost->greet(fn(string $who = 'world') => 'Hello ' . $who);

echo $ghost->greet('Ada'); // Hello Ada
echo PHP_EOL;
echo $ghost->name; // Ghost

Useful helpers:

$ghost->ghosts(GhostFunction::methods);    // list registered methods
$ghost->ghosts(GhostFunction::properties); // list registered properties

GhostFunction is flexible, but it is not IDE-friendly. Use it when you want lightweight runtime behavior, or wrap it with GhostProxy and GhostClass for more structured usage.

GhostProxy

GhostProxy creates a dynamic proxy container and maps it to a class or anonymous object. The mapping callback receives a GhostDraft instance, which can be used to retrieve the proxy id and the underlying GhostFunction data.

<?php
require 'vendor/autoload.php';

use Ghost\GhostDraft;
use Ghost\GhostFunction;
use Ghost\GhostProxy;

$ghost = new GhostFunction(['greet']);
$ghost->greet(fn(string $who = 'world') => 'Hello ' . $who);

GhostProxy::new($ghost, function (GhostDraft $draft) {
    return new class($draft) {
        public function __construct(protected GhostDraft $get) {}

        public function hello(string $name = 'world'): string {
            return $this->get->ghost()->greet($name);
        }
    };
});

$object = GhostProxy::object();
echo $object->hello('Ada');

A few important points:

  • Call GhostProxy::object() immediately after GhostProxy::new() to retrieve the mapped object.
  • The mapping callback is responsible for creating the final object instance.
  • GhostProxy::map() is used internally by GhostClass to connect the proxy state with the mapped object.

GhostDraft

GhostDraft is the minimal abstraction used by GhostProxy to hand a mapped object the proxy identity and its GhostFunction payload.

abstract class GhostDraft {
    abstract public function id(): int;
    abstract public function ghost(): GhostFunction;
}

In practice, you rarely need to implement GhostDraft yourself. GhostProxy builds a compatible instance and passes it into the mapping callback.

GhostClass

GhostClass is the recommended base class when you want a clean and structured way to use GhostProxy. Its constructor is final, so child classes should implement ghostInit() instead of overriding the constructor.

<?php
require 'vendor/autoload.php';

use Ghost\GhostClass;
use Ghost\GhostDraft;
use Ghost\GhostFunction;
use Ghost\GhostProxy;

class GreetingService extends GhostClass
{
    protected function ghostInit(): void
    {
        echo $this->proxy->greet('Bob');
    }
}

$ghost = new GhostFunction(['greet']);
$ghost->greet(fn(string $who = 'world') => 'Hello ' . $who);

GhostProxy::new($ghost, fn(GhostDraft $draft) => new GreetingService($draft));
$service = GhostProxy::object();

Notes about GhostClass

  • The protected property proxy is created automatically during construction.
  • The proxy is available inside ghostInit() and remains intact during initialization.
  • The proxy property should not be overwritten manually.

Summary

If you want a lightweight runtime-only API, use GhostFunction. If you want a cleaner object-oriented integration with IDE-aware usage, prefer GhostProxy together with GhostClass.