Upload
adler-medrado
View
1.647
Download
1
Embed Size (px)
DESCRIPTION
Metaprogramação com PHP
Citation preview
Não é feitiçaria, é tecnologia.Metaprogramação com PHP
Adler MedradoPHP Conference Brasil 2012
Monday, December 3, 12
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
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
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
Exemplos de metadados
• XML
• Bancos de dados
• Anotações
• docComment
• YAML
• O próprio código, porque não?
Monday, December 3, 12
Como podem ser usados ?
• WSDL
• .svn, .git, etc.
• Arquivos de configuração
• Dicionários de dados
Monday, December 3, 12
O que é metaprogramação ?
Alguns acham que é coisa de programador nerd level hard
Monday, December 3, 12
Ou feitiçaria, magia negra, coisas assim...
Monday, December 3, 12
MAS NÃO É
Monday, December 3, 12
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
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
Metaprogramação + PHP
• A maioria dos frameworks PHP atuais utilizam recursos de metaprogramação em algum ponto;
Monday, December 3, 12
Metaprogramação + PHP
• A propósito, eu arrisco dizer que você também usa ou já usou tais recursos;
Monday, December 3, 12
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
__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
__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
__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
OK, isso não é metaprogramação, mas...
Demonstra que o PHP é flexível
Flexibilidade é ponto chave em quase tudo
Monday, December 3, 12
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
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
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
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
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
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
Reflection API
$r = new ReflectionClass('App');echo $r->getMethod('getVersion')->invoke(new App());
ReflectionClass -Invocando métodos
Monday, December 3, 12
Reflection APIReflectionObject- Invocando métodos
$app = new App();$r = new ReflectionObject($app);echo $r->getMethod('getVersion')->invoke($app);
Monday, December 3, 12
Quer ver um exemplo melhor?
Monday, December 3, 12
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
<?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
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
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
Formulário gerado
Monday, December 3, 12
HTML gerado
Monday, December 3, 12
Annotations
• Injeção de comportamento;
• Desacoplamento;
• Aonde costuma ser usado?
• ORMs;
• Dependency Injection Container;
Monday, December 3, 12
Annotations
• Não é um recurso nativo do PHP;
• Existem bibliotecas que fazem o trabalho sujo;
https://wiki.php.net/rfc/annotationsMonday, December 3, 12
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
Annotations• Quem usa annotations no mundo PHP ?
• Doctrine;
• Symfony;
• Flow3 Framework;
• PHPUnit;
Monday, December 3, 12
Annotations
• Bibliotecas que implementam annotations
• Doctrine/Common (packagist/composer);
• php-annotations (https://github.com/mindplay-dk/php-annotations
Monday, December 3, 12
Annotations
• Lembra do gerador de formulário?
• Que tal adicionarmos annotations a ele?
Monday, December 3, 12
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
<?phpabstract class ReflectionForm {
private $metadataFields; public function __construct() { $this->metadataFields = array(); }
ReflectionForm.php
Monday, December 3, 12
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
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
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
Formulário gerado
Mesmo resultado que o exemplo anterior
Monday, December 3, 12
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
Lidando com objeto
• Anteriormente, adicionamos propriedades a um objeto;
• Que tal adicionarmos um método?
Monday, December 3, 12
Antes, uma pergunta:
Você já usou lambda functions com PHP?
Monday, December 3, 12
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
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
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
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
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
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
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
Perguntas?
Monday, December 3, 12
Obrigado, até a próxima.http://adlermedrado.com.br
http://getoncode.com.br
@adlermedrado
Monday, December 3, 12