WordPress e qualità: sistemi di cache manuali

Per parlare di qualità, tra le tante cose, si devono definire requisiti ed obiettivi e poi trovare degli indicatori per poterli misurare.
Io sto cercando di intrudurre una serie di linee quida nel mio modo di sviluppare ad una di queste si riferisce alla velocità ed al numero di query con cui vengono generate le pagine.

 

Gli indicatori che mi danno la misura e mi permettono di capire se sto rispettando i miei requisiti sono di tre tipi:

  1. Lato browser: YSlow, Page Speed ma anche il pannello “Net ” di Firebug. Con questi verifico i tempi di caricamento dei singoli componenti della pagina ed eventuali colli di bottiglia nel caricamento di js.
  2. Lato server: XHProf . Si tratta di un profiler gerarchico per verificare i tempi di esecuzione di ogni singola funzione.
  3. Lato WordPress: Debug Query e Debug Object. Tramite questi plugin ho sotto controllo il numero di query ed un dettaglio dei singoli oggetti interni a WordPress.

E’ ovvio che uno dei primi livelli di ottimizzazione sia l’intruduzione dei sistemi di cache.
Un uso appropriato può farci risparmiare diverse query e parecchi ciclo di CPU ma dato che in molti hanno già parlato dei plugin che possono aiutarci nella gestione “semiautomatica” della cache vado oltre e vi segnalo solo un articolo fatto da qualcuno che si è anche preoccupato di andare un po’ più a fondo preparando qualche test.

Dalla versione 2.0 nel core di WordPress è presente un sistema di caching non permanente (nel senso che i dati sono residenti in memoria e solo per la durata della singola sessione).

Qui un breve esempio:


$last5 = wp_cache_get( 'elenco_ultimi_5_post' );
if ( false == $last5 ) {
$sql_last5 = "select ID from $wpdb->posts where post_type = 'post' and post_status = 'publish' order by post_date desc limit 5";
$last5 = $wpdb->get_results( $sql_last5 );
wp_cache_set( 'elenco_ultimi_5_post', $last5 );
}

Io spesso lo uso anche per salvarmi anche pezzi di html


$last5 = wp_cache_get( 'elenco_ultimi_5_post' );
if ( false == $last5 ) {
$sql_last5 = "select ID, post_title from $wpdb->posts where post_type = 'post' and post_status = 'publish' order by post_date desc limit 5";
$last5_data = $wpdb->get_results( $sql_last5 );
if ($last5) :
foreach ($fivesdrafts as $post) :
$last5 .= "

“;
endforeach;
wp_cache_set( ‘elenco_ultimi_5_post’, $last5 );
endif;
}

Usando un sistema di questo tipo se volessi visualizzare l’elenco degli ultimi 5 post pubblicati nella mia sidebar eviterei di chiedere al database la stessa informazione per ogni pagina visualizzata ma dopo la prima richiesta verrebbe messo in memoria il risultato e reso subito pronto e disponibile per la volte successive.

Dal rilascio della 2.8 le cose sono anche migliorate con l’introduzione delle Transients API che offrono sistema “standardizzato” per il salvataggio della cache su database.
Questo ci permette di salvare le informazioni “cross-sessione” rendendo riutilizzabile la cache anche per utenti diversi.

Questo è il codice con l’esempio di prima riadattato con le Transients API


$last5 = get_transient( 'elenco_ultimi_5_post' );
if ( !$last5 ) {
$sql_last5 = "select ID, post_title from $wpdb->posts where post_type = 'post' and post_status = 'publish' order by post_date desc limit 5";
$last5_data = $wpdb->get_results( $sql_last5 );
if ($last5) :
foreach ($fivesdrafts as $post) :
$last5 .= "

“;
endforeach;
set_transient( ‘elenco_ultimi_5_post’, $last5,3600 );
endif;
}

Questo piccolo cambiamento rende persistente la mia cache per un ora (3600 secondi) ed accessibile per ogni visitatore.

Il sistema potrebbe però avere ancora un limite in quanto nel caso venisse modificato o pubblicato un nuovo post questo non verrebbe visualizzato nell’elenco sino al momento in cui la cache non risultasse invalidata e quindi ricreata.
Tramite un semplice hook possiamo pero risolvere il problema…

add_action(‘save_post’, ‘delete_last5_cache’);
function delete_last5_cache( $post_id ) {
global $post;
if ( $post->post_status == “publish”) {
// cancello la cache
delete_transient(‘elenco_ultimi_5_post’);
}
}

In alcune situazioni però questo non mi bastava ancora in quanto avrei voluto rendere persistente una porzione ancora più grossa (es. tutta la sidebar e/o tutto il footer). Quello che cercavo era qualcosa di simile a get_template_part ma con un salvataggio su cache.

Ho quindi scritto io una funzione che ho chiamato cache_template_part.
Questo il sorgente:

/*
* 2010 | maurizio | http:maurizio.mavida.com
* versione 1.2
* viene effettuata una cache su disco di una porzione di codice
*  @param string $file - nome del template da caricare
*  @param bool always - visualizza sempre la cache (anche all'amministratore)
*  @param bool rewrite - ricrea sempre la cache anche se ancora valida
*  @param int cachetime - numero di secondi dopo il quale viene invalidata
*  @param string cachepath - percorso per il salvataggio della cache
*
* es. cache_template_parts("sidebar.php");
* es. cache_template_parts("sidebar.php" , array( 'cachetime ' => 14400 )  );
*/
function cache_template_part( $file , $args = null  ) {

$defaults = array(
'always' => false,
'rewrite' => false,
'cachetime' => 3600,
'cachepath' => 'wp-content/cache/',

);

$args = wp_parse_args( $args, $defaults );
extract( $args, EXTR_SKIP );

$cachefile = $cachepath . str_replace( "/", "-", $file);
$cachefile_created = ((@file_exists($cachefile))  ) ? @filemtime($cachefile) : 0;

if ( !$rewrite and ( time() - $cachetime < $cachefile_created ) and ( $always || !is_user_logged_in())  ) {
echo  file_get_contents( $cachefile );
} else {
//ob_start(ob_gzhandler);
ob_start();

include($file);

if ( $always || !is_user_logged_in() ) {
// in caso di utente loggato manda subito l'output
$b = ob_get_clean();
//$b = trim(preg_replace('/\s+/', ' ', $b));
$b = preg_replace('/[ ]+/', ' ', $b);
$b = str_replace( array("\r\n", "\r", "\n", "\t" ), '', $b);

if ( strlen($b) > 1 ) {
// controllo che il buffer sia valorizzato
$b = "" . $b;
file_put_contents( $cachefile , $b);
}

echo $b;

}
ob_end_flush(); //
}
}

L’unico argomento obbligatorio è il nome della file da caricare con una serie di argomenti opzionali (che devono essere passati come array).

Al momento, a differenza delle Transients API, ho deciso di salvare la cache su disco sequendo l’appoccio suggerito anche da altri plugin che in caso di problemi permette un debug più semplice e veloce (una semplice eliminazione dei file forze la rigenerazione della cache )

Se qualcuno volesse usarla, debuggarla e/o suggerirmi come migliorarla la può scaricare da qui sotto.

cache_template_part

Questo vuole essere un’esempio di quello che intendo per qualità che può essere riassunto in un uso consapevole delle funzionalità core di WordPress e la realizzazione di “utility” che possano aiutare al raggiungimento dei requisiti.

Ultima nota finale:
Il fatto di usare sistemi di cache manuale non vuol dire che si debbano usare solo quelli.
Un bella spinta la può dare l’utilizzo dell’APC Object Cache Backend (se il server lo supporta).
Se siete tra i fortunati ad avere nginx come web server vi suggerisco Nginx proxy cache integrator.
E poi per tutti il forse poco conosciuto Widget Cache


Image credits: Derek Heisler

One Reply to “WordPress e qualità: sistemi di cache manuali”

Leave a Reply

Your email address will not be published. Required fields are marked *