Rapidinha: Obter cotação da bolsa de valores em tempo real …

19 11 2007

… claro, com um pequeno atraso de 15 minutos. E ajuda do sempre útil simplexml, do PHP5. Segue:

<?php
// author: Eziel Silva
@list($self, $papel, $time, $tot) = $argv;
if (!$papel) {
    echo sprintf('uso:    %s <PAPEL> [temp][valor]%s', $self, "\n");
    echo sprintf('PAPEL:  VALE5%s', "\n");
    echo sprintf('temp:   tempo de atualizacao - default = 20%s', "\n");
    echo sprintf('valor:  quanto em papeis da empresa voce possui%s', "\n");
    return 1;
}

$url  = sprintf('http://www.bovespa.com.br/Cotacoes2000/formCotacoesMobile.asp?codsocemi=%s', $papel);
$sXml = @simplexml_load_string(@file_get_contents($url));

if (!$sXml) {
    echo sprintf('Simbolo "%s" nao encontrado', $papel);
    return 1;
}

$format = "Hor: %s Osc.: %s%% Rend.: %s\n";
if (!$tot) {
    $format = "Hor: %s Osc.: %s%%\n";
}
if (!$time) {
    $time = 20;
}

while(true) {
    $sXml = @simplexml_load_string(@file_get_contents($url));
    if (!$sXml) {
        echo sprintf('Erro atualizando... aguardando 5s%s',"\n");
        sleep(5);
        continue;
    }
    $c = $tot * str_replace(',','.', $sXml->PAPEL['OSCILACAO']) / 100;
    // hack pra me deixar feliz... caso esteja perdendo $$$, 
    // descomente a linha abaixo
    //$c = $c * -1; 
    echo sprintf($format, $sXml->PAPEL['HORA'], $sXml->PAPEL['OSCILACAO'], $c);
    sleep($time);
}

$ php bov.php
uso: bov.php [temp][valor]
PAPEL: VALE5
temp: tempo de atualizacao – default = 20
valor: quanto em papeis da empresa voce possui

$ php bov.php VALE5
Hor: 18:08:06 Osc.: -3,55%

$ php bov.php VALE5 5 1000
Hor: 18:08:06 Osc.: -3,55% Rend.: -35.5

Arquivo:
bov.php





PHP + TDD + “Generics”

16 11 2007

(Observação: Poucas coisas são tão terríveis quanto se formatar um post nesse wordpress. Todos devem ficar assim…)

“Test-Driven Development (TDD) is a software development technique consisting of short iterations where new test cases covering the desired improvement or new functionality are written first, then the production code necessary to pass the tests is implemented, and finally the software is refactored to accommodate changes. The availability of tests before actual development ensures rapid feedback after any change. Practitioners emphasize that test-driven development is a method of designing software, not merely a method of testing.”

Acredito que já conheçam TDD. O que ??? Não conhece ??? Ok, ótima hora para conhecer:

Wikipedia – TDD
Introduction to Test Driven Design (TDD)
testdriven.com
improveit – tdd
phpunit

Não será eu quem vai dizer os benefícios de se utilizar TDD, até porque não é o objetivo deste post. Vamos ver de forma bem rápida como podemos criar uma classe que tem o comportamente semelhante a generics em java. E, novamente, não será eu que vai dizer o que são generics.

1 – Os testes
Vejamos um simples teste, com os comentários:

TestGenericList.php

<?php

include 'g.class.php';
include 'invalidgenerictypeexception.class.php';
include 'genericlist.class.php';

// Classe que servira de modelo e teste para o que
// vamos desenvolver
class TestGenericList extends PHPUnit_Framework_TestCase {

    /**
    * Testa o tamanho da nossa lista
    */
    public function testSize()
    {
        // instanciamos a GenericList
        // o parametro que vamos passar indica
        // o tipo de lista que teremos, neste
        // caso uma lista de Integer (int)
        // List l = new List<Integer>()
        $list = new GenericList(G::Integer);
        $list->add(1); // adicionamos um item...
        $list->add(2); // ... e mais um ...
        $list->add(3); // ... e mais um, totalizando tres
        
        // testamos se o tamanho da nossa lista
        // realmente eh igual a 3
        $this->assertEquals($list->size(), 3);
    }

    /*
        Note que ao final do teste acima, ja temos pelo menos
        tres metodos e duas propriedades definidas:
        __construct($param):
            O construtor da classe, vai receber pelo menos um
            parametro, indicando o tipo de lista. $param
            devera ser salvo para uso futuro, entao teremos
            uma propriedade na classe para isso
        add($param):
            Adiciona um item na lista. Assim como o parametro
            passado no construtor, deveremos salvar este parametro
            para uso futuro. Entao teremos mais uma propriedade na 
            classe
        size():
            Retorna o tamanho da lista. Provavelmente vai retornar
            a quantidade de itens adicionados em add($param), que
            estao armazenados em uma propriedade da classe, que
            certamente sera um array. Pensando em sizeof() ?
    */

    /**
    * Testa uma lista de Integers se aceita adicao
    * de somente novos integers
    */
    public function testAddSameTypeInteger()
    {
        // criamos uma lista de integers
        $list = new GenericList(G::Integer);
        // adicionamos um integer
        $list->add(1);
        // se tudo deu certo, teremos um integer
        // na lista e ela vai ter um tamanho = 1
        $this->assertEquals($list->size(), 1);
    }
    
    /**
    * Testa uma lista de Strings se aceita adicao
    * de somente novas Strings
    */
    public function testAddSameTypeString()
    {
        // criamos uma lista de Strings
        $list = new GenericList(G::String);
        // adicionamos uma string
        $list->add('foo');
        // se tudo deu certo, teremos uma string
        // na lista e ela vai ter um tamanho = 1
        $this->assertEquals($list->size(), 1);
    }

    /**
    * Testa uma lista de Doubles se aceita adicao
    * de somente novas Doubles
    */
    public function testAddSameTypeDouble()
    {
        // criamos uma lista de doubles
        $list = new GenericList(G::Double);
        // adicionamos um double
        $list->add(10.1);
        // e se tudo deu certo, teremos um double
        // na lista e ela vai ter um tamanho = 1
        $this->assertEquals($list->size(), 1);
    }
    
    /**
    * Testa uma lista de Objetos se aceita adicao
    * de somente novas Objetos DO MESMO TIPO. Ou seja
    * Uma lista de "new Pessoa()" deve aceitar adicao
    * de somente "new Pessoa()"
    */
    public function testAddSameTypeObject()
    {
        // criamos um objeto que servira de
        // modelo para a lista...
        $obj = new StdClass();
        
        // ...e um objeto que sera adiciona na lista.
        // Note que ambos sao instancias de StdClass
        $obj2 = new StdClass();
        
        // criamos uma lista de objetos do tipo
        // stdclass
        $list = new GenericList($obj);
        // e adicionamos um objeto a esta lista
        $list->add($obj2);
        // se tudo deu certo, teremos um objeto
        // na lista e ela vai ter um tamanho = 1
        $this->assertEquals($list->size(), 1);

        // novamente criamos uma lista de objetos do tipo
        // stdclass, mas desta vez ao inves de passar um objeto
        // da classe, passamos o tipo de objeto que a lista
        // ira conter
        $list = new GenericList(G::type('stdclass')); // same as above, but using class name
        // e adicionamos um objeto do tipo especificado a lista
        $list->add($obj2);
        // novamente, se tudo deu certo, teremos uma
        // lista de tamanho = 1, e testamos isso
        $this->assertEquals($list->size(), 1);
    }
    
    /**
    * Aqui iremos forcar um erro no teste
    * Vamos criar uma lista de Integer e tentar
    * adicionar outros tipos. Em todos os casos
    * diferentes do tipo original, devera ocorrer
    * um erro
    */
    public function testAddDifferentTypeInteger()
    {
        // criamos a lista de integer
        $list = new GenericList(G::Integer);

        // nos casos em que vamos utilizar algo diferente
        // de integer, utilizamos bloco try/catch. O
        // comportamento da lista sera de gerar uma
        // exception no caso de adicao de um tipo diferente

        try {
            // tentamos adicionar uma string na lista de
            // integer
            $list->add('foo');
            
            // nao deveria chegar neste ponto, visto que
            // na linha acima deveria ter sido gerada. Entao
            // dizemos ao phpunit que este teste falhou
            $this->fail('Generic Integer accepted a string');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add(2.1);
            $this->fail('Generic Integer accepted a double');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add(new StdClass());
            $this->fail('Generic Integer accepted a object');
        } catch (InvalidGenericTypeException $e) {}
    }
    
    /**
    * Aqui iremos forcar um erro no teste
    * Vamos criar uma lista de Strings e tentar
    * adicionar outros tipos. Em todos os casos
    * diferentes do tipo original, devera ocorrer
    * um erro
    */
    public function testAddDifferentTypeString()
    {
        // criamos a lista de strings
        $list = new GenericList(G::String);

        // e tentamos adicionar outros tipos, do mesmo
        // modo que fazemos no teste anterior
        
        try {
            $list->add(1);
            $this->fail('Generic String accepted a integer');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add(1.0);
            $this->fail('Generic String accepted a double');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add(new StdClass());
            $this->fail('Generic String accepted a object');
        } catch (InvalidGenericTypeException $e) {}
    }
    
    /**
    * Aqui tambem iremos forcar um erro no teste
    * Vamos criar uma lista de Double e tentar
    * adicionar outros tipos. Em todos os casos
    * diferentes do tipo original, devera ocorrer
    * um erro
    */
    public function testAddDifferentTypeDouble()
    {
        // criamos a lista de Double
        $list = new GenericList(G::Double);

        // e depois eh aquilo...

        try {
            $list->add(1);
            $this->fail('Generic Double accepted a integer');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add('foo');
            $this->fail('Generic Double accepted a string');
        } catch (InvalidGenericTypeException $e) {}

        try {
            $list->add(new StdClass());
            $this->fail('Generic Double accepted a object');
        } catch (InvalidGenericTypeException $e) {}
    }
    
    /**
    * Novamente o erro.
    * Vamos criar uma lista de Objetos e tentar
    * adicionar outros tipos. Em todos os casos
    * diferentes do tipo original, devera ocorrer
    * um erro
    */
    public function testAddDifferentTypeObject()
    {
        $obj = new StdClass();
        // criamos uma lista de objetos StdClass
        $list = new GenericList($obj);

        try {
            // nao podemos adicionar Integer
            $list->add(1);
            $this->fail('Generic Object accepted a integer');
        } catch (InvalidGenericTypeException $e) {}

        try {
            // e nem String
            $list->add('foo');
            $this->fail('Generic Object accepted a string');
        } catch (InvalidGenericTypeException $e) {}

        try {
            // nem double
            $list->add(2.5);
            $this->fail('Generic Object accepted a double');
        } catch (InvalidGenericTypeException $e) {}

        try {
            // e nem outro tipo de objeto, neste caso uma
            // Exception
            $list->add( new Exception());
            $this->fail('Generic Object accepted a Exception');
        } catch (InvalidGenericTypeException $e) {}

        // mesma ideia, mas utilizando uma lista criada
        // com o nome da classe
        $obj = new StdClass();
        $list = new GenericList(G::type('stdclass'));

        try {
            $list->add(1);
            $this->fail('Generic Object accepted a integer');
        } catch (InvalidGenericTypeException $e) {}

    }
    
    /**
    * Neste teste vamos verificar se a nossa lista
    * aceita um objeto de uma classe diferente da
    * classe definida como o tipo da lista, mas sendo
    * uma classe "extendida":
    *
    * class Car{}
    * class Ferrari extends Car {}
    *
    * criamos uma lista de Car e tentaremos adicionar
    * uma Ferrari
    */
    public function testAddParentClassObject()
    {
        // criamos 2 classes em runtime
        // php nao aceita criacao de nested classes
        eval('class Car {}');
        eval('class Ferrari extends Car {}');
        
        $obj = new Car();
        // criamos uma lista de Car, da mesma forma que criavamos
        // anteriormente. Entao se tentarmos adicionar uma Ferrari
        // na lista, devera ser gerada uma exception
        $list = new GenericList($obj); // creates a Car's list

        try {
            // adicionar uma Ferrari em uma lista de Car
            // deve gerar uma exception
            $list->add(new Ferrari()); // must throw a exception
            $this->fail('Generic Object accepted a parent class object without G::extend');
        } catch (InvalidGenericTypeException $e) { }

        $obj = new Car();

        // agora vamos definir que a nossa lista pode
        // receber alem de Car, classes que herdem Car
        // Collection c = new Collection<? extends Car>()
        $list = new GenericList(G::extend($obj));
        // Ferrari herda Car, entao esperamos que funcione
        $list->add(new Ferrari()); 


        // fazemos a mesma coisa, mas utilizando o nome da classe
        // ao inves de um objeto
        
        $obj = new Car();
        $list = new GenericList(G::type('car')); // creates a Car's list

        try {
            $list->add(new Ferrari()); // must throw a exception
            $this->fail('Generic Object accepted a parent class object without G::extend');
        } catch (InvalidGenericTypeException $e) { }

        $obj = new Car();
        $list = new GenericList(G::extend($obj));
        $list->add(new Ferrari()); // should work
    }
}

Após terminarmos de escrever o teste, temos bem definido uma grande parte da classe que vamos implementar. Já temos pelo menos 3 métodos públicos definidos:

__construct($param):
O construtor da classe, vai receber pelo menos um
parâmetro, indicando o tipo de lista. $param
deverá ser salvo para uso futuro, então teremos
uma propriedade na classe para isso

add($param):
Adiciona um item na lista. Assim como o parâmetro
passado no construtor, deveremos salvar este parâmetro
para uso futuro. Então teremos mais uma propriedade na
classe

size():
Retorna o tamanho da lista. Provavelmente vai retornar
a quantidade de itens adicionados em add($param), que
estao armazenados em uma propriedade da classe, que
certamente sera um array. Pensando em sizeof() ?

E também pelo menos 2 propriedades.
Observando o teste notamos também mais duas classes: G, que contem os tipos disponíveis e dois métodos, e também uma classe InvalidGenericTypeException, que representa as exceptions geradas por GenericList.

2 – Implementação: g.class.php
A Classe G (de Generic), contém tres constantes – Integer, String e Double – e dois métodos estáticos – extend e type.
O método type será utilizado para informar para lista o tipo de valor que ela vai conter, sem que ela precise descobrir por si. Já o método extend será utilizado para informar a lista que o tipo a ser utilizado pode ser também subtipos. Os dois métodos vão retornar um array que será trabalhado pela lista. Vamos a implementação:

g.class.php

<?php
/**
* Classe que contem a definicao dos tipos disponiveis
*/
class G {
    /**
    * Constantes com os tipos
    */
    const Integer = 'integer';
    const String  = 'string';
    const Double  = 'double';

    public static function extend($obj)
    {
        if (!is_object($obj)) {
            throw new InvalidGenericTypeException('G::parentsOf must be called with a valid object');
        }
        return array('extend' => strtolower(get_class($obj)));
    }
    
    public static function type($val)
    {
        return array('type' => strtolower($val));
    }
}

2 – Implementação: invalidgenerictypeexception.class.php
Uma exception simples, desnecessária explicação

invalidgenerictypeexception.class.php

<?php
class InvalidGenericTypeException extends Exception {
    public function __construct($message)
    {
        parent::__construct($message);
    }
}

4 – Implementação: genericlist.class.php
Aqui temos, finalmente, a implementação da lista em si. Note que esta implementação chega a ser bem mais simples que o teste. Veja o código e acompanhe pelos comentários:

genericlist.class.php

<?php
/**
* Implementacao da lista
*
* Tem apenas proposito educacional, poderia implementar
* algum iterator, ter metodos para remover ou retornar
* um item, buscar por um item, etc.
*/
class GenericList {
    
    /**
    * Tipo de valores da lista
    */
    private $type = null;
    
    /**
    * Valores da lista
    */
    private $values = array();
    
    /**
    * Informa se o tipo deve levar em consideracao
    * tambem os subtipos
    */
    private $useExtend = false;
    
    /**
    * Construtor da classe
    *
    * Baseado no parametro recebido, inicializa as propriedades
    * $this->type e $this->useExtend
    */
    public function __construct($type)
    {
        // se o parametro for um objeto, o tipo
        // da lista sera definido para o nome da
        // classe utilizada para criacao do objeto,
        // em minusculo
        if (is_object($type)) {
            $this->type = strtolower(get_class($type));
        } 
        // se nao for um objeto, pode ser um array,
        // G::String, G::Double ou G::Integer
        else {
            // caso seja um array...
            if (is_array($type)) {
                // ... verifica se existe a chave
                // extend. Caso exista, significa que
                // o tipo foi definido por meio de
                // G::extend
                if (array_key_exists('extend', $type)) {
                    // define que estara levando em consideracao
                    // os subtipos do tipo
                    $this->useExtend = true;
                    // define o tipo
                    $this->type = $type['extend'];
                // caso exista a chave 'type', significa que o tipo
                // da lista foi definida com g::type. Apenas
                // pega o valor de type e seta como padrao para
                // a lista
                } else if(array_key_exists('type', $type)) {
                    $this->type = $type['type'];
                }
                // nao deveria chegar aqui
                else {
                    throw new InvalidGenericTypeException('Undefined type');
                }
            }
            // se nao for array, o tipo foi passado diretamente como
            // parametro. Apenas utiliza-o
            else {
                $this->type = $type;
            }
        }
    }
    
    /**
    * Adiciona um valor a lista
    */
    public function add($val)
    {
        // verifica se o tipo sendo adicionado eh um objeto
        // e se o tipo do objeto eh diferente do tipo definido
        // para a lista
        if (is_object($val) && strtolower(get_class($val)) != $this->type) {
            // caso o tipo seja diferente, verifica se a lista foi criada
            // com a opcao de se utilizar subtipos do tipo (extend)
            if ($this->useExtend != true || !($val instanceof $this->type)) {
                // caso nao tenha, gera uma exception
                throw new InvalidGenericTypeException(sprintf('Invalid object type: %s, expected: %s', strtolower(get_class($val)), $this->type));
            }
        }
        
        // caso o tipo sendo adicionado nao seja um objeto
        // e seja diferente do tipo definido para a lista,
        // gera uma exception (tambem)
        if (!is_object($val) && gettype($val) != $this->type) {
            throw new InvalidGenericTypeException(sprintf('Invalid type: %s, expected: %s', get_class($val), $this->type));
        }
        
        // se chegou ate aqui, nao ha erros
        $this->values[] = $val;
        
        // retorna o valor sendo adicionado a lista
        return $val;
    }
    
    /**
    * Retorna o tamanho da lista
    */
    public function size()
    {
        // exatamente como imaginou no teste,
        // um simples sizeof
        return sizeof($this->values);
    }
}

5 – Testando
Depois de termos as implementações, já podemos testar:

$ phpunit TestGenericList.php
PHPUnit 3.0.6 by Sebastian Bergmann.

……….

Time: 00:00

OK (10 tests)

E tudo ok. Não é comum passar 100% logo na primeira implementação, mas tudo bem.

6 – Considerações
Algumas coisas que podemos notar:

– Antes mesmo de escrever a implementação da classe GenericList já tinhamos “pronto” nos testes o comportamento que desejavamos
– Todos os testes FALHAVAM antes da implementação da classe. E falhavam de duas formas: Primeiro porque não havia a classe e nem os métodos, então havia a falha de classe não encontrada. Segundo, por mais que tivessemos a classe e os métodos, mas os métodos não tivessem sido implementados, os testes iriam falhar. Este é o comportamento correto de um teste. É comum quando se começa a escrever testes (unitários) fazer com que eles passem antes mesmo de falhar.
– Escreva um teste para falhar e não para passar
– Em PHP não poderemos (não no 5, 6 e nem tão cedo, acredito) utilizar de forma “elegante”, como se faz em outras linguagens, a GenericList como parâmetro de um método, mas podemos emular isso:
java: public void showNomes(GenericList<Pessoa> pessoas)
c#: public void ShowNomes(GenericList<Pessoa> pessoas)
php: um leve “hack”
public function showNomes(GenericList $pessoas)
{
$pessoas->getType() == ‘pessoa’ || eval(‘throw new InvalidGenericTypeException(“Expected GenericList”);’);
}
– Como escrevi ali antes, sabiamos o comportamento que desejamos para a nossa classe. Há um outro método de design que dá mais ênfase nisso. Veja:
behavior-driven.org
Behavior driven development

É isso ai. Mais, em breve.

Arquivos:
g.class.php
genericlist.class.php
invalidgenerictypeexception.class.php
TestGenericList.php





xml to mysql: Transformando um arquivo XML em queries SQL (e blablabla)

7 07 2007

xml to mysql: Como fazer o reverso indicado aqui: mysql to xml: Como transformar um resultado de uma query para XML ( de forma simples ) ( de forma simples )

Sugerido aqui, por Robertson Matos

Para o script, vamos utilizar um banco de dados de exemplo. O banco terá a seguinte estrutura:

-- criamos o banco de dados, caso ainda nao exista
CREATE DATABASE IF NOT EXISTS XML2MYSQL;

-- selecionamos o banco de dados
USE XML2MYSQL;

-- criamos a tabela se nao existir
CREATE TABLE IF NOT EXISTS exemplo (
    id_exemplo INT(11) NOT NULL AUTO_INCREMENT,
    descricao VARCHAR(32) NOT NULL,
    PRIMARY KEY(id_exemplo)
)ENGINE=INNODB;

O XML de exemplo é este:
demo.xml


<?xml version=“1.0”?>
<resultset>
    <line>
        <id_exemplo>1</id_exemplo>
        <descricao>Primeiro exemplo</descricao>
    </line>
    <line>
        <id_exemplo>2</id_exemplo>
        <descricao>Segundo exemplo</descricao>
    </line>
    <line>
        <id_exemplo>3</id_exemplo>
        <descricao>Terceiro exemplo</descricao>
    </line>
</resultset>

E o script é este:


<?php
    
// arquivo xml2mysql.php

    // definimos informacoes para conexao ao banco de dados:
    $banco   ‘xml2mysql’;             // nome do banco
    
$host    ‘localhost’;             // host
    
$usuario ‘root’;                  // usuario
    
$senha   ;               // senha
    
    // connectamos ao host e selecionamos o banco
    
mysql_connect($host$usuario$senha);
    
mysql_select_db($banco);

    // esta variavel sera a query utilizada para insercao dos
    // dados no banco, tendo o %d substituido pelo id_exemplo
    // e o %s substituido pela descricao
    
$query ‘insert into exemplo (id_exemplo, descricao) values (%d, “%s”)’;
    
    
// carregamos o conteudo do arquivo XML
    
$xml file_get_contents(‘demo.xml’);
    
    
// transformamos o XML em um objeto, para que possamos trabalhar
    // de forma mais simples com ele
    
$sxml = new SimpleXMLElement($xml);
    
    
// aqui iteramos por todo o XML:
    // a cada iteracao, $line ira conter um objeto to tipo
    // SimpleXMLElement, sendo o equivalente a:
    //
    //  <line>
    //      <id_exemplo>1</id_exemplo>
    //      <descricao>Primeiro exemplo</descricao>
    //  </line>
    //
    
foreach ($sxml as $linhaAtual) {
        
// aqui vamos obter cada item do xml que seria referente
        // ao campo da tabela.
        // Necessario cast pelo fato de que a propriedade do objeto
        // tambem é um objeto do tipo SimpleXMLElement
        
$id        = (int) $linhaAtual->id_exemplo;
        
$descricao = (string) $linhaAtual->descricao;
        
        
// aqui preparamos a query para ser utilizada
        
$tmpQuery sprintf($query$id$descricao);
        
        
// exibimos a query que será executada
        
echo sprintf(‘Executando query: %s%s’$tmpQuery“\n”);

        // e aqui executamos a query
        
mysql_query($tmpQuery);
    }

Sem controle de erros, prevenção de sql injection, blablabla, apenas para ilustrar, como de costume.
Executando temos:

$ php xml2mysql.php
Executando query: insert into exemplo (id_exemplo, descricao) values (1, “Primeiro exemplo”)
Executando query: insert into exemplo (id_exemplo, descricao) values (2, “Segundo exemplo”)
Executando query: insert into exemplo (id_exemplo, descricao) values (3, “Terceiro exemplo”)

E conferindo no banco:

mysql> select * from exemplo;
+————+——————+
| id_exemplo | descricao |
+————+——————+
| 1 | Primeiro exemplo |
| 2 | Segundo exemplo |
| 3 | Terceiro exemplo |
+————+——————+
3 rows in set (0.00 sec)

É isso ai, valeu pela sugestão Robertson

Arquivos:
xml2mysql.php
xml2mysql.sql
demo.xml





PHP / refactoring: Como ? (Parte 2)

25 05 2007

Pois é, eu não sabia que teria parte 2, mas este é para ser uma continuação do post PHP / refactoring: Como ?.

Vejamos agora como modificar o nome de uma função e as chamadas para a mesma. O exemplo anterior com algumas modificações será utilizado:


<?php
// arquivo sample2.php

$foo   ‘foo’;
$hello “Ola $foo\n”;

// funcao foo() no comentario nao sera modificada

function foo()
{
    
// variavel $foo nao sera modificada
    
$foo “dentro de foo()\n”;
    echo 
$foo;
}

// exibimos $hello
echo $hello;

// observe que foo() e foo () sao diferentes: 
// foo() equivale a:
//      T_STRING        (foo)
//      (               (abre parenteses)
//
// enquanto foo () equivale a:
//      T_STRING        (foo)
//      T_WHITESPACE    (‘ ‘)
//      (               (abre parenteses)
//
// e por esse motivo deve ser tratado como diferente
foo ( );
?>

E a função com exemplo de uso:


<?php
// arquivo change_function_name.php

/**
 * Funcao que ira modificar o nome de uma funcao
 *
 * @param  array  $tokens  Array com os tokens a serem analisados
 * @param  string $oldName Nome da funcao que sera modificada
 * @param  string $newName Novo nome da funcao
 * @return string Codigo-fonte referente aos tokens fornecidos, com a 
 * funcao devidamente modificada
 */
function changeFunctionName($tokens$oldName$newName)
{
    
// variavel que ira conter o codigo-fonte a ser retornado
    
$ret ;

    // contador, marcara o indice do token em uso
    
$tokenIndex = –1;
    
    
// como $tokens e’ um array, utilizamos foreach para
    // passar por todos os seus itens
    
foreach ($tokens as $token) {
        
$tokenIndex++;
        
// quando se obtem os tokens de um arquivo, eles vem em um 
        // array contendo o numero do token e o valor ou em uma string,
        // contendo apenas o valor. Entao verificamos qual o caso.
        
if (!is_array($token)) {
            
$tokenName  false;
            
$tokenValue $token;
        } else {
            
$tokenName  token_name($token[0]);
            
$tokenValue $token[1];
        }

        // verificamos se o token atual pode ser considerado uma funcao
        
if ($tokenName == ‘T_STRING’) {
            
// obtemos os dois proximos tokens
            
$nextToken  $tokens[$tokenIndex 1];
            
$nnextToken $tokens[$tokenIndex 2];

            // aqui temos o seguinte:
            // se o proximo token for igual a “(” E se o valor do token atual for igual
            // ao nome da funcao que queremos modificar…
            
$flag  = ($nextToken[0] == ‘(‘ && $tokenValue == $oldName);
            
            
// … OU o proximo token for um espaco E o token apos o proximo for igual a “(“
            // E o valor do token atual for igual ao nome da funcao que queremos modificar…
            
$flag |= (@token_name($nextToken[0]) == ‘T_WHITESPACE’ && $nnextToken[0] == ‘(‘ && $tokenValue == $oldName);

            // … entao modificamos o valor do token,
            
if ($flag) {
                
$tokenValue $newName;
                
$ret .= $tokenValue;
                continue;
            }
        }
        
        
// se chegou ate aqui, significa que nao e’ um token que nos interessa, apenas pegamos
        // o valor do token e o adicionamos ao final do fonte a ser retornado
        
$ret .= $tokenValue;
    }
    
    
// retornamos o fonte
    
return $ret;
}

// exemplo de uso da funcao

// obtemos o conteudo do arquivo que iremos modificar
$arquivo file_get_contents(‘sample2.php’);

// e utilizamos a funcao token_get_all para obter todos
// os tokens do arquivo. Observe que tudo no script e’
// tratado como sendo um token
$tokens  token_get_all($arquivo);

// modificamos foo() para bar()
$bla changeFunctionName($tokens‘foo’‘bar’);

echo $bla;

?>

Executando:

$ php change_function_name.php
<?php
// arquivo sample2.php

$foo = ‘foo’;
$hello = "Ola $foo\n";

// funcao foo() no comentario nao sera modificada

function bar()
{
// variavel $foo nao sera modificada
$foo = "dentro de foo()\n";
echo $foo;
}

// exibimos $hello
echo $hello;

// observe que foo() e foo () sao diferentes:
// foo() equivale a:
// T_STRING (foo)
// ( (abre parenteses)
//
// enquanto foo () equivale a:
// T_STRING (foo)
// T_WHITESPACE (‘ ‘)
// ( (abre parenteses)
//
// e por esse motivo deve ser tratado como diferente
bar ( );
?>

Observe que a declaração da função foi modificada ( function bar ) e a chamada à função também foi modificada (bar ( ); ).
Ótimo, esta pronto ? Não…

Como bem sabemos, é possível executar funções de outras formas em PHP, como por exemplo, utilizando call_user_func, eval, variáveis utilizadas como funções ( $foo(); ), funções utilizadas como callback, …

Então, em uma implementação completa desta função, deveriamos nos atentar a estes fatos.
Para alguns casos, seria relativamente fácil criar uma implementação:
– Em eval, poderiamos utilizar a string que esta sendo executada e, recursivamente, chamar a função changeFunctionName para os tokens obtidos para esta string
– Em call_user_func / call_user_func_array, poderiamos verificar se o token atual é um T_STRING e o valor do token é ‘call_user_func’, desta forma. Se for, analisaria-se os tokens seguintes para ver se não teria relação com a função sendo renomeada;

Já em outros casos, seria necessário um pouco mais de malabarismos:
– Em variáveis utilizadas como funções, provavelmente precisariamos de um trackback das variáveis, para verificar se o valor dela não corresponde ao nome da função que desejamos renomear
– Utilizar um grande switch para verificar todas as funções builtin do PHP que utilizam funções como callback

De qualquer forma, para fontes onde não se utilize a função sendo renomeada de forma mais “avançada”, este exemplo deve funcionar.

change_var_name.php (exemplo anterior)
change_function_name.php





Resposta do Desafio do dia / 2

20 04 2007

Resposta do Desafio do dia / 2

Vamos por partes:

Um dos objetivos deste desafio era demonstrar que blocos de códigos podem ser utilizados em qualquer parte, e não apenas em estruturas de controle, decisão, etc. O seguinte trecho de código é perfeitamente válido e funcional (apesar de “estranho”)

<?php
  
{
      
$foo ‘foo’;
      echo 
$foo;
  }

Como pode se observar no desafio, coloquei o código em diversos blocos, mas apenas para isso: Demonstrar que é possível utilizar em qualquer lugar. Para resolução do desafio, é indispensável conhecimento de operações bit-a-bit. Como não é todo mundo que tem esse conhecimento, um outro objetivo do desafio era despertar a curiosidade de quem não soubesse e quisesse resolver e também falar um pouco sobre isso (na resposta). Segue agora então o mesmo código, comentado, com a resposta do desafio:


<?php
  
// desafio2.php

  // para acompanhar, segue alguns numeros binarios:
  //
  // 000 0
  // 001 1
  // 010 2
  // 011 3
  // 100 4
  // 101 5
  // 110 6
  // 111 7
  // iniciamos $x com valor 2
  $x 2;
  {
    
// aqui temos também um pequeno truque:
    // print é utilizado para exibir uma mensagem,
    // e sempre retorna 1. Logo, sera exibida a
    // mensagem ‘Iniciando ‘ e $x sera incrementado
    // em 1. Neste momento temos:
    // $x = 3
    
$x += print(‘Iniciando ‘);
    {
        
// aqui sera exibido ‘desafio 2’
        
echo ‘desafio 2’;
        
        
// aqui temos a primeira operacão bit-a-bit,
        // um ‘left shift’ de $x em 1 passo. Vejamos
        // o que tinhamos e o que sera retornado para $y
        // 
        // $x = 011 (3)
        //
        // deslocando 1 bit a esquerda teremos 110 (6)
        //
        // nesse momento temos:
        //
        // $x = 3 (nao modificamos $x)
        // $y = 6
        
$y $x << 1;
    }
    
// aqui são feitas várias operações. Vamos começar
    // pelas mais internas e com maior prioridade:
    //
    // $y^$x
    //
    // Aqui temos um $y xor $x. XOR – exclusive or – retornará
    // os bits que estão ativos em $y e os que estão ativos
    // em $x,  mas não os que estão ativos em ambos: veja:
    //
    // $x = 011
    // $y = 110
    // —- 101
    //
    // observe que como o segundo bit estava ativo em ambos,
    // ele nao estara ativo na saída. Entao após esta operação, 
    // o retorno será 5 (101)
    //
    // logo após esta operação, temos um “not”:
    //
    // ~($y^$x), ou seja ~(5) ou ~5.
    //
    // aqui ocorre uma inversão de cada bit: Os que estão ativos
    // serão desativados e os que estão desativados serão ativados:
    //
    // 5 – 101
    // — 010
    //
    // o resultado desta operação então seria 2 (010)
    //
    // Mas tem um pequeno “problema”: A operacao deve ser feita em
    // todos os bits. Logo, ~5 = 11111111111111111111111111111010 e
    // 11111111111111111111111111111010 = -6
    //
    // temos entao que o resultado da operacao ~($y^$x) = -6
    // o resultado disso tudo é retornado por sprintf.
    // 
    // Aproxima operação, por ordem dos operadores, é $y + (-6), ou 
    // seja $y + sprintf(‘%d’,~($y^$x)). Isso porque o operador ‘+’ tem
    // precedência ante ao ‘|’. Então temos
    //
    // $6 + (-6) = 0
    //
    // apos isto, temos “$x | 0”. Aqui o os bits que estão ativos ou 
    // em $x ou em 0 (ou ambos, diferentemente do xor) estarão ativos na
    // saída. Como 0 não tem bits ativos, o resultado da operação sera o
    // própio valor de $x:
    //
    // $x = 011
    // 0  = 000
    // —- 011 (3)
    //
    // finalmente, temos que $y = 3
    //
    // ao final disso entao:
    //
    // $x = 3
    // $y = 3
    
$y $x $y sprintf(‘%d’,~($y^$x));

    // apenas exibimos um “\n”. O $z não é utilizado mais
    
$z = print(“\n”);
    
    
// verificamos se $x é menor do que $y (não é)
    
if ($x $y) {
        
$x ^= $x;
    
// observe que aqui não temos um else. Então esse trecho
    // será executado de qualquer forma (mesmo que o if anterior
    // fosse verdadeiro. Foi utilizado apenas para “confundir” : )
    
} {
        
// Fazemos um xor de $y em $y. Lembre-se que no xor, o retorno
        // são os bits que estão ativos ou em um ou em outro número, mas
        // não em ambos. Como o xor está sendo feito no mesmo número, o que
        // estiver ativo em um também estará ativo em outro. Logo, o retorno
        // será 0 (este tipo de operação é bastante utilizada em assembly, 
        // quando se deseja zerar o valor de algum registrador: “xor eax, eax”
        // “xor ebx, ebx”, …)
        //
        // $y = 011
        // $y = 011
        // —- 000
        //
        //
        
$y ^= $y;
    
// novamente utilizamos um bloco, que será executado de qualquer forma
    
} {
        
// verificamos se $x (3) é maior do que $y (0). Como sabemos que é,
        // será exibido ‘$y menor do que $x’
        
if ($x $y) {
            echo 
‘$y menor do que $x’;
        
// e este trecho será ignorado : )
        
} else {
            echo 
‘$y maior do que $x’;
        }
    }
  }
  
// finalmente, exibimos um último “\n”
  
echo “\n”;
?>

Ao final disso, temos então:

Iniciando desafio 2
$y menor do que $x

É isso, espero que tenha aprendido um pouco sobre bitwise operations neste desafio.





PHP / refactoring: Como ?

15 04 2007

Refatorar código em PHP normalmente não é simples.

As IDEs que mais utilizei até hoje, como pdt (aka eclipse + plugins), zend studio e quanta plus não oferecem (ou não ofereciam na época) suporte a nenhum tipo de refatoração. Nada de renomear variável, função, classe, método.

Talvez a maior dificuldade em se trabalhar com com isso seja a tipagem fraca do PHP (somente talvez). Mas alguns trabalhos encontrados na internet, como por exemplo o phc – php compiler (não se deixe enganar pelo nome), podem ajudar: Há aqui um exemplo de como modificar chamadas de uma função para outra. Bastante trabalho para uma tarefa relativamente simples; O phc transforma o código php em uma AST – Abstract Syntax Tree e o exemplo atravessa essa árvore efetuando as modificações.

Algumas classes implementadas em PHP mesmo também são encontradas no PEAR. Mas também não há nada que seja muito utilizável.

Acredito que o que se tenha de mais “útil” no momento são as funções da extensão tokenizer (que é compilada por padrão). Com ela podemos trabalhar mais facilmente com um script, para criarmos algum tipo de ferramenta útil para refatoração. Vejamos por exemplo, como criar uma função para modificar o nome de uma variável:


<?php
// arquivo sample.php

$foo   ‘foo’;
$hello “Ola $foo\n”;

// variavel $foo no comentario nao sera modificada

function foo()
{
    
// variavel $foo neste contexto sera modificada
    
$foo ‘1’;
    echo 
$foo;
}
    
echo 
$hello;
?>


<?php
// arquivo change_var_name.php

/**
 * Funcao que ira modificar o nome de uma variavel
 *
 * @param  array  $tokens  Array com os tokens a serem analisados
 * @param  string $oldName Nome da variavel que sera modificada
 * @param  string $newName Novo nome da variavel
 * @return string Codigo-fonte referente aos tokens fornecidos, com a 
 * variavel devidamente modificada
 */
function changeVarName($tokens$oldName$newName)
{
    
// variavel que ira conter o codigo-fonte a ser retornado
    
$ret ;
    
    
// como $tokens e’ um array, utilizamos foreach para
    // passar por todos os seus itens
    
foreach ($tokens as $token) {
    
        
// quando se obtem os tokens de um arquivo, eles vem em um 
        // array contendo o numero do token e o valor ou em uma string,
        // contendo apenas o valor. Entao verificamos qual o caso.
        
if (!is_array($token)) {
            
$tokenName  false;
            
$tokenValue $token;
        } else {
            
$tokenName  token_name($token[0]);
            
$tokenValue $token[1];
        }

        // se nao ha o nome/numero do token, siginifica que ele nao e’ uma variavel,
        // pois elas contem o numero e o valor. Entao podemos adicionar o token ao
        // codigo-fonte que sera retornado e passar para o proximo token
        
if (!$tokenName) {
            
$ret .= $tokenValue;
            continue;
        }        
        
        
// verificamos se o token e’ uma variavel – T_VARIABLE. Se nao for, adicionamos
        // o valor do token ao codigo-fonte que sera retornado e passar para o proximo token
        
if ($tokenName != ‘T_VARIABLE’) {
            
$ret .= $tokenValue;
            continue;
        }

        // verificamos se o valor do token, ou seja, o nome da variavel, e’ o mesmo
        // que queremos modificar. Se nao for, o processo e’ o mesmo do anterior, adicionamos
        // o valor ao codigo-fonte e passamos para o proximo token
        
if ($tokenValue != $oldName) {
            
$ret .= $tokenValue;
            continue;
        }

        // se chegou ate aqui, significa que o token e’ uma variavel e tem o nome que
        // precisa ser modificado. Entao modificamos o valor do token e o adicionamos ao
        // codigo-fonte que sera retornado
        
        
$tokenValue $newName;
        
$ret .= $tokenValue;
    }
    
    
// retornamos o fonte
    
return $ret;
}

// exemplo de uso da funcao

// obtemos o conteudo do arquivo que iremos modificar
$arquivo file_get_contents(‘sample.php’);

// e utilizamos a funcao token_get_all para obter todos
// os tokens do arquivo. Observe que tudo no script e’
// tratado como sendo um token
$tokens  token_get_all($arquivo);

// modificamos $foo para $bar
$bla changeVarName($tokens‘$foo’‘$bar’);

echo $bla;

E, executando:

$ php change_var_name.php
<?php
// arquivo sample.php

$bar = ‘foo’;
$hello = "Ola $bar\n";

// variavel $foo no comentario nao sera modificada

function foo()
{
// variavel $foo neste contexto sera modificada
$bar = ‘1’;
echo $bar;
}

echo $hello;
?>

Exemplo bem simples de como poderiamos utilizar tokenizer para refatorar código. Observe que o nome da variável foi modificada tanto no contexto global, quanto no contexto da função “foo”. Alguns outros detalhes poderiam ser observados, mas quem sabe em um post futuro.

É bem possível (e acho que relativamente fácil) trabalhar melhor neste exemplo para se definir onde se deseja modificar a variável. E quem sabe nos próximos dias escrever uma classe para refatorar realmente, permitindo modificar nome funções, classes e outras coisas úteis.

E para quem não sabe o que é refatoração, vale uma lida no artigo da wikipedia e em Refactoring Home Page.





Validação de campos usando XML

23 03 2007

Há quanto tempo pessoas!
Pois é, a vida está bastante corrida, e o tempo anda curto… quando sobra algum, a gente acaba dormindo : | Meleca viu… Mas, lamentações a parte, vamos falar sobre o que realmente interessa: PHP!
Bom, como ando meio sem criatividade para postar, decidi que vou demonstrar uma maneira de realizar validação de campos, utilizando xml para caracterizar essa validação, fazendo com que você não precise sair que nem louco caçando as validações no meio do seu código o dia que precisar alterar alguma. Esse é o tipo de validação que o E. Silva e eu temos utilizado em nosso framework do qual ele já comentou ai em alguns comentários (frase linda).
Algumas idéias aqui, poderão parecer confusas de início para quem não está muito acostumado com PHP, mas como sempre digo, se tem alguma duvida, fique a vontade para mandar email ou deixar comentário que a gente resolve como pode.
Como sempre, gosto de começar com exemplos, então, vamos começar com o XML de exemplo:

validador.xml

<validator>
    <field name=‘nome’ validation=‘validator::isFilled’ when=‘insert,update’ />
    <field name=‘cpf’ validation=‘validator::isCpf’ when=‘insert,update’ />
    <field name=‘data’ validation=‘validator::isData’ when=‘insert’ />
</validator>

Esse ai será o xml que iremos usar para validar nossos campos. Vamos entender algumas coisas:
A propriedade ‘name’, identifica o nome do campo que será validado
A propriedade ‘validation’, define o método que será utilizado para validação
A propriedade ‘when’ define quando a validação deverá ocorrer. Essa propriedade será identificada através da ação que o botão do formulário fornece.

Os exemplos que serão trabalhados, utilizam o método POST para envio dos dados, e tem o action definido na própria página. Preparem-se, pois hoje teremos bastante código.
Vamos dar uma olhada agora, no código do formulário, que ficará no script que chamaremos de index.php

index.php

<form method=“POST”>
<input type=“text” name=“nome” /><br />
<input type=“text” name=“cpf” /><br />
<input type=“text” name=“data” /><br />
<!– agora vem o botão de submit, que identificará a ação que ocorre –>
<input type=“submit” name=“acao” value=“Insert” />
</form>

Esse script ainda não está pronto. Falta ainda o código PHP do mesmo. Agora, vou esclarecer um pouco melhor como será o funcionamento do script.
Todo o formulário, será (ou não), preenchido, e será feito o envio das informações ao se clicar no botão de Inserir. Quando o script receber esses dados, será invocada a classe, que lê o xml de validação, e aplica essa validação aos campos que foram recebidos. Os campos serão validados conforme descrição feita pelo XML, e a partir dai serão geradas as mensagens do sistema. O script que será demonstrado é um exemplo, para dar uma idéia de como se utilizar esse método de validação. Não estou demonstrando exatamente igual ao funcionamento do nosso framework, pelo simples fato, de que eu iria precisar de muitas classes do framework para isso, e então deixaria de ser um post de blog, para virar um livro : |
Veja então a classe que irá fazer o parse do arquivo xml, e validará as entradas fornecidas pelo usuário:

action.class.php


<?php

class Action {

    // Essa propriedade da classe, ira conter
    // o resultado da validacao de cada um dos
    // campos.
    
private $validacao = array( );

    // Essa propriedade ira dizer se ocorreu algum erro
    // para o script 
    
private $error null;

    // Retorna o valor da propriedade $error
    
public function getError( ) {
        return 
$this->error;
    }

    // Retorna o valor da propriedade $validacao
    
public function getValidacao( ) {
        return 
$this->validacao;
    }

    public function run( ) {
        
// normalmente, possuimos uma classe para trabalhar
        // com metodo POST e GET. No nosso caso, de forma a 
        // simplificar a quantidade de codigo, vamos usar a 
        // variavel $_POST diretamente

        // Essas aqui serao as acoes que iremos validar. Como
        // ja comentado, as acoes sao definidas atraves do valor
        // do botao que faz o metodo POST
        $validActions = array(
                            
'insert',
                            
'update',
                            
'delete' 
                        
);

        $acao strtolower$_POST['acao'] );
        if( 
in_array$acao $validActions ) ) {
            
// Inicializar a variavel de controle de erro
            
$this->error true;

            // Se chegou nesse ponto, significa que a acao realizada
            // foi uma das acima. Agora deve-se carregar o xml e começar a
            // fazer o seu parsing
            
$xml simplexml_load_file'validador.xml' );

            // A partir de agora, vamos avaliar cada uma das linhas do xml
            // e realizar a validacao.
            
foreach( $xml->field as $campo ) {

                // Aqui, vamos procurar pelo campo atual que encontramos no xml
                // dentro do array $_POST. Se o campo for encontrado, iremos
                // validar o valor que nos foi enviado.
                
if( array_key_exists( (string)$campo['name'], $_POST ) ) {

                    // Otimo, encontramos o campo no array POST. Porem antes
                    // de validarmos o campo, precisamos verificar se ele
                    // deve ser validado na acao que esta acontecendo.
                    // Lembra-se da propriedade 'when' no xml? Eh aqui
                    // que ela entra, nos informando se o campo deve ou nao
                    // ser validado. As acoes devem ser separadas por uma ','
                    // no xml
                    
$whenToValidate explode(','$campo['when'] );
                    if( 
in_array$acao$whenToValidate ) ) {

                        // Finalmente chegamos! Chegou aqui, entao esta na hora
                        // de validar o campo de acordo com o que esta no xml.
                        // O conteudo especificado na propriedade 'validation'
                        // do xml, define um metodo estatico a ser chamado para
                        // validar o valor que foi enviado no metodo post
                        // nos permitindo assim, assumit que esse metodo recebe
                        // ao menos 1 parametro, que eh o valor a ser validado
                        // Veja a classe com os metodos de validacao, para
                        // entender exatamente o que acontece
                        
$codigo sprintf('$ret = %s( $_POST["%s"] );',
                                          (string)
$campo['validation'],
                                          (string)
$campo['name']
                                         );

                        // Suponto que estamos validando o campo 'nome', que
                        // da maneira como está descrito no xml do tutorial,
                        // teremos $codigo valendo algo como:
                        // $ret = validator::isFilled( $_POST["nome"] );
                        // Executa-se o código de validação agora. A variavel
                        // $ret ira conter o resultado da validacao.
                        
eval( $codigo );

                        // Caso $ret seja diferente de true, estou assumindo
                        // que o retorno é entao uma mensagem de erro
                        
if( $ret === true ) {
                            
$this->validacao[(string)$campo['name']] = true;
                        } else {
                            
$this->error true;
                            
$this->validacao[(string)$campo['name']] = $ret;
                        }
                    }
                }
            }
        }
    }
}

?>

Com esse código em mãos, precisamos também ter a classe que faz validação do conteúdo digitado nos campos. Como foi denifido no xml, a classe que estamos usando é a classe Validator. A que irei apresentar, faz validações hipóteticas, não sendo recomendado o uso delas, da forma que estão. Veja o código:

validator.class.php


<?php

class Validator {

    // Método que verifica se há algum valor em $valor
    
public static function isFilled$valor ) {
        if( empty( 
$valor ) )
            return 
'Campo nao pode ser vazio';

        return true;
    }

    // Metodo que iria validaro valor, para saber se este
    // eh um cpf valido
    
public static function isCpf$valor ) {
        if( 
$valor == '11111111111' )
            return 
'CPF invalido';

        return true;
    }

    // Metodo que verificaria se a data informada
    // eh valida
    
public static function isData$valor ) {
        
$data explode('/',$valor);
        if( 
sizeof$data ) != )
            return 
'Data invalida';

        return true;
    }

}

?>

Já está quase tudo pronto, agora basta adicionarmos o código em nosso index.php, que fará a validação dos campos. Lembra-se daquele nosso primeiro index.php não é mesmo? Ele deve ficar assim agora:

index.php


<?php

include_once('validator.class.php');
include_once(
'action.class.php');
$action = new Action( );
$action->run( );

if( $action->getError( ) === true ) {
    
$camposInvalidos $action->getValidacao( );
    foreach( 
$camposInvalidos as $campo=>$e ) {
        if( 
$e !== true )
            
printf('Erro: campo %s - %s <br>'$campo$e ); 
    }
}

?>

<form method="POST">
    <input type="text" name="nome" /><br />
    <input type="text" name="cpf" /><br />
    <input type="text" name="data" /><br />
    <!-- agora vem o botão de submit, que identificará a ação que ocorre -->
    <input type="submit" name="acao" value="Insert" />
</form>

Nestes exemplos, eu levei em conta todos os arquivos estão em um só diretório, como pode ser observado pelos include_once que faço no início do script.

Esclarecendo tudo então:

  • O arquivo xml é o responsável por descrever a validação
  • A propriedade validation contém o nome da classe e do método, como se fossem uma chamada estática Classe::metodo
  • A propriedade when define em quais acoes o campo deve ser validado, e essa ações são separadas por ‘,’
  • A propriedade name define o nome do cmapo que está sendo validado

Bom, é isso!
Como de costume, os arquivos para visualização do código seguem abaixo, pois o wordpress é tosco, e zoa todo o código, para copiar e colar.
Qualquer dúvida/comentário/sugestão/elogio/doações, não deixe de contatar-me : )

[]s

Index.php
Action.class.php
Validator.class.php
Validador.xml