<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ataraxia!</title>
	<atom:link href="http://www.ataraxia.com.br/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ataraxia.com.br</link>
	<description>O estado da arte em TI</description>
	<lastBuildDate>Sun, 17 Jul 2011 21:36:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Importando contatos do Facebook no Google+</title>
		<link>http://www.ataraxia.com.br/posts/importando-contatos-do-facebook-no-google</link>
		<comments>http://www.ataraxia.com.br/posts/importando-contatos-do-facebook-no-google#comments</comments>
		<pubDate>Sun, 17 Jul 2011 21:36:57 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[dicas]]></category>
		<category><![CDATA[exportação]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[google+]]></category>
		<category><![CDATA[importação]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=394</guid>
		<description><![CDATA[Dica rápida pra todo mundo que está querendo importar todos os seus contatos do Facebook no Google+, a mais nova rede social. Como já era de se esperar, o Facebook vem fazendo o possível e o impossível para bloquear quaisquer meios de exportar os contatos. Já bloqueou extensões e plugins que faziam isso. Tudo para [...]]]></description>
			<content:encoded><![CDATA[<p>Dica rápida pra todo mundo que está querendo importar todos os seus contatos do Facebook no Google+, a mais nova rede social.</p>
<p>Como já era de se esperar, o Facebook vem fazendo o possível e o impossível para bloquear quaisquer meios de exportar os contatos. Já bloqueou extensões e plugins que faziam isso. Tudo para dificultar o crescimento do Google+. Ok, dá pra entender, vai contra o que é útil para o usuário, mas vai a favor dos interesses do Facebook.</p>
<p>Aqui vai a dica então&#8230; tudo que você vai precisar para isso é uma conta de email no Yahoo! Caso não tenha, dá pra criar uma rápido e sem custo.</p>
<h2>Passo a passo</h2>
<p>1. Abra a página do Yahoo! em http://www.yahoo.com.br/</p>
<p>2. Clique em &#8220;email&#8221;, no menu lateral esquerdo, para acessar o seu email @yahoo.</p>
<p>3. Entre com seu handle Yahoo! e sua senha, e clique no botão para entrar.</p>
<p>4. No menu superior do webmail, clique em &#8220;Contatos&#8221;</p>
<p>5. No lado direito da tela que vai abrir, siga com a primeira opção, &#8220;Importar contatos&#8221;</p>
<p>6. Escolha Facebook, e autorize o acesso na janela que vai abrir.</p>
<p>7. Com isso, o Yahoo! vai fazer a importação de todos os seus contatos do Facebook.</p>
<p>8. Clique no link para ver a lista dos contatos que acabaram de ser importados.</p>
<p>9. Marque todos eles, vá no botão &#8220;Ações&#8221; logo em cima, e escolha &#8220;Exportar tudo&#8221;.</p>
<p>10. Clique para exportar os contatos no formato Yahoo! CSV, e os contatos já estarão prontos para serem importados no Google, ou abertos em uma planilha.</p>
<p>11. Pronto! Só se logar no Gmail, e importar o arquivo que você fez download na parte de contatos. Entrando no Google+, todos estarão lá, prontos para serem convidados / adicionados.</p>
<p>É bom fazer isso o quanto antes&#8230; vai saber quanto tempo vai demorar até o Facebook bloquear isso tambem.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li>Nenhum artigo relacionado</li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/importando-contatos-do-facebook-no-google/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Agendando eventos no WordPress</title>
		<link>http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress</link>
		<comments>http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress#comments</comments>
		<pubDate>Wed, 13 Apr 2011 15:13:07 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[programação]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=382</guid>
		<description><![CDATA[As vezes, precisamos que alguma ação seja executada de tempos em tempos no WordPress. Pode ser uma função que busca conteúdo externo, ou uma rotina de manutenção qualquer. A forma mais comum de fazer isso, é usando o serviço cron (em ambientes Unix), ou o agendador de tarefas (no Windows). Embora ambos funcionem bem, existe [...]]]></description>
			<content:encoded><![CDATA[<p>As vezes, precisamos que alguma ação seja executada de tempos em tempos no WordPress. Pode ser uma função que busca conteúdo externo, ou uma rotina de manutenção qualquer.</p>
<p>A forma mais comum de fazer isso, é usando o serviço cron (em ambientes Unix), ou o agendador de tarefas (no Windows). Embora ambos funcionem bem, existe uma alternativa, que é usar o próprio WordPress para isso.</p>
<h2>Como fazer</h2>
<p>Para agendar uma tarefa, existe uma função chamada wp_schedule_event(). Uma chamada típica dessa função seria da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
wp_schedule_event<span style="color: #009900;">&#40;</span><span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'hourly'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'hook'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
add_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'hook'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'funcao_agendada'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Isso agendaria a função &#8220;funcao_agendada()&#8221; para ser executada de hora em hora, na hora atual.</p>
<p>O primeiro parâmetro informa a hora de agendamento. Como passamos time(), que se traduz na hora atual, o WordPress usará esse valor como hora. O segundo parâmetro é a frequência, que pode ser hourly, daily ou twicedaily (respectivamente de hora em hora, diariamente, ou duas vezes por dia). E por último, o nome de uma &#8220;action&#8221;, que indicará a função a ser executada.</p>
<p>Para remover o agendamento, a chamada é feita a wp_clear_schedule_hook(), passando como parâmetro somente o nome do hook utilizado (no caso, &#8220;hook&#8221;).</p>
<p>Um detalhe é que essa função deve ser chamada uma única vez, por isso, pode ser conveniente colocá-la dentro de um plugin, e executar o agendamento na ativação, e remover o agendamento na desativação.</p>
<p>Uma outra forma de se agendar, sem usar plugins, é usar a função wp_next_scheduled() para checar se o evento já foi agendado, da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
add_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'meu_evento'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'funcao'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">function</span> ativacao<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>wp_next_scheduled<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'meu_evento'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        wp_schedule_event<span style="color: #009900;">&#40;</span><span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'hourly'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'meu_evento'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
add_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'wp'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'ativacao'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> funcao<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// isso executará de hora em hora</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Assim, caso o evento não esteja agendado, o WordPress se encarregará de agendá-lo.</p>
<h2>As vantagens</h2>
<p>A principal vantagem é que o agendamento fica independente de qualquer coisa externa. Tudo fica gerenciado pelo próprio WordPress, e numa eventual mudança de servidor, não haveria necessidade de se preocupar com as tarefas agendadas.</p>
<h2>As desvantagens</h2>
<p>Como não temos um daemon pra verificar a hora, e chamar as tarefas, dependemos de um visitante (humano ou não) pra executar as tarefas. Em cada acesso ao site, o WordPress checa se existe alguma tarefa que já deveria ter sido executada. Caso exista, ele executa. Simples assim.</p>
<p>Um problema nisso é que não há garantia de que a tarefa será executada precisamente no horário agendado, mas sim, no primeiro acesso após esse horário (que pode ser segundos, num site muito visitado, ou minutos/horas em um site com quase nenhum acesso).Caso a rotina que se queira agendar seja sensível a hora, é melhor usar os métodos convencionais.</p>
<h2>Conclusão</h2>
<p>Usar a funcionalidade de agendamento do WordPress pode ser uma boa alternativa, principalmente quando não há preocupação com o &#8220;quando&#8221; a tarefa será executada, ou quando não se tem acesso aos sistemas de agendamento do servidor, o que pode ser comum em ambientes compartilhados.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress" title="Removendo o &#8220;generator&#8221; do Wordpress">Removendo o &#8220;generator&#8221; do Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress" title="Escrevendo plugins para o Wordpress">Escrevendo plugins para o Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php" title="Evitando SQL injection em PHP">Evitando SQL injection em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/criando-captchas-em-php" title="Criando CAPTCHAs em PHP">Criando CAPTCHAs em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/pegadinhas-no-php" title="Pegadinhas no PHP">Pegadinhas no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/precedencia-no-php" title="Precedência no PHP">Precedência no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/imprimindo-em-formularios-continuos-em-php" title="Imprimindo em formulários contínuos em PHP">Imprimindo em formulários contínuos em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-2-de-2" title="XML no PHP com XML_Serializer, parte 2 de 2">XML no PHP com XML_Serializer, parte 2 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-1-de-2" title="XML no PHP com XML_Serializer, parte 1 de 2">XML no PHP com XML_Serializer, parte 1 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/envio-de-emails-em-php" title="Envio de emails em PHP">Envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/validacao-de-dados-em-php" title="Validação de dados em PHP">Validação de dados em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/seguranca-no-envio-de-emails" title="Segurança no envio de emails em PHP">Segurança no envio de emails em PHP</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Removendo o &#8220;generator&#8221; do WordPress</title>
		<link>http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress</link>
		<comments>http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress#comments</comments>
		<pubDate>Thu, 26 Aug 2010 15:43:15 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[dicas]]></category>
		<category><![CDATA[programação]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=375</guid>
		<description><![CDATA[Ok, dica rápida para os que não gostam do WordPress anunciando para o mundo a versão em suas tags meta. Normalmente, qualquer site em WordPress vai ter uma tag meta em seu header mais ou menos assim: &#60;meta name=&#34;generator&#34; content=&#34;WordPress 2.8.4&#34; /&#62; Muita gente não gosta de anunciar para o mundo a versão usada. Seja [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, dica rápida para os que não gostam do WordPress anunciando para o mundo a versão em suas tags meta. Normalmente, qualquer site em WordPress vai ter uma tag meta em seu header mais ou menos assim:</p>

<div class="wp_syntax"><div class="code"><pre class="xhtml" style="font-family:monospace;">&lt;meta name=&quot;generator&quot; content=&quot;WordPress 2.8.4&quot; /&gt;</pre></div></div>

<p>Muita gente não gosta de anunciar para o mundo a versão usada. Seja por não querer exibir o uso do WordPress, ou por medo de alguem usar essa informação para um possível ataque contra essa versão específica do WordPress, existe uma forma muito simples de retirar isso do cabeçalho, sem precisar editar os arquivos do sistema (que a priori, nunca devem ser editados).<br />
Essa tag é inserida no cabeçalho através de uma <em>action</em> padrão, chamada <strong>wp_generator</strong>. Para não exibir, basta remover a action, adicionando o seguinte código no functions.php do seu tema:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
remove_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'wp_head'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'wp_generator'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Pronto, com isso o seu site não irá mais exibir esta tag no cabeçalho.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress" title="Agendando eventos no WordPress">Agendando eventos no WordPress</a></li><li><a href="http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress" title="Escrevendo plugins para o Wordpress">Escrevendo plugins para o Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php" title="Evitando SQL injection em PHP">Evitando SQL injection em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/criando-captchas-em-php" title="Criando CAPTCHAs em PHP">Criando CAPTCHAs em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/pegadinhas-no-php" title="Pegadinhas no PHP">Pegadinhas no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/precedencia-no-php" title="Precedência no PHP">Precedência no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/imprimindo-em-formularios-continuos-em-php" title="Imprimindo em formulários contínuos em PHP">Imprimindo em formulários contínuos em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-2-de-2" title="XML no PHP com XML_Serializer, parte 2 de 2">XML no PHP com XML_Serializer, parte 2 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-1-de-2" title="XML no PHP com XML_Serializer, parte 1 de 2">XML no PHP com XML_Serializer, parte 1 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/envio-de-emails-em-php" title="Envio de emails em PHP">Envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/validacao-de-dados-em-php" title="Validação de dados em PHP">Validação de dados em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/seguranca-no-envio-de-emails" title="Segurança no envio de emails em PHP">Segurança no envio de emails em PHP</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Envio de emails em massa</title>
		<link>http://www.ataraxia.com.br/posts/envio-de-emails-em-massa</link>
		<comments>http://www.ataraxia.com.br/posts/envio-de-emails-em-massa#comments</comments>
		<pubDate>Thu, 24 Dec 2009 17:01:11 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[email]]></category>
		<category><![CDATA[internet]]></category>
		<category><![CDATA[smtp]]></category>
		<category><![CDATA[spam]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=367</guid>
		<description><![CDATA[Venho trabalhando em um projeto de envio de emails em massa, e como passei por muitos problemas para conseguir melhorar a taxa de entrega dos emails, queria compartilhar algumas dicas. Antes de mais nada, quero deixar bem claro: sou totalmente contra o SPAM. Por mim, podiam condenar cada spammer do mundo a uma morte bem [...]]]></description>
			<content:encoded><![CDATA[<p>Venho trabalhando em um projeto de envio de emails em massa, e como passei por muitos problemas para conseguir melhorar a taxa de entrega dos emails, queria compartilhar algumas dicas.<br />
Antes de mais nada, quero deixar bem claro: sou totalmente contra o <a href="http://pt.wikipedia.org/wiki/Spam">SPAM</a>. Por mim, podiam condenar cada spammer do mundo a <del datetime="2009-12-24T15:47:49+00:00">uma morte bem lenta e dolorosa</del> prisão. Hoje em dia, spam é um problema mundial, e boa parte dos problemas que precisamos enfrentar só existe porque existe spam. Por exemplo, não precisaríamos ficar nos preocupando em criar emails tomando todo tipo de cuidado com palavras e marcações se não existissem filtros antispam.<br />
Porem, assim como spammers fazem envio de email em massa (usando técnicas muitas vezes inescrupulosas), existem empresas que tem a necessidade legítima de fazer envio de emails em massa. Por exemplo, assinantes de um site podem querer receber uma newsletter semanal, ou um email com promoções de uma loja online. São usos aceitáveis, e a grande diferença entre email marketing e spam é que no primeiro caso houve o consentimento do usuário em receber o email.<br />
Enfim&#8230; agora que já deixei claro que esse artigo não tem a intenção de ajudar spammers, vamos aos pontos.</p>
<h2>IPs usados no envio</h2>
<p>Tente fazer os seus envios usando poucos endereços IPs, e que sejam consistentes. Se os seus emails estão saindo de vários IPs de redes diferentes, os servidores de destino podem achar que seu sistema está falsificando os IPs de envio, ou usando alguma rede para fazer o envio (botnets, por exemplo). Com isso, sua chance de ter o email marcado como spam aumenta bastante.<br />
Se você se limita a fazer o envio a partir de um único IP, ou poucos IPs, não vai sofrer com esse tipo de problema. Além disso, os provedores agora trabalham com o conceito de &#8220;reputação&#8221; de um IP. Ou seja, se você envia emails consistentemente através de um IP, e esses emails tem taxa de reclamação baixa, a tendência é que a reputação suba.</p>
<h2>DNS reverso</h2>
<p>O sistema de <acronym title="Domain Name System">DNS</acronym> serve para traduzir nomes para IPs. Por exemplo, quando digita &#8220;www.ataraxia.com.br&#8221; no seu navegador, ele é traduzido para um endereço IP (no momento em que escrevo, 75.119.213.142). Hoje em dia, o DNS é essencial para o funcionamento da Internet.<br />
Assim como existe a tradução de nomes para IPs, tambem existe a tradução inversa, ou seja, de IPs para nomes. E é importantíssimo que o endereço IP do seu servidor de email possa ser traduzido de volta para o nome dele.<br />
Servidores que não tem DNS reverso cadastrado e apontando de volta para ele mesmo normalmente não conseguem mandar email de forma consistente, pois a maior parte dos filtros já barra a conexão antes mesmo de receber a mensagem. Ou seja, o conteúdo nem mesmo é analisado.</p>
<h2>Use um remetente consistente</h2>
<p>O remetente é o nome e email que aparecem para quem recebe o email. Tente usar o mesmo para a campanha inteira. Colocar nomes e emails diferentes para cada email que sai é mais um indício de spam, pois spammers falsificam essas informações para tentar ocultar sua identidade. Coloque no remetente algo que deixe bem claro quem você é.</p>
<h2>Publique registros SPF</h2>
<p>O <acronym title="Sender Policy Framework">SPF</acronym> é um framework que utiliza registros DNS para divulgar informações sobre permissão de envio de emails, e tem a intenção de combater o envio de emails falsificados.<br />
Um registro SPF indica para o mundo quem tem permissão de mandar emails usando um domínio. E como quem tem controle sobre um domínio e seus registros é normalmente a organização detentora da marca, seria uma forma da organização dizer: &#8220;somente os servidores X, Y e Z podem mandar email usando minha marca&#8221;.<br />
Todos os grandes provedores checam registros SPF, e um email que falhe nessa checagem já é mais um indicativo de possível falsificação de email.<br />
Não vou colocar um tutorial sobre SPF nesse artigo, pois foge um pouco do escopo, mas para saber mais sobre a tecnologia, e como implementar, basta visitar o site <a href="http://www.openspf.org/">http://www.openspf.org/</a>. Existe tambem um ótimo <a href="http://old.openspf.org/wizard.html">assistente para geração do registro SPF</a> para publicação no servidor DNS.</p>
<h2>Assinaturas DomainKeys e DKIM</h2>
<p><a href="http://en.wikipedia.org/wiki/DomainKeys">DomainKeys</a> é uma tecnologia que usa criptografia para verificar o domínio de quem enviou a mensagem, e a integridade das mensagens. É usada principalmente pelo <a href="http://www.yahoo.com/">Yahoo!</a>, e foi usada como base pelo <acronym title="Internet Engineering Task Force">IETF</acronym> para o desenvolvimento do <a href="http://www.dkim.org/">DKIM</a> (DomainKeys Identified Mail), sucessor do DomainKeys.<br />
A idéia é usar duas chaves, uma pública e uma privada. A chave privada fica guardada no servidor que faz o envio, e cada email é assinado digitalmente usando essa chave privada. A chave pública é disponibilizada via DNS, e qualquer um que receba um email assinado usando a DomainKeys e/ou DKIM pode verificar se a mensagem foi realmente enviada por um servidor autorizado, checando a assinatura através da chave pública.<br />
Além disso, a assinatura garante também que a mensagem não foi modificada em trânsito, pois caso tenha sido modificada, a assinatura não vai conferir.<br />
Embora o DKIM tenha vindo para ser o sucessor do DomainKeys, a maior parte dos grandes provedores checam as duas assinaturas, então não existe problema em assinar a mensagem duas vezes, pois ambas coexistem sem problema.</p>
<h2>Lista de emails</h2>
<p>Qualquer email que esteja na sua lista deve ter dado a permissão explícita para que você faça o envio de emails. Qualquer inscrição deve preferencialmente utilizar o mecanismo de &#8220;double opt-in&#8221;, ou seja, a pessoa coloca seu email em um formulário no seu site, você envia um email de confirmação com um link, e só adiciona a pessoa na sua lista após essa confirmação. Isso evita a adição de pessoas que não pediram para ser cadastradas, e com isso, evita reclamações.</p>
<h2>Manutenção da lista</h2>
<p>A regra número 1 é bem simples: respeite seus usuários. Se alguem não quer mais receber seus emails, não envie mais. Vai ser um usuário a menos gerando reclamações. Não vale a pena forçar seus emails goela abaixo dos usuários.<br />
Todo email que você enviar deve conter um link para que o usuário possa sair da lista. Esse link deve funcionar, de preferência de forma automática e imediata.<br />
Além disso, qualquer email que gere um &#8220;bounce&#8221; deve ser removido da lista, caso seja um erro permanente (por exemplo, &#8220;email inexistente&#8221;). Erros não permanentes (por exemplo, &#8220;caixa postal cheia&#8221;) devem ser removidos caso persistam por alguns envios.</p>
<h2>Formato do email</h2>
<p>Todas as mensagens devem seguir o padrão descrito na <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>. Esse padrão é o sucessor da <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a>, que por sua vez é o sucessor da <a href="http://tools.ietf.org/html/rfc822">RFC 822</a>. Esse padrão descreve o formato que um email deve seguir.<br />
Caso envie emails em HTML, siga os padrões descritos pelo <a href="http://www.w3c.org/">W3C</a>.<br />
Adicione sempre o cabeçalho &#8220;Precedence: bulk&#8221;, para sinalizar ao servidor de destino de que seu email faz parte de um envio em massa. Isso ajuda o servidor que recebe a priorizar melhor a entrega das mensagens, caso esteja sobrecarregado.<br />
O assunto do email deve ser relevante, e ter relação com o conteúdo da mensagem.</p>
<h2>O email em si</h2>
<p>Evite uso de palavras chaves normalmente associadas a spam. Evite o uso de pontuação em excesso, como por exemplo, muitas exclamações.<br />
Caso esteja enviando email em HTML, não esqueça de fazer tambem uma versão em texto puro, para quem não lê HTML. Por exemplo, muitos dispositivos móveis não lêem emails em HTML. Além disso, emails somente em HTML muitas vezes ganham uma pontuação nos sistemas antispam.<br />
No HTML, evite colocar somente imagens. Tente manter uma boa proporção de texto para imagem. Siga os padrões da W3C, colocando o atributo &#8220;alt&#8221; nas imagens. Além disso, lembre-se que por padrão, os grandes provedores não exibem imagens externas por padrão, então caso seu email contenha somente imagens, existe uma boa probabilidade do email não ser lido.</p>
<h2>Blacklists e whitelists</h2>
<p>Tente fazer monitoramento constante nas várias listas negras existentes na Internet. Caso seu servidor seja listado, começará a ter problemas de entrega, pois qualquer servidor configurado para checar a lista negra irá barrar sua conexão. Tente trabalhar de forma a evitar a entrada nessas listas, e caso entre, tente corrigir o problema e providenciar a saída o mais rápido possível.<br />
Assim como existem as listas negras, existem tambem listas brancas, de servidores que foram cuidadosamente verificados, e que fazem envio de email de forma responsável. A mais famosa é a certificação <a href="https://www.senderscore.org/">SenderScore</a> da <a href="http://www.returnpath.net/">ReturnPath</a>. Existe um custo, mas por sua vez, uma vez certificado, basta manter as boas práticas para conseguir melhores taxas de entrega para grandes provedores, como Hotmail e Yahoo!.</p>
<h2>Conclusão</h2>
<p>Resumindo: mandar emails para a Internet de forma consistente é um verdadeiro inferno hoje em dia. E acredite, você certamente terá problema de entregas uma hora ou outra. Não tem muito pra onde fugir. Mas seguindo essas regras (simples?), dá pra se manter o nível de rejeição em um patamar aceitável.<br />
Alguem tem alguma dica extra? Postem nos comentários!</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/evitando-spam-em-formularios" title="Evitando spam em formulários">Evitando spam em formulários</a></li><li><a href="http://www.ataraxia.com.br/posts/evitando-fornecer-seu-email-verdadeiro" title="Evitando fornecer seu email verdadeiro">Evitando fornecer seu email verdadeiro</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-parte-2-compressao" title="Otimização de sites, parte 2 &#8211; Compressão">Otimização de sites, parte 2 &#8211; Compressão</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/acessando-sites-fora-do-ar" title="Acessando sites fora do ar">Acessando sites fora do ar</a></li><li><a href="http://www.ataraxia.com.br/posts/envio-de-emails-em-php" title="Envio de emails em PHP">Envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/seguranca-no-envio-de-emails" title="Segurança no envio de emails em PHP">Segurança no envio de emails em PHP</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/envio-de-emails-em-massa/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Personalizando CFLAGS por pacote no Gentoo</title>
		<link>http://www.ataraxia.com.br/posts/personalizando-cflags-por-pacote-no-gentoo</link>
		<comments>http://www.ataraxia.com.br/posts/personalizando-cflags-por-pacote-no-gentoo#comments</comments>
		<pubDate>Tue, 10 Nov 2009 03:06:24 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[dicas]]></category>
		<category><![CDATA[gentoo]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[portage]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=356</guid>
		<description><![CDATA[O Gentoo é uma distribuição Linux famosa por ter todos os seus pacotes compilados do código fonte durante a instalação. Dessa forma, é possível gerar binários otimizados especificamente para a máquina onde vai rodar, especificando as flags que serão passadas para o compilador. Além disso, também é possível especificar quais features cada pacote terá disponível. [...]]]></description>
			<content:encoded><![CDATA[<p>O <a href="http://www.gentoo.org/">Gentoo</a> é uma distribuição Linux famosa por ter todos os seus pacotes compilados do código fonte durante a instalação. Dessa forma, é possível gerar binários otimizados especificamente para a máquina onde vai rodar, especificando as flags que serão passadas para o compilador.<br />
Além disso, também é possível especificar quais features cada pacote terá disponível. Assim, é possível, por exemplo, ter um pacote instalado sem suporte a determinadas coisas que não serão necessárias, evitando-se assim a instalação como dependência de pacotes que não serão usados.<br />
Um problema que tive em uma instalação foi exatamente com as CFLAGS, as tais flags de otimização que são passadas para o compilador. Estava usando flags bem básicas, assim:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #007800;">CFLAGS</span>=<span style="color: #ff0000;">&quot;-O2 -fomit-frame-pointer -march=nocona -pipe&quot;</span></pre></div></div>

<p>Essas flags especificam uma otimização nível 2 (pode ir de 0 a 3 &#8211; o 3 quebra alguns pacotes, por isso é recomendável usar o 2), e especifica também para qual conjunto de instruções os binários serão gerados. No meu caso, estou dizendo que os binários deverão ser compilados para se usar todo o poder do processador nocona (os dual/quad cores da Intel).<br />
Até aí, tudo funcionando bem. Montei um sistema todo com essas flags, e estava tudo muito bem, até que precisei instalar o <a href="http://www.bacula.org/">Bacula</a>, um software para se fazer backups de sistemas completos.<br />
O Bacula, com as CFLAGS padrões estava gerando um binário quebrado. Pesquisando, descobri que o projeto <a href="http://www.gentoo.org/proj/en/hardened/">Hardened</a> do Gentoo, que usa uma série de artifícios para gerar executáveis mais seguros e imunes contra ataques como <a href="http://en.wikipedia.org/wiki/Buffer_overflow">buffer overflow</a> era o responsável, pois por padrão ele passa a flag &#8220;FORTIFY_SOURCE&#8221; para o compilador, e isso estava quebrando o Bacula.<br />
Uma das formas de se resolver esse problema seria alterando as CFLAGS no /etc/make.conf, adicionando <i>-U_FORTIFY_SOURCE</i> a elas. Porém, o grande problema é que as CFLAGS são válidas globalmente, e dessa forma, todos os pacotes seriam compilados com essa flag extra, matando parte dos recursos do projeto Hardened.<br />
O que eu queria era poder compilar <strong>somente</strong> o Bacula com essa flag extra. Uma das formas é usar o diretório /etc/portage/env para criar variáveis de ambientes específicas para cada pacote.<br />
Basta seguir os seguintes passos:</p>
<ol>
<li>1. Pegue o nome completo do pacote, na forma <i>categoria</i>/<i>pacote</i>. No caso do Bacula, o nome completo é <strong>app-backup/bacula</strong>.</li>
<li>2. Crie o arquivo /etc/portage/env/<strong>categoria</strong>/<strong>pacote</strong>, ou no nosso caso, /etc/portage/env/app-backup/bacula. Caso o diretório &#8220;env&#8221; não exista, crie toda a árvore necessária.</li>
<li>3. Nesse arquivo, você pode especificar as variáveis de ambiente que serão definidas exclusivamente para a compilação do pacote. Assim, podemos definir CFLAGS neste arquivo:

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #007800;">CFLAGS</span>=<span style="color: #ff0000;">&quot;<span style="color: #007800;">${CFLAGS}</span> -U_FORTIFY_SOURCE&quot;</span></pre></div></div>

</li>
</ol>
<p>Pronto. Com isso, temos a flag extra -U_FORTIFY_SOURCE adicionada às flags globais, e só serão utilizadas para o pacote em questão.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/o-limite-de-4-ou-3-gb-de-memoria" title="O limite de 4 (ou 3?) Gb de memória">O limite de 4 (ou 3?) Gb de memória</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/personalizando-cflags-por-pacote-no-gentoo/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Escrevendo plugins para o WordPress</title>
		<link>http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress</link>
		<comments>http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress#comments</comments>
		<pubDate>Mon, 14 Sep 2009 04:39:52 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[programação]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=347</guid>
		<description><![CDATA[O WordPress é um excelente sistema para blogs, feito em PHP. Além de ser bastante fácil de instalar e usar, existe um sem número de plugins já prontos e disponíveis para estender suas funcionalidades, e mais tantos temas que podem ser usados para dar &#8220;cara nova&#8221; ao blog. De fato, este blog que você está [...]]]></description>
			<content:encoded><![CDATA[<p>O <a href="http://wordpress.org/">WordPress</a> é um excelente sistema para blogs, feito em PHP. Além de ser bastante fácil de instalar e usar, existe um sem número de plugins já prontos e disponíveis para estender suas funcionalidades, e mais tantos temas que podem ser usados para dar &#8220;cara nova&#8221; ao blog. De fato, este blog que você está lendo neste momento também usa o WordPress!<br />
Os <a href="http://wordpress.org/extend/plugins">vários plugins disponíveis</a> adicionam todo tipo de funcionalidade nova ao sistema, seja um widget para mostrar os últimos tweets de alguém, links para posts relacionados, enfim&#8230; tudo é possível!<br />
Porém, volta e meia precisamos adicionar uma funcionalidade e não existe um plugin já pronto que sirva exatamente para o que queremos. Neste caso, uma opção é criar o nosso próprio plugin. A linguagem do WordPress é o PHP, e tendo alguma noção de como as coisas funcionam no WordPress, essa não é uma tarefa muito complexa.</p>
<h2>Criando um plugin</h2>
<p>Neste artigo, vamos mostrar o passo a passo da criação de um plugin simples, que visa guardar em uma tabela o número de visualizações de cada post do blog. Só isso. Não teremos gráficos, tabelas, nada do tipo. Apenas vamos guardar os dados, que podem ser utilizados por uma outra ferramenta qualquer.<br />
O objetivo não é apenas mostrar como se criar um plugin. A partir deste esqueleto, adicionar mais funcionalidades fica mais fácil.</p>
<h2>O cabeçalho do plugin</h2>
<p>Antes de mais nada, logo no início precisamos ter um cabeçalho no arquivo do plugin que informará ao WordPress algumas informações, como nome do plugin, uma descrição, autor, versão, e outras coisas. É importante ter isso bem definido.<br />
Essas informações entram em um comentário logo no início do arquivo. No nosso caso, teremos o seguinte:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">/*
Plugin Name: View Counter
Description: Increments a counter each time a post is viewed
Version: 1.0
Author: Bruno Lustosa
Author URI: http://www.ataraxia.com.br/
*/</span></pre></div></div>

<p>Pronto, já temos aí o nome do plugin (View Counter), uma breve descrição do que ele faz, a versão, o nome do autor e a URL do autor, para referência. O WordPress exibe essas informações na página dos plugins na área administrativa. Coloquei as informações em inglês, pois a interface administrativa deste blog está toda em inglês.</p>
<h2>Os hooks</h2>
<p>Entenda os hooks como ações onde você pode criar um callback. Ou seja, sempre que determinada ação for executada no WordPress, uma função definida por você será chamada. No nosso caso, vou definir 2 hooks e uma ação (a ação é parecida com o hook, mostrarei a diferença logo abaixo).</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// Hooks</span>
register_activation_hook<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">__FILE__</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;viewcounter_activate&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
register_deactivation_hook<span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">__FILE__</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;viewcounter_deactivate&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
add_action<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'save_post'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'viewcounter_insert'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>O primeiro hook determina que a função <em>viewcounter_activate()</em> será executada quando o plugin for ativado. Esta função cuidará da criação da tabela, caso necessário. Da mesma forma, o segundo hook executará <em>viewcounter_deactivate()</em>, que destruirá nossa tabela, para deixar o sistema limpo como estava antes da ativação. É sempre uma boa prática remover o que criamos após a desinstalação.<br />
A ação é bem semelhante ao hook, e no caso, executará a função <em>viewcounter_insert()</em> assim que um post for salvo. Esta função irá inserir uma linha em nossa tabela, definindo o número de leituras como sendo zero.</p>
<h2>Nossa tabela</h2>
<p>Iremos criar uma tabela bem simples para guardar as nossas informações. Tudo que precisaremos, é de um campo para guardar o ID do post, e outro pra guardar o número de visualizações. Sim, poderíamos alterar a tabela de posts e adicionar um campo com o número de visualizações, porém, prefiro não mexer em tabelas do sistema, para evitar problemas de compatibilidade futura (vai que uma versão futura do WordPress possui um campo com esse mesmo nome?).<br />
O comando para a criação da tabela:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">CREATE</span> <span style="color: #993333; font-weight: bold;">TABLE</span> viewcounter <span style="color: #66cc66;">&#40;</span>
    post_ID <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">PRIMARY</span> <span style="color: #993333; font-weight: bold;">KEY</span><span style="color: #66cc66;">,</span>
    views <span style="color: #993333; font-weight: bold;">INT</span> <span style="color: #993333; font-weight: bold;">NOT</span> <span style="color: #993333; font-weight: bold;">NULL</span> <span style="color: #993333; font-weight: bold;">DEFAULT</span> <span style="color: #cc66cc;">0</span>
<span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<h2>O hook de ativação</h2>
<p>Com a definição da nossa tabela em mãos, vamos partir para a função de ativação do plugin. Ela será executada uma única vez, quando o administrador ativar o plugin.<br />
Nossa função, por segurança, irá checar se a tabela que queremos criar já existe, para evitar erros. A checagem usará o comando SQL <em>SHOW TABLES</em>.<br />
A função ficará assim:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> viewcounter_activate<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">,</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get_var</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SHOW TABLES LIKE '<span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span>'&quot;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>
        <span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">query</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;CREATE TABLE <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span> (post_ID INT NOT NULL PRIMARY KEY, views INT NOT NULL DEFAULT 0)&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">endif</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;">// viewcounter_activate</span></pre></div></div>

<p>Note que temos duas variáveis globais. A primeira, <em>$wpdb</em>, é do próprio WordPress, e é usada para acessar o banco de dados e executar queries. A segunda, foi definida por nós, e contem o nome da tabela que usaremos. Lembra, que durante a instalação do WordPress, ele te pediu um prefixo para as tabelas? Ao invés de deixarmos um nome fixo, é sempre bom definir o nome da tabela usando este prefixo.<br />
Assim, esta segunda variável global foi definida fora da função, da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$viewcounter_table</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;<span style="color: #006699; font-weight: bold;">{$wpdb-&gt;prefix}</span>viewcounter&quot;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Resumindo, se você definiu o prefixo para as tabelas do WordPress como <em>wp_</em>, então nossa tabela se chamará <em>wp_viewcounter</em>. Isso é especialmente importante caso seu plugin seja usado em um sistema multi-usuário, como o <a href="http://mu.wordpress.org/">WordPress MU</a>.</p>
<h2>A desativação</h2>
<p>Bem semelhante à nossa função de ativação, temos a função que será executada quando o plugin for desativado. A idéia aqui é limpar tudo que fizemos, deixando o sistema como estava antes. Para tanto, basta destruir a nossa tabela.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> viewcounter_deactivate<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">,</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">query</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;DROP TABLE <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;">// viewcounter_deactivate</span></pre></div></div>

<p>Simples assim. Desativou o plugin, destruiu a tabela.</p>
<h2>Quando salvamos o post</h2>
<p>Assim que um post é salvo, temos a função <em>viewcounter_insert()</em> que será chamada, tendo como parâmetro o ID do novo post. Esta função deve inserir uma linha em nossa tabela, indicando que esse novo post teve 0 visualizações até o momento, o que é lógico, pois ele acabou de ser criado.<br />
A função também fica bem simples:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> viewcounter_insert<span style="color: #009900;">&#40;</span><span style="color: #000088;">$post_ID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">,</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">query</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;INSERT INTO <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span> (post_ID, views) VALUES (<span style="color: #006699; font-weight: bold;">{$post_ID}</span>, 0)&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;">// viewcounter_insert</span></pre></div></div>

<h2>A contagem propriamente dita</h2>
<p>E por último, temos a contagem das visualizações. Para tanto, teremos uma função chamada <em>viewcounter_update()</em>, que receberá como parâmetro o ID do post a ser atualizado. Para conveniência, essa função também irá retornar o número atualizado de visualizações que o post teve, que poderá ser usado no template em algum lugar.<br />
Esta função tem algumas checagens extras que são feitas por segurança. Por exemplo, caso um post antigo seja visualizado, precisaremos rodar a função que cria a linha para o post, pois como ele foi criado antes da ativação do plugin, o hook anterior não foi chamado.<br />
A função pode ficar assim:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> viewcounter_update<span style="color: #009900;">&#40;</span><span style="color: #000088;">$post_ID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">global</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">,</span> <span style="color: #000088;">$viewcounter_table</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">is_null</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get_var</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT views FROM <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span> WHERE post_ID = <span style="color: #006699; font-weight: bold;">{$post_ID}</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>
        viewcounter_insert<span style="color: #009900;">&#40;</span><span style="color: #000088;">$post_ID</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">endif</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">query</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;UPDATE <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span> SET views = views + 1 WHERE post_ID = <span style="color: #006699; font-weight: bold;">{$post_ID}</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$wpdb</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get_var</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT views FROM <span style="color: #006699; font-weight: bold;">{$viewcounter_table}</span> WHERE post_ID = <span style="color: #006699; font-weight: bold;">{$post_ID}</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span> <span style="color: #666666; font-style: italic;">// viewcounter_update</span></pre></div></div>

<p>A primeira query checa se o retorno é vazio, e caso seja, chama a função viewcounter_insert() para inserir uma linha com 0 visualizações. Em seguida, executa um <em>update</em> incrementando em 1 o número de visualizações. E por último, retorna o número atual de visualizações do post.</p>
<h2>Atualizando</h2>
<p>Tudo que faltou agora, é indicar o ponto onde viewcounter_update() deve ser chamada. Na <a href="http://codex.wordpress.org/Template_Hierarchy">hierarquia dos templates</a>, o ponto ideal para colocarmos nossa chamada é no arquivo <em>single.php</em>.<br />
Bastaria uma simples chamada como:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$num</span> <span style="color: #339933;">=</span> viewcounter_update<span style="color: #009900;">&#40;</span><span style="color: #000088;">$post</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">ID</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>E já teríamos o banco atualizado (claro, supondo que temos em $post o objeto com o post). Caso queiramos exibir o número atual de visualizações, podemos utilizar a variável $num em qualquer ponto do template para mostrar ao usuário quantas vezes aquele post foi visto.</p>
<h2>Conclusão</h2>
<p>Este foi apenas um exemplo, bem tosco, de como criar um template para o WordPress. É claro que ele pode ser melhorado, e muito. Inclusive, aceito sugestões nos comentários.<br />
Estou disponibilizando um <a href="http://www.ataraxia.com.br/wp-content/uploads/2009/09/viewcounter.zip">arquivo com este plugin</a>, para quem quiser baixar e experimentar.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress" title="Agendando eventos no WordPress">Agendando eventos no WordPress</a></li><li><a href="http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress" title="Removendo o &#8220;generator&#8221; do Wordpress">Removendo o &#8220;generator&#8221; do Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php" title="Evitando SQL injection em PHP">Evitando SQL injection em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/criando-captchas-em-php" title="Criando CAPTCHAs em PHP">Criando CAPTCHAs em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/pegadinhas-no-php" title="Pegadinhas no PHP">Pegadinhas no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/precedencia-no-php" title="Precedência no PHP">Precedência no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/imprimindo-em-formularios-continuos-em-php" title="Imprimindo em formulários contínuos em PHP">Imprimindo em formulários contínuos em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-2-de-2" title="XML no PHP com XML_Serializer, parte 2 de 2">XML no PHP com XML_Serializer, parte 2 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-1-de-2" title="XML no PHP com XML_Serializer, parte 1 de 2">XML no PHP com XML_Serializer, parte 1 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/envio-de-emails-em-php" title="Envio de emails em PHP">Envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/validacao-de-dados-em-php" title="Validação de dados em PHP">Validação de dados em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/seguranca-no-envio-de-emails" title="Segurança no envio de emails em PHP">Segurança no envio de emails em PHP</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Evitando conteúdo duplicado</title>
		<link>http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado</link>
		<comments>http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado#comments</comments>
		<pubDate>Tue, 09 Jun 2009 13:30:04 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[dicas]]></category>
		<category><![CDATA[seo]]></category>
		<category><![CDATA[html]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=342</guid>
		<description><![CDATA[Suponha que você tenha um site. E seu site possui páginas, que você anuncia através de um feed RSS, como por exemplo, o Feed RSS deste site, e também através de um boletim enviado por email periodicamente. Até aí, tudo ótimo. Até que chega um momento em que você decide contabilizar os usuários que chegam [...]]]></description>
			<content:encoded><![CDATA[<p>Suponha que você tenha um site. E seu site possui páginas, que você anuncia através de um feed <acronym title="Really Simple Syndication">RSS</acronym>, como por exemplo, o <a href="http://www.ataraxia.com.br/feed">Feed RSS deste site</a>, e também através de um boletim enviado por email periodicamente.<br />
Até aí, tudo ótimo.<br />
Até que chega um momento em que você decide contabilizar os usuários que chegam no seu site através do feed, ou através dos boletins.<br />
Uma alternativa bastante simples é adicionar uma variável qualquer no fim da URL das páginas anunciadas, para que seu site &#8220;saiba&#8221; de onde veio. Por exemplo, digamos que eu tenha escolhido esta página para anunciar. A URL desta página é:</p>
<p><a href="http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado">http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado</a></p>
<p>Para diferenciar, eu poderia usar URLs como as abaixo:</p>
<p><a href="http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado?rss">http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado?rss</a></p>
<p><a href="http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado?email">http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado?email</a></p>
<p>Desta forma, o site tem como saber de onde o visitante veio, bastando colocar no feed o sufixo &#8220;?rss&#8221;, e nos boletins o &#8220;?email&#8221;. O site checaria se a URL contém um desses sufixos, e incrementaria um contador em algum lugar (banco de dados, arquivo, etc).<br />
O grande problema, é que uma vez que os buscadores encontrem essas URLs alternativas, irão notar que o conteúdo é o mesmo para as 3 páginas, e existe a chance de que seu site seja penalizado por isso.<br />
Uma das formas de se lidar com isso é usar a tag &lt;link&gt; para informar qual é a URL &#8220;canônica&#8221; da página atual. A tag é simples:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">link</span> <span style="color: #000066;">rel</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;canonical&quot;</span> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;http://www.example.org/caminho-da-pagina&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span></pre></div></div>

<p>Esta tag é a mesma nas 3 páginas, e indica ao buscador qual é a URL canônica da página. Seguindo o nosso exemplo, nas 3 páginas acima, teríamos a tag:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">link</span> <span style="color: #000066;">rel</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;canonical&quot;</span> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span></pre></div></div>

<p>Claro, esta tag serve como uma &#8220;dica&#8221; para os buscadores, e eles podem usá-la ou não. Na maioria das vezes, ela será usada.<br />
E embora eu tenha dado o exemplo de páginas anunciadas por email ou feed RSS, a utilidade vai bem além disso.<br />
Se você tem um site, digamos de e-commerce, e tem uma página que ordena os produtos por preço, popularidade, etc, e essa ordenação é passada como parâmetro na URL, o conteúdo da página será essencialmente o mesmo, porém com a posição diferente (dependendo da ordenação). O buscador pode entender que a página é duplicada. Uma tag &lt;link&gt; como esta, apontando para a URL sem ordenação resolveria o problema.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/otimizacao-imagens-inline" title="Otimização: imagens inline">Otimização: imagens inline</a></li><li><a href="http://www.ataraxia.com.br/posts/paginas-de-manutencao" title="Páginas de manutenção">Páginas de manutenção</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Otimização de sites com memcached</title>
		<link>http://www.ataraxia.com.br/posts/otimizacao-de-sites-com-memcached</link>
		<comments>http://www.ataraxia.com.br/posts/otimizacao-de-sites-com-memcached#comments</comments>
		<pubDate>Sat, 16 May 2009 16:01:32 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[programação]]></category>
		<category><![CDATA[bd]]></category>
		<category><![CDATA[memoria]]></category>
		<category><![CDATA[otimização]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=328</guid>
		<description><![CDATA[Um projeto muito interessante, e que pode ser usado pra aumentar drasticamente o desempenho de aplicativos web é o Memcached. Pela descrição do site do projeto, o &#8220;memcached é um sistema distribuído de alto desempenho para o cacheamento de objetos na memória, genérico por natureza, mas feito para se aumentar a velocidade de sites dinâmicos [...]]]></description>
			<content:encoded><![CDATA[<p>Um projeto muito interessante, e que pode ser usado pra aumentar drasticamente o desempenho de aplicativos web é o <a href="http://www.danga.com/memcached/">Memcached</a>. Pela descrição do site do projeto, o &#8220;memcached é um sistema distribuído de alto desempenho para o <i>cacheamento</i> de objetos na memória, genérico por natureza, mas feito para se aumentar a velocidade de sites dinâmicos diminuindo a carga no banco de dados&#8221;.<br />
O memcached funciona como um grande dicionário, que armazena tuplas do tipo [chave, valor]. Para armazenar um objeto qualquer, basta conectar no memcached e passar uma chave para acessá-lo e o objeto (e opcionalmente outros parâmetros, como tempo de armazenamento). Para buscar o objeto, basta conectar ao memcached e pedir a ele o objeto que possui a chave previamente criada.<br />
Digamos que uma página web precise fazer algumas consultas pesadas em um banco de dados. A idéia é justamente pegar o resultado da consulta e jogar pra dentro do memcached. Na próxima vez que a consulta precisar ser feita, o programa irá primeiro checar se o resultado já está disponível no memcached. Se estiver, ótimo, economizamos uma query pesada. Se não estiver, o programa faz a consulta e guarda o resultado no memcached. Simples assim!</p>
<h2>Exemplo em PHP</h2>
<p>O memcached pode ser acessado de muitas linguagens de alto nível, como C/C++, Java, Python, PHP, e até .NET. Abaixo, um exemplo em PHP.<br />
Antes do memcache, uma página poderia ter uma consulta do seguinte tipo:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">// A query SQL está abreviada, apenas como exemplo</span>
<span style="color: #666666; font-style: italic;">// Digamos que essa consulte demore cerca de 1 segundo</span>
<span style="color: #666666; font-style: italic;">// para ser completada</span>
<span style="color: #000088;">$sql</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;SELECT * FROM tabela INNER JOIN ......&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$res</span> <span style="color: #339933;">=</span> <span style="color: #990000;">pg_query</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$arr</span> <span style="color: #339933;">=</span> <span style="color: #990000;">pg_fetch_all</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$res</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// array com todos os resultados</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Com esse código, a cada acesso na página, teremos uma consulta de 1 segundo feita ao banco de dados. Muitas vezes, a consulta será exatamente a mesma. Modificando a consulta para uso do memcached, teríamos algo assim:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">// Conecta ao memcached</span>
<span style="color: #000088;">$mc</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Memcache<span style="color: #339933;">;</span>
<span style="color: #000088;">$mc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">addServer</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'ip.do.servidor'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$sql</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;SELECT * FROM tabela INNER JOIN ......&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$cache</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$mc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Usamos o md5() da query como chave</span>
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$cache</span> <span style="color: #339933;">===</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// Resultado não está no cache</span>
    <span style="color: #000088;">$res</span> <span style="color: #339933;">=</span> <span style="color: #990000;">pg_query</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$arr</span> <span style="color: #339933;">=</span> <span style="color: #990000;">pg_fetch_all</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$res</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$mc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">set</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$arr</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$cache</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Pelo exemplo acima, temos algumas modificações. Primeiramente conectamos ao memcached. A chamada a addServer() pode ser feita múltiplas vezes, caso existam vários memcached rodando em máquinas separadas. Pode ser dado um &#8220;peso&#8221; pra cada servidor também, de acordo com a memória disponível em cada um.<br />
Logo em seguida, é feita a verificação no memcached se o resultado da consulta já está lá. Como chave, usei o hash md5 da consulta, pra gerar uma identificação única. O método get() pode retornar false, caso a chave não esteja no memcached, ou retornar o objeto que foi guardado.<br />
A checagem é simples. Se o get() retornou false, então o sistema faz a consulta como já fazia antes, e no fim guarda o resultado no memcached, pra agilizar as próximas consultas. E caso não tenha retornado false, temos já o resultado da consulta diretamente. Ao fim do bloco <i>if</i>, teremos, de uma forma ou de outra, o resultado da consulta em $arr.<br />
A diferença é que rodando a consulta no banco, o tempo será de cerca de 1 segundo (tempo que estimamos para a execução da consulta), e pegando o resultado direto do memcached, a consulta demorará algo da ordem de milissegundos para ser completada.</p>
<h2>Problemas</h2>
<p>Um dos problemas clássicos em qualquer abordagem que use cache é como saber se as informações em cache ainda estão atuais. Com o memcached, isso não é diferente. Existem algumas formas de se lidar com o problema.<br />
A primeira delas, mais simples, e que pode ser usada livremente caso não seja de suma importância que os dados estejam atuais, é mexer no tempo em que a informação ficará no cache. O tempo pode ser especificado na chamada ao método set(), da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$mc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">set</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">md5</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr</span><span style="color: #339933;">,</span> MEMCACHE_COMPRESSED<span style="color: #339933;">,</span> <span style="color: #cc66cc;">60</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Temos neste exemplo todos os parâmetros para a chamada de set(): a chave, o objeto a ser guardado, uma flag (pode ser usado MEMCACHE_COMPRESSED, que indica que o objeto será comprimido para ocupar menos espaço, ou 0 pra indicar que deve ser guardado sem compressão), e por último o tempo de vida do objeto, no caso, 60 segundos.<br />
Ou seja, caso não haja problema das informações permanecerem desatualizadas por 60 segundos no máximo, essa abordagem pode ser usada.<br />
Caso a informação tenha que estar sempre atualizada, a forma de tratamento terá que ser outra. O memcached possui método para apagar o objeto associado a uma chave. Tendo a chave, basta usar:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$mc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">delete</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'chave'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>e a entrada que existia no memcached para aquela chave será invalidada.<br />
A idéia, nesse segundo caso, é justamente localizar no código todos os pontos que modificam o banco de dados de forma que possa alterar o resultado da consulta feita, e adicionar código para invalidar o cache nesses pontos. Assim, quando for feita a consulta no memcached, caso alguma informação tenha sido modificada, a entrada correspondente no memcached já terá sido apagada, e a chamada a get() retornará false naturalmente.<br />
É claro que essa segunda abordagem dá muito mais trabalho, por isso a sugestão de se usar a primeira delas nos casos em que pode ser usada. Lembre-se que em um site com 100 acessos por segundo, um tempo de vida de 60 segundos evitará 5999 consultas ao banco (a primeira será feita e guardada).</p>
<h2>Mais informações</h2>
<p>O site do projeto tem <a href="http://code.google.com/p/memcached/wiki/Start">um Wiki</a> que responde a maior parte das dúvidas que se pode ter, em relação a instalação e ao uso.<br />
No caso do PHP, as funções são bem documentadas na <a href="http://br.php.net/manual/en/book.memcache.php">parte do manual sobre memcache</a>.<br />
Eu uso em produção o memcached, com 2 servidores, um com 768mb de memória disponíveis para o memcached e outro com 256mb. Dei pesos 3 e 1 respectivamente, para que um objeto tenha 3 vezes mais chance de cair no primeiro servidor. A configuração está bastante satisfatória, e o banco de dados agradece.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php" title="Evitando SQL injection em PHP">Evitando SQL injection em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-imagens-inline" title="Otimização: imagens inline">Otimização: imagens inline</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-parte-3-cache" title="Otimização de sites, parte 3 &#8211; Cache">Otimização de sites, parte 3 &#8211; Cache</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-parte-2-compressao" title="Otimização de sites, parte 2 &#8211; Compressão">Otimização de sites, parte 2 &#8211; Compressão</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/problemas-com-acentuacao" title="Problemas com acentuação?">Problemas com acentuação?</a></li><li><a href="http://www.ataraxia.com.br/posts/o-limite-de-4-ou-3-gb-de-memoria" title="O limite de 4 (ou 3?) Gb de memória">O limite de 4 (ou 3?) Gb de memória</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/otimizacao-de-sites-com-memcached/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Evitando SQL injection em PHP</title>
		<link>http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php</link>
		<comments>http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php#comments</comments>
		<pubDate>Thu, 23 Apr 2009 00:13:09 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[programação]]></category>
		<category><![CDATA[segurança]]></category>
		<category><![CDATA[fieo]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[validação]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=318</guid>
		<description><![CDATA[Em um mundo ideal, todos os desenvolvedores, ao programar seus aplicativos em qualquer linguagem, deveriam se preocupar com segurança, em especial com a validação dos dados. No PHP, isso não é diferente. Uma classe de problemas de segurança bem específica do ambiente web é a chamada SQL Injection, literalmente uma &#8220;Injeção de SQL&#8221;. E embora [...]]]></description>
			<content:encoded><![CDATA[<p>Em um mundo ideal, todos os desenvolvedores, ao programar seus aplicativos em qualquer linguagem, deveriam se preocupar com segurança, em especial com a <a href="http://www.ataraxia.com.br/posts/validacao-de-dados-em-php">validação dos dados</a>. No PHP, isso não é diferente.<br />
Uma classe de problemas de segurança bem específica do ambiente web é a chamada <em>SQL Injection</em>, literalmente uma &#8220;Injeção de SQL&#8221;. E embora a solução seja simples, é preciso que o programador preste atenção, pra evitar que seu aplicativo acabe sendo explorado por uma falha de segurança como esta.</p>
<h2>O problema</h2>
<p>Digamos que na página principal do nosso site, a gente tenha um formulário de login mais ou menos assim:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">form</span> <span style="color: #000066;">action</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;login.php&quot;</span> <span style="color: #000066;">method</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;post&quot;</span>&gt;</span>
Login: <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;login&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span>
Senha: <span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;password&quot;</span> <span style="color: #000066;">name</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;senha&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span>
<span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">input</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span>
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">form</span>&gt;</span></pre></div></div>

<p>Um formulário bem simples. E para tratar este formulário, temos o login.php, que vai checar em um banco de dados se o login e a senha estão corretos, da seguinte forma (colocarei apenas o trecho com a consulta SQL relevante):</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$sql</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;SELECT * FROM usuarios WHERE login = '<span style="color: #006699; font-weight: bold;">{$_POST['login']}</span>' AND senha = '<span style="color: #006699; font-weight: bold;">{$_POST['senha']}</span>'&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$res</span> <span style="color: #339933;">=</span> <span style="color: #990000;">mysql_query</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">mysql_num_rows</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$res</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">print</span> <span style="color: #0000ff;">&quot;Login OK&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">print</span> <span style="color: #0000ff;">&quot;Login e senha não conferem&quot;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Ou seja, verificamos na tabela <em>usuarios</em> se temos uma linha com o login e a senha fornecidos. Caso essa consulta retorne alguma coisa, significa que existe uma linha com o login e a senha fornecidos, caso contrário, não existe.<br />
Se no formulário eu preencho o login com &#8220;joe&#8221; e a senha com &#8220;xxxx&#8221;, a seguinte consulta SQL será montada e executada:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #66cc66;">*</span> <span style="color: #993333; font-weight: bold;">FROM</span> usuarios <span style="color: #993333; font-weight: bold;">WHERE</span> login <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'joe'</span> <span style="color: #993333; font-weight: bold;">AND</span> senha <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'xxxx'</span></pre></div></div>

<p>Porém, o que acontece se eu preecher o formulário com o login = joe, e a senha com:  xxxx&#8217; OR true &#8211;<br />
Neste caso, teremos a seguinte consulta:</p>

<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #66cc66;">*</span> <span style="color: #993333; font-weight: bold;">FROM</span> usuarios <span style="color: #993333; font-weight: bold;">WHERE</span> login <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'joe'</span> <span style="color: #993333; font-weight: bold;">AND</span> senha <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'xxxx'</span> <span style="color: #993333; font-weight: bold;">OR</span> <span style="color: #993333; font-weight: bold;">TRUE</span> <span style="color: #808080; font-style: italic;">--'</span></pre></div></div>

<p>Essa consulta vai retornar todas as linhas do banco de dados (por causa do &#8220;OR true&#8221; no final), e como a checagem no código é se a consulta retornou mais de uma linha, estaremos logados.<br />
Existem formas mais sofisticadas de proceder com este tipo de ataque, mas o foco do artigo é na defesa.</p>
<h2>FIEO</h2>
<p><acronym title="Filter Input/Escape Output">FIEO</acronym> é uma técnica bastante usada para aumentar a segurança dos aplicativos web. Significa &#8220;Filter Input/Escape Output&#8221;, ou traduzindo, &#8220;Filtre a Entrada/Escape a Saída&#8221;.<br />
O primeiro passo é a filtragem da entrada, já que a regra número 1 da segurança em aplicativos web diz que não podemos jamais confiar em informações vindas de fontes externas. No nosso caso, poderíamos fazer a filtragem do login e da senha checando se eles possuem apenas caracteres permitidos. Por exemplo, o site poderia ter uma regra especificando que o login deve conter apenas letras e números. A filtragem então, poderia ser feita usando a função <a href="http://br2.php.net/ctype_alnum">ctype_alnum()</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">ctype_alnum</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'login'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #990000;">die</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;login possúi caracteres inválidos&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Se o <em>FI</em> do FIEO lida com a filtragem da entrada, o <em>EO</em> lida com a manutenção da saída no contexto em que for lida.<br />
Ou seja, <em>FI</em> garante que a nossa entrada é valida, e <em>EO</em> garante que quem quer que receba os nossos dados, irá interpretá-los da forma correta.<br />
No nosso caso, é importante que as aspas simples não sejam interpretadas como delimitadores na consulta SQL. E se eu quisesse ter uma senha que tivesse uma aspa simples no meio?<br />
Existem 3 formas clássicas de se resolver este problema.</p>
<h2>Forma 1: addslashes() no PHP</h2>
<p>É a forma mais simples (e ingênua) de se tratar o problema. Por exemplo, se queremos nos proteger das aspas simples, poderíamos simplesmente escapá-las no PHP da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$login</span> <span style="color: #339933;">=</span> <span style="color: #990000;">addslashes</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'login'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$senha</span> <span style="color: #339933;">=</span> <span style="color: #990000;">addslashes</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'senha'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Dessa forma, caso uma aspa simples chegasse do usuário, seria devidamente <em>escapada</em> com uma &#8220;\&#8221; antes, e o servidor SQL não iria interpretá-la como um delimitador.<br />
Para o nosso exemplo de ataque, resolveria.<br />
O grande problema com esta solução é que ela só prevê ataques que usem aspas simples. Bancos de dados diferentes podem usar caracteres de controle diferentes, como aspas duplas, quebras de linha, ou outras coisas mais estranhas. E checar por todas estas formas é longe de ser a maneira mais eficaz (e viável!) de se proceder.<br />
Temos uma outra opção, suportada por vários bancos de dados.</p>
<h2>Forma 2: deixar o próprio banco escapar</h2>
<p>A segunda opção é passar a string para o banco e deixar ele se virar. Afinal, ele sabe o que pode e o que não pode entrar em uma string. No MySQL, isso poderia ser feito da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$login</span> <span style="color: #339933;">=</span> <span style="color: #990000;">mysql_real_escape_string</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'login'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$senha</span> <span style="color: #339933;">=</span> <span style="color: #990000;">mysql_real_escape_string</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'senha'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Segundo a documentação, chamando esta função, o banco escapa automaticamente as seguintes sequencias: \x00, \n, \r, \, &#8216;, &#8221; e \x1a. Ou seja, pelo visto as aspas simples não eram nosso único problema mesmo.<br />
Existem funções semelhantes para outros bancos de dados (por exemplo, <em>pg_escape_string()</em> para o PostgreSQL).<br />
Porém, a solução que considero mais elegante é usar consultas preparadas.</p>
<h2>Forma 3: consultas preparadas</h2>
<p>A idéia de se preparar uma consulta é dizer ao banco de dados o formato da consulta (apenas os parâmetros formais), e fazer a amarração dos parâmetros reais somente na hora da execução.<br />
A maior parte dos bancos de dados suportam consultas preparadas, mas vou mostrar o exemplo usando MySQL, devido a popularidade.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$dbh</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> mysqli<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;localhost&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;ususario&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;senha&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;banco&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$c</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$dbh</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">prepare</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT * FROM usuarios WHERE login = ? AND senha = ?&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$c</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">bind_param</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;login&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'login'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$c</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">bind_param</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;senha&quot;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$_POST</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'senha'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$c</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">execute</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Ou seja, primeiro informamos ao banco qual será a consulta a ser executada. Perceba que usamos interrogações no lugar dos parâmetros reais. Com isso, o banco prepara a consulta pra ser executada.<br />
Logo em seguida, dizemos ao banco o valor que cada parâmetro irá receber. Esta etapa se chama amarração (binding). Como o banco de dados sabe o tipo de cada campo, ele também sabe como proceder pra escapar corretamente cada valor.<br />
E por último, executamos a consulta. Simples, fácil e seguro!</p>
<h2>Conclusão</h2>
<p>SQL Injection é um problema sério que afeta muitos aplicativos web por aí afora, porém é um problema que só existe por causa da ingenuidade dos programadores, que não programam pensando em segurança. A solução, como vimos, é bem simples.</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/criando-captchas-em-php" title="Criando CAPTCHAs em PHP">Criando CAPTCHAs em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/seguranca-no-envio-de-emails" title="Segurança no envio de emails em PHP">Segurança no envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/agendando-eventos-no-wordpress" title="Agendando eventos no WordPress">Agendando eventos no WordPress</a></li><li><a href="http://www.ataraxia.com.br/posts/removendo-o-generator-do-wordpress" title="Removendo o &#8220;generator&#8221; do Wordpress">Removendo o &#8220;generator&#8221; do Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/escrevendo-plugins-para-o-wordpress" title="Escrevendo plugins para o Wordpress">Escrevendo plugins para o Wordpress</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-com-memcached" title="Otimização de sites com memcached">Otimização de sites com memcached</a></li><li><a href="http://www.ataraxia.com.br/posts/os-10-piores-captchas-do-mundo" title="Os 10 piores CAPTCHAs do mundo">Os 10 piores CAPTCHAs do mundo</a></li><li><a href="http://www.ataraxia.com.br/posts/pegadinhas-no-php" title="Pegadinhas no PHP">Pegadinhas no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li><li><a href="http://www.ataraxia.com.br/posts/precedencia-no-php" title="Precedência no PHP">Precedência no PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/evitando-spam-em-formularios" title="Evitando spam em formulários">Evitando spam em formulários</a></li><li><a href="http://www.ataraxia.com.br/posts/imprimindo-em-formularios-continuos-em-php" title="Imprimindo em formulários contínuos em PHP">Imprimindo em formulários contínuos em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-2-de-2" title="XML no PHP com XML_Serializer, parte 2 de 2">XML no PHP com XML_Serializer, parte 2 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/xml-no-php-com-xml_serializer-parte-1-de-2" title="XML no PHP com XML_Serializer, parte 1 de 2">XML no PHP com XML_Serializer, parte 1 de 2</a></li><li><a href="http://www.ataraxia.com.br/posts/envio-de-emails-em-php" title="Envio de emails em PHP">Envio de emails em PHP</a></li><li><a href="http://www.ataraxia.com.br/posts/validacao-de-dados-em-php" title="Validação de dados em PHP">Validação de dados em PHP</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/evitando-sql-injection-em-php/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Otimização: imagens inline</title>
		<link>http://www.ataraxia.com.br/posts/otimizacao-imagens-inline</link>
		<comments>http://www.ataraxia.com.br/posts/otimizacao-imagens-inline#comments</comments>
		<pubDate>Fri, 17 Apr 2009 14:48:33 +0000</pubDate>
		<dc:creator>Bruno Lustosa</dc:creator>
				<category><![CDATA[dicas]]></category>
		<category><![CDATA[programação]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[otimização]]></category>

		<guid isPermaLink="false">http://www.ataraxia.com.br/?p=216</guid>
		<description><![CDATA[Sempre que queremos colocar uma imagem em uma página, podemos usar a tag img: &#60;img src=&#34;imagem.png&#34; /&#62; ou então, podemos carregá-la como imagem de fundo de um elemento qualquer usando CSS: .classe &#123; background-image:url&#40;imagem.png&#41;; &#125; Um dos problemas em páginas que possuem muitas imagens é que cada imagem requer a abertura de uma nova conexão [...]]]></description>
			<content:encoded><![CDATA[<p>Sempre que queremos colocar uma imagem em uma página, podemos usar a tag <b>img</b>:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">img</span> <span style="color: #000066;">src</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;imagem.png&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span></pre></div></div>

<p>ou então, podemos carregá-la como imagem de fundo de um elemento qualquer usando <acronym title="Cascating Style Sheet">CSS</acronym>:</p>

<div class="wp_syntax"><div class="code"><pre class="css" style="font-family:monospace;"><span style="color: #6666ff;">.classe</span> <span style="color: #00AA00;">&#123;</span> <span style="color: #000000; font-weight: bold;">background-image</span><span style="color: #00AA00;">:</span><span style="color: #993333;">url</span><span style="color: #00AA00;">&#40;</span><span style="color: #ff0000; font-style: italic;">imagem.png</span><span style="color: #00AA00;">&#41;</span><span style="color: #00AA00;">;</span> <span style="color: #00AA00;">&#125;</span></pre></div></div>

<p>Um dos problemas em páginas que possuem muitas imagens é que cada imagem requer a abertura de uma nova conexão HTTP, e com a limitação de 2 conexões por host (é uma restrição dos navegadores que seguem a especificação HTTP), o tempo de carga da página pode acabar crescendo bastante.</p>
<h2>A lógica</h2>
<p>Uma solução para este problema é ao invés de carregar as imagens externas, colocá-las diretamente dentro da página, seguindo a mesma lógica que já usamos com CSS. Por exemplo, podemos especificar uma folha de estilo externa:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">link</span> <span style="color: #000066;">rel</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;stylesheet&quot;</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/css&quot;</span> <span style="color: #000066;">href</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;arquivo.css&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span></pre></div></div>

<p>ou então podemos colocar os estilos inline:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">style</span> <span style="color: #000066;">type</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;text/css&quot;</span>&gt;</span>
.classe1 { font-color:#f00; }
#id2 { width:200px; }
<span style="color: #009900;">&lt;<span style="color: #66cc66;">/</span><span style="color: #000000; font-weight: bold;">style</span>&gt;</span></pre></div></div>

<h2>Codificando a imagem</h2>
<p>Até aí, tudo bem, afinal CSS é texto, e texto entra bem dentro de uma página. Mas como colocar uma imagem dentro de uma página, uma vez que a imagem é binária?<br />
Simples! Primeiro, codificamos a imagem de binário para ASCII usando a codificação <b>base64</b>. Existem várias formas de se fazer isso, e boa parte das linguagens de programação disponibiliza funções para fazer esta codificação. Por exemplo, em PHP, podemos fazer:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #b1b100;">print</span> base64encode<span style="color: #009900;">&#40;</span><span style="color: #990000;">file_get_contents</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;arquivo.png&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>A saída disso é algo mais ou menos assim:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">iVBORw0KGgoAAAANSUhEUgAAAxYAAAAKCAAAAAAMKZcGAAAACXZwQWcAAAMWAAAACgALWq6
YAAAAOklEQVRYw<span style="color: #339933;">+</span>3TsQ0AIAwEMZT9F4Uh8vTUFCDZM9zV6nyt08kMXOypBnCwBdgCbAG2AF
uALcAWYAt4xQZXmxLmG7UZ3wAAAABJRU5ErkJggg<span style="color: #339933;">==</span></pre></div></div>

<p>Pronto, já temos a representação codificada da imagem.<br />
Para que a saída coubesse na tela, quebrei a saída em 3 linhas. A saída real é toda em uma só linha, mas como veremos adiante, tanto faz, pois podemos usar quebras de linha.</p>
<h2>URLs do tipo &#8220;data&#8221;</h2>
<p>Antes de colocarmos a imagem dentro da página, precisamos ver como funcionam as URLs do tipo &#8220;data&#8221;. A URL segue o seguinte formato:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;">data:[<span style="color: #009900;">&lt;tipo MIME&gt;</span>][;charset=&quot;<span style="color: #009900;">&lt;charset&gt;</span>&quot;][;base64],<span style="color: #009900;">&lt;dados&gt;</span></pre></div></div>

<p>onde:</p>
<ul>
<li><em>tipo MIME</em> é o tipo do arquivo que vamos colocar, no nosso caso, <b>image/png</b>;</li>
<li><em>charset</em> é a codificação utilizada, para o caso de arquivos texto (não usaremos no nosso exemplo).</li>
<li><em>base64</em> é a codificação utilizada. Se for omitida, o navegador entenderá que estamos usando US-ASCII, e no caso de imagens, não vai dar certo;</li>
<li>e por último, <em>dados</em> contém a string com o arquivo codificado (aquela sequencia grande de caracteres ali de cima). A string pode ser quebrada com espaços ou quebras de linha.</li>
</ul>
<p></p>
<h2>Juntando tudo</h2>
<p>Pois bem, agora que temos a representação codificada da imagem e já sabemos usar URLs do tipo &#8220;data&#8221;, podemos colocá-la diretamente na página, usando a própria tag <b>img</b>:</p>

<div class="wp_syntax"><div class="code"><pre class="html4strict" style="font-family:monospace;"><span style="color: #009900;">&lt;<span style="color: #000000; font-weight: bold;">img</span> <span style="color: #000066;">src</span><span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAxYAAAAKCAAAAAAMKZc</span>
<span style="color: #009900;">GAAAACXZwQWcAAAMWAAAACgALWq6YAAAAOklEQVRYw+3TsQ0AIAwEMZT9F4Uh8vTUFCDZM9zV6n</span>
<span style="color: #009900;">yt08kMXOypBnCwBdgCbAG2AFuALcAWYAt4xQZXmxLmG7UZ3wAAAABJRU5ErkJggg==&quot;</span> <span style="color: #66cc66;">/</span>&gt;</span></pre></div></div>

<p>Ou então uma regra de CSS, da seguinte forma:</p>

<div class="wp_syntax"><div class="code"><pre class="css" style="font-family:monospace;"><span style="color: #6666ff;">.classe</span> <span style="color: #00AA00;">&#123;</span> <span style="color: #000000; font-weight: bold;">background-image</span><span style="color: #00AA00;">:</span><span style="color: #993333;">url</span><span style="color: #00AA00;">&#40;</span>data<span style="color: #00AA00;">:</span>image/png<span style="color: #00AA00;">;</span>base64<span style="color: #00AA00;">,</span>iVBORw0KGgoAAAANSUh
EUgAAAxYAAAAKCAAAAAAMKZcGAAAACXZwQWcAAAMWAAAACgALWq6YAAAAOklEQVRYw<span style="color: #00AA00;">+</span>3TsQ0
AIAwEMZT9F4Uh8vTUFCDZM9zV6nyt08kMXOypBnCwBdgCbAG2AFuALcAWYAt4xQZXmxLmG7U
Z3wAAAABJRU5ErkJggg<span style="color: #00AA00;">==</span><span style="color: #00AA00;">&#41;</span><span style="color: #00AA00;">;</span> <span style="color: #00AA00;">&#125;</span></pre></div></div>

<p>É claro que usar esta técnica, como quase tudo na vida, tem várias vantagens e desvantagens.</p>
<h2>As vantagens</h2>
<p>Temos várias vantagens em usar um esquema deste tipo:</p>
<ul>
<li>Suporte na grande maioria dos navegadores modernos: Firefox, Opera, Safari, Konqueror e Chrome;</li>
<li>Antes de mais nada, estamos diminuindo o número de requisições HTTP, e consequentemente, evitamos o tráfego associado à requisição (cabeçalhos HTTP);</li>
<li>Como a imagem está inline, conseguimos &#8220;fugir&#8221; do limite de 2 conexões por host, já que estamos evitando a abertura de novas conexões;</li>
<li>Para imagens pequenas, temos um ganho real em bytes transferidos, pois embora a imagem codificada em base64 fique maior, estamos evitando de enviar os cabeçalhos HTTP (que somam em média 200 bytes).</li>
<li>Conexões seguras (que usam https) só mostram o &#8220;cadeado&#8221; fechado se todos os elementos externos forem carregados através de https também. E o <em>overhead</em> associado a uma conexão https é bem maior que o de uma conexão http normal. Com a imagem inline, ganhamos mais um pouco.</li>
</ul>
<p></p>
<h2>As desvantagens</h2>
<p>Temos várias desvantagens, algumas contornáveis:</p>
<ul>
<li>Internet Explorer até a versão 7 não tem suporte. Só a partir do 8. A forma de se usar para os dois navegadores é usando arquivos CSS separados para cada navegador (ou um pré-processador de CSS que detecte o navegador e dê a saída com o que for suportado);</li>
<li>Imagem inline em uma página (usando tag <b>img</b>) não são cacheadas pelo navegador. Se usada dentro do CSS, aí sim teremos o cache em ação, pois o navegador irá cachear o CSS;</li>
<li>Uma imagem codificada em base64 tem seu tamanho aumentado em 33%. Para imagens grandes, é uma perda considerável, mas para imagens pequenas (da ordem de 500 bytes), temos um ganho, pois mesmo com o aumento da imagem, estamos evitando o envio dos cabeçalhos, e podemos sair ganhando na soma;</li>
<li>Dificuldade de manutenção: se uma imagem é alterada, ela precisa ser recodificada. Existem formas de se automatizar isso.</li>
</ul>
<p></p>
<h2>Um exemplo</h2>
<p>A imagem que aparece aqui embaixo está inline, e foi colocada usando uma tag <b>img</b> (a qualidade está baixa pra não ficar muito grande):</p>
<p><img src="data:image/png;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////////////////////////////////////////////////////////////wAARCADwAUADASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwT/xAAxEAACAgEDBAEEAgEEAwADAAABAgARIQMSMSJBUWETMnGBkaHwwSMzQrFS0eFigvH/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/xAAdEQEBAQADAQEBAQAAAAAAAAAAEQEhMUFhAlFx/9oADAMBAAIRAxEAPwDHev8A4j9RbwPUkmhxmRN7rV1puvvK27gRMZemxuozf6l/pVQoxULmxKsOpbPmAOmvIz7jcUtNcWR9sxM1Gh2lq25pmykE3x2l8Auob6uJq4sBhkV2mAGfM3J+OjeaqMMZKttXM3ZlApvx5EzGsSabj1K1VJO4cHv4hapSu0lATQ7yRquHonHePRUqpY4HuNH0y4oEHtZxBRqIzHi/xLcKQAzUarMzdnGpg1niW6bm5APgmFoYqtLRMZB3gAWv2g7KpClb7Xcqn+Uc1f4gqNg3HjAi2bh9MtQoZipsgcQvfpm6XMVagoq4Ni4DSqzUskKFwG9yttapN2a4ilZBQynFVGy0AR+5QO4MCAoHcQGNOtMkxSpcBKJGfEGQMQwIA94jP+2DqDP8xH/VQbceoSlqi1AGRXPmNCfjtsgeYC9PT93JR2clWG4QUhq9VbQB6jbSB7gAeYbFDWWH+Yteya7VcChQUgG+9zmYcY5mmjYbFynCEWSVvmO0tTpDJ7YlM4X6gD9xGpWqX9+Zi2STHh4v5QccfiGopK2JiJshG3qyCIzambe2TA1EBNW0zZK5BkBGPMm4bgUG5Q5wazKVdq1+8SHNfcTXWL0l+ZPaWQSPckYGZjcZKUv1DzF9poiUNx/EZgDjMzM1PUD/ABM6oUZf0aSmjjmb3jIBHOZiqljQE2f6SBmME/Iq4VR95RHyICpsjNeZhU20VtT9xmMpUrpOW+kj7zR3OnQQ58zJtRiecTVafT6sEdxFKWm/yWrm7HMp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" /></p>
<p>Clicando o botão direito na imagem e escolhendo &#8220;Exibir imagem&#8221; (no Firefox ou em outro navegador que dê suporte), dá pra se ver na barra de endereços a URL usada pra criar a imagem.<br />
E claro, se você não está vendo imagem nenhuma, provavelmente está usando o Internet Explorer. E já passou da hora de passar a usar um <a href="http://www.getfirefox.com/">navegador decente, que suporta os padrões web</a>, não é?</p>
<h3  class="related_post_title">Artigos relacionados</h3><ul class="related_post"><li><a href="http://www.ataraxia.com.br/posts/evitando-conteudo-duplicado" title="Evitando conteúdo duplicado">Evitando conteúdo duplicado</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-com-memcached" title="Otimização de sites com memcached">Otimização de sites com memcached</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-parte-3-cache" title="Otimização de sites, parte 3 &#8211; Cache">Otimização de sites, parte 3 &#8211; Cache</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-de-sites-parte-2-compressao" title="Otimização de sites, parte 2 &#8211; Compressão">Otimização de sites, parte 2 &#8211; Compressão</a></li><li><a href="http://www.ataraxia.com.br/posts/otimizacao-em-php-parte-1-minify" title="Otimização em PHP, parte 1: Minify">Otimização em PHP, parte 1: Minify</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.ataraxia.com.br/posts/otimizacao-imagens-inline/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

