57
Não é feitiçaria, é tecnologia. Metaprogramação com PHP Adler Medrado PHP Conference Brasil 2012 Monday, December 3, 12

Não é Feitiçaria, é Tecnologia

Embed Size (px)

DESCRIPTION

Metaprogramação com PHP

Citation preview

Page 1: Não é Feitiçaria, é Tecnologia

Não é feitiçaria, é tecnologia.Metaprogramação com PHP

Adler MedradoPHP Conference Brasil 2012

Monday, December 3, 12

Page 2: Não é Feitiçaria, é Tecnologia

Quem sou eu ?• Desenvolvedor, Consultor,

Instrutor;

• Trabalha na Sigma Dataserv;

• Co-Fundador do PHPDF;

• Fui apresentado ao PHP em 1999;

• Tenho meu próprio podcast (getOnCode);

• ZCE

• PHP 5;

• Zend Framework;Monday, December 3, 12

Page 3: Não é Feitiçaria, é Tecnologia

Meta

• Do grego, μετά

• após, além, adjacente

Indica o conceito de abstração de outro conceito, que completa e adiciona a este último.

Monday, December 3, 12

Page 4: Não é Feitiçaria, é Tecnologia

Metadado (metadata)

• Termo criado por Philip Bagley em 1986, na obra “Extension of Programming Language Concepts”;

• Dados sobre outros dados;

• Informa, descreve sobre o dado em questão;

• Serve como marco ou ponto de referência;

Monday, December 3, 12

Page 5: Não é Feitiçaria, é Tecnologia

Exemplos de metadados

• XML

• Bancos de dados

• Anotações

• docComment

• YAML

• O próprio código, porque não?

Monday, December 3, 12

Page 6: Não é Feitiçaria, é Tecnologia

Como podem ser usados ?

• WSDL

• .svn, .git, etc.

• Arquivos de configuração

• Dicionários de dados

Monday, December 3, 12

Page 7: Não é Feitiçaria, é Tecnologia

O que é metaprogramação ?

Alguns acham que é coisa de programador nerd level hard

Monday, December 3, 12

Page 8: Não é Feitiçaria, é Tecnologia

Ou feitiçaria, magia negra, coisas assim...

Monday, December 3, 12

Page 9: Não é Feitiçaria, é Tecnologia

MAS NÃO É

Monday, December 3, 12

Page 10: Não é Feitiçaria, é Tecnologia

O que é metaprogramação ?

• Fornece a capacidade de gerar ou alterar o comportamento de um programa em tempo de execução ou compilação baseado em metadados;

Monday, December 3, 12

Page 11: Não é Feitiçaria, é Tecnologia

Metaprogramação + PHP

• O PHP não oferece tantos recursos como o Ruby para utilizar tal técnica;

• Mas os recursos que já existiam somados a outros oferecidos com o advento do PHP 5.3 e 5.4 nos permite fazer coisas interessantes;

Monday, December 3, 12

Page 12: Não é Feitiçaria, é Tecnologia

Metaprogramação + PHP

• A maioria dos frameworks PHP atuais utilizam recursos de metaprogramação em algum ponto;

Monday, December 3, 12

Page 13: Não é Feitiçaria, é Tecnologia

Metaprogramação + PHP

• A propósito, eu arrisco dizer que você também usa ou já usou tais recursos;

Monday, December 3, 12

Page 14: Não é Feitiçaria, é Tecnologia

Metaprogramação + PHP

• Você conhece os métodos mágicos do PHP?

• Eles podem ser usados para alterar o comportamento do objeto;

Monday, December 3, 12

Page 15: Não é Feitiçaria, é Tecnologia

__get()class Examples { private $property; public function __construct() { $this->property = 'Esse é um exemplo'; } public function __get($property) { return $this->$property; }}

$e = new Examples;echo $e->property;

Monday, December 3, 12

Page 16: Não é Feitiçaria, é Tecnologia

__set($prop, $val)class Examples { private $property; public function __construct() { $this->property = 'Esse é um exemplo'; } public function __get($property) { return $this->$property; } public function __set($property, $value) { $this->$property = $value; }}

$e = new Examples;echo $e->property;$e->property = '<br />mudei o valor<br />';echo $e->property;

Monday, December 3, 12

Page 17: Não é Feitiçaria, é Tecnologia

__call($name, $params)<?php class Exemplos { private $property; public function __construct() { $this->property = 'Esse é um exemplo'; } public function __call($name, $args) { echo 'invocando o método ' . $name . ' com os argumentos ' . join(', ' , $args); }}

$e = new Exemplos;$e->umMetodoQualquer('param1', 'param2', 'param3');

Monday, December 3, 12

Page 18: Não é Feitiçaria, é Tecnologia

OK, isso não é metaprogramação, mas...

Demonstra que o PHP é flexível

Flexibilidade é ponto chave em quase tudo

Monday, December 3, 12

Page 19: Não é Feitiçaria, é Tecnologia

Falando em flexibilidade ...

Que tal adicionarmos propriedades em tempo de execução?

<?php class Person {}

$adler = new Person;var_dump($adler);

object(Person)[1]

Não existem propriedades neste objeto.

Monday, December 3, 12

Page 20: Não é Feitiçaria, é Tecnologia

Falando em flexibilidade ...

<?php class Person {}

$adler = new Person;

$adler->nome = 'Adler Medrado';$adler->bonitao = true;

var_dump($adler);?>

object(Person)[1] public 'nome' => string 'Adler Medrado' (length=13) public 'bonitao' => boolean true

Monday, December 3, 12

Page 21: Não é Feitiçaria, é Tecnologia

Reflection API

• Desde a primeira release do PHP 5;

• Introspecção;

• Engenharia-Reversa;

• Acesso a metadados (PHPDoc);

• http://www.php.net/manual/en/book.reflection.php

Monday, December 3, 12

Page 22: Não é Feitiçaria, é Tecnologia

Reflection API

• Possíveis usos no mundo real;

• Geração de WSDL;

• Geração de formulários;

• Uso de anotações em PHP;

• Entre outros . . .

• Zend_XmlRpc usa Reflection;

Monday, December 3, 12

Page 23: Não é Feitiçaria, é Tecnologia

Reflection APIImagine a seguinte classe

<?php class App { private $name; private $version; public function __construct() { $this->name = 'Exemplo'; $this->version = '1.0'; } public function getVersion() { return $this->version; }}

Monday, December 3, 12

Page 24: Não é Feitiçaria, é Tecnologia

Reflection APIecho '<pre>';ReflectionClass::export('App');echo '</pre>';

Class [ class App ] { @@ /Users/adler/Dropbox/palestras-phpconference-2012/metaprogramacao/reflection/sample_class.php 2-14

- Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [2] { Property [ private $name ] Property [ private $version ] } - Methods [2] { Method [ public method __construct ] { @@ /Users/adler/Dropbox/palestras-phpconference-2012/metaprogramacao/reflection/sample_class.php 6 - 9 } Method [ public method getVersion ] { @@ /Users/adler/Dropbox/palestras-phpconference-2012/metaprogramacao/reflection/sample_class.php 11 - 13 } }}

Monday, December 3, 12

Page 25: Não é Feitiçaria, é Tecnologia

Reflection API

$r = new ReflectionClass('App');echo $r->getMethod('getVersion')->invoke(new App());

ReflectionClass -Invocando métodos

Monday, December 3, 12

Page 26: Não é Feitiçaria, é Tecnologia

Reflection APIReflectionObject- Invocando métodos

$app = new App();$r = new ReflectionObject($app);echo $r->getMethod('getVersion')->invoke($app);

Monday, December 3, 12

Page 27: Não é Feitiçaria, é Tecnologia

Quer ver um exemplo melhor?

Monday, December 3, 12

Page 28: Não é Feitiçaria, é Tecnologia

Um simples gerador de formulários

• Consiste em um gerador de formulário HTML baseado em um objeto PHP;

• Pode ser incrementado posteriormente implementando filtros, validações, etc.;

Monday, December 3, 12

Page 29: Não é Feitiçaria, é Tecnologia

<?phprequire 'ReflectionForm.php';

class Empresa extends ReflectionForm {

private $nome; private $razao_social; private $endereco; private $bairro; private $cidade; private $estado; private $telefone; private $email;

}

Empresa.php

Monday, December 3, 12

Page 30: Não é Feitiçaria, é Tecnologia

ReflectionForm.php

<?phpabstract class ReflectionForm { private $generated; public function parse() { $r = new ReflectionObject($this); $fields = $r->getProperties(); array_walk($fields, function($field) { $this->generated .= "<div>" . ucfirst($field->getName()) . "</div>\n"; $this->generated .= "<div><input type=\"text\" name=\"" . $field->getName() . "\" /></div>\n"; });

return $this->generated; }}

Obtém as propriedades

Obtém o objeto de Reflexão

Monday, December 3, 12

Page 31: Não é Feitiçaria, é Tecnologia

exemplo.php

<?php require 'Empresa.php';

$empresa = new Empresa(); ?><html> <head> <title>Exemplo - Form Generator</title> </head> <body id="formGenerator"> <form action="" method="post" accept-charset="utf-8"> <?=$empresa->parse(); ?> </form> </body></html>

Monday, December 3, 12

Page 32: Não é Feitiçaria, é Tecnologia

Formulário gerado

Monday, December 3, 12

Page 33: Não é Feitiçaria, é Tecnologia

HTML gerado

Monday, December 3, 12

Page 34: Não é Feitiçaria, é Tecnologia

Annotations

• Injeção de comportamento;

• Desacoplamento;

• Aonde costuma ser usado?

• ORMs;

• Dependency Injection Container;

Monday, December 3, 12

Page 35: Não é Feitiçaria, é Tecnologia

Annotations

• Não é um recurso nativo do PHP;

• Existem bibliotecas que fazem o trabalho sujo;

https://wiki.php.net/rfc/annotationsMonday, December 3, 12

Page 36: Não é Feitiçaria, é Tecnologia

Annotations• Por essência, annotation é

metaprogramação;

• No PHP, annotation é uma simulação feita usando a sintaxe PHPDoc + Reflection;

• Metaprogramação para implementar um recurso de Metalinguagem;

Monday, December 3, 12

Page 37: Não é Feitiçaria, é Tecnologia

Annotations• Quem usa annotations no mundo PHP ?

• Doctrine;

• Symfony;

• Flow3 Framework;

• PHPUnit;

Monday, December 3, 12

Page 38: Não é Feitiçaria, é Tecnologia

Annotations

• Bibliotecas que implementam annotations

• Doctrine/Common (packagist/composer);

• php-annotations (https://github.com/mindplay-dk/php-annotations

Monday, December 3, 12

Page 39: Não é Feitiçaria, é Tecnologia

Annotations

• Lembra do gerador de formulário?

• Que tal adicionarmos annotations a ele?

Monday, December 3, 12

Page 40: Não é Feitiçaria, é Tecnologia

Empresa.php

<?phprequire 'ReflectionForm.php';

class Empresa extends ReflectionForm {

/** * @var string * @type text */ private $nome; /** * @var string * @type text */ private $razao_social; /** * @var string * @type text */ private $endereco;

// Demais propriedades...}

Monday, December 3, 12

Page 41: Não é Feitiçaria, é Tecnologia

<?phpabstract class ReflectionForm {

private $metadataFields; public function __construct() { $this->metadataFields = array(); }

ReflectionForm.php

Monday, December 3, 12

Page 42: Não é Feitiçaria, é Tecnologia

ReflectionForm.php (Continuação)

public function parse() { $this->getFormInfo(); $generated = '';

foreach($this->metadataFields as $fieldName => $fieldParams) { $generated .= "<div>" . ucfirst($fieldName) . "</div>\n"; $generated .= "<div><input type=\"" $generated .= $fieldParams['type'] . "\" name=\"" $generated .=. $fieldName . "\" /></div>\n"; } return $generated; }

Monday, December 3, 12

Page 43: Não é Feitiçaria, é Tecnologia

ReflectionForm.php (Continuação)

private function getFormInfo() { $r = new ReflectionObject($this); $fields = $r->getProperties(); array_walk($fields, function($field) { $lines = array(); $doc = $field->getDocComment(); if (preg_match('#^/\*\*(.*)\*/#s', $doc, $comment) === false) throw new Exception('Error getting comment'); $params = trim($comment[1]);

if(preg_match_all('#^\s*\*(.*)#m', $params, $lines) === false) throw new Exception('Error getting lines'); foreach($lines[1] as $line) { $this->getVariables($field->getName(), $line); } }); }

Monday, December 3, 12

Page 44: Não é Feitiçaria, é Tecnologia

ReflectionForm.php (Continuação)

private function getVariables($fieldName, $line) {

$line = trim($line); if(empty($line)) return false; if(strpos($line, '@') === 0) { $param = substr($line, 1, strpos($line, ' ') - 1); $value = substr($line, strlen($param) + 2);

$this->metadataFields[$fieldName] = array($param => $value); } }

Monday, December 3, 12

Page 45: Não é Feitiçaria, é Tecnologia

Formulário gerado

Mesmo resultado que o exemplo anterior

Monday, December 3, 12

Page 46: Não é Feitiçaria, é Tecnologia

Geração de CódigoPrazer, meu nome é eval()

<?php

$codigo = 'for ($i = 0; $i < 10; $i++) {';$codigo .= 'echo \'Contando: \' . $i . \'<br />\';';$codigo .= '}';

eval($codigo);

Contando: 0Contando: 1Contando: 2Contando: 3Contando: 4Contando: 5Contando: 6Contando: 7Contando: 8Contando: 9

Monday, December 3, 12

Page 47: Não é Feitiçaria, é Tecnologia

Lidando com objeto

• Anteriormente, adicionamos propriedades a um objeto;

• Que tal adicionarmos um método?

Monday, December 3, 12

Page 48: Não é Feitiçaria, é Tecnologia

Antes, uma pergunta:

Você já usou lambda functions com PHP?

Monday, December 3, 12

Page 49: Não é Feitiçaria, é Tecnologia

De acordo com a documentação oficial: Anonymous functions, also known as closures, allow the creation of functions which have no specified name. They are most useful as the value of callback parameters, but they have many other uses.

<?php

$silvio = function() { return "maaa oeeeee";};

echo $silvio();

Monday, December 3, 12

Page 50: Não é Feitiçaria, é Tecnologia

Criamos uma classe... <?phpclass Carro {

private $modelo, $ano, functionArgs; public function __construct($modelo, $ano) { $this -> modelo = $modelo; $this -> ano = $ano; }

public function ligar() { echo "Ligando o carro\n"; }

public function __call($method, $args) { if ($this->{$method} instanceof Closure) { return call_user_func_array($this->{$method}, $args); } }}

Monday, December 3, 12

Page 51: Não é Feitiçaria, é Tecnologia

e adicionamos um método

<?phprequire 'Carro.php';

$carro = new Carro('Uno','1995');

$carro->ligar();

$str = '$carro->buzinar = function() {';$str .= "echo \"fom fom \n\";";$str .= "};";

eval($str);

$carro->buzinar();

?>

Monday, December 3, 12

Page 52: Não é Feitiçaria, é Tecnologia

Vamos adicionar um método nessa classe?

public function createNewMethod($name, $args, $code) {

if ((!is_null($args)) && (sizeof($args) == 0)) { array_walk($args, function($value) { if (empty($this->functionArgs)) { $this->functionArgs .= '$' . $value; } else { $this->functionArgs .= ',$' . $value; } }); }

$functionDefinition = '$this->{$name} = function ('. $this->functionArgs. ')'; $functionDefinition .= '{'.$code.'};'; eval($functionDefinition); $this->functionArgs = null; }

Monday, December 3, 12

Page 53: Não é Feitiçaria, é Tecnologia

Adicionando método...

<?phprequire 'Carro.php';

$carro = new Carro('Uno','1995');

$carro->ligar();$carro->createNewMethod('buzinar', null, ' return "biii bii\n";');echo $carro->buzinar();

Monday, December 3, 12

Page 54: Não é Feitiçaria, é Tecnologia

Que tal deixar a classe mais limpa?

Obs: O método __call também pode ser definido na trait

<?phptrait genMetodo { private $functionArgs; public function createNewMethod($name, $args, $code) { if ((!is_null($args)) && (sizeof($args) == 0)) { array_walk($args, function($value) { if (empty($this->functionArgs)) { $this->functionArgs .= '$' . $value; } else { $this->functionArgs .= ',$' . $value; } }); }

$functionDefinition = '$this->{$name} = function ('. $this->functionArgs. ')'; $functionDefinition .= '{'.$code.'};'; eval($functionDefinition); $this->functionArgs = null; }

Monday, December 3, 12

Page 55: Não é Feitiçaria, é Tecnologia

Que tal deixar a classe mais limpa?

<?phpclass Carro { use genMetodo;

private $modelo, $ano, $functionArgs; public function __construct($modelo, $ano) { $this->modelo = $modelo; $this->ano = $ano; $this->functionArgs = null; } public function ligar() { echo "Ligando o carro\n"; }

public function __call($method, $args) { if ($this -> {$method} instanceof Closure) { return call_user_func_array($this -> {$method}, $args); } }}

Monday, December 3, 12

Page 56: Não é Feitiçaria, é Tecnologia

Perguntas?

Monday, December 3, 12

Page 57: Não é Feitiçaria, é Tecnologia

Obrigado, até a próxima.http://adlermedrado.com.br

http://getoncode.com.br

@adlermedrado

Monday, December 3, 12