Mirror, mirror on the wall (Nomad PHP US 2015)

Post on 26-Jan-2017

528 views 0 download

Transcript of Mirror, mirror on the wall (Nomad PHP US 2015)

Mirror, Mirror on the WallJames Titcumb

NomadPHP December 2015

Reflection

© 1937 Disney’s Snow White - disneyscreencaps.com

● Structure● Metadata● Values● Type introspection● Modification

Reflection

How does it work?

GET_REFLECTION_OBJECT_PTR(ce);

lc_name = zend_str_tolower_dup(name, name_len);

if ((ce == zend_ce_closure && (name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1)

&& memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0)

|| zend_hash_str_exists(&ce->function_table, lc_name, name_len)) {

efree(lc_name);

RETURN_TRUE;

} else {

efree(lc_name);

RETURN_FALSE;

}

Okay. What now?

github.com/ /BetterReflection

Better Reflection!

What?

Why?

How?

Magic of the AST

source: http://goo.gl/HORwLQ

WTF is AST?

use PhpParser\ParserFactory;

$parser = (new ParserFactory)

->create(ParserFactory::PREFER_PHP7);

print_r($parser->parse(

file_get_contents('ast-demo-src.php')

));

<?php

echo "Hello world";

AST

`-- Echo statement

`-- String, value "Hello world"

<?php

echo "Hello " . "world";

AST

`-- Echo statement

`-- Concat

|-- Left

| `-- String, value "Hello "

`-- Right

`-- String, value "world"

AST to Reflection

Benefits?

ReflectionProperty->getDocBlockTypes()

Types!

Using Better Reflection

$reflection = new ReflectionClass(

'BetterReflectionTest\Fixture\ExampleClass'

);

$this->assertSame(

'ExampleClass',

$reflection->getShortName()

);

$reflection = ReflectionClass::createFromName(

'BetterReflectionTest\Fixture\ExampleClass'

);

$this->assertSame(

'ExampleClass',

$reflection->getShortName()

);

// In ReflectionClass :

public static function createFromName($className)

{

return ClassReflector::buildDefaultReflector()->reflect($className);

}

// In ClassReflector :

public static function buildDefaultReflector()

{

return new self(new AggregateSourceLocator([

new PhpInternalSourceLocator(),

new EvaledCodeSourceLocator(),

new AutoloadSourceLocator(),

]));

}

(don’t try this at home kids!)

Some voodoo...

class MyClass

{

public function foo()

{

return 5;

}

}

// Create the reflection first

// ***BEFORE*** class is loaded

$classInfo = ReflectionClass::createFromName('MyClass');

// Override the body...!

$methodInfo = $classInfo->getMethod('foo');

$methodInfo->setBody(function () {

return 4;

});

// Save the class and require it

$classCode = (new CodePrinter())->prettyPrint([$classInfo->getAst()]);

$tmpFile = tempnam(sys_get_temp_dir(), 'br-monkey-patching');

file_put_contents($tmpFile, '<?php ' . $classCode);

require_once($tmpFile);

unlink($tmpFile);

// Now create an instance, and call the function...

$c = new MyClass();

var_dump($c->foo()); // will be 4!!!

What’s in it for me?

The future...

● DONE Currently: rewriting the internals○ SourceLocators “might” return some code ?!

● DONE Returning AST/Code for method/fn

Ideas/plans - DONE

● WIP More compatibility core reflection● WIP Reflect core fns with default params

○ This is not currently possible with core reflection● WIP Modification & exporting● WIP Reflecting on closures

Ideas/plans - WIP

● Adding a getColumn() function● PHP 7 compatibility● @inheritDoc traversal (to inherit type hints)● Make it faster

Ideas/plans

¯\_(ツ)_/¯

github.com/ /BetterReflection

Better Reflection

Any questions? :)

https://joind.in/16523James Titcumb @asgrim