1. Não é feitiçaria, é tecnologia.
Metaprogramação com PHP
Adler Medrado
PHP Conference Brasil 2012
Monday, December 3, 12
2. 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
3. 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
4. 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
5. Exemplos de
metadados
• XML
• Bancos de dados
• Anotações
• docComment
• YAML
• O próprio código, porque não?
Monday, December 3, 12
6. Como podem ser
usados ?
• WSDL
• .svn, .git, etc.
• Arquivos de configuração
• Dicionários de dados
Monday, December 3, 12
7. O que é
metaprogramação ?
Alguns acham que é coisa de programador nerd
level hard
Monday, December 3, 12
10. 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
11. 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
12. Metaprogramação +
PHP
• A maioria dos frameworks PHP atuais
utilizam recursos de metaprogramação em
algum ponto;
Monday, December 3, 12
13. Metaprogramação +
PHP
• A propósito, eu arrisco dizer que você
também usa ou já usou tais recursos;
Monday, December 3, 12
14. 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
15. __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
16. __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
17. __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
18. OK, isso não é
metaprogramação, mas...
Demonstra que o
PHP é flexível
Flexibilidade é ponto
chave em quase tudo
Monday, December 3, 12
19. 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
20. 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
21. 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
22. 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
23. Reflection API
Imagine 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
25. Reflection API
ReflectionClass -Invocando métodos
$r = new ReflectionClass('App');
echo $r->getMethod('getVersion')->invoke(new App());
Monday, December 3, 12
26. Reflection API
ReflectionObject- Invocando métodos
$app = new App();
$r = new ReflectionObject($app);
echo $r->getMethod('getVersion')->invoke($app);
Monday, December 3, 12
27. Quer ver um exemplo melhor?
Monday, December 3, 12
28. 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
34. Annotations
• Injeção de comportamento;
• Desacoplamento;
• Aonde costuma ser usado?
• ORMs;
• Dependency Injection Container;
Monday, December 3, 12
35. Annotations
• Não é um recurso nativo do PHP;
• Existem bibliotecas que fazem o trabalho
sujo;
https://wiki.php.net/rfc/annotations
Monday, December 3, 12
36. 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
37. Annotations
• Quem usa annotations no mundo PHP ?
• Doctrine;
• Symfony;
• Flow3 Framework;
• PHPUnit;
Monday, December 3, 12
38. Annotations
• Bibliotecas que implementam annotations
• Doctrine/Common (packagist/composer);
• php-annotations (https://github.com/mindplay-dk/
php-annotations
Monday, December 3, 12
39. Annotations
• Lembra do gerador de formulário?
• Que tal adicionarmos annotations a ele?
Monday, December 3, 12
40. Empresa.php
<?php
require '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
41. ReflectionForm.php
<?php
abstract class ReflectionForm {
private $metadataFields;
public function __construct()
{
$this->metadataFields = array();
}
Monday, December 3, 12
47. Lidando com objeto
• Anteriormente, adicionamos
propriedades a um objeto;
• Que tal adicionarmos um
método?
Monday, December 3, 12
48. Antes, uma pergunta:
Você já usou lambda
functions com PHP?
Monday, December 3, 12
49. 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
50. Criamos uma classe...
<?php
class Carro {
private $modelo, $ano, functionArgs;
public function __construct($modelo, $ano) {
$this -> modelo = $modelo;
$this -> ano = $ano;
}
public function ligar() {
echo "Ligando o carron";
}
public function __call($method, $args) {
if ($this->{$method} instanceof Closure) {
return call_user_func_array($this->{$method}, $args);
}
}
}
Monday, December 3, 12
51. e adicionamos um
método
<?php
require '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
52. 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
54. Que tal deixar a classe
<?php
mais limpa?
trait 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;
}
Obs: O método __call também pode ser definido na trait
Monday, December 3, 12
55. Que tal deixar a classe
mais limpa?
<?php
class 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 carron";
}
public function __call($method, $args) {
if ($this -> {$method} instanceof Closure) {
return call_user_func_array($this -> {$method}, $args);
}
}
}
Monday, December 3, 12