{"id":2795,"date":"2014-07-31T03:25:59","date_gmt":"2014-07-31T03:25:59","guid":{"rendered":"http:\/\/www.deuzebranaweb.com.br\/?p=2795"},"modified":"2014-07-31T03:25:59","modified_gmt":"2014-07-31T03:25:59","slug":"criando-um-sistema-de-cache-no-php","status":"publish","type":"post","link":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/2014\/07\/31\/criando-um-sistema-de-cache-no-php\/","title":{"rendered":"Criando um sistema de cache no PHP"},"content":{"rendered":"<p>\u201cCache\u201d \u00e9 uma forma de armazenar um valor para um consulta futura mais r\u00e1pida. Com o cache conseguimos otimizar o carregamento dos sites e de suas informa\u00e7\u00f5es.<\/p>\n<p>Suponhamos que voc\u00ea tenha um site que fa\u00e7a uma consulta em um tabela do banco de dados que possua 3.000.000 registros e essa consulta demore mais de 30 segundos (acredite, isso acontece). Com o cache voc\u00ea pode reduzir esse tempo em alguns segundos.<\/p>\n<p>Cachear uma informa\u00e7\u00e3o significa salv\u00e1-la em algum lugar (seja em um arquivo ou diretamente na mem\u00f3ria RAM do servidor) para depois poder consultar essa informa\u00e7\u00e3o sem ter que obt\u00ea-la da forma mais demorada (no exemplo a cima, com a consulta ao banco de dados).<\/p>\n<p>Vamos criar aqui uma classe que servir\u00e1 para armazenar qualquer tipo de texto, vari\u00e1vel, n\u00famero inteiro, resultado SQL e etc.<\/p>\n<p>Para come\u00e7ar, come\u00e7amos criando uma classe vazia:<\/p>\n<p>01<br \/>\n< ?php\n02\n \n03\n\/**\n04\n * Sistema de cache\n05\n *\n06\n * @author Thiago Belem <contato@thiagobelem.net><br \/>\n07<br \/>\n * @link http:\/\/blog.thiagobelem.net\/<br \/>\n08<br \/>\n *\/<br \/>\n09<br \/>\nclass Cache {<br \/>\n10<\/p>\n<p>11<br \/>\n}<br \/>\n12<\/p>\n<p>13<br \/>\n?><br \/>\nAgora vamos adicionar alguns atributos que ser\u00e3o usados pelo sistema de cache:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Tempo padr\u00e3o de cache<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * @var string<br \/>\n05<br \/>\n *\/<br \/>\n06<br \/>\nprivate static $time = &#8216;5 minutes&#8217;;<br \/>\n07<\/p>\n<p>08<br \/>\n\/**<br \/>\n09<br \/>\n * Local onde o cache ser\u00e1 salvo<br \/>\n10<br \/>\n *<br \/>\n11<br \/>\n * Definido pelo construtor<br \/>\n12<br \/>\n *<br \/>\n13<br \/>\n * @var string<br \/>\n14<br \/>\n *\/<br \/>\n15<br \/>\nprivate $folder;<br \/>\nO atributo $time define por quanto tempo as informa\u00e7\u00f5es ficar\u00e3o salvas no cache, tempo esse que poder\u00e1 ser mudado para cada valor salvo (veremos mais a diante).<\/p>\n<p>Agora vamos criar um m\u00e9todo chamado setFolder() que servir\u00e1 para definir o local onde os arquivos de cache ser\u00e3o salvos:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Define onde os arquivos de cache ser\u00e3o salvos<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * Ir\u00e1 verificar se a pasta existe e pode ser escrita, caso contr\u00e1rio<br \/>\n05<br \/>\n * uma mensagem de erro ser\u00e1 exibida<br \/>\n06<br \/>\n *<br \/>\n07<br \/>\n * @param string $folder Local para salvar os arquivos de cache (opcional)<br \/>\n08<br \/>\n *<br \/>\n09<br \/>\n * @return void<br \/>\n10<br \/>\n *\/<br \/>\n11<br \/>\nprotected function setFolder($folder) {<br \/>\n12<br \/>\n    \/\/ Se a pasta existir, for uma pasta e puder ser escrita<br \/>\n13<br \/>\n    if (file_exists($folder) &#038;&#038; is_dir($folder) &#038;&#038; is_writable($folder)) {<br \/>\n14<br \/>\n        $this->folder = $folder;<br \/>\n15<br \/>\n    } else {<br \/>\n16<br \/>\n        trigger_error(&#8216;N\u00e3o foi poss\u00edvel acessar a pasta de cache&#8217;, E_USER_ERROR);<br \/>\n17<br \/>\n    }<br \/>\n18<br \/>\n}<br \/>\nEsse m\u00e9todo recebe o caminho (pasta) onde os arquivos ser\u00e3o criados e, ap\u00f3s verificar se o caminho existe, \u00e9 um diret\u00f3rio e pode ser manipulado, ele define um atributo com o caminho passado. Caso ele n\u00e3o consiga localizar a pasta ou n\u00e3o seja poss\u00edvel escrever nela, um erro ser\u00e1 gerado.<\/p>\n<p>Com esse m\u00e9todo criado, podemos criar um construtor para essa classe com o seguinte c\u00f3digo:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Construtor<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * Inicializa a classe e permite a defini\u00e7\u00e3o de onde os arquivos<br \/>\n05<br \/>\n * ser\u00e3o salvos. Se o par\u00e2metro $folder for ignorado o local dos<br \/>\n06<br \/>\n * arquivos tempor\u00e1rios do sistema operacional ser\u00e1 usado<br \/>\n07<br \/>\n *<br \/>\n08<br \/>\n * @uses Cache::setFolder() Para definir o local dos arquivos de cache<br \/>\n09<br \/>\n *<br \/>\n10<br \/>\n * @param string $folder Local para salvar os arquivos de cache (opcional)<br \/>\n11<br \/>\n *<br \/>\n12<br \/>\n * @return void<br \/>\n13<br \/>\n *\/<br \/>\n14<br \/>\npublic function __construct($folder = null) {<br \/>\n15<br \/>\n    $this->setFolder(!is_null($folder) ? $folder : sys_get_temp_dir());<br \/>\n16<br \/>\n}<br \/>\nO construtor ser\u00e1 chamado sempre que instanciarmos a classe Cache e, como voc\u00ea pode ver, ele recebe um par\u00e2metro (opcional) onde podemos definir o local onde os arquivos ser\u00e3o criados\u2026 Se n\u00e3o passarmos nenhum par\u00e2metro para ele o mesmo ir\u00e1 usar o local de arquivos tempor\u00e1rios definido pelo seu sistema operacional.<\/p>\n<p>Agora que j\u00e1 conseguimos definir o local onde os caches ser\u00e3o salvos, vamos criar o m\u00e9todo que ir\u00e1 gerar o nome dos arquivos de cache:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Gera o local do arquivo de cache baseado na chave passada<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * @param string $key Uma chave para identificar o arquivo<br \/>\n05<br \/>\n *<br \/>\n06<br \/>\n * @return string Local do arquivo de cache<br \/>\n07<br \/>\n *\/<br \/>\n08<br \/>\nprotected function generateFileLocation($key) {<br \/>\n09<br \/>\n    return $this->folder . DIRECTORY_SEPARATOR . sha1($key) . &#8216;.tmp&#8217;;<br \/>\n10<br \/>\n}<br \/>\nE o m\u00e9todo que ir\u00e1 criar o arquivo de cache propriamente dito:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Cria um arquivo de cache<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * @uses Cache::generateFileLocation() para gerar o local do arquivo de cache<br \/>\n05<br \/>\n *<br \/>\n06<br \/>\n * @param string $key Uma chave para identificar o arquivo<br \/>\n07<br \/>\n * @param string $content Conte\u00fado do arquivo de cache<br \/>\n08<br \/>\n *<br \/>\n09<br \/>\n * @return boolean Se o arquivo foi criado<br \/>\n10<br \/>\n *\/<br \/>\n11<br \/>\nprotected function createCacheFile($key, $content) {<br \/>\n12<br \/>\n    \/\/ Gera o nome do arquivo<br \/>\n13<br \/>\n    $filename = $this->generateFileLocation($key);<br \/>\n14<\/p>\n<p>15<br \/>\n    \/\/ Cria o arquivo com o conte\u00fado<br \/>\n16<br \/>\n    return file_put_contents($filename, $content)<br \/>\n17<br \/>\n        OR trigger_error(&#8216;N\u00e3o foi poss\u00edvel criar o arquivo de cache&#8217;, E_USER_ERROR);<br \/>\n18<br \/>\n}<br \/>\nO nosso sistema est\u00e1 quase pronto.. J\u00e1 podemos criar arquivos de cache na pasta de cache, precisamos ent\u00e3o criar dois m\u00e9todos: um para salvar um valor no cache (seja ele uma string, n\u00famero, resultado SQL e etc.) e outro pra ler esse valor do cache.<\/p>\n<p>Primeiro o m\u00e9todo que salva um valor no cache:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Salva um valor no cache<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * @uses Cache::createCacheFile() para criar o arquivo com o cache<br \/>\n05<br \/>\n *<br \/>\n06<br \/>\n * @param string $key Uma chave para identificar o valor cacheado<br \/>\n07<br \/>\n * @param mixed $content Conte\u00fado\/vari\u00e1vel a ser salvo(a) no cache<br \/>\n08<br \/>\n * @param string $time Quanto tempo at\u00e9 o cache expirar (opcional)<br \/>\n09<br \/>\n *<br \/>\n10<br \/>\n * @return boolean Se o cache foi salvo<br \/>\n11<br \/>\n *\/<br \/>\n12<br \/>\npublic function save($key, $content, $time = null) {<br \/>\n13<br \/>\n    $time = strtotime(!is_null($time) ? $time : self::$time);<br \/>\n14<\/p>\n<p>15<br \/>\n    $content = serialize(array(<br \/>\n16<br \/>\n        &#8216;expires&#8217; => $time,<br \/>\n17<br \/>\n        &#8216;content&#8217; => $content));<br \/>\n18<\/p>\n<p>19<br \/>\n    return $this->createCacheFile($key, $content);<br \/>\n20<br \/>\n}<br \/>\nE agora o m\u00e9todo para ler esse valor do cache:<\/p>\n<p>01<br \/>\n\/**<br \/>\n02<br \/>\n * Salva um valor do cache<br \/>\n03<br \/>\n *<br \/>\n04<br \/>\n * @uses Cache::generateFileLocation() para gerar o local do arquivo de cache<br \/>\n05<br \/>\n *<br \/>\n06<br \/>\n * @param string $key Uma chave para identificar o valor cacheado<br \/>\n07<br \/>\n *<br \/>\n08<br \/>\n * @return mixed Se o cache foi encontrado retorna o seu valor, caso contr\u00e1rio retorna NULL<br \/>\n09<br \/>\n *\/<br \/>\n10<br \/>\npublic function read($key) {<br \/>\n11<br \/>\n    $filename = $this->generateFileLocation($key);<br \/>\n12<br \/>\n    if (file_exists($filename) &#038;&#038; is_readable($filename)) {<br \/>\n13<br \/>\n        $cache = unserialize(file_get_contents($filename));<br \/>\n14<br \/>\n        if ($cache[&#8216;expires&#8217;] > time()) {<br \/>\n15<br \/>\n            return $cache[&#8216;content&#8217;];<br \/>\n16<br \/>\n        } else {<br \/>\n17<br \/>\n            unlink($filename);<br \/>\n18<br \/>\n        }<br \/>\n19<br \/>\n    }<br \/>\n20<br \/>\n    return null;<br \/>\n21<br \/>\n}<br \/>\nSe voc\u00ea reparar, esse \u00faltimo m\u00e9todo ir\u00e1 excluir o arquivo de cache caso ele tenha expirado.<\/p>\n<p>Usando o sistema de cache<\/p>\n<p>Veja um exemplo de uso do sistema de cache onde primeiro verificamos se h\u00e1 um valor armazenado no cache e, se n\u00e3o houver, geramos o valor novamente e salvamos ele no cache para futuras verifica\u00e7\u00f5es:<\/p>\n<p>01<br \/>\n\/\/ Verifica se a frase j\u00e1 est\u00e1 no cache<br \/>\n02<br \/>\n$cache = new Cache();<br \/>\n03<br \/>\n$frase = $cache->read(&#8216;frase-dia&#8217;);<br \/>\n04<br \/>\n\/\/ Se n\u00e3o houver frase no cache ou j\u00e1 tiver expirado<br \/>\n05<br \/>\nif (!$frase) {<br \/>\n06<br \/>\n    \/\/ Cria uma nova frase e salva-a no cache por 30s<br \/>\n07<br \/>\n    $frase = &#8216;CALMA! O s\u00e1bio jamais se aborrece (&#8216;. date(&#8216;H:i:s&#8217;) .&#8217;)&#8217;;<br \/>\n08<br \/>\n    $cache->save(&#8216;frase-dia&#8217;, $frase, &#8217;30 seconds&#8217;);<br \/>\n09<br \/>\n}<br \/>\n10<br \/>\necho &#8220;<\/p>\n<p>{$frase}<\/p>\n<p>&#8220;;<br \/>\nVeja o c\u00f3digo-fonte completo da classe: http:\/\/pastebin.com\/p4m0CpwH<\/p>\n<p>Um grande abra\u00e7o e at\u00e9 a pr\u00f3xima! \ud83d\ude42<br \/>\nhttp:\/\/blog.thiagobelem.net\/criando-um-sistema-de-cache-no-php\/<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u201cCache\u201d \u00e9 uma forma de armazenar um valor para um consulta futura mais r\u00e1pida. Com o cache conseguimos otimizar o carregamento dos sites e de suas informa\u00e7\u00f5es. Suponhamos que voc\u00ea tenha um site que fa\u00e7a uma consulta em um tabela do banco de dados que&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_angie_page":false,"page_builder":"","footnotes":""},"categories":[6],"tags":[],"class_list":["post-2795","post","type-post","status-publish","format-standard","hentry","category-php"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/posts\/2795","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=2795"}],"version-history":[{"count":0,"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/posts\/2795\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=2795"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/categories?post=2795"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.deuzebranaweb.com.br\/index.php\/wp-json\/wp\/v2\/tags?post=2795"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}