PHP é à quinta-feira – 50 dicas sobre desempenho e segurança
Por Jorge Gomes para o Pplware
Desafiado pelo nosso caro Pedro Pinto para escrever um artigo, apresento este artigo sobre programação web em PHP.
Como devem saber, o PHP é uma linguagem de scripting "server-side", Open-Source e bastante popular, sendo utilizada por inúmeros sites e aplicações web dinâmicas, inclusive o próprio pplware e seu fórum. O facto de ser Open-Source significa que qualquer pessoa pode contribuir para o projecto, por exemplo, consultando o seu código fonte e sugerindo possíveis correcções, optimizações e funcionalidades, ou testando e documentando o mesmo, ou mesmo compilar e construir os binários à sua medida, escolhendo as bibliotecas que deseje e alterando o código fonte do que desejar ou precisar.
Como qualquer linguagem de programação, esta encontra-se optimizada para se obter o maior rendimento possível das suas funcionalidades, mas cabe ao programador a decisão da abordagem que irá tomar para desenvolver a sua aplicação e chegar a um consenso entre segurança e desempenho.
Venho então trazer um conjunto de dicas e boas práticas para optimização do vosso código PHP, para melhorar o desempenho geral das vossas aplicações web. Muitas destas dicas foram confirmadas em tutoriais, apresentações e relatórios pela própria Zend (empresa que desenvolve activamente o PHP), ou em outros locais, sendo citado esses mesmos locais onde estas foram encontradas.
50 dicas sobre desempenho e segurança no PHP:
1 - A função echo é mais rápida que print. [Citação] Mas a diferença não é muito notável, sendo mais lento em algumas ocasiões. [Citação]
2 - Ao trabalhar com Strings, caso não utilize avaliação de variáveis no meio da string ( "Olá {$name} !" ), evite utilizar aspas duplas (“) e opte por aspas simples (‘) porque o PHP irá sempre procurar pela existência de variáveis em strings com aspas duplas ("string exemplo"). [Citação]
3 - Prefira usar a função sprintf ao invés de variáveis no meio de strings com aspas duplas. É aproximadamente 10x mais rápido. [Citação]
4 - Utilize múltiplos parâmetros na função echo (echo 'string1', 'string2' , 'string 3' ; )em vez de concatenação de strings ( echo 'string1'. 'string2' . 'string 3' ; ). Note que a diferença está entre a utilização de ponto (.) - concatenação, e vírgula (,) - parâmetro. Isto apenas se refere a strings na função echo! [Citação]
5 - Evite realizar cálculos dentro de ciclos. Defina o limite máximo para os seus ciclos for antes e não lá dentro. Neste exemplo: for ($x=0; $x < count($array); $x), a função count é chamada a cada iteração do ciclo, quando o valor poderia estar ser guardado numa variável $max=count($array); antes desse mesmo ciclo. [Citação]
6 - Realize o Unset das suas variáveis ou atribua-lhes o valor NULL para libertar memória, especialmente em arrays com grandes dimensões. No caso de objectos, ambas as acções anteriores irão causar a invocação do destrutor (função __destruct()) da classe. Note que no caso da atribuição de NULL, a variável só será destruída assim que perder todas as referências para a mesma. [Citação]
7 - Evite métodos mágicos como __get, __set, __autoload. [Citação]
8 - Utilize require() / include() invés de require_once() / include_once(), pois estas últimas necessitam de verificar se o ficheiro já não foi incluído no código. [Citação]
9 - Utilize caminhos absolutos em includes e requires ao invés de caminhos relativos (../ficheiro.php) porque levam menos tempo a serem resolvidos para endereços reconhecíveis pelo sistema operativo. [Citação]
10 - require() e include() são idênticos com uma diferença: Na função require, a execução falha com erro fatal se o ficheiro estiver em falta. [Citação]
11 - Desde a versão 5 do PHP, o momento temporal em que o script iniciou a sua execução pode ser encontrado em $_SERVER[’REQUEST_TIME’]. Utilize isto em vez das funções time() ou microtime(). [Citação]
12 - Regex baseado em bibliotecas PCRE é muito mais rápido que EREG, mas se possivel recorra a funções nativas mais rápidas como strncasecmp, strpbrk e stripos. [Citação]
13 - Quando analisar XML no PHP, experimente xml2array, que faz uso das funções XML do PHP. Para HTML pode experimentar DOM document do PHP ou DOM XML na versão 4 do PHP. [Citação]
14 - str_replace é mais rápido que preg_replace, mas às vezes strtr é mais rápido que str_replace com grandes strings. Utilizar array() dentro de str_replace é usualmente mais rápido que múltiplos str_replace. [Citação]
15 - Ao contrário de outras linguagens de programação populares, declarações“else if” no PHP são mais rápidas que declarações select também conhecido como case/switch. [Citação] Com a evolução contínua do php, actualmente a diferença é muito insignificante, mas denota-se que a utilização do operador === (idêntico) nas condições if é mais rápido que a utilização do operador == (igual).
16 - Supressão de erros com @ é bastante custoso a nível de desempenho. [Citação]
17 - Para reduzir a utilização de largura de rede, active mod_deflate no Apache v2 [Citação] ou tente mod_gzip em Apache v1. [Citação]
18 - Encerre as conexões à sua base de dados quando não precisar mais delas. [Citação]
19 - Utilizar $row[’id’] é 7 vezes mais rápido que $row[id], porque se não fornecer aspas, o PHP terá que "adivinhar" qual o índice que se refere, assumindo que não seja uma constante. [Citação]
20 - Utilize as tags <?php … ?> para declarar PHP pois todos os outros estilos encontram-se classificados como DEPRECATED, incluindo as tags curtas (<? ... ?> ). [Citação]
21 - Utilize códico estrito, evite supressao de erros, notificações (notices) e avisos (warnings) resultando num código mais limpo e com menos sobrecargas. Considere activar sempre error_reporting(E_ALL) (em ambiente de desenvolvimento). [Citação]
22 - Scripts PHP são gerados 2-10 vezes mais lentamente pelo httpd do Apache que uma pagina estática. Tente utilizar mais páginas estáticas em vez de scripts no servidor. [Citação]
23 - Os scripts PHP (se não utilizarem cache) são interpretados e compilados no momento e cada vez que são chamados. Instale um produto de gestão de cache do PHP (como o memcached ou eAccelerator ou Turck MMCache) para tipicamente incrementar o desepenho em 25-100% removendo o tempos de compilação. Pode também configurar eAccelerator no cPanel utilizando EasyApache3. [Citação]
24 - Uma alternativa às técnicas de cache quando se tem páginas que não mudam muito frequentemente é realizar cache do HTML produzido pelas páginas PHP. Tente Smarty ou Cache Lite. [Citação]
25 - Utilize a função isset onde possivel em lugar da função strlen. (por exemplo: if (strlen($foo) < 5) { echo “Foo is too short”; } VS. if (!isset($foo{5})) { echo “Foo is too short”; } ). [Citação]
26 - ++$i é mais rápido que $ i++, logo utilize pre-incremento quando possível. [Citação]
27 - Faça uso das inúmeras funções predefinidas do PHP, não tente construir as suas próprias pois as nativas irão ser com certeza muito mais rápidas. Se tiver funções dispendiosas a nível temporal e recursos, considere escreve-las como extensões C ou módulos. [Citação]
28 - Crie um perfil (profile) do seu código. Um profiler revelará os consumos das variadas secções do seu código. O Xdebug debugger já contem um profiler. Profiling irá mostrar uma síntese dos gargalos existentes. [Citação]
29 - Documente o seu código! Adopte o estilo de documentação do phpdoc (semelhante a javadoc do Java), para poder utilizar sem problemas a ferramenta de geração automática de documentação do php. [Citação]
30 - Aprenda a diferença entre bom código e mau código. [Citação]
31 - Agarre-se aos padrões de programação, pois estes irão facilitar a compreensão dos códigos criados por outras pessoas e vice-versa. [Citação]
32 - Separa o código, conteúdo e apresentação: deixe o seu código PHP separado do seu HTML. [Citação]
33 - Não se preocupe em utilizar sistemas complexos de templates como Smarty, se possivel utilize funções incluídas no PHP como ob_get_contents e simplesmente puxe os dados da sua base de dados (aconselho vivamente a leitura da citação). [Citação]
34 - *Nunca* confie nas variáveis provenientes do lado do utilizador (como o $_POST) utilize mysql_real_escape_string quando recorrer ao mysql, e htmlspecialchars quando realizar output dessas mesmas variáveis para HTML. [Citação] Utilize mysql_real_escape_string em vez de mysql_escape_string (DEPRECATED) ou addslashes.[Citação]
35 - Por questões de segurança, nunca deixe algo que possa expor informação sobre caminhos, extensões e configurações, como o display_errors ou phpinfo() na sua directoria raiz (aplica-se principalmente em ambientes de produção). [Citação]
36 - Desligue as register_globals (estão desactivadas por defeito por alguma razão!). Nenhum script em ambiente de produção necessita disto activado, pois representa um enorme risco de segurança. Corrija qualquer script que necessite esta funcionalidade activa e que recorra a unregister_globals(). Faça isto o mais rapidamente possivel porque a funcionalidade será removida no PHP6. [Citação]
37 - Evite usar texto simples quando armazenar ou avaliar passwords, evitando assim qualquer exposição. Ao invés, utilize uma hash, como a hash md5 ou sha1 ou uma combinação de ambas (e se possivel e necessário, recorrendo a sal (salt)). [Citação]
38 - Utilize ip2long() e long2ip() para armazenar endereços IP como inteiros (long) ao invés de strings. [Citação]
39 - Quando utilizar header(‘Location: ‘.$url); lembre-se de colocar à frente um die(); porque o script continua a executar mesmo que a localização mude. [Citação]
40 - Em Programação Orientada a Objectos, se existir um método que possa ser estático (static method), declare-o estático. Velocidade é melhorada por um factor de 4. [Citação].
41 - Incrementar uma variável local num método de Programação OO é o mais rápido, sendo aproximadamente o mesmo que aceder a uma variável local numa função. Incrementar uma variável global é 2 vezes mais lento que uma variável local. [Citação]
42 - Incrementar uma propriedade de um objecto (eg. $this->prop++) é 3 vezes mais lento que incrementar uma variável local. [Citação]
43 - Incrementar uma variável local não definida é 9 a 10 vezes mais lento que incrementar uma variável pré-inicializada. [Citação]
44 - Somente declarar uma variável global dentro de uma função sem a utilizar têm efeitos negativos no desempenho (aproximadamente o mesmo que incrementar uma variável local), visto o PHP provavelmente verificar se a mesma já existe. [Citação]
45 - Invocação de métodos parece ser independente do número de métodos definidos na classe, porque ao adicionar 10 métodos extras na classe de teste (antes e depois do método de teste), não houve mudanças no desempenho. [Citação]
46 - Métodos em classes derivadas executam mais depressa que que os métodos nas classes base. [Citação]
47 - A chamada de uma função com um parâmetro e sem corpo (corpo vazio), leva aproximadamente o mesmo tempo que fazer 7-8 operações de incrementação de variáveis locais ($var++). Chamadas de métodos similares levam aproximadamente o tempo de 15 operações de incrementação de variáveis locais. [Citação]
48 - Nem tudo precisa de ser realizado em Programação Orientada a Objectos, pois normalmente só sobrecarrega o programa, visto as chamadas de métodos e objectos consumirem muita memória. [Citação]
49 - Evite o problema de injecção de cabeçalhos na função mail() do PHP. [Citação]
50 - RTFM! O PHP oferece um enorme e fantástico manual, sendo possivelmente um dos melhores na Internet, contendo a documentação de todas as bibliotecas em inglês simples (também disponível em português do Brasil), um conjunto de exemplos práticos e participação da comunidade com ainda mais exemplos e dicas. Simplesmente UTILIZE-O!!! [Citação]
A lista foi retirada, traduzida e adaptada de hm2k.com.
Deixo este site onde poderão verificar alguns testes simples de benchmarking onde se analisa a velocidade de diferentes funções e estruturas de decisão. Tenham em mente que os testes podem ser facilmente adulterados pela carga do servidor onde a página está alojada, sendo necessário actualizar (recarregando a página) e observar os resultados varias vezes.
Deixo o desafio para enumerarem o vosso top 5 de dicas e outras que conheçam e não foram mencionadas, se possível indicando a fonte. 😉
Este artigo tem mais de um ano
Deixo aqui outro bom site com resultados de benchmarks em PHP: http://www.phpbench.com/
Esse site também tem testes interessantes, já faz parte dos meus favoritos 😉
Obrigado por partilhares!
Como é que é possível nesse site dizer que “echo ”, ”, ”,” é pior do que usar “echo ”.”.”.” ?!!!!
Tem a haver com o modo de funcionamento do PHP e da funcão echo.
O echo na verdade não é igual às funções, mas sim um “language construct” – Tanto que não precisas de parentesis – e e suporta multiplos parametros
echo “string”.”string”.”string”; estás a utilizar um parametro e a concatenar 3 strings, e depois faz o output das finais. Caso não saibas, as strings são imutáveis, o que quer dizer que para juntares 2 strings tens que criar uma nova com o tamanho das 2, copiar o conteúdo e destruir as anteriores.
echo “string”,”string”,”string”; estás a utilizar parametros, que são processados mais rapidamente pelo php. Esta parte não te sei explicar correctamente, mas o echo na verdade consegue processar mais depressa usando multiplos parametros que concacatenações.
quanto ao site, sinceramente não faço a mais mínima ideia porque tal ocorre…
Yah, eu sei isso da language construct, mas fiquei mesmo desiludido com o que vi no site!! Eheh, obrigado pela resposta.
(o que contraria o ponto 4 aqui dito no pplware)
😆
“PHP é a…” PHP é à… 😀
Torna-se um vício! 😛
És a minha sombra (má) 🙂
Excelente artigo Jorge Gomes !!!!
Tudo que leve a performance é sempre bem vindo.
Boas Dicas!
Excelentes dicas! Tive contacto com PHP há poucos meses e por isso aprendi imensas dicas com o teu artigo. Parabéns!
Muito bom!
Obrigado
obrigado pessoal 😉
Esqueci-me de deixar uma nota, existe ainda muito pessoal que programa para versões de PHP 5.2 ou inferiores com funções da biblioteca de Regex POSIX (entre elas ereg(), eregi(), split()). Estas funções estão todas DEPRECATED no PHP 5.3 e serão removidas no PHP6, por funções da biblioteca PCRE que têm melhor desempenho e funcionam praticamente do mesmo modo.
Lista de funções POSIX regex:
http://www.php.net/manual/en/ref.regex.php
Lista de funções PCRE:
http://www.php.net/manual/en/ref.pcre.php
Evitem também utilizar o operador de referência & em objectos, pois estes desde do PHP 5 que são sempre passados por referencia.
Cumprimentos
Obrigado! Vai dar jeito com certeza.
oh Jorge Gomes
Fantástico artigo. Conjunto de dicas magnífico. Sem dúvida sensacional.
Parabens
Parabéns!
Excelente artigo!
Embora já conheça algumas dicas, a maioria (quase todas), não conhecia, principalmente as diferenças de rapidez dos métodos utilizados. São “pequenas” diferenças, mas acredito que todas juntas, façam diferença.
Muito bom mesmo! 😉
exactamente, na maioria dos casos falamos de diferenças de uns insignificantes milissegundos (tanto que é difícil realizar benchmarks conclusivos), mas tal como o provérbio popular “grão a grão enche a galinha o papo”, uma aplicação PHP complexa que utiliza milhares de vezes estas funções irá beneficiar de um enorme ‘boost’ de desempenho.
Muito obrigado pela partilha =D
Obrigado pelo artigo que contém algumas dicas bastante interessantes e que fazem bastante sentido. Pessoalmente só fiquei com algumas dúvidas na dica nº8 (“Utilize require() / include() invés de require_once() / include_once(), pois estas últimas necessitam de verificar se o ficheiro já não foi incluído no código”), não que a dica esteja errada, mas talvez porque a justificação não esteja tão correcta; na realidade o require_once não é significativamente mais lento do que que o require; os testes que o Peter Bowyer fez são é um pouco tendenciosos e, se inverteres a ordem dos testes, os resultados poderão ser bem diferentes; ao inserir o require_once em primeiro lugar é feita uma chamada ao sistema por cada um dos directórios que constituem o caminho até ao ficheiro a incluir para verificar as permissões de cada um deles; se existirem permissões até ao ficheiro e se este puder ser lido, então está será aberto e, só neste momento é que será analisado se o ficheiro já foi incluído ou não na compilação (este processo poderá ser ainda mais demorado, se o caminho para o ficheiro for relativo). Se após a instrução require_once fizermos um require, esta última será muito mais rápida pois poderá ter em cache o processo de análise de todos os directórios até ao ficheiro não tendo de repetir este processo novamente. (Se quiserem saber mais sobre isto, recomendo a leitura deste comentário http://www.php.net/manual/en/function.require-once.php#90017).
“Então mas a dica estava errada?” Não necessariamente e não existe um grande consenso relativamente a este tema. Pessoalmente prefiro utilizar o require_once por uma questão de comodidade e de manutenção de código. Em todo o caso, é relativamente simples e replicar a funcionalidade do require_once utilizando require: no ficheiro que estão a incluir definem uma variável com um nome único para todo o projecto e, transformam os vossos habituais require por algo deste tipo:
if(!isset($nome_da_variavel)) require(‘caminho/nome_do_ficheiro’);
Um abraço
evita colocares “wall of text” nos comentários, custa um pouco a ler.
Tal como outras dicas que estão no tutorial, a diferença entre o require() e require_once() é muito muito pequena, se a aplicação for pequena.
“The time difference between require_once() vs. require() is so tiny, it’s almost always insignificant in terms of performance. The one exception is if you have a very large application that has hundreds of require*() calls.”
Eu pessoalmente utilizo também o require_once para não existir código repetido (um exemplo de escolha entre escalabilidade vs performance).
Quanto à dica, esta talvez esteja ambígua mas o objectivo é comparar o require/include com o include_once / require_once:
Assumindo que os ficheiros existem e o caminho fornecido está correcto, o require irá sempre incluir o código fonte no programa mesmo que este ja tenha sido incluido anteriormente.
No entanto o require_once vai verificar se o ficheiro ja foi incluindo, e isso leva tempo (por mais insignificante que seja) e caso positivo, não o inclui.
Nota: se não estou em erro, se fizermos require() e de seguida require_once() do mesmo ficheiro, este é incluído em ambos os casos no programa.
Cumprimentos
Excelente tópico.
Apesar de já trabalhar com PHP há uns anos e ter cuidado para melhorar a performance, ainda deu para aprender muito com este tópico.
Tenho de fazer umas revisões nos meus códigos!
Mas que excelente artigo. Muitos parabéns.
Vou já começar a optimizar o meu código 😀
Excelente coletânea!
Erro nas funções php obsoletas ereg e split: http://www.naninho.blog.br/web/php/erro-funcoes-php-obsoletas-ereg-split.html