Skip to content

Latest commit

 

History

History
177 lines (124 loc) · 4.15 KB

README.md

File metadata and controls

177 lines (124 loc) · 4.15 KB

#prototype.php

Prototypal inheritance in PHP

##Tired of classical inheritance ?

prototype.php is a proof-of-concept that demonstrate that the prototypal inheritance, which is the strenght of many modern languages like JavaScript, is also applicable to PHP.

Simply download it, require Object & FunctionObject and voilà. Theses classes are namespaced so you may want to use use statement to ease your developments:

<?php
require_once "Prototype/PrototypicalTrait.php";
require_once "Prototype/PrototypicalObject.php";
require_once "Prototype/FunctionObject.php";

use Prototype\PrototypicalObject as Object,
    Prototype\FunctionObject;

// really cool code here

##Create an object

Very simple, instanciate Prototype\Object class and treat it like a JavaScript object.

$object = new Object;

$object->aProperty = 'hello';

$object->aMethod = function () {
    return $this->aProperty . 'world!';
};

echo $object->aMethod(); // 'helloworld!'

$another_object = new Object;

$another_object->aProperty = 'strange ';

// aply is also available
echo $object->aMethod->call($another_object); // 'strange world!'

Note that call and apply are available for aMethod since we added it to $object because it was wrapped into a Prototype\FunctionObject. We could get the same result using directly an instance of Prototype\FunctionObject.

$function = new FunctionObject(function () {
    return $this->aProperty . 'world!';
});

// another syntax to create objects
$object = new Object([
    'aProperty' => 'hello',
]);

echo $function->call($object); // 'helloworld!'

As you may have noticed, we use PHP 5.4 Closures.

##Inherit objects

The first parameter of Prototype\Object::__construct can be either a structure (array) or a prototype for the new object (instance of Prototype\Object). In the later, the new object will then inherit all the members from his parent unless it redefines them.

$parent = new Object;
$parent->a = 1;
$parent->getA = function () {
    return $this->a;
};

$child = new Object($parent);
echo $child->getA(); // '1'

$child->a = 2;
echo $child->getA(); // '2'
echo $parent->getA(); // '1'

Obviously, you can use transitivity.

$object_A = new Object;
$object_B = new Object($object_A);
$object_C = new Object($object_B);

$object_A->a = 1;
$object_B->b = 2;
$object_C->c = 3;

echo $object_C->a, $object_C->b, $object_C->c; // '1 2 3'

If you want to prevent a value change in the parent to occur in the children, simply clone them !

$object_A = new Object;
$object_A->method = function () {
    return 'foo';
};

$object_B = new Object;
$object_B->prototype = clone $object_A; // another way to define the prototype

echo $object_B->method(); // 'foo'

$object_A->foo = function () {
    return 'bar';
};

echo $object->foo(); // still 'foo'

##More cool stuff

Prototyped objects are also traversables structures...

$object = new Object([1,2,3]);

foreach ($object as $value)
    echo $value; // '1 2 3'

... as well as arrays.

$object = new Object;

$object['a'] = 1;
$object['getA'] = function () {
    return $this['a'];
};

echo $object->getA();

You can even turn a function into a factory.

$function = new FunctionObject(function ($a, $b, $c) {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
    // yep, you can override toString
    $this->toString = function () {
        return "{$this->a} {$this->b} {$this->c}";
    };
    return clone $this;
});

$object = $function(1,2,3);
echo $object; // '1 2 3'

Now you can also use the Prototype\PrototypicalTrait in order to give more power to existing classes.

class ObjectSorage extends SplObjectStorage {
    use Prototype\PrototypicalTrait ;
}

$warehouse = new ObjectStorage;

$warehouse->attachScalar = function ($scalar) {
    return $this->attach((object)compact('scalar'));
}

$warehouse->attachScalar(1);
$warehouse->attachScalar(2);
$warehouse->attachScalar(3);

foreach ($warehouse as $object)
    var_dump( $object );