Quem aqui nunca atualizou uma página web, alterou um CSS, JS ou imagem, enviou e-mail para o cliente e ele disse que na máquina dele não aparece?

Eu consegui ganhar esta batalha e de uma forma muito boa e ainda melhorar o pagespeed dos meus sites. Como? Muito simples.

Para este exemplo não se esqueça que a pasta site_cache precisa de permissão de escrita.

Primeiro eu criei uma classe CompilaCss e nesta classe fiz a seguinte estrutura:

<?php
class CompilaCss 
{
    private $css = array();
 
    private $filemtime = 0;
    private $file      = '';
 
    public function addCss( $cssName )
    {
        $this->css[] = $cssName;
 
        // Soma todos as datas de alterações do arquivo
        // Aqui esta a mágica. Mudando algum CSS a soma dos filemtime serão
        // diferentes
        $this->filemtime += filemtime( $cssName );
    }
     
    public function linkCss( $base )
    {
        $this->file = 'site_cache/css_' . md5( $this->filemtime ) . '.css';
 
        if( !file_exists( $this->file ))
        {
            // Se o arquivo do CSS ainda não existir, cria outro. 
            // Basicamente se mudar algum filemtime de qualquer arquivo
            // um novo arquivo é criado
            $this->createCssFile();
        }
 
        echo '<link href="' . $base . $this->file . '" rel="stylesheet" type="text/css" />';
    }
     
    private function createCssFile()
    {
        $salvaCSS = '';
 
        foreach ( $this->css as $css )
        {
            // Pega o conteúdo dos arquivos CSS
            // adiciona todos os conteúdos a variável $salvaCSS
            $salvaCSS .= file_get_contents( $css );
        }
 
        // Retira todos os comentários
        $salvaCSS = preg_replace('!/\*.*?\*/!s', ' ', $salvaCSS);
        // Retira todos os espaços em branco e quebras de linhas
        $salvaCSS = preg_replace('/\s+/', ' ', $salvaCSS);

        // espaços extras
        $salvaCSS = str_replace(' :', ':', $salvaCSS);
        $salvaCSS = str_replace('; ', ';', $salvaCSS);
        $salvaCSS = str_replace(': ', ':', $salvaCSS);
        $salvaCSS = str_replace(' {', '{', $salvaCSS);
        $salvaCSS = str_replace('{ ', '{', $salvaCSS);
        $salvaCSS = str_replace(' }', '}', $salvaCSS);
        $salvaCSS = str_replace('} ', '}', $salvaCSS);
        $salvaCSS = str_replace(';} ', '}', $salvaCSS);
 
        // Salva
        file_put_contents( $this->file, $salvaCSS );
    }
}

Veja que em addCss($cssName) eu apenas adiciono todos os CSS em um array e acrescento o valor do filemtime do arquivo á $this->filemtime.

Já foi possível perceber que se eu alterar qualquer arquivo CSS o filemtime será diferente e por padrão teremos uma soma diferente.

Para adicionarmos CSS fazemos assim:

<?php
$css = new CompilaCss();
$css->addCss('css/erro.css');
$css->addCss('css/style.css');
$css->addCss('modulos/lightbox/css/jquery.lightbox-0.5.css');
$css->addCss( 'css/style-print.css' );
$css->linkCss( URL::getBase() );
Parte do site www.cidadedesaobonifacio.com.br. Qualquer um dos CSS acima podem ser acessados normalmente prefixando com o domínio.
Ex: www.cidadedesaobonifacio.com.br/css/style-print.css

Sempre que for chamado $css->linkCss( URL::getBase() ); a classe verificará se o arquivo já compilado existe e se não existe criará um arquivo novo. Se já existe apenas retorna o link. Caso não exista primeiro compila o arquivo e depois retorna o link.

O método createCssFile() além de unir todos os CSS em um único arquivo, também remove os caracteres em branco sobrando, retira os comentários e as quebras de linha.

Veja este sistema funcionando em: http://php.eduardokraus.com/code/10044/ e o fonte esta em http://php.eduardokraus.com/code/10044/10044.zip.

8 comentários

Deixe uma resposta

  1. Notei que a publicação é de junho de 2013, porém segue bastante atual, parabéns pelo conteúdo!

     
  2. Caro Kraus,

    Como aproveitar esse recurso no WordPress?

    Estou tentando aqui sem sucesso até o momento.

    Grato,

     
  3. Verdade Eduardo, nesse caso acredito que possa utilizar no lado server:

    <?php
    header('Expires: Sat, 01 Jan 1990 01:00:00 GMT'); 
    header('Last-Modified: ' .gmdate( 'D, d M Y H:i:s' ). ' GMT'); 
    header('Cache-Control: no-store, no-cache, must-revalidate'); 
    header('Cache-Control: post-check=0, pre-check=0', false); 
    header('Pragma: no-cache');
    ?>
    Ou utilizar seu script apenas para browsers antigos! ;)

     
  4. Sempre pense no cache como seu amigo.

     
  5. Legal a sua ideia, porém gera processamento para cada arquivo e (na minha humilde opinião) em vão.
    Como desenvolvedor você deve alterar o cache somente quando necessário apenas criar/alterando uma variável global com o número da versão do sistema e chamar os arquivos com esta variável.
    Exemplo:

    PHP:

    define('CACHE_VERSION', '1.0.0');
    HTML:
    <link href="url/style.css?v=<?php echo CACHE_VERSION ?>" rel="stylesheet" type="text/css" />
    <script src="url/script.js?v=<?php echo CACHE_VERSION ?>" type="text/javascript"></script>
    *Existem outras formas para controlar o valor da versão sem interação humana como tags de versionamento, etc...Mas aí o buraco é mais embaixo! ;)

     
  6. Antes de eu partir para esta luta que faço hoje já testei este.

    Mais existe um grande problema!

    Alguns navegadores ignoram o ?v= e não carregam quando muda o ?v=.. e outros navegadores não aplicam cache a itens que estejam dinâmicos(sufixados em ?)

    Então o script que fiz faz uma única vês e sem muito trabalho todos as regras abaixo:
    http://browserdiet.com/pt/#minify-css
    http://browserdiet.com/pt/#combine-css

     
  7. Ok, se considerar navegadores sem suporte é só controlar com metatag's:

    <meta http-equiv="pragma" content="no-cache" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="UMA_DATA" />
    Espero ter ajudado, abraço! :)

     
  8. Mais estas TAG são para o HTML e o CSS usa somente as definidas pelo servidor no header.