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





Desafio do dia / 2

18 04 2007

Depois de ter sido explicado o primeiro desafio, vamos partir para o segundo.
Que tal algumas operações bit-a-bit ?


<?php
  
// desafio2.php
  
$x 2;
  {
    
$x += print(‘Iniciando ‘);
    {
        echo 
‘desafio 2’;
        
$y $x << 1;
    }
    
$y $x $y sprintf(‘%d’,~($y^$x));

    $z = print(“\n”);
    
    if (
$x $y) {
        
$x ^= $x;
    } {
        
$y ^= $y;
    } {
        if (
$x $y) {
            echo 
‘$y menor do que $x’;
        } else {
            echo 
‘$y maior do que $x’;
        }
    }
  }
  echo 
“\n”;

A pergunta é: O que será exibido na execução do script ?
Resposta e explicação em breve.
Nota: Não vale executar para saber : )





Resposta Desafio do Dia

18 04 2007

Fala galerinha : )
Bom, existem certas pessoas ai que propoem desafios, não sabem resolver, e ai ficam me implorando no msn para que eu resolva.
Vamos então dar uma explicação aí sobre o funcionamento desse código, e demonstrar o resultado.


<?php
// desafio1.php

$n  'X';
$
$n 'N';
$m  'Y';
$
$m 'M';
$c  = (int)($n == $$n);

echo $
    {
        $c == 
    
$n:
    
$m
    
};
?>

Como todos sabem, o símbolo de $, indica a declaração de uma variável, mas muitos não conhecem a utilização do $$
O símbolo $$, significa que estamos utilizando o valor definido na váriavel, para transforma-la em variável. Hein?
Vejamos esse exemplo:


<?php

$x 'var1';

// Essa linha é o mesmo que $var1 = 'Oi, sou a variavel 1';
$$x 'Oi, sou a variavel 1';

echo $var1;

?>

Acho que esse exemplo deixa tudo bem claro né?
Acredito que outra parte que pode confundir no código, é o cast que o nosso amiguinho faz:
$c = (int)($n == $$n);
Isso ai nada mais é do que o retorno de um if. Ele verifica se $n == $$n, ou seja, está verificando se $n == $X. Caso seja, isso ai deve retornar true, caso não seja deve retornar false. Aquele (int) na frente, converte o tipo bool para o tipo int. True será convertido para 1, false será convertido para 0.
Como sabemos que $n não é igual a $X, essa sentença irá retornar 0.

Agora vem a pegadinha da coisa, a função echo. Irei escreve-la em uma só linha para ficar mais claro:

echo ${ $c == 1 ? $n : $m };

O que o rapazinho faz ai, é basicamente um if também, perguntando se $c é igual a 1 (que nós já sabemos que não é). Caso $c fosse igual a 1, essa sentença retornaria o valor de $n, mas como não é, o que ela faz é retornar o valor de $m. Sabemos que $m vale ‘Y’, logo, reescrevendo essa linha após o resultado do if, teriamos:

echo $Y;

$Y nós sabemos que vale ‘M’. Tadam! Respondido o desafio do menino E. Silva ; D
Espero que tenham achado interessante ; D

[]s





Desafio do dia

17 04 2007

O que será exibido durante a execução do seguinte script (se funcionar):


<?php
// desafio1.php

$n  ‘X’;
$
$n ‘N’;
$m  ‘Y’;
$
$m ‘M’;
$c  = (int)($n == $$n);

echo $
    {
      $c == 
        
$n:
        
$m
    
};
?>

Explicação no próximo post (junto com mais um 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.