Aug 06 2008

Função simples para testar tempo de execução em PHP

Category: PHPfabiomcosta @ 11:14

Vou ser bem objetivo neste post. Mostrarei uma função bastante simples para que rapidamente você verifique o tempo de execução de seus scripts PHP. Após mostrar a função, exemplo de uso e resultado, explicarei o fucionamento da função.

function getTime(){
    static $tempo;
    list($usec, $sec) = explode(" ",microtime());
    if( $tempo == NULL ){
        $tempo = ((float)$usec + (float)$sec);
    }
    else{
        echo 'Tempo (segundos): '.(((float)$usec + (float)$sec)-$tempo).'';
    }
}
Esta função funciona no PHP 4 e 5. No PHP 5 foi adicionado um parâmetro a esta função, que serve como uma flag para que o retorno da função seja float ou não (documentação do PHP para a função microtime). Então uma forma de escrever esta função somente para PHP 5 seria:
function getTime(){
    static $tempo;
    if( $tempo == NULL ){
        $tempo = microtime(true);
    }
    else{
        echo 'Tempo (segundos): '.(microtime(true)-$tempo).'';
    }
}
Fica mais elegante porém só funciona para a versão 5 do PHP. Vou mostrar agora um exemplo de como usá-la para que vocês vejam a simplicidade de uso.
getTime();

$testeIteracao = 0; for($i=0;$i<1000000;$i++){ $testeIteracao++; }

getTime();

No código acima o resultado aqui na minha máquina foi:
Tempo (segundos): 0.19565415382385
Ou seja o PHP levou 195,65 milissegundos para executar este loop.

Como vimos, no código de exemplo, a função é chamada uma vez antes do código a ser testado e outra vez depois do código. Na primeira chamada a variável estática “$tempo” é declarada e possui valor “NULL” e por isso obedece ao “if( $tempo == NULL )” e recebe o valor da função “microtime()”. Na segunda chamada a variável “$tempo”, por ser estática, já possui um valor que é o tempo que foi armazenado na primeira chamada. Então entramos no “else” que subtrai o valor de uma nova chamada da função “microtime()” com o valor de “$tempo” e imprime esse valor. Dessa forma obtemos o tempo que levou da primeira chamada de “getTime()” até a segunda, ou seja o tempo de execução do código que está entre as duas chamadas da função.

As variáveis estáticas em classes, são variáveis que podem ser acessadas mesmo sem se ter um instância de um objeto daquela classe (Palavra-chave ‘static’ em classes). Porém no caso de uma variável estática em uma função este valor é mantido entre as chamadas da função, sendo mantido em seu escopo, por isso aconteceu o que foi mostrado.

Espero que tenham gostado, Abraço e até o próximo post.

Tags: , ,


Jun 02 2008

CakePHP – Herança de Helper

Category: CakePHP,PHPvbmendes @ 14:03

Ultimamente tenho utilizado o Framework CakePHP, e uma dúvida que me surgiu esses dias foi como um Helper herdar de outro helper. Por exemplo, criei um helper MyTimeHelper que herda do TimeHelper, e adicionei um método que sobrescreve o format. Entretanto, me ocorria um problema que o script não conseguia encontrar o TimeHelper, por ele não ter sido invocado pelo core do cake. Então eu adicionei a linha App::import(‘Helper’,'Time’); no início da definição do MyTimeHelper e o problema foi resolvido. Então o codigo fica assim:

App::import('Helper', 'Time');

class MyTimeHelper extends TimeHelper { ... }

Tags: ,


Mar 10 2008

Evitando múltiplos posts com session

Category: PHPvbmendes @ 17:52

Muitos desenvolvedores já se depararam com a situação de que usuários impacientes enviam formulários dezenas de vezes, e isso realmente incomoda, pois aparecem vários registros idênticos.

Uma forma de solucionar tal problema seria usando session. No momento do envio, salva algo que identifique(que chamarei de identidade) o formulário na SESSION, e sempre que for enviar algo, verifica se a identidade da session bate com a do fomulário enviado, se bater, o sistema impede que o formulário seja enviado novamente.

No caso de formulários de e-mail, eu particularmente uso como identidade a mensagem do e-mail criptografada por md5, visto que reduz significativamente o tamanho do que será guardado na session.

Bom, vamos aos códigos:

session_start();
$md5Mensagem = md5($_POST['mensagem']);
if(isset($_SESSION['md5Mensagem'] &amp;&amp; $md5Mensagem == $_SESSION['md5Mensagem']){
echo 'Esta mensagem já foi enviada';
} else {
if(ENVIA_MENSAGEM){
$_SESSION['md5Mensagem'] = $md5Mensagem;
echo 'Mensagem enviada com sucesso';
} else echo 'Mensagem não foi enviada';
}
O código acima está bem simplificado, eu encapsulei o processo de enviar a mensagem (função mail, gravar no banco, ou qualquer outro método) por fugir do escopo deste post.

O código verifica se existe algum md5Mensagem na session, e se ele é igual ao md5 da mensagem enviada, se sim, ele informa que a mensagem já foi enviada, se não, o sistema tenta fazer o envio, se der certo ele salva o md5 na session, se não, ele apenas informa que não deu certo.

Bom, é isso, qualquer dúvida, postem comentários que responderemos.

Tags: ,


Feb 11 2008

Mensagens de Erro com Session em PHP (serialize)

Category: PHPvbmendes @ 09:49

Pra quem pensa que a variável global SESSION é apenas para autenticação de usuários, aqui vai mais uma. O objetivo principal da SESSION, assim como dos COOKIES é enviar variáveis calculadas em uma página para outra página.

Irei apresentar aqui um sistema simples de mensagens de erro e confirmação com o uso da SESSION.

Primeiramente vou apresentar uma classe que eu usei para armazenar as mensagens:

<?
class Mensagem {
    public static $ERRO=1,$SUCESSO=2;

    private $mensagem;
    private $tipo;

    public function __construct($mensagem,$tipo){
        $this->mensagem = $mensagem;
        $this->tipo = $tipo;
    }

    public function getMensagem(){
        return $this->mensagem;
    }

    public function getTipo(){
        return $this->tipo;
    }

    public static function tipoToString($tipo){
        if($tipo == 1) return 'erro';
        else if($tipo == 2) return 'sucesso';
    }
}
?>

Pra quem já mexe com orientação a objetos, a classe acima é simples. Uma classe com 2 variáveis estáticas($ERRO e $SUCESSO) que seriam os tipos de mensagens, uso isso mais para definir o CSS que irá acompanhar a mensagem. Tem duas variáveis, uma para a mensagem em si, e outra para o tipo da mensagem, um construtor que define as variáveis, e funções para obter os valores das variáveis.

O uso é simples, vamos ver um caso abaixo:

Temos a página de formulário simplificada (form.php) da seguinte forma:

<?
if(isset($_SESSION['mensagens'])){
    $mensagens = unserialize($_SESSION['mensagens']);
    unset($_SESSION['mensagens']);
} else {
    $mensagens = array();
}

if(isset($_SESSION['pessoa'])){
    $pessoa = unserialize($_SESSION['pessoa']);
    unset($_SESSION['pessoa'];
} else {
    $pessoa = new Pessoa();
}

?></p>

<ul class="mensagens">
    <?= foreach($mensagens as $mensagem): ?>
    <li><p class="<?=Mensagem::tipoToString($mensagem->getTipo())?>"><?=$mensagem->getMensagem()?></p>  </li>
    <? endforeach; ?>
</ul>

<form action="insert.php" method="post">
    <input name="nome" value="<?=$pessoa->getNome()?>" type="text" />
    <input name="idade" value="<?=$pessoa->getIdade()?>" type="text" />
    <input type="submit" />
</form>

<p>

No código acima, criamos um formulário solicitando nome e idade. As primeiras linhas são código PHP, atente para a função unserialize, ela desserializa os dados que vieram da SESSION, visto que a mesma não suporta objetos. Pegamos da SESSION as mensagens, e apagamos da SESSION para que a mensagem só apareça uma vez. Fazemos o mesmo com um objeto do tipo pessoa, o qual armazena os dados que já foram enviados previamente, evitando que o usuário preencha o formulário novamente.

Abaixo vem uma lista, com todas as mensagens que estavam na SESSION. Atente para a função tipoToString, da classe mensagem, que eu criei apenas para definir o atributo class do parágrafo da mensagem.

Em seguida vem o formulário pegando todos os values do objeto pessoa que estava na SESSION.

Abixo teremos o script que irá processar este formulário (submit.php):

<?

$nome = $_POST['nome'];
$idade = $_POST['idade'];

$pessoa = new Pessoa();

if(!$nome) $mensagens[] = new Mensagem("O nome não foi informado",Mensagem::$ERRO);
else $pessoa->setNome($nome);

if(!is_numeric($idade)) $mensagens[] = new Mensagem("A idade deve ser um número!",Mensagem::$ERRO);
else $pessoa->setIdade($idade);

if(count($mensagens)){
    $_SESSION['mensagens'] = serialize($mensagens);
    $_SESSION['pessoa'] = serialize($pessoa);
    header("Location: form.php");
}

if(cadastra no banco){
    $mensagens[] = new Mensagem("Pessoa cadastrada com sucesso!",Mensagem::$SUCESSO);
} else {
    $mensagens[] = new Mensagem("Ocorreu um erro durante o cadastramento da pessoa!",Mensagem::$ERRO);
}

header("Location: form.php");

?>

No código acima, pegamos os dados do formulário através do POST e verificamos se os dados validam de acordo com algumas regras, caso contrário colocamos, em um array de mensagens, uma mensagem de erro. Depois de feita a validação, verificamos se existem mensagens de erro, caso positivo, serializamos as mensagens e a pessoa que possui alguns dados já cadastrados e colocamos na SESSION, fazendo o redirecionamento para o formulário, de forma que o usuário possa retificar seus erros.

Caso tudo ocorra dentro do normal, armazenamos uma mensagem de sucesso na SESSION, e redirecionamos para o formulário, para que o usuário efetue novo cadastro. Poderia redirecionar para a lista de cadastrados, ou qualquer outro lugar, contanto que o script tenha os códigos que lessem a mensagem de sucesso, mas para simplificar o meu post, eu criei apenas duas páginas.

Espero que isso ajude, e qualquer dúvida, comenta.

Tags: ,


Oct 25 2007

Compactar com GZip e armazenar em Cache arquivos .js e .css (solução)

Category: CSS,Javascript,PHPmeiocodigo @ 21:04

Editado:

Pessoal, para a melhor solução para este tipo de problema vejam a regra número 4 deste post. Se quizer entender mais sobre GZip continue lendo.

Fim da edição.

GZip é uma compactação feita pelo servidor web antes de enviar os arquivos para o browser, mas para que isso ocorra com sucesso deve-se mudar o cabeçalho dos arquivos para indicar que o arquivo aceita esse tipo de compactação, para que o servidor compacte este arquivo e quando esse arquivo chegar no browser, este descompacte-o, tornando-o um arquivo normal como todos os outros novamente.

A motivo de informação sobre os benefícios da compactação GZip darei um exemplo de uma biblioteca muito famosa, a JQuery, que tem em torno de 77kb em sua forma normal. Se for usado o compactador de javascript do Yahoo! ele passa a ter em torno de 46kb, e se for usada a compactação GZip em cima desse arquivo a biblioteca fica com incríveis 15kb!

Vinha a um tempo pensando em uma forma inteligente de compactar arquivos “.js” e “.css” usando GZip. Li vários artigos no Yahoo! e em fórums diversos e não encontrei nada falando a respeito, e quando falavam não era de forma clara, tanto é que só fui ver uma forma inteligente de se fazer isso a uns minutos atrás, quando achei algo a respeito no site do Joomla. É uma extensão que modifica o cabeçalho dos arquivos que são incluídos nas páginas desse sistema. Vendo a extensão eu pude ver o quão simples é modificar o cabeçalho de arquivos .css e .js de forma inteligente. Antes havia pensado em modificar a extensão dos arquivos para “.php” e fazer essas modificações (trabalho de corno), vi que funcionou mas não era viável em um projeto grande.

Bom, falei demais já. Vamos para o código da solução:

/**
* @author Fábio Miranda Costa<br />
* Usado para compactar arquivos JS e CSS, acrescenta-os ao cache do browser
* e faz algumas melhorias adicionais. <script src="incs/file_inc.php?file=funcs.js" type="text/javascript"><!--mce:0--></script>
*/
$file = $_GET["file"];
$ext = substr($file,strrpos($file, ".")+1,strlen($file));
ob_start ("ob_gzhandler");
if($ext=="js") $ext="javascript";
header( "Content-type: text/".$ext."; charset: <span class="attribute-value">iso-8859-1</span>");//não se esqueça de mudar para o charset que você usa
header( "Content-Encoding: gzip,deflate");
header( "Expires: ".gmdate("D, d M Y H:i:s", time() + (24 * 60 * 60)) . " GMT");//adiciona 1 dia ao tempo de expiração
header( "ETag: ");//a idéia é apagar o conteúdo da Etag, ver post http://www.meiocodigo.com/2007/12/21/melhorando-o-tempo-de-carregamento-de-um-site/
header( "Cache-Control: must-revalidate, proxy-revalidate" );
include($file);
ob_flush();
Fiz esse código o mais compacto possível, já que será usado em todos os includes de css e js do site. Não vou explicar muito como funciona, apenas como usá-lo, que é o que importa. Para usar é muito fácil, apenas crie um arquivos incluindo o código acima, no meu exemplo o arquivo é “file_inc.php”, e coloque no cabeçalho do seu site os includes de arquivos css e javascript seguindo os exemplos abaixo:

para javascript:

<script src="incs/file_inc.php?file=funcs.js" type="text/javascript"></script>
No atributo “src” coloque o caminho para o arquivo “file_inc.php” depois passe a variável “file” usando GET, ou seja adicione o texto “?file=” logo após o nome do arquivo, como pode-se ver no exemplo e depois coloque o endereço do arquivo “.js” que você quer incluir em seu site. Lembrando que esse endereço será sempre relativo ao arquivos file_inc.php, ou seja, se o “file_inc.php” estiver na pasta “inc/includer/” e o arquivo que você quer incluir está na pasta “inc/js/” o “src” do include ficará “inc/includer/file_inc.php?file=../js/funcs.js”.

para css:

<link href="incs/file_inc.php?file=styles.css" rel="stylesheet" type="text/css" />
Com o css é a mesma coisa, o que muda é apenas o óbvio, ou seja, a tag será “link” e o atributo usado passará a ser “href”.

Tags: , , , , ,


Jun 12 2007

Operaçoes matemáticas com datas em PHP

Category: PHPvbmendes @ 10:14

Hoje eu vou falar de um problema que eu vejo muito nos fórums de comunidades de programadores PHP iniciantes. As lendárias operações sobre datas.

Vou listar as funções mais significativas para o nosso problema:

Com estas três funções acima já é possível converter datas de um formato para outro facilmente.

Para exemplificar, eu irei criar um script que, baseado na minha data de nascimento(11/08/1986), converta-a para um formato usado em SGBDs (aaaa-mm-dd). Depois o script irá retornar a minha idade em dias, meses e anos.

Vamos primeiro aprender a converter data do formato mais amigável ao ser humano(dd/mm/aaaa) para o formato utilizado pela maioria dos SGBDs (aaaa-mm-dd). Para isso vamos usar o método explode, para separar os valores de dias, meses e anos, supondo que temos uma string no formato dd/mm/aaaa:

$data = "11/08/1986"; //minha data de nascimento
$vetor = explode("/",$data);
$dias = $vetor[0];
$meses = $vetor[1];
$anos = $vetor[2];
$data_formatada = $anos."-".$meses.'-'.$dias;
No código acima, foi gerado uma string de data no formato aceito pelos campos DATE dos SGBDs.

Para realizar operações entre datas, e desta forma, encontrarmos a diferença entre o dia de hoje e meu nascimento, usaremos o método mktime e o método date, aplicados aos valores de $dias, $meses e $anos obtidos no código acima. E o método date, para obter a data atual.

$dias_atuais = date("d");
$meses_atuais = date("m");
$anos_atuais = date("Y");
$diferenca_segundos = mktime(0,0,0,$meses_atuais - $meses,$dias_atuais-$dias,$anos_atuais-$anos);
$diferenca_minutos = $diferenca_segundos/60;
$diferenca_horas = $diferenca_minutos/60;
Pronto, com o código acima eu encontrei a minha idade em segundo, em minutos e em horas. Com essa mesma lógica é possível encontrar em dias e semanas. Meses e anos é um pouco mais complicado pois necessita de verificar quais meses tem 28, 30 ou 31 dias, e que anos são ou não bissextos.

Vamos deixar este detalhe para um próximo post.

Espero que tenha ajudado.


« Previous PageNext Page »