Meta-object Protocol

A Meta-object Protocol (MOP) is an API that exposes a language’s own object system as a first-class, programmable layer. Through a MOP, user code can redefine how classes are created, how methods are looked up and dispatched, how instances are allocated, and how inheritance is resolved: extending or replacing the object model itself from within the language. The canonical MOP is CLOS (Common Lisp Object System), which exposes generic functions such as make-instance, compute-applicable-methods, and slot-value as overridable entry points.

Python’s data model, with __new__, __init_subclass__, __class_getitem__, metaclasses, and Ruby’s BasicObject and method_missing give partial MOP-like access. Smalltalk’s entire class hierarchy is itself a set of Smalltalk objects, making it fully reflective by design.

PHP does not have a Meta-object Protocol. PHP’s object system is implemented in C (the Zend Engine) and is not programmable from userland. PHP does provide several approximations:

  • The Reflection API, ReflectionClass, ReflectionMethod, ReflectionProperty, exposes read access to the object model at runtime, but cannot modify it.

  • Magic methods, (__get, __set, __call, __callStatic, __isset, __unset, intercept individual property and method access operations, giving hook points that resemble parts of a MOP.

  • class_alias() lets user code create alternative names for classes, but cannot alter dispatch behaviour.

  • Closure::bind() and Closure::bindTo() allow closures to be rebound to a different object scope, approximating dynamic method injection.

  • Libraries such as ocramius/proxy-manager implement lazy-loading and interception proxies on top of these primitives, simulating part of what a MOP would provide natively.

<?php

    // PHP Reflection: read-only introspection of the object model.
    // A true MOP would also allow redefining dispatch and instantiation.

    $rc = new ReflectionClass(ArrayObject::class);

    foreach ($rc->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
        echo $method->getName() . "\n";
    }

    // Magic methods: intercept property/method access — limited MOP hooks.
    class DynamicEntity {
        private array $data = [];

        public function __get(string $name): mixed {
            return $this->data[$name] ?? null;
        }

        public function __set(string $name, mixed $value): void {
            $this->data[$name] = $value;
        }

        public function __call(string $name, array $args): mixed {
            // intercept unknown method calls — not full method-dispatch override
            throw new \BadMethodCallException("No method: $name");
        }
    }

    // Rebind a closure to a different object scope (approximates method injection)
    $greet = Closure::bind(
        static function (string $suffix): string { return self::class . $suffix; },
        null,
        DynamicEntity::class
    );

?>

Documentation

See also The Art of the Metaobject Protocol (Kiczales et al.), PHP Reflection API and ocramius/proxy-manager.

Related : Reflection, Magic Methods, Metaprogramming, Homoiconicity, Proxy, Closure, __get() Method, __call() Method, Metaclass

Related packages : ocramius/proxy-manager