@Ocramius
@asgrim
@RoaveTeam
Imma start calling you Gandalphp.
— Lee Davis (@leedavis81)
9 July 2015
/ˈ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 just got you interested in the talk
I don't want to talk about a religion that I do not know
PHP Magic!
In Programming, Magic is used to describe code that handles complex tasks, hiding that complexity to present a simple interface.
Thanks, Wikipedia!
Many Junior developers trying to over-abstract
Very few Senior developers, when building compromises
(just a refresh)
__construct
__destruct
__get
__set
__isset
__unset
__sleep
__wakeup
__clone
__invoke
__call
__callStatic
__set_state
__debugInfo
ReflectionClass
ReflectionProperty
ReflectionMethod
ReflectionFunction
$f = function () {};
$f = (function () {})->bindTo($that, 'Foo');
(No, this syntax won't work, sadly)
func_get_args()
debug_backtrace()
isset
unset
(array)
cast
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());
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'));
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()); // ???
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()); // ???
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
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();
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());
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');
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();
Super fast lazy-initialized registry
$map = new \LazyMap\CallbackLazyMap(function ($name) {
return new \ReflectionClass($name);
});
var_dump($map->ArrayObject);
var_dump($map->ArrayIterator);
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
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();
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();
DEV-MASTER only!!!
(not really)
class Foo {
/**
* @var \stdClass[]
*/
public $bar = [];
}
$foo = new Foo();
$foo->bar = [new \stdClass()];
$foo->bar = new \stdClass; // crash