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





OFF: Netbeans GUI Builder

5 05 2007

Nunca gostei do eclipse.
Quando comecei a programar em java (há um tempo já) comecei com netbeans. Gostava muito dele, bem simples, organizado e vinha “com tudo” que eu precisava.

Cheguei a instalar e utilizar o eclipse durante um tempo, mas sempre achando o netbeans melhor. E em algum momento que não lembro quando, deixei de utilizar essas IDEs, utilizando editores mais simples e mais leves (sentia dor na alma quando utilizava kde+firebird+tomcat+eclipse/netbeans, por isso “abandonei”).

Quando passei a trabalhar mais intensivamente com PHP, voltei a utilizar o eclipse com plugins para habilitar desenvolvimento em PHP, e como trabalhava também com java, acabou que o eclipse voltou a tornar-se meu ambiente de desenvolvimento único. Era java e PHP no mesmo local. Simples assim.
E nesse tempo havia esquecido quase que completamente do netbeans, apenas acompanhando de longe o desenvolvimento do matisse, que era um “gui builder” em fase alpha dele.

Hoje vejo o estado que está esse “gui builder” e… Tirem 10 minutos e assistam:

netbeans 6 gui builder

Tão ou mais simples criar uma aplicação em java para desktop quanto o delphi (não, não o delphi4php :) )

Será que fazem isso no eclipse ?





Link: Comparação de IDES para desenvolvimento em PHP

5 05 2007

É um post antigo, mas bem interessante, onde é feita uma comparação entre Eclipse, Komodo, PHP Designer, PhpED, PHPEdit e Zend Studio:

Seven great PHP IDEs compared

Tinha idéia de escrever um post parecido, comparando também (se é que alguns desses podem ser comparados) o jdeveloper da oracle, com “Oracle PHP Extension for Oracle JDeveloper 10g”, o delphi4php, o pdt, o quanta, o kate, o kdevelop, o dreamweaver, o vim e o mcedit (este por ser o editor que uso atualmente : ) ).

Além desses, há outra dezena que poderia entrar na comparação / descrição, mas por não ter muito tempo pra isso, acho que vale a dica da comparação do link.





Notícias e links – 03/05

3 05 2007

Sim, elas ainda existem.
Links que fui guardando pra postar “amanhã”:

Implementing the Stage Pattern in PHP 5


Manipulating String Literals with Interpreter Classes in PHP 5
Quem conhece outras linguagens, ruby, java, python, certamente já conhece isso…

Emulating Analytic (AKA Ranking) Functions with MySQL: Part 2


PHP 5 Security Techniques
Segurança nunca é demais.

PHP and Serial Ports


MySQL UDF that interprets PHP
Bem antigo, mas não deixa de ser interessante.
Code As Data: Reflection in PHP

PHP on Hormones
Apresentação do Rasmus Lerdorf na Mysql Conf 2007