Não Importa

Desenvolvimento | Sysadmin | Sandice.

Otimização Extrema: Use CDN!

| Comments

Continuando com a série de artigos relacionados a otimização extrema de sua aplicação, hoje começaremos com a abordagem do uso de cache para diminuir a latência no carregamento de suas páginas.

Com a pequena curva de aprendizado e grande quantidade de bibliotecas para a preparação do front-end, cada vez mais, cresce a quantidade de documentos de estilo e arquivos javascripts anexados a uma página. Com esse crescimento, também cresce virtiginosamente a quantidade de requisições e até mesmo o tamanho do documento que deve ser entregue ao usuário. Como já visto anteriormente em outros artigos, o tempo de carregamento de sua página pode ser um fator decisivo na permanência e decisão de um usuário em sua aplicação/página.

Pensando nisso, algumas gigantes da internet, como Google e Microsoft, criaram um repositório de bibliotecas(js) e frameworks(css) para servi-las de forma otimizada. Esses repositórios, denominados CDN(Content Delivery Network), distribuem bibliotecas como jquery, mootools, prototype e frameworks css, como o twitter bootstrap, com um excelente controle de cache, e ainda em servidores otimizados para entrega de conteúdo estático, como o nginx e lighttpd. Mais a frente vamos ver um artigo em como montar o seu próprio servidor de arquivos estáticos.

A ideia de usar CDNs em seus projetos é a de aumentar consideravelmente a possibilidade de requisições a objetos que já estão em cache, utilizando recursos que foram requisitados em outra requisição, em outro site, a seu favor.

Digamos que o exista um site “exemplo.com”, e no código html do site há a requisição para o carregamento de um código javascript de uma das bibliotecas servidas por uma das CDNs. Quando o usuário acessar a página, o código será carregado para o browser do usuário e colocado em cache, devido as fortes regras que as CDNs tem relacionados a isso. Caso eu use a mesma biblioteca em um projeto, e também usar a CDN, não haverá a necessidade do usuário carregar novamente a biblioteca, já que o arquivo estará em seu cache. Como o controle de cache nos browsers atuais é feito por intermédio do domínio onde tal recurso está alocado, o conteúdo vai estar cacheado e pronto para ser utilizado.

Com o propósito de saber qual a CDN com maior velocidade, e também exemplificar o que disse anteriormente, vou criar dois hosts virtuais(hosta e hostb), em minha máquina e descobrir qual a melhor opção de CDN a ser utilizada em seu projeto.

Urls protocol-relatives

Antes de iniciarmos nossos testes, precisamos dar uma olhada em um ótimo recurso que as CDNs tratadas aqui possui. Esse recurso em questão, trata-se de servir as bibliotecas tanto em http, quanto em https, possibilitando as urls serem montadas sem a especificação do protocolo. Obviamente que isso não é um recurso dos hosts das cdns cedentes, mas sim uma regra que é seguida por quase todos os browsers(todos os browsers modernos seguem), para levar em consideração o protocolo atual da sessão do usuário. Com tal recurso, fica fácil fazer a troca de ambiente sem se preocupar com o que será feito com as urls que seguem outro protocolo, como por exemplo o https.

Se o usuário estiver navegando por algum área de sua apliacação onde não há nenhum tipo de encriptação https, seus links começaram com “http://”, e se em algum momento ele fizer login e todas as páginas que ele visitou passasem a ser servidas pelo protocolo https, como o programador faria para trocar todas as URLS especificando o protocolo seguro?

Fácil! É só ele não se preocupar com isso, omitindo a especificação do protocolo, contruindo url absolutas, mas com relatividade ao protocolo. Veja no exemplo abaixo a ausência do protocolo no início das URL.

1
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
1
<script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"></script>
1
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

Vamos ao testes!

Para nossos testes usaremos três grandes CDNs. A primeira é a do Google, que serve as principais bibliotecas disponíveis atualmente, tais como Jquery, MooTools, Ext, entre outras.

Como pode ser visto na imagem acima, o tempo de espera( do momento do pedido até o ínicio da entrega), foi de 202ms, fazendo com que o tempo da CDN do google, nos testes efetuados, seja o pior. Mas se você considerar a quantidade de aplicações/sites utilizando o serviço, esse valor é aceitável. Por outro lado, sem dúvida alguma essa CDN vence todas as outras por compressão. A biblioteca foi entregue em apenas 32k, de um total de 90k, sem compressão. Na imagem abaixo, utilizei outro host(hostb), para demostrar como a biblioteca já está carregada ao browser do usuário e não será requisitada novamente.

Partindo para nosso segundo teste, utilizaremos a da Microsoft, que ao meu ver é a mais limitada. Além da padrão Jquery, possue em seu arquivo alguns plugins da mesma biblioteca, e a biblioteca para se trabalhar com o MVC da tecnologia .NET.

Confesso que me supreendi com os resultados da CDN da Microsoft. O tempo de espera foi de apenas 32ms, ignorando o tempo em que o browser procurava o DNS! Mas isso pode significar que a CDN é pouco usada. Mas infelizmente esse ganho na espera é neutralizado com o tempo baixando a biblioteca, como pode ser visto.

A última CDN que utilizaremos é a disponibilizada pela CloudFlare, denominada cdnjs. Em relação a quantidade, é sem dúvida alguma a melhor. Enquanto escrevia esse artigo havia 268 objetos sendo disponibilizados pela empresa.

Essa CDN pode ser problemática em sua aplicação! Apesar de seu tempo de espera não ser o pior, o tempo de download foi bem ruim comparado aos outros dois. Além disso, três em dez vezes que executei os testes, o host apresentava problemas com a conexão. Por algum motivo obscuro, não resolvendo o DNS. Use-a, mas com cautela.

Terminado os testes, podemos concluir algumas coisas:

  1. Na maioria das vezes, é mais rápido carregar um objeto de uma CDN que do seu próprio host.
  2. A CDN com maior performance definitivamente é a do google. Porém, nada lhe impede de carregar uma outra biblioteca para completar sua aplicação, como por exemplo, da cdnjs.
  3. Utilize sempre as URLs “protocol-relatives”. Você nunca sabe quando sua aplicação possa passar a servir em protocolo seguro. Isso pode lhe evitar retrabalho futuro. Além disso, pode ser sensível, mas querendo ou não diminui o tamanho do seu documento omitindo o protocolo.
  4. Apesar da grande quantidade de bibliotecas agregadas ao repositório da CDN disponibilizada pela CloudFlare, definitivamente ela é a CDN menos eficiente.

Terminado este artigo, lhe faço uma pergunta: Você utiliza sua biblioteca javascript conscientemente? Jquery é a solução para todos os seus problemas? Esse é o assunto de nosso próximo artigo.

Otimização extrema: Otimizando e reduzindo o seu CSS

| Comments

Continuando com a série de artigos relacionados a otimização extrema de sua aplicação, hoje vamos continuar falando sobre os arquivos CSS. Algumas das técnicas abaixo podem parecer besteira, mas vão lhe poupar grandes problemas relacionados a performance de sua aplicação. Então vamos lá!

Use CSS Sprites

Como já disse no artigos sobre CSS Sprites, eles podem ser bastante úteis e economizar banda com a baixa quantidade de requisições que serão feitas por seu documento requisitando objetos para sua aplicação.

Não utilize a regra @import!

Existem duas formas de básicas de se anexar um documento CSS a um documento

A primeira é usando a tag link, para anexar os documentos, um por um, utilizamos o atributo href para anexar cada um dos documentos css.

1
2
<link rel="stylesheet" type="text/css" href="design.css" />
<link rel="stylesheet" type="text/css" href="estilo.css" />

A segunda, é utilizando a regra import do CSS.

1
2
3
4
<style type="text/css">
  @import url('design.css');
  @import url('estilo.css');
</style>

Pode parecer interessante utilizar a opção @import, por diversos motivos, desde utilizar somente um arquivo css para linkar todos os seus outros documentos, até diminuir o número de caracteres para anexá-los. Segundo um artigo de 2009, utilizar @import é incrivelmente ineficiente se comparador com o a tag link. Então não a use! Não se deixe enganar por qualquer outra coisa.

Usar a regra imports pode fazer com que a ordem dos elementos da página seja alterada, fazendo com que o CSS tenha alguma demora para carregar, e assim fazendo a página ter uma demora significativa de renderização. Além disso, se você utilizar a tag link e regras import na mesma página, o que deve ser carregado por imports, só aconteçam quando o que tiver que ser carregado pelo link terminar, aniquilando seu paralelismo.

USE A TAG link

Combine todos os seus estilos em apenas um arquivo

Temos a ideia de separar as CSS em diversos arquivos com a intenção de organizá-los por objetivo. Por exemplo, existirá um arquivo para a disposição dos elementos na tela, outro para a tipografia, outro para cores e outro para hacks. Isso é só um exemplo, já coisas mais absurdas, onde cada página do site tinha um css diferente, causando mais de 20 requisições de arquivos desnecessariamente.

SEMPRE agrupe todos os seus arquivos CSS em apenas um, assim você vai economizar nas requisições http feitas ao servidor, além disso lhe economizará tempo debugando seus estilos se algum não estiver de acordo com o esperado.

Caso ainda assim você ache interessante utilizar os arquivos separados por motivo de organização, existe um recurso do Apache que pode lhe auxiliar. Esse recurso é um módulo include, comumente chamados de SSI(Server Side Includes). Em suma, o que o módulo fará por nós é concatenar todos os arquivos que quisermos em apenas um, em tempo de processamento.

Para que o módulo funcione, primeiramente precisamos ativá-lo! Se você estiver usando o ubuntu, o comando abaixo fará a ativação e restart do apache.

1
sudo a2enmod include && sudo service apache2 restart

Com o módulo ativado, iremos colocar todos os arquivos css likados, como o exemplo abaixo, dentro de um outro arquivo css. Para o exemplo abaixo, peguei todos os arquivos do framework CSS Blueprint e os coloquei em um diretório chamado css.

screen.css
1
2
3
4
5
  <!--#include file="css/reset.css" -->
  <!--#include file="css/typography.css" -->
  <!--#include file="css/forms.css" -->
  <!--#include file="css/grid.css" -->
  <!--#include file="css/ie.css" -->

Com o documento CSS preparado, nosso próximo passo é criar ou editar o arquivo .htaccess. Explicando a grosso modo, se ele casar o arquivo screen.css em alguma requisição, o módulo includes é ativado e é passado por um filtro, chamado INCLUDE.

.htaccess
1
2
3
4
<FilesMatch "screen.css">
        Options +Includes
        SetOutputFilter INCLUDES
</FilesMatch>

Se você tiver feito tudo correto, quando linkar o screen.css ao seu documento html, você não verá os includes, mas sim um arquivo contendo todos os arquivos css que você gostaria de incluir. Caso contrário, vá ao arquivo de logs do seu apache e veja qual o erro está acontecendo.

Essa técnica gera um problema de performance, se você tiver prestado bastante atenção. Todas as vezes que o arquivo screen.css for chamado, haverá uma ativação do módulo includes e o arquivo será construido. Isso pode ser bastante estressante para o servidor, ainda mais se houver uma grande quantidade de arquivos a serem incluídos.

Como alternativa para esse módulo, usando o linux, o simples comando abaixo fará o arquivo css estaticamente, cabe a você toda vez que modificar algum arquivo dentro do diretório css rodá-lo novamente.

1
cat css/*.css > screen.css

Externalize seus arquivos CSS

Com externalizar, quero dizer colocar em um arquivo independente! De modo algum incorpore seus estilos ao documento HTML. Colocando em um arquivo independente, e um bom controle de cache, asseguramos que o arquivo não vai ser requisitado toda vez que a página for carregada, assim economizando uma grande quantidade de banda. Mais uma vez digo:

USE A TAG link

Sempre coloque seus arquivos CSS no topo(header)

Colocando seus documentos de estilo dentro da tag header(no topo, é claro) você garante que a página carregue progressivamente, dando a sensação que a página está sendo carregada com mais rapidez. Além disso, você evita que seja mostrada uma página branca ou pior, partes do sistema sendo carregados sem estilo e deformando toda a página, até que o documento de estilo é carregado e tudo é corrigido.

Lembre-se: A experiência do usuário em sua aplicação é muito importante.

Use a forma contraídas das regras CSS

Algumas regras CSS possuem formas contraídas. Segue exemplos:

1
2
3
4
5
6
7
8
9
10
font-variant: normal
line-height: normal
font-family: varies
font-weight: normal
font-style: normal
font-size: medium

/*font: [font-style] [font-variant] [font-weight] [font-size]/[line-height] [font-family];*/

font: italic small-caps lighter 14px/1.4 Georgia, serif;
1
2
3
4
5
6
7
list-style-position: outside;
list-style-image: none;
list-style-type: disc;

/*list-style: [list-style-type] [list-style-position] [list-style-image];*/

list-style: disc outside url(bg.png);
1
2
3
4
5
6
7
8
9
background-attachment: scroll;
background-color: transparent;
background-position: top left;
background-repeat: repeat;
background-image: none;

/*background: [background-color] [background-image] [background-repeat] [background-attachment] [background-position];*/

background: #777 url(http://domain.tld/images/bg.png) repeat-x fixed 0 0;
1
2
3
4
5
6
7
border-width: none;
border-style: none;
border-color: none;

/*border: [border-width] [border-style] [border-color];*/

border: 3px dashed #333;
1
2
3
4
5
6
7
8
9
10
11
12
13
margin-top: auto;
margin-right: auto;
margin-bottom: auto;
margin-left: auto;

/*
margin: [top-margin] [right-margin] [bottom-margin] [left-margin];
ou
margin: [top-bottom-margin] [right-left-margin];
*/

margin: 1px 1px 1px 1px;
margin: 10px 10px;
1
2
3
4
5
6
7
8
9
10
11
12
13
padding-top: auto;
padding-right: auto;
padding-bottom: auto;
padding-left: auto;

/*
padding: [top-padding] [right-padding] [bottom-padding] [left-padding];
ou
padding: [top-bottom-padding] [right-left-padding];
*/

padding: 1px 1px 1px 1px;
padding: 10px 10px;

Agrupe estilos similares

Quando você está há algum tempo desenvolvendo uma aplicação, ou mesmo dando manutenção em uma aplicação legada, é fácil encontrar em um documento de estilo elementos que tem as mesmas regras aplicadas repetidas vezes, ou mesmo parte das regras iguais. Agrupe-os e use a maravilha das regras em cascata a seu favor!

O exemplo abaixo descreve claramente o agrupamento quando todas as regras são idênticas!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.botao-ok {
  display:block;
  padding: 5px 0;
  float: left
}
.botao-error {
  display:block;
  padding: 5px 0;
  float: left
}
.botao-aviso {
  display:block;
  padding: 5px 0;
  float: left
}

O exemplo acima pode ser reescrito da seguinte forma:

1
2
3
4
5
.botao-ok, .botao-error, .botao-aviso {
  display:block;
  padding: 5px 0;
  float: left
}

No exemplo abaixo, usamos a herança para configurar o botão e adicionamos cores ao texto dos elementos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.botao {
  display:block;
  padding: 5px 0;
  float: left
}
.botao-ok {
  color: green;
}
.botao-error {
  color: red;
}
.botao-aviso {
  color: yellow;
}

Omita o desnecessário!

Existe uma série de recursos que você pode usar para diminuir ainda mais o tamanho do seu arquivo CSS.

Reduza o número de quebras de linha

Como dito no artigo sobre CSS Sprites, existe uma ferramenta chamada yumcompressor, que faz uma série de otimizações em seu código CSS. Uma dessas é a remoção de todos os espaços e quebras de linhas do código. Como o browser ignora completamente os espaços e quebras de linha você removê-los sem nenhuma dó!

Remova o último ponto e vírgula

O último ponto e vírgula das regras aplicado a qualquer elemento pode ser eliminado. Veja o exemplo abaixo para esclarecer:

1
2
3
4
5
.botao {
  display:block;
  padding: 5px 0;
  float: left
}
Use comentários simples

Não utilize comentários gigantescos como o abaixo! Além de não trazer nenhum tipo de ajuda real ao que está sendo feito pelo arquivo, faz com que o arquivo cresça desnecessariamente! Pessoas “letradas” em CSS não precisarão de nenhum tipo de auxílio explicativo para suas regras. CSS é auto-explicativo. Seria como documentar a expressão ‘1+1=2’.

1
2
3
4
5
6
7
8
9
10
11
12
/* -----------------------------------------------------------------------


 Blueprint CSS Framework 1.0.1
 http://blueprintcss.org

   * Copyright (c) 2007-Present. See LICENSE for more info.
   * See README for instructions on how to use Blueprint.
   * For credits and origins, see AUTHORS.
   * This is a compressed file. See the sources in the 'src' directory.

----------------------------------------------------------------------- */
Simplifique a forma hexadecimal das cores

Além da forma contraída de expressar regras, como visto anteriormente, existe uma forma de expressar cores em hexadecimal contraidamente.

O código abaixo pode tranquilamente ser trocado pelo seu posterior.

1
2
3
4
5
6
7
8
9
.a {
  color: #FFFFFF
}
.b {
  background: #000000
}
.c {
  border: 1px solid #FF0000
}
1
2
3
4
5
6
7
8
9
.a {
  color: #FFF
}
.b {
  background: #000
}
.c {
  border: 1px solid #F00
}
Remova a medida usada!

Quando você estiver escrevendo alguma regra que tenha o valor 0(zero), não há necessidade de explicitar qual a medida que está sendo usada, afinal px, pc, em, não terão sentido se você está utilizando ZERO. Por que afinal, zero será zero em qualquer uma dessas medidas.

Assim termino as técnicas de otimização extrema relacionadas a CSS. Espero que o que foi informado nesses dois artigos lhe ajude na construção de sua aplicação. Se vocẽ ainda não leu o artigo anterior falando sobre CSS Sprites, aconselho a ler.

Otimização Extrema: Maximizando o uso de CSS Sprites

| Comments

Começando a série de otimização extrema de seu site ou aplicação, iremos caminhar inicialmente pela otimização de itens relacionados ao front-end. Temos que ter um conhecimento inicial sobre como os browsers funcionam quando relacionado ao paralelismo de itens que são buscados do servidor.

Por padrão, os browser do mercado atualmente buscam paralelamente no máximo 8 ítens, incluindo páginas (x)html, folhas de estilo, arquivos javascript e imagens. Levando isso em consideração, quanto maior a quantidade de itens a serem carregados, maior será o tempo gasto para o carregamento da página. Óbvio, não é?

Em alguns browsers, o acesso a informação de quantas requisições simultaneas são feitas pelo browser é feita de modo tranquilo, como por exemplo no firefox. Entrando nas configurações do browser, digitando about:config na barra de endereços, e confirmando a mensagem de que você sabe o que está fazendo, procure pela diretiva network.http.max-persistent-connections-per-server, e claramente você pode ver que o ítem está com o valor seis, mostrando que o browser só vai buscar seis itens por vez. Você, é claro, pode mudar esse valor, mudando para quaisquer que seja o valor de itens que você queira que o browser busque por vêz. É claro que isso só vai mudar pra você, e não para todos os usuários que utilizarem seu serviço.

1
network.http.max-persistent-connections-per-server

Os servidores http em geral, tem diversas rotinas que farão automaticamente o tratamento das requisições, com a proposta de otimizar seus serviços, economizando recursos do servidor onde ele está alocado. Mas eles não são tão inteligentes assim. É seu trabalho como programador front-end, disponibilizar seus documentos de forma que o browser e o servidor trabalhem de forma otimizada.

Nossos próximos passos serão para diminuir ao máximo a quantidade de requisições que o browser vai fazer ao servidor. Se o que você está trabalhando tende a ter bastante acesso, vale muito a pena diminuir o número de requisições HTTP que serão feitas.

O html gerado na imagem acima, é o objeto dos nossos testes. Montei um pequeno documento linkando um documento css, relacionando três imagens a três classes distintas. O documento em questão ilustra uma situação normal em interfaces, onde um item tem ações diversas relacionadas, e essas ações estão relacionadas semióticamente com uma imagem.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html dir="ltr">
<head>
    <meta charset="UTF-8" />
    <title>base64</title>
    <link rel="stylesheet" href="css.css" type="text/css" media="screen" />
</head>
<body>
    <h1> Minha lista de coisas </h1>
    <div class="list">
        <ul>
            <li>
                <span class="sprite add">&nbsp;</span>
                <span class="sprite del">&nbsp;</span>
                <span class="sprite edit">&nbsp;</span>
                Coisa 1
            </li>
            <li>
                <span class="sprite add">&nbsp;</span>
                <span class="sprite del">&nbsp;</span>
                <span class="sprite edit">&nbsp;</span>
                Coisa 2
            </li>
            <li>
                <span class="sprite add">&nbsp;</span>
                <span class="sprite del">&nbsp;</span>
                <span class="sprite edit">&nbsp;</span>
                Coisa 3
            </li>
            <li>
                <span class="sprite add">&nbsp;</span>
                <span class="sprite del">&nbsp;</span>
                <span class="sprite edit">&nbsp;</span>
                Coisa 4
            </li>
            <li>
                <span class="sprite add">&nbsp;</span>
                <span class="sprite del">&nbsp;</span>
                <span class="sprite edit">&nbsp;</span>
                Coisa 5
            </li>
        </ul>
    </div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ul {
  list-style: none;
}
.sprite{
  display:block;
  float:left;
  height:16px;
  width:16px;
}
.sprite.add{
  background:url(add.gif);
}
.sprite.del{
  background:url(delete.gif);
}
.sprite.edit{
  background:url(pencil.gif);
}

Aqui vamos começar nossa nossa primeira otimização no documento css, iremos minimizá-lo removendo tudo o que é desnecessário para o browser, como por exemplo, espaços. Para tal, utilizaremos a ferramenta yuicompressor. Como você pode ver abaixo, o código fica um pouco ilegível para nós, mas para o browser continua sendo o mesmo documento.

1
ul{list-style: none;}.sprite{display:block;float:left;height:16px;width:16px;}.sprite.add{background:url(add.gif);}.sprite.del{background:url(delete.gif);}.sprite.edit{background:url(pencil.gif);}

Fazer isso pode se tornar uma tarefa bem chata, ainda mais se você tem uma grande quantidade de arquivos CSS(o que desencorajo, em um post mais a frente vou falar disso), então se o projeto no qual você está trabalhando possui integração contínua, e como servidor o jenkins, não custa nada configurá-lo para fazer esse trabalho para você. A tarefa que o ant deve executar pode ser algo como o código que você digita no sue prompt de comando:

1
./yuicompressor-2.4.jar --type css -o estilo_c.css estilo.css

Como você pode ver na imagem abaixo, em uma página simples, composto de um documento htmt, uma folha de estilo e três imagens geraram cinco requisições HTTP, onde 4.23KB foram transferidos e houve 26ms para os elementos serem carregados na página.

É pouco, se você levar em consideração as conexões atuais, e claro que o exemplo é apenas uma situação que quase não existe, uma página com 5 registros e somente esses estilos. Em aplicações e sites de verdade podemos multiplicar sem nenhum medo esse número por 100. Olhando mais intrinsecamente para esse exemplo, com relação ao custo dessa página para a empresa que o servirá, a quantidade de tráfego que essa página gera é descomunal(!) se você multiplicar por 10000 usuários, e 100000, 1000000!

Ok ok! Exagerei um pouco! Já que é uma página simples, mas sempre pense que a sua aplicação é feita de diversos documentos sendo carregados, e tudo o que você puder fazer para tornar a experiência do usuário mais ágil, é um ganho para alguém!

Ok! Vou comprimir meus arquivos CSS. E o tal dos CSS Sprites?

Se você trabalha com front-end e nunca ouviu falar dessa técnica definitivamente você tem problemas em suas aplicações! A técnica consiste em juntar várias imagens em apenas uma, assim diminuindo o número de requisições http feitas para o servidor. O próximo passo é mapear a posição de cada item dentro do arquivo CSS. Para gerar o sprite, utilizei um serviço muito interessante chamado spritegen. Testei o alinhamento horizontal e vertical na tentativa de conseguir algum ganho em relação ao tamanho da imagem, mas permaneceu o mesmo.

Abaixo, mapeamos nossa imagem como mencionado.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ul {
  list-style: none;
}
.sprite{
  background:url(sprite.gif);
  display:block;
  float:left;
  height:16px;
  width:16px
}
.sprite.add{
  background-position:0 0
}
.sprite.del{
  background-position:0 -17px
}
.sprite.edit{
  background-position:0 -34px
}

Como já aprender como compactar o código css, vamos aplicar o compressor nesse aqui também!

1
.sprite{background:url(sprite.gif);display:block;float:left;height:16px;width:16px}.sprite.add{background-position:0 0}.sprite.del{background-position:0 -17px}.sprite.edit{background-position:0 -34px}

A imagem abaixo ilustra claramente nosso ganho. Além de agora termos carregado somente 2.69KB(quase metade do tamanho anterior), nossa página levou apenas 13ms para ser carregada. Metade do tempo que levava para ser carregada anteriormente. Além disso, Diminuímos de cinco requisições para apenas 3!

Ok! Entendi! Mas e temos como melhorar isso? SIM!

Há alguns posts atrás, mostrei uma técnica onde usamos a codificação base64 para diminuir o número de requisições http feitas ao servidor, e agora vamos aplicar a mesma técnica em nosso sprite, incorporando a imagem ao documento CSS.

Utilizei o código que está anexado ao post mencionado para gerar a imagem codificada e o código abaixo quando anexado ao documento CSS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.sprite{
  background:url(data:image/gif;base64,R0lGODlhEAAyAPcAAFpBE5NtHqpgFrpSNL9VOpxpJbR2LLV9NMJnFcFNM9BPPsRQNMBVPcxSPMBZP79cQNtWS9ddUeZWTOZZTelZTeZWUOZaUuxfVOddWcNmTM1jU8drVMlrVs1oWNZgVO5mUuBlXOplXO5qXvBkVvNzXdZuZNtpZd1sadRxZd55dONtYuJwZvJ3Y/N5Y+FvcON6c+l7cuN8e+l/eD2GNUKNNkSNOkmPP0mOQlScQ1SWTV6cVmGeWGSgVmWrVWirU2ejWmurWm+1WHS3XHG5WnasZ3iuaHi0ZHe8YH28Zn+yan26bHDBY3nBZH3EbLmJJr+QJ8eDNNmPO8OZMeS6L4O1boa3cYq6d4u8eZO/f8GLSMqVQsmRT8+cSsqTUc2WWNKdW+iYQNipSdmoUN6xXea0XfaEa+6Kde2EffCFcPiGcPCMd/iMdvCDe/GOevWSfvmTf8ipZ9ilat2ubNWqct2rc9CweuyqYYHMdY3Ce4bKeorMfvTWRNzEbfPPb/XcYPXZaJW/gu6MgfSOge6Sj/qah/qdi/Gdltynl+GpnvqjkfSinvuqneGroPSnovasovivpPKtquuyrvS0qvi2qPq6r/C2to3Mg5XEgpfMhZjMh5PNiJjMio7Sg5bUipnRi5vBl5vBmJ3YkqXPlabQlqDZlqvUnaXbm6fJoa/fprTZpbjVr7Xcqrrdr7rWsbzVubfgr77jtvrujsXduezPqfTPpfTUp+HLsOfEufrFvPfbvfjiqPLmvvz0s8rexe7JxPnLxPbPz/vTzf3b1P3c2MzixcXowM7pydTrz9fu0tnp0dzx2Pjhxfjtw/Thyfnjy/rn0eDy3OPu4vXn4vXp5frq6ufw5ejx5u306/z26////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAANoALAAAAAAQADIAAAj+ALUJHEiwYMFrsi5VqWKl1TWD2pLhGUXsmDFWSooQK5jMCitosEyRQlUMz49eA68BWgUtFCZLnTpxeqUEiDWBCZGR6qQnW7Y8TZqwwnFK4EpUlvIo8QnkiBBPl3YIrAKrUx6fWLMNYVLqhsAkq/TcyeozCJJSNQRa2dSpyRIfZYcMwYQnh0BVPlYxWSLE55AgQ1LV+CTQGhElqZjIDRJESCogN6INJLbDSKpMVJBgejzDVUHKOCaKskLjhmeD1U7psFEjByjJEGPLnk17NrVKKVyceBGJGkRgMQbhCvZL0ooSvgoCcwFpGKVEhRbhgqEhuUBqMRoJI+SmjaA3ayb+qegwTWClQMAKvVFTpn2ZFo8UMBIYw9EiNRfy5//QQpAMDgKdQIkgZZCVDQUiGMKAQCY4kkaBZFEQgoICpRCIGyRQoOGGF7DxQgYCRQKBIyN8sKGGFiiSwCHXlaCCIxdoOIEEFTTiwQPSDOSLBiA0wkYIGJxhSAQD3FLQjg3IMMggLyTwgJEGTYPIBg4QkMEhOdam5ZZcQpTNGF1sEQY2s4URRzPOaDGHbFPUsowzzuTSRWxTMKOLHLTQkosXENXJyx99fEHHFn0yw4sfe8RCRhSFHproohBJYSiiijJqkBO7OFpplE7YAgelkBoUQDPPHCAFH1xAEVsAztgBBgIWAhggGwBf1DJLFrLOCkABBtTR5WwBAQA);
  display:block;
  float:left;
  height:16px;
  width:16px
}
.sprite.add{
  background-position:0 0
}
.sprite.del{
  background-position:0 -17px
}
.sprite.edit{
  background-position:0 -34px
}

Vamos comprimir esse código também, para obtermos ainda mais performance da nossa aplicação.

1
ul{list-style: none;}.sprite{background:url(data:image/gif;base64,R0lGODlhEAAyAPcAAFpBE5NtHqpgFrpSNL9VOpxpJbR2LLV9NMJnFcFNM9BPPsRQNMBVPcxSPMBZP79cQNtWS9ddUeZWTOZZTelZTeZWUOZaUuxfVOddWcNmTM1jU8drVMlrVs1oWNZgVO5mUuBlXOplXO5qXvBkVvNzXdZuZNtpZd1sadRxZd55dONtYuJwZvJ3Y/N5Y+FvcON6c+l7cuN8e+l/eD2GNUKNNkSNOkmPP0mOQlScQ1SWTV6cVmGeWGSgVmWrVWirU2ejWmurWm+1WHS3XHG5WnasZ3iuaHi0ZHe8YH28Zn+yan26bHDBY3nBZH3EbLmJJr+QJ8eDNNmPO8OZMeS6L4O1boa3cYq6d4u8eZO/f8GLSMqVQsmRT8+cSsqTUc2WWNKdW+iYQNipSdmoUN6xXea0XfaEa+6Kde2EffCFcPiGcPCMd/iMdvCDe/GOevWSfvmTf8ipZ9ilat2ubNWqct2rc9CweuyqYYHMdY3Ce4bKeorMfvTWRNzEbfPPb/XcYPXZaJW/gu6MgfSOge6Sj/qah/qdi/Gdltynl+GpnvqjkfSinvuqneGroPSnovasovivpPKtquuyrvS0qvi2qPq6r/C2to3Mg5XEgpfMhZjMh5PNiJjMio7Sg5bUipnRi5vBl5vBmJ3YkqXPlabQlqDZlqvUnaXbm6fJoa/fprTZpbjVr7Xcqrrdr7rWsbzVubfgr77jtvrujsXduezPqfTPpfTUp+HLsOfEufrFvPfbvfjiqPLmvvz0s8rexe7JxPnLxPbPz/vTzf3b1P3c2MzixcXowM7pydTrz9fu0tnp0dzx2Pjhxfjtw/Thyfnjy/rn0eDy3OPu4vXn4vXp5frq6ufw5ejx5u306/z26////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAANoALAAAAAAQADIAAAj+ALUJHEiwYMFrsi5VqWKl1TWD2pLhGUXsmDFWSooQK5jMCitosEyRQlUMz49eA68BWgUtFCZLnTpxeqUEiDWBCZGR6qQnW7Y8TZqwwnFK4EpUlvIo8QnkiBBPl3YIrAKrUx6fWLMNYVLqhsAkq/TcyeozCJJSNQRa2dSpyRIfZYcMwYQnh0BVPlYxWSLE55AgQ1LV+CTQGhElqZjIDRJESCogN6INJLbDSKpMVJBgejzDVUHKOCaKskLjhmeD1U7psFEjByjJEGPLnk17NrVKKVyceBGJGkRgMQbhCvZL0ooSvgoCcwFpGKVEhRbhgqEhuUBqMRoJI+SmjaA3ayb+qegwTWClQMAKvVFTpn2ZFo8UMBIYw9EiNRfy5//QQpAMDgKdQIkgZZCVDQUiGMKAQCY4kkaBZFEQgoICpRCIGyRQoOGGF7DxQgYCRQKBIyN8sKGGFiiSwCHXlaCCIxdoOIEEFTTiwQPSDOSLBiA0wkYIGJxhSAQD3FLQjg3IMMggLyTwgJEGTYPIBg4QkMEhOdam5ZZcQpTNGF1sEQY2s4URRzPOaDGHbFPUsowzzuTSRWxTMKOLHLTQkosXENXJyx99fEHHFn0yw4sfe8RCRhSFHproohBJYSiiijJqkBO7OFpplE7YAgelkBoUQDPPHCAFH1xAEVsAztgBBgIWAhggGwBf1DJLFrLOCkABBtTR5WwBAQA7);display:block;float:left;height:16px;width:16px}.sprite.add{background-position:0 0}.sprite.del{background-position:0 -17px}.sprite.edit{background-position:0 -34px}

Olhando a imagem abaixo de relance não vemos muitas diferenças, mas muita coisa mudou se relacionado aos exemplos anteriores! Conseguimos diminuir ainda mais o tamanho de nossa tranferência. Agora temos somente 2.34KB! Se você é um grande observador vai ver que o tempo continua inalterado, ainda em 13ms. Porém existe uma diferença aí. O tempo de load foi menor. Apesar da ferramenta que utilizei dizer que houve três requisições, apenas duas foram feitas. Como a imagem está dentro do documento CSS não há necessidade do browser fazer uma terceira chamada, isso pode ser observado pelo recurso gráfico que a ferramenta me fornece. Se você obsevar atentamente, na requisição os documentos localhost(azul), css.css(verde) possuem um rastro de cor clara que relaciona o tempo que o browser fez a chamada ao servidor até a entrega. Agora observe a terceira chamada(roxo). Não possui rastro, por que não houve nenhuma requisição feita para o browser. Não era necessário, a imagem estava codificada dentro do arquivo de CSS que já havia previamente sido carregada.

Com o que fizemos do início até agora, conseguimos além de diminuir o tempo de carregamento da página, o tamanho final somando todas os ítens que foram requisitados e além disso o número de requisições. Passamos de cinco para apenas duas.

Essa técnica pode ser bem trabalhosa inicialmente, mas pode gerar ganhos inacreditáveis se aplicados a interfaces onde existem muitos itens semióticos. Observando ainda um pouco mais, seu documento CSS pode crescer de tamanho, porém, se aplicadas as técnicas de compressão do arquivo como visto nesse artigo, você pode até diminuir seu tamanho comparando a soma do arquivo css mais os diversos arquivos de imagem que podem existir em sua aplicação.

Existe ainda um outro artifício para ser aplicado para tornar essa técnica ainda mais eficaz! O controle quase nazista do cache dos arquivos de sua aplicação. Mas isso é assunto para outro artigo!

Otimização extrema: Construindo aplicações optimizadas

| Comments

Com a facilidade de se construir páginas para a internet, seja blogs, pequenos aplicativos para algum fim específico ou ainda grandes aplicações, em algum momento, aqueles que desenvolvem tal aplicação vão acabar esbarrando na questão do “sistema lento”. Isso por que, a necessidade pela rapidez da entrega das informações é tão grande que pode significar em perdas de cifras ou a total desistência dos usuários do site/aplicação pela demora que a informação é entregue.

Há algum tempo venho pensando nessa questão como sendo um dos principais problemas da ineficiência dos sistemas que são projetados atualmente. A grande maioria dos programadores/engenheiros/designers não trabalham em conjunto por que em algum momento foi decidido que seus trabalhos são atividades que são em momentos distintos e então não há necessidade de revisão dos seus trabalhos. Mal esses sabem que seu trabalho feito com algum tipo de despreocupação em algum momento relacionando a otimização ou mesmo a falta de conhecimento de que tal decisão no projeto pode afetar a velocidade que essa informação vai ser entregue, pode tirar uma aplicação do ar pro conta da demora na entrega!

Por experiência, já vi alguns sistemas que inicialmente o projeto parecia perfeito. Vários diagramas de classes explicando detalhadamente por que X padrão de projeto estava sendo implementado naquela parte de código para resolver tal problema, regras de negócios maravilhosas, limitando ao máximo a questão de segurança dos dados que serão inseridos no banco de dados, e finalmente o documento de interface. Mas em nenhum documento dos que passava algum tempo lendo para implementar tudo o que foi pensando previamente vi a preocupação com a qualidade e rapidez com que as informações são entregues ao usuário final.

No final me sobra uma dúvida: No final das contas, de quem é a responsabilidade pela questão velocidade da entrega da informação? Arquiteto, desenvolvedor, designer? Todos juntos?

Levando isso em consideração, hoje inicio um compendio de dicas que afetam todas as disciplinas, desde a infraestrutura até o designer. Não vou seguir uma linha lógica para publicação dos artigos, até por que não existe um pensamento linear. Levando isso em consideração, o primeiro artigo é para os designers.

Front-end

Back-end

    * [Otimização Extrema: Maximizando o uso de CSS Sprites][0]
    * [Otimização extrema: Otimizando e reduzindo o seu CSS][1]

Instalando o instantclient da oracle e a extensão OCI8 no debian

| Comments

Primeiramente é necessário baixar dois arquivos do sitio da oracle. Não tenho como lhe indicar os links diretos para estes dois arquivos, pois primeiro é necessário você aceitar a licença e fazer o login.

Existe a versão 11.2.0.3.0, porém há algum tipo de incompatibilidade do instant client com a extensão do PHP. Então a versão que temos que instalar para que tudo ocorra bem é a 10.2.0.5. Os arquivos que devem ser baixados são:

1
2
basic-10.2.0.5.0-linux-x64.zip
sdk-10.2.0.5.0-linux-x64.zip

Após baixar os dois arquivos crie o diretório oracle/instantclient dentro de opt

1
mkdir -p /opt/oracle/instantclient

Mova os arquivos compactados para a pasta instant cliente e descompacte-os

1
2
unzip basic-10.2.0.5.0-linux-x64.zip
unzip sdk-10.2.0.5.0-linux-x64.zip

Mova o conteúdo da pasta criada para dentro de instantcliente

1
mv instantclient_10_2/* .

Remova os arquivos e pastas que agora são desnecessárias

1
rm -rf instantclient_10_2/ basic-10.2.0.5.0-linux-x64.zip sdk-10.2.0.5.0-linux-x64.zip

Crie dois links simbólicos para duas bibliotecas usadas pela extensão do PHP, que estão com nomes diferentes do esperado pela extensão.

1
2
ln -s libclntsh.so.10.1 libclntsh.so
ln -s libocci.so.10.1 libocci.so

Instale o pacote php5-dev e php-pear. Se eu não me engano o dpkg consegue resolver todas as dependências dos pacotes, mas se houver alguma coisa a mais pode me procurar.

1
apt-get install php5-dev php-pear

Instale a extensão oci8

1
pecl install oci8

Aqui ele vai te fazer uma pergunta sobre onde está o seu instant client. Responda com: instantclient,/opt/oracle/instantclient/

Execute o comando abaixo para instruir o php a carregar a extensão

1
echo 'extension=oci8.so' > /etc/php5/conf.d/oci8.ini

Verifique se a extensão foi carregada corretamente

1
2
3
4
5
6
7
8
9
10
11
12
root@server:/# php -i | grep oci8
/etc/php5/cli/conf.d/oci8.ini,
oci8
oci8.connection_class => no value => no value
oci8.default_prefetch => 100 => 100
oci8.events => Off => Off
oci8.max_persistent => -1 => -1
oci8.old_oci_close_semantics => Off => Off
oci8.persistent_timeout => -1 => -1
oci8.ping_interval => 60 => 60
oci8.privileged_connect => Off => Off
oci8.statement_cache_size => 20 => 20

E é só! Agora su aplicação PHP fará a comunicação com o banco de dados da Oracle.

Novidades do PHP 5.4 - Declaração de arrays

| Comments

Uma das novidades que estão vindo com a nova versão do PHP, a versão 5.4, é a versão reduzida de declaração de arrays.

O propósito para essa adição é exclusivamente estético e visa a melhora da manutenção e leitura de códigos onde existe a necessidade de por exemplo a passagem de arrays como parâmetros de alguma função. Mais sobre a proposta pode ser lida aqui

Se você deu uma olhada no documento, a proposta inicial era disponibilizar duas formas de fazer referências de índice e valor. A primeira forma, se você já trabalhou com php alguma vez já foi visto, que é a forma “indice => valor”. A outra seria o padrão “indice : valor”, mas felizmente ficamos somente com a primeira. Esse padrão é utilizado por diversas outras linguagens para referências entre índice e valor, se você já usou json você provavelmente já viu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$array = [
  'isto', 'eh', 'um', 'array', 'com', 'varios', 'itens', 'e', 'indice', 'numerico'
];

print_r($array);


/* output
Array
(
  [0] => isto
  [1] => eh
  [2] => um
  [3] => array
  [4] => com
  [5] => varios
  [6] => itens
  [7] => e
  [8] => indice
  [9] => numerico
)
*/

No exemplo acima, você pode ver uma declaração simples de um array usando a forma contraída, se fossemos utilizar a forma extendida, era só trocar os [] por array();

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$frutas = [
  'banana' => 'amarela', 'limao' => 'verde', 'morango' => 'vermelho'
];

echo $frutas['limao'] . PHP_EOL;
echo end($frutas) . PHP_EOL;

/* output
verde
vermelho
*/

No exemplo acima, vemos a declaração de um array com a definição de valores com índices pré-definidos. Usei a função implode no array para demostrar que essa forma contraída é apenas um alias para a forma padrão que já utilizamos e é perfeitamente possível utilizar as funções que você sempre usou para manipular arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
namespace test;

final class Test
{
  public static function testMethod(array $list = [])
  {
    return implode(' ', $list);
  }
}

$obj = Test::testMethod([
  'varios',
  'itens',
  'passados',
  'como',
  'array'
]);

echo $obj;

/* output:
varios itens passados como array
*/

No exemplo acima, demonstramos a qual propósito esse alias foi implementado. Vemos claramente a questão da legibilidade e fácil leitura do código.

Outra coisa que eu diria que é no mínimo interessante e que vai poupar algumas linhas de códigos em alguns projetos é dereferencing que é ilustrado no código abaixo

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$url = 'http://google.com';

echo 'host:' . parse_url($url)['host'] . PHP_EOL;
echo 'scheme:' . parse_url($url)['scheme'];


/*
output
host:google.com
scheme:http
*/

O maior ganho dessa implementação é que se existe uma função ou método que tenha como saída um array, você não precisará armazenar em um conteúdo para depois usá-lo. Sabendo o índice do array, você só o usará. Mais informações você pode encontrar na documentação dessa implementação.

Indução de tipos com PHP - Utilizando a Spl_Types para tipagem forte

| Comments

Esse artigo é continuação de como tornar seus scripts fortemente tipados com php. A primeira e segunda parte você pode ler aqui: Indução de tipos com PHP – O Básico, Indução de tipos com PHP – Instalando o Spl_Types


Como já vimos nos últimos dois artigos, o PHP por padrão tem é relaxado em relação aos seus tipos, o que tem suas vantagens e desvantagens. Como nosso objetivo é tornar nosso script fortemente tipado, iremos utilizar a extensão Spl_Types para fazer esse trabalho. A extensão possui cinco classes para trabalhar com os tipos, são eles:

Vamos construir nosso script principal e fazer alguns testes para certificar que a extensão funciona como esperado. Vamos por partes, e no final teremos uma classe completona!

Primeiro vamos nomear nossa classe e criar nosso método construtor e inicializar todas as variáveis que utilizaremos na classe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Basket {

  private $id_cart;
  private $items = array();
  private $lock_cart;

  public function __construct()
  {
    $this->id_cart = uniqid();
    $this->lock_cart = new SplBool;
    $this->items = new ArrayObject;
  }
}

Como esse artigo é meramente didático, não entrarei em assuntos como por que isei a uniqid() para gerar um id para meu carrinho de compras. Você sabe! É só didática.

Com propriedade lock_cart já começamos com nossa mágica de tipagem forte. Iniciaremos com a explicação do SplBool.

Como você já deve imaginar, a classe SplBool é usada para forçar que essa variável ao tipo booleano, então se tentarmos passar qualquer outro valor para essa variável teremos alguma exceção, que veremos daqui há pouco, quando terminarmos nossa classe. Uma outra propriedade que setamos em nosso construtor foi items, que foi preenchida com uma instância da classe ArrayObject. A classe é poderosa, mais uma Spl, feita inteiramente para se trabalhar com arrays. Para saber mais, dê uma lida na documentação da classe. É incrível o que se pode fazer com ela em associação das outras Spls, como a ArrayIterator. Agora vamos adicionar um método para adicionar items ao nosso carritnho de compras.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Basket {

  private $id_cart;
  private $items = array();
  private $lock_cart;

  public function __construct()
  {
    $this->id_cart = uniqid();
    $this->lock_cart = new SplBool;
    $this->items = new ArrayObject;
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
     ->offsetGet( (int) $index)
     ->offsetset('price', $price);
  }
}

Nesse vamos apresentar mais duas classes de indução de tipo, são elas SplFloat e SplString. A SplFloat cuida de pontos flutuantes que usaremos para especificar o preço de nosso produto e usaremos também a SplString, para espefificar o nome do nosso produto.

Aqui fazemos duas verificações! A primeira que estamos fazendo sem saber é a verificação de Instância da classe, se é da SplFloat e SplString. E a segunda que estamos fazendo conscientemente é a que estamos vamos explicar aqui, a tipagem forte! Veja que o parâmetro price deve ser da instância SplFloat e sendo assim ser um float, e também nosso parâmetro name, que deve ser instância SplString e assim sendo obrigatoriamente uma string. Os conceitos podem estar meio confusos aqui, mas quando começarmos a usar a classe você entenderá com mais claridade. Fique tranquilo!

Agora que temos como adicionar novos itens ao nosso carrinho. Vamos adicionar a possibilidade de editar e remover itens.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
class Basket {

  private $id_cart;
  private $items = array();
  private $lock_cart;

  public function __construct()
  {
    $this->id_cart = uniqid();
    $this->lock_cart = new SplBool;
    $this->items = new ArrayObject;
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
     ->offsetGet( (int) $index)
     ->offsetset('price', $price);
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('price', $price);
  }

  public function editItemName(SplInt $index, SplString $name)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('name', $name);
  }

  public function delItem(SplInt $index)
  {
    $this->items
      ->offsetUnset( (int) $index);
  }
}

Nos dois métodos utilizamos os já conhecidos SplFloat e SplString, e introduzimos mais uma classe a SplInt. Como você já deve ter presumido até agora, ela força o tipo Inteiro em alguma variável. Mais uma vez fazemos uma checagem dupla. Checamos primeiro se pertence a uma instância da SplInt e ainda se é um inteiro.

Agora vamos implementar a possibilidade de dar um lock em nosso carrinho para podermos fechar nossa compra!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
class Basket {

  private $id_cart;
  private $items = array();
  private $lock_cart;

  public function __construct()
  {
    $this->id_cart = uniqid();
    $this->lock_cart = new SplBool;
    $this->items = new ArrayObject;
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
     ->offsetGet( (int) $index)
     ->offsetset('price', $price);
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('price', $price);
  }

  public function editItemName(SplInt $index, SplString $name)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('name', $name);
  }

  public function delItem(SplInt $index)
  {
    $this->items
      ->offsetUnset( (int) $index);
  }

  public function lockCart(SplBool $bool)
  {
     $this->lock_cart = $bool;
  }
}

Vamos agora implementar nosso último tipo de indução de tipo com a SplEnum. Para isso, além de criar o método que irá setar o tipo de nosso carrinho de compras, precisamos criar também a classe que iremos setar os nossos tipos de carrinho.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php
class Basket {

  private $id_cart;
  private $items = array();
  private $lock_cart;

  public function __construct()
  {
    $this->id_cart = uniqid();
    $this->lock_cart = new SplBool;
    $this->items = new ArrayObject;
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
     ->offsetGet( (int) $index)
     ->offsetset('price', $price);
  }

  public function editItemPrice(SplInt $index, SplFloat $price)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('price', $price);
  }

  public function editItemName(SplInt $index, SplString $name)
  {
    $this->items
      ->offsetGet( (int) $index)
      ->offsetset('name', $name);
  }

  public function delItem(SplInt $index)
  {
    $this->items
      ->offsetUnset( (int) $index);
  }

  public function lockCart(SplBool $bool)
  {
     $this->lock_cart = $bool;
  }

  public function setCartType(SplInt $type)
  {
    $this->cart_type = new CartType(CartType::ESPECIAL);
  }
}

class CartType extends SplEnum {
    const __default = self::NODEFINED;

    const NODEFINED = 0;
    const NORMAL = 1;
    const ESPECIAL = 2;
}

Pronto, agora temos um exemplo prático para testarmos todos os nossos conceitos até agora! Usamos a SplBool para setarmos se o nosso carrinho está ou não ‘lockado’(estrangeirismo feio). Vamos testar!

Vamos começar criando uma instância de nosso carrinho e adicionando dois itens.

1
2
3
4
<?php
$basket = new Basket();
$basket->addItem(new SplFloat(9.99), new SplString('Bola'));
$basket->addItem(new SplFloat(14.99), new SplString('Carrinho'));

Nosso objeto está assim até então:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Basket Object
(
    [id_cart:Basket:private] => 4f3aa8521d98a
    [items:Basket:private] => ArrayObject Object
        (
            [storage:ArrayObject:private] => Array
                (
                    [0] => ArrayObject Object
                        (
                            [storage:ArrayObject:private] => Array
                                (
                                    [price] => SplFloat Object
                                        (
                                            [__default] => 9.99
                                        )

                                    [name] => SplString Object
                                        (
                                            [__default] => Bola
                                        )

                                )

                        )

                    [1] => ArrayObject Object
                        (
                            [storage:ArrayObject:private] => Array
                                (
                                    [price] => SplFloat Object
                                        (
                                            [__default] => 14.99
                                        )

                                    [name] => SplString Object
                                        (
                                            [__default] => Carrinho
                                        )

                                )

                        )

                )

        )

    [lock_cart:Basket:private] => SplBool Object
        (
            [__default] => 
        )

)

Vamos fazer nosso primeiro teste para saber se tudo está funcionando como deveria funcionar mesmo! As checagens de instâncias e tipos, ou seja, vamos induzir um erro aqui…

1
2
3
4
$basket->addItem(new SplFloat(50.44), new SplString(15));
$basket->addItem(new SplFloat('17.99'), new SplString('moto'));
$basket->addItem(new SplString('Barco'), new SplFloat(17.99));
$basket->setCartType(new CartType(CartType::OUTRO));

Aqui induzimos quatro erros completamente diferentes.

O primeiro erro, onde nós deveríamos ter passado uma string para a SplString, passamos um inteiro.

1
Fatal error: Uncaught exception 'UnexpectedValueException' with message 'Value not a string'

No segundo erro, passamos uma string para a SplFloat, enquanto deveriamos ter passado um float.

1
Fatal error: Uncaught exception 'UnexpectedValueException' with message 'Value not a float'

No terceiro, invertemos tudo, passamos os valores corretos, mas em ordens erradas. Passamos primeiro uma string e depois um float, enquanto o que se espera é o contrário, um float e depois uma string.

1
Catchable fatal error: Argument 1 passed to Basket::addItem() must be an instance of SplFloat, instance of SplString given

Já no quarto, induzimos uma constante que não existe na classe CartType.

1
Value not a const in enum CartType

Apenas para ver que tudo está funcionando como o esperado, o código abaixo edita nossos itens, deleta e ‘locka’ nosso carrinho.

1
2
3
4
$basket->editItemPrice(new SplInt(1), new SplFloat(12.45));
$basket->editItemName(new SplInt(1), new SplString('Helicoptero'));
$basket->delItem(new SplInt(1));
$basket->lockCart(new SplBool(true));

Nosso objeto final vai se parecer com isso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Basket Object
(
    [id_cart:Basket:private] => 4f3ab574309a2
    [items:Basket:private] => ArrayObject Object
        (
            [storage:ArrayObject:private] => Array
                (
                    [0] => ArrayObject Object
                        (    
                            [storage:ArrayObject:private] => Array
                                (
                                    [price] => SplFloat Object
                                        (
                                            [__default] => 9.99
                                        )

                                    [name] => SplString Object
                                        (
                                            [__default] => Bola
                                        )

                                )

                        )

                )

        )

    [lock_cart:Basket:private] => SplBool Object
        (
            [__default] => 1
        )

)

Indução de tipos com PHP - Instalando o Spl_Types

| Comments

Esse artigo é continuação de como tornar seus scripts fortemente tipados com php. A primeira parte você pode ler aqui: Indução de tipos com PHP


Nesta segunda parte do artigo vamos demonstrar para aqueles haters que já discutimos, como tornar o PHP melhor do que já é.

Já tem algum tempo que temos como tornar nossos scripts fortemente tipados com php, porém, isso não vem como padrão. Para tal necessitaremos de uma extensão, a SPL_Types. Na documentação da extensão temos a sua descrição que não poderia ser melhor, ela ajuda a tornar seus scripts a serem fortemente tipados.

Como já vimos na primeira parte desse artigo, o php nos oferece duas opções por padrão de indução de tipos, são eles arrays e instâncias. Com essa extensão ganhamos todas as outras que queríamos Strings, Inteiros, Booleanos, Pontos Flutuantes e Enumeráveis. Antes de continuarmos é preciso saber uma coisa. Até o momento que escrevia esse artigo a extensão estava marcada como experimental no repositório PECL, então não se assuste se acontecer algum bug, apesar de ter testado a extensão ad nauseam, nunca se sabe.

Como ela é uma extensão PECL, precisaremos fazer a instalação em nossa máquina. Primeiramente tentei recorrer ao pacote pecl para instalar, mas tive um problema, e depois de ver que há sim diferença entre o que é liberado por quem cuida da extensão e a versão estável disponível na página do pacote, resolvi fazer tudo passo a passo. O erro que consegui fazendo do jeito fácil foi o abaixo:

1
2
3
4
5
6
$  pecl install SPL_Types
...
/tmp/pear/temp/SPL_Types/spl_type.c:405:1: error: duplicate 'static'
/tmp/pear/temp/SPL_Types/spl_type.c:415:1: error: duplicate 'static'
make: ** [spl_type.lo] Erro 1
ERROR: `make' faile

Então fui para a versão difícil da instalação do pacote. Os próximos passos consistem em fazer o que o comando acima fez, só que de outra fonte. Primeiramente vamos buscar os arquivos do pacote em seu repositório oficial. Aqui, todas as ações que faço costumo fazer dentro do diretório /tmp, mas fique tranquilo para fazer onde quiser. Nosso próximo passo exige um pacote específico o subversion, use o comando abaixo para instalar.

1
sudo apt-get install subversion

Agora que já temos o subversion instalado, vamos buscar os arquivos do pacote.

1
2
3
$ cd /tmp
$ svn co https://svn.php.net/repository/pecl/spl_types/trunk/ spltypes
$ cd spltypes

Com os arquivos necessários dentro do diretório spltypes, o nosso próximo passo é transformá-lo em uma versão compilável e instalável. A ferramenta phpize faz isso para nós, ele está dentro do pacote php5-dev no ubuntu, vamos instalar com o comando abaixo.

1
sudo apt-get install php5-dev

Após instalar o pacote, o nosso próximo passo é utilizar o phpize na pasta spltypes, como abaixo:

1
2
3
4
5
6
$ phpize
...
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626

Agora com nossa extensão para ser instalada, vamos configurar, compilar e instalar com os comando abaixos:

1
2
3
4
5
6
$ ./configure
...(aguardar)
$ make
...(aguardar)
$ make install
...(aguardar)

Após isso, nosso último passo é habilitar a extensão. Para tal, vamos criar um arquivo de configuração para a extensão com o comando abaixo

$ sudo echo 'extension=spl_types.so' > /etc/php5/conf.d/spltypes.ini

Reinicie seu apache e veja em seu phpinfo() se a nova extensão foi carregada. Para fazer isso de uma forma rápida pelo terminal, utilize o comando abaixo:

1
2
3
4
$ php -m | grep -i spl
...
SPL
SPL_Types

No próximo parte e final desse artigo, vou falar como usar essa extensão a seu favor! See Ya!

Indução de tipos com PHP - O básico

| Comments

Existe todo um preconceito em relação ao PHP ser relaxado com seus tipos. A questão é que esses haters se apegaram tanto a essa questão que já foi resolvida há algum tempo e não consegue largar. É como dizem: se você começa a afirmar uma coisa com certa frequência, você acaba acreditando sem retorno por aquele conceito e não há nada que o fará mudar de ideia. Mas não posso julgá-los, ainda em PHP existem pessoas usando getters e setters, enquanto há algum tempo temos ferramentas de sobrecarga e você acaba com a arquitetura de encapsulamento de seus valores. Ooops, isso também acontece em outras linguagens. My bad. Enfim, esquecendo a questão filosófica envolvida nesse assunto, vamos explicar como induzir tipos a seus objetos!

Mas o que é indução de tipo e por que isso é importante!?

Bem, essa é uma pergunta interessante é que precisaremos percorrer um certo caminho até chegar a sua resposta, mas resumindo, indução de tipo é quando você tem que ter certeza que o valor que está passando por determinado ponto do código é proveniente de algum tipo de dado (string, array, boolean,…) ou algum tipo de objeto(MinhaClasse, Usuario, PessoaFisica, PessoaJuridica, …).

No PHP, por padrão você pode fazer esse teste com apenas alguns tipos de dados. Você pode testá-lo se é um array ou alguma instância. Vamos a prática:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
namespace test;

  final class Test
  {
    public static function testMethod(array $list = [])
    {
      return implode(' ', $list);
    }
  }

  $obj = Test::testMethod([
    'varios',
    'itens',
    'passados',
    'como',
    'array'
  ]);

  echo $obj;

  /* output:
  varios itens passados como array
  */

Eu sei. Já usei esse exemplo anteriormente para explicar as novas implementações da versão reduzida de declaração de arrays, mas o exemplo é completamente reusável se levarmos em consideração que logo a versão 5.4 do php estará lançada(isso se você não está lendo este artigo muito tempo depois que o php5.4 fora lançado).

No código acima, temos a indução de tipo sendo feito na declaração do método. Aquele array logo depois do parantese de abertura de nossos parâmetros indica que a variável list, obrigatoriamente deve ser um array, caso contrário, você verá algum apito do php avisando que fez alguma coisa errada como essa abaixo:

1
2
3
4
5
6
7
<?php

$obj = Test::testMethod('Isso é uma string');
/* output:
  Catchable fatal error: Argument 1 passed to 
  test\Test::testMethod()     must be an array, string given,
*/

Ilustrando o que nós já sabíamos, ele quer um array e você está dando uma string. Vamos agora para um exemplo com objetos, para ficar mais divertido.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

class Pessoa {

  public static function contratar(PessoaFisica $obj) {
    return $obj ->nome . ' contratado!';
  }
}

class PessoaFisica extends Pessoa {

  protected $nome;

  public function __construct($nome) {
    $this->nome = $nome;
  }

}

$pessoafisica = new PessoaFisica('Fábio Luciano');
$pessoa = new Pessoa();

print $pessoa->contratar($pessoafisica);

O código acima funciona perfeitamente como esperado. Queremos contratar um novo funcionário e quem faz isso é somente a classe Pessoa. E só se pode fazer contratações de pessoas físicas. Ditamos essa regra colocando em nosso código, que somente deve ser passado como parâmetro de nosso método contratar objetos da classe PessoaFisica.

E se alguém achou que poderia contratar uma empresa e pudesse usar os mesmos métodos(entenda-se por meios), que uma pessoa física?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

class PessoaJuridica extends Pessoa {

  protected $nome;

  public function __construct($nome) {
    $this ->nome = $nome;
  }

}

$pessoajuridica = new PessoaJuridica('Coisa Inc.');
$pessoa = new Pessoa();

print $pessoa->contratar($pessoajuridica);
/* 
Catchable fatal error: Argument 1 passed to Pessoa::contratar() 
must be an instance of PessoaFisica, instance of PessoaJuridica given
*/

Bem, acho que você já deve ter entendido qual o ganho que tem quando se se usa esse tipo de artifício em seu código: Confiança. No fim é tudo sobre isso. Você ganha confiabilidade que seu código só será usado se for de um determinado tipo ou de uma determinada instância.

Ok. Entendi! Mas e os outros tipos!?

Até o momento que estava escrevendo esse artigo o php havia implementado essa característica para apenas os dois tipos comentados. Porém, existe uma forma muito bacana se usar a indução de tipo para mais alguns. São eles: Inteiros, Enums, Ponto Flutuante, Booleanos e strings.

Nesse momento nós quebramos aquele conceito dos haters, então vamos como calma. Vamos dividir esse artigo em dois para tomarmos um ar.

Tunning - Diminuindo o número de requisições com base64 encoding

| Comments

Lendo esse artigo hoje, me lembrei que há algum tempo, mentalmente, comecei a escrever algo sobre quando usar e  não usar a técnica que usa a  imagem codificada em base64 para diminuir o número de requisições ao servidor. Antes de continuarmos, precisamos compreender por alto o que é o base64. Em termos gerais, base64 é uma forma de codificação para qualquer tipo de dado. Neste caso, nós podemos simplificar dizendo que a codificação base64 é a representação textual de uma imagem. Antes de mais nada, é bom lembrar que isso é uma técnica de tunning, e como em grande maioria delas, só é válida em alguns casos específicos. Por exemplo, essa técnica tem maiores ganhos somente quando existe uma quantidade considerável de imagens a serem carregadas em uma página.

Para continuarmos é necessário compreender um pouco sobre como é o passo do carregamento de uma página que contem vários itens a ser carregados.
Considere o código abaixo para os próximos exemplos e explicações.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html dir="ltr">
    <head>
        <meta charset="UTF-8" />
        <title>base64</title></title>
    <script type='text/javascript' src='js.js'></script>
    <link rel="stylesheet" href="css.css" type="text/css" media="screen" />
</head>
<body>
    <div class="image-list">
        <img src="imgs/01.jpg" />
        <img src="imgs/02.jpg" />
        <img src="imgs/03.jpg" />
        <img src="imgs/04.jpg" />
        <img src="imgs/05.jpg" />
        <img src="imgs/06.jpg" />
        <img src="imgs/07.jpg" />
        <img src="imgs/08.jpg" />
        <img src="imgs/09.jpg" />
        <img src="imgs/10.jpg" />
    </div>
</body>
</html>

O código é simples, mas consiste em doze requisições: Um arquivo de estilo, outro de javascript e mais dez requisições para cada imagem que será anexes-coada ao documento. Fiz com dez para não criar um arquivo monstruoso e não perder tempo.
A primeira coisa que será carregada do documento acima será o arquivo javascript, mas o browser não pára para analizá-lo para depois depois ir para o próximo item, que seria o arquivo de estilo. Ao ler o documento, o browser vai fazer cada requisição de cada vez, o que pode ser perigoso, caso algum item do seu cabeçalho dê algum problema, o carregamento da sua página pode ser comprometido. Sabendo disso, que tal deixar a cargo do browser apenas duas requisições, e as outras dez serem feitas internamente pelo sistema operacional? Na prática, o que iremos fazer com essa técnica é pegar o conteúdo da imagem, criptografar em base64 e incluí-la no código fonte. A ferramenta de codificação e tradução base64 está implementada em diversas linguagens(python e ruby), e aqui neste exemplo vou utilizar em PHP.

O primeiro passo que irei fazer é codificar uma classe que me retorne a url codificada em base64 e que seja compatível com o que o browser espera. O formato é

1
data:[<mime type>][;charset=<charset>][;base64],<encoded data>

A partir disso, temos o código abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php

final class DataUri {

  protected $file;

  public function __construct($file) {
    $this->file = $file;

    return $this;
  }

  public function generate() {
    if (file_exists($this->file)) {
      return $this->make_uri();
    } else {
      return false;
    }
  }

  private function make_uri() {
    return 'data:'
            . $this->get_mime()
            . ';base64,'
            . base64_encode(file_get_contents($this->file));
  }

  private function get_mime() {
    $info = new finfo(FILEINFO_MIME);

    return $info->file($this->file);
  }

}

$uri = new DataUri('./imgs/gluttony.jpg');
print $uri->generate();

O código acima, quando executado de forma correta, vai gerar as data uri no formato esperado. Veja o exemplo de uma uri gerada. Se você copiar e colar o código abaixo na sua barra de endereço, verá o resultado aqui.

Agora vamos criar o código que vai exibir todas as imagens de um determinado diretório e aplicar nossa técnica.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php require_once 'DataUri.php'; ?>
<!DOCTYPE html>
<html dir="ltr">
  <head>
    <meta charset="UTF-8" />
    <title>base64</title></title>
    <script type='text/javascript' src='js.js'></script>
    <link rel="stylesheet" href="css.css" type="text/css" media="screen" />
  </head>
  <body>
    <div class="image-list">
    <?php
      $dir = './imgs/';
      $iterator = new DirectoryIterator($dir);
      foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
          $uri = new DataUri($dir. $fileinfo->getFileName());
          echo '<img src="' . $uri->generate() . '" />' . PHP_EOL;
        }
      }
    ?>
    </div>
  </body>
</html>

Prontinho. O código não está bonito, mas está bom para demonstrar sua aplicação.

Dica: essa técnica não pode ser aplicada em qualquer lugar. Como pode-se ver, nós diminuímos drasticamente a quantidade de requisições ao servidor, mas também aumentamos exponencialmente o tamanho do lógico html. Essa técnica não terá a menor valência se as imagens estiverem em outro servidor.
Só aplique essa técnica se a quantidade de requisições for um gargalo para sua aplicação, senão, continue fazendo como sempre fez.