Hi!



Hi!

0 5


voodoo-php


On Github Ocramius / voodoo-php

Hi!

I'm Marco!

Ocramius

@Ocramius @asgrim @RoaveTeam Imma start calling you Gandalphp.

— Lee Davis (@leedavis81) 9 July 2015

What is Voodoo?

/ˈvuːduː/ Vodou

An African / Caribbean / South US religion

Vodou means Spirit in west-african cultures

There are different variations of Vodou

Vodun is a fatalist creed

Events are pre-defined, in a chain of cause and effect

One must ask the lwa (spirits).

One does not act on his or her own.

I will skip on Vodou rituals

I just got you interested in the talk

I don't want to talk about a religion that I do not know

What Vodou are we talking about?

PHP Magic!

In Programming, Magic is used to describe code that handles complex tasks, hiding that complexity to present a simple interface.

Thanks, Wikipedia!

Who does Vodou PHP?

Many Junior developers trying to over-abstract

Very few Senior developers, when building compromises

Programming Magic can be Very Dangerous

Practice Extreme Caution

What Tools do we need for Magic?

(just a refresh)

Magic Methods

__construct __destruct __get __set __isset __unset __sleep __wakeup __clone __invoke __call __callStatic __set_state __debugInfo

Reflection

ReflectionClass

ReflectionProperty

ReflectionMethod

ReflectionFunction

Closures!

$f = function () {};

$f = (function () {})->bindTo($that, 'Foo');

(No, this syntax won't work, sadly)

Some constructs and functions

func_get_args()

debug_backtrace()

isset

unset

(array) cast

Let's create a puppet!

Generally referred to as friend object

Our puppet:

class User
{
    private $name;
    private $surname;

    public function __construct($name, $surname)
    {
        $this->name    = $name;
        $this->surname = $surname;
    }

    public function sayHello()
    {
        return 'Hello, I\'m ' . $this->name . ' '
            . $this->surname . '!' . PHP_EOL;
    }
}

Simplistic puppeteer:

class Puppeteer {
public $properties = [];

public function __construct($puppet)
{
    foreach (
        (new ReflectionClass($puppet))->getProperties()
        as $property
    ) {
        Closure::bind(function (& $properties) use ($property) {
            $name = $property->getName();

            $properties[$name] = & $this->$name;
        }, $puppet, $property->getDeclaringClass()->getName())
            ->__invoke($this->properties);
    }
}
}
$user = new User('James', 'Titcumb');

var_dump($user->sayHello());

$puppeteer = new Puppeteer($user);

$puppeteer->properties['name']    = 'A';
$puppeteer->properties['surname'] = 'Minion';

var_dump($user->sayHello());

leedavis81/altr-ego

A decent puppeteer example

class Foo
{
    private $bar = '0';

    private saySomething($baz)
    {
        return $this->bar . $baz;
    }
}
$altrEgo = AltrEgo::create(new Foo());

$altrEgo->bar = '11';

var_dump($altrEgo->saySomething('22'));

Hiding Properties

class Thing
{
    public $something = 'foo';

    public function saySomething()
    {
        return $this->something;
    }
}
$thing = new Thing();

var_dump($thing->saySomething());

unset($thing->something);

var_dump($thing->saySomething()); // ???

What can we do with that?

class MagicThing
{
    public $something = 'foo';

    public function saySomething()
    {
        return $this->something;
    }

    public function __get($name)
    {
        throw new DomainException('Stop touching my things!');
    }
}
$thing = new Thing();

var_dump($thing->saySomething());

unset($thing->something);

var_dump($thing->saySomething()); // ???

AOP

A programming paradigm to separate cross-cutting concerns

Usually considered magic

Not very different from macros

In PHP, usually rewrites files when they are included

lisachenko/go-aop-php

AOP framework for PHP

No extensions required

class CatchAll implements \Go\Aop\Aspect
{
    /** @Go\Lang\Annotation\Before("execution(*->*(*))") */
    public function beforeAnything(
        \Go\Aop\Intercept\MethodInvocation $invocation
    ) {
        var_dump($invocation->getMethod()->getName());

        return $invocation->proceed();
    }
}
$foo->bar();
$bar->baz();

__debugInfo to make things more confusing

class Foo
{
    private $thisWillGoWrongSomewhere = 'OHAI!';

    public function __debugInfo() {
        // let me confuse you a bit
        return [];
    }
}
var_dump(new Foo());
var_dump((object) ["\0Foo\0bar" => 'baz']);
$foo = (object) ["\0Foo\0bar" => 'baz'];

var_dump($foo->{"\0Foo\0bar"});
$foo = (object) ["\0Foo\0bar" => 'baz'];

var_dump($foo->bar);
$foo = (object) ["\0Foo\0bar" => 'baz'];

$rp = new ReflectionProperty('stdClass', 'bar');

$rp->setAccessible(true);

var_dump($rp->getValue($foo));
$foo = (object) ["\0Foo\0bar" => 'baz'];

var_dump(Closure::bind(function () {
    return $this->baz;
}, $foo, 'Foo')->__invoke());

The perfect Immutable Object?

Voodoo Libraries

doctrine/instantiator

Instantiate classes without calling their constructor.

class NotInstantiableSingleton
{
    public static function getInstance() { /* horror */ }

    private function __construct() {}
}
$instance = new NotInstantiableSingleton(); // crash
$instance = (new \Doctrine\Instantiator\Instantiator())
    ->instantiate('NotInstantiableSingleton');

leedavis81/vent

Object internal initialization event system

class Foo {
    use Vent\VentTrait;
    private $bar;

    public function __construct() {
        $this->registerEvent('read', 'bar', function() {
            echo 'Reading "bar"' . PHP_EOL;
        });
    }

    public function doSomething() {
        return $this->bar;
    }
}
$foo = new Foo();

$foo->doSomething();

ocramius/lazy-map

Super fast lazy-initialized registry

$map = new \LazyMap\CallbackLazyMap(function ($name) {
    return new \ReflectionClass($name);
});
var_dump($map->ArrayObject);
var_dump($map->ArrayIterator);

TimeToogo/Pinq

Inspects Closures to build custom expressions in various DSLs

$db
    ->table('customers')
    ->where(function ($row) { return $row['age'] <= 50; })
    ->orderByAscending(function ($row) { return $row['firstName']; })
    ->select(function ($row) {
        return [
            'fullName' => $row['firstName'] . ' ' . $row['lastName']
        ];
    })
    ->asArray()
SELECT
    CONCAT(CONCAT(customers.firstName, ' '), customers.lastName)
    AS fullName
FROM (
    SELECT * FROM (
        SELECT * FROM (SELECT * FROM customers) AS customers
        WHERE (customers.age <= 50)
    ) AS customers
    ORDER BY customers.firstName ASC
    LIMIT 50 OFFSET 0
) AS customers

jeremeamia/super_closure

Save/load code

Save/load your bugs! (you're welcome!)

$serializer = new SuperClosure\Serializer();

$serialized = $serializer->serialize(function () {
    echo file_get_contents(__FILE__);
});

$serializer->unserialize($serialized)->__invoke();

ocramius/proxy-manager

All sorts of crazy hacks around object state

Lazy objects, AOP, Decorators

class Foo {
    public function __construct()
    {
        sleep(5);
    }

    public function doFoo()
    {
        echo "Foo!";
    }
}
$factory = new LazyLoadingValueHolderFactory();

$proxy = $factory->createProxy(
    'Foo',
    function (& $wrapped, $p, $m, $args, & $initializer) {
        $initializer   = null;
        $wrapped       = new Foo();
    }
);

var_dump($proxy);
class Foo {
    public function doFoo()
    {
        echo "Foo!";
    }
}
$factory = new AccessInterceptorScopeLocalizerFactory();

$proxy = $factory->createProxy(
    new Foo(),
    ['doFoo' => function ($proxy) { echo "pre\n"; }],
    ['doFoo' => function ($proxy) { echo "post\n"; }]
);

$proxy->doFoo();
class Foo {
    private $bar = 'baz';

    public function sayBar()
    {
        echo $this->bar;
    }
}
$factory = new LazyLoadingGhostFactory();

$proxy = $factory->createProxy(
    'Foo',
    function ($p, $m, $args, & $initializer, array $properties) {
        $initializer              = null;
        $properties["\0Foo\0bar"] = 'HAHA!';
    }
);

$proxy->sayBar();

roave/strict-php

DEV-MASTER only!!!

Turns PHP into JAVA

(not really)

class Foo {
    /**
     * @var \stdClass[]
     */
    public $bar = [];
}
$foo = new Foo();
$foo->bar = [new \stdClass()];
$foo->bar = new \stdClass; // crash
Hi!