não é feitiçaria, é tecnologia

Post on 18-Nov-2014

1.643 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Metaprogramação com PHP

TRANSCRIPT

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

top related