Evitare il caching del css di WordPress

wordpress_logo

In questo brevissimo articolo vi dimostrerò come evitare che il browser degli utenti usi una versione non aggiornata del vostro css di WordPress.

Con questa breve funzione andremo ad inserire un parametro in fondo al richiamo del foglio di stile che inserisca il timestamp dell’ultima modifica del file, in modo che la cache continui a funzionare fino a quando non sarete voi a modificare il file.

<link href="<?php echo get_bloginfo('template_url'); ?>/style.css?v=<?php echo filemtime(get_stylesheet_directory().'/style.css'); ?>" rel="stylesheet" media="screen">

Come potete vedere abbiamo applicato il concetto al css principale di wordpress ma si può applicare a qualsiasi file css, js o di immagini. La funzione di php utilizzata è la filemtime.

Eseguire chiamate AJAX con jQuery e JSON in WordPress

Ajax and WordPress

Succede sempre più di frequente di dover effettuare delle chiamate ajax in WordPress. In questo articolo vedremo come effettuare delle chiamate asincrone senza dover creare nuove pagine WordPress ma sfruttando lo script utilizzato dall’interfaccia del wp-admin.
Nell’esempio in questione, la chiamata AJAX restituirà un file JSON formato davvero utile per rappresentare i dati restituiti da una query. Lato javascript useremo la funzione $.getJSON(…) di jQuery mentre lato server scriveremo il nostro script all’interno del file functions.php presente in qualsiasi tema WordPress.

Per prima cosa dobbiamo creare la corrispondenza tra una nuova action e la nostra funzione php:

add_action('wp_ajax_myaction', 'my_function');

Fatto ciò possiamo scrivere la nostra funzione my_function la quale corrisponderà all’azione myaction (il nome è puramente indicativo, potete chiamare l’azione e la funzione come preferite):

function my_function() {
  global $wpdb;
  $id = $_GET['id'];
  $rows = array();
  $q = "SELECT * FROM my_table WHERE id = ".$id;
  $rows['results'] = $wpdb->get_results($q, ARRAY_A);
  if(count($rows) <= 0){
    return false;
  }
  echo json_encode($rows);
  exit();
}

In questo script trovate tutto l’indispensabile per completare la parte lato server. Come si può intuire la query dovrà utilizzare un parametro passato in GET, poi utilizzando la variabile globale wpdb effettueremo la query sul database. La json_encode trasformerà l’array associativo con i record letti tramite la SELECT in una stringa JSON e la stamperemo a video tramite l’echo. Non dimenticate la exit() finale perchè altrimenti verrà stampato uno “0” che vi metterà in difficoltà nel parsificare il JSON.

Un ultimo particolare di cui tenere conto è che la add_action crea un’azione per l’admin-ajax.php che è un file accessibile solo da utenti loggati, quindi per consentire l’esecuzione dell’azione sia da utenti registrati sia da ospiti sarà necessario aggiungere un’ulteriore linea di codice come questa:

add_action('wp_ajax_nopriv_myaction', 'my_function');

Dopo aver completato lo script PHP possiamo passare al lato client, con il codice javascript per la chiamata della funzione:

$jQ.getJSON('<?php echo get_bloginfo('url'); ?>/wp-admin/admin-ajax.php',{action: 'myaction',id: 'tuo_id'}, function(json){...Codice javascript per parsificare il json...}

Ho omesso l’inclusione di jquery in WordPress, ma potete trovare come fare in un mio precedente articolo che trattava l’argomento nello specifico.

A questo punto non vi resta che sbizzarrirvi con le chiamate ajax nel vostro template oppure dai vostri plugin (in quest’ultimo caso basta spostare il codice PHP in un file del vostro plugin).

Creare un web service con SOAP e PHP

Ai tempi dell’università, nel corso di programmazione in rete tenuto dal Prof. Matteo Baldoni, avevamo realizzato un ottimo progetto in Java di agenti che interagivano tra di loro.
Era anche questo una specie di servizio web anche se era realizzato con RMI ovvero Remote Method Invocation, ossia una funzione di java che consente di invocare metodi di oggetti remoti da altre java virtual machine.

Oggi visto che sono ancora a casa per l’influenza, ho deciso di documentarmi sui web services in php, dato che probabilmente dovrò realizzare un progetto in cui potrebbero tornarmi utili.

Seguendo l’ottima guida “PHP 6, Guida per lo sviluppatore” di Lecky-Thompson, Novicki e Myer (ve la consiglio vivamente se siete interessati all’argomento, è davvero ottima), ho deciso di riprodurre l’esempio di un client-server SOAP(Simple Object Access Protocol). Innanzitutto SOAP è un protocollo per lo scambio di messaggi tra agenti software, ossia un client tramite soap può richiamare funzioni di un server inviando e ricevendo parametri. E fin qui nulla di strano mi direte voi… Infatti, la cosa interessante deve ancora arrivare! La peculiarità di scrivere software implementando SOAP è che client e server possono essere scritti in linguaggi diversi, essendo quest’ultimo appunto un protocollo e non una componente software. Tutti i linguaggi più comuni hanno delle classi che implementano SOAP, sia lato server sia lato client. Diventa così facile capire che una volta scritto un server SOAP che implementa alcune funzioni, possono esserci N client che sfruttano queste funzioni per leggere dati comuni o interagire con applicazioni sul web.

Ecco allora che ci spieghiamo come sia possibile che tutte le applicazioni web più famose, da quelle di google, a facebook fino a twitter, mettano a disposizione metodi per interagire dall’esterno con i loro servizi (le famose API, Application Programming Interface).

Come vi dicevo ho messo in pratica l’esempio che ho trovato sulla guida di PHP, potete raggiungerla a questo indirizzo. L’esempio non fa altro che realizzare un server che ha un metodo che prende in input un nome e restituisce un saluto, comprensivo del nome inviato. Il client ovviamente preso in input il nome invia la richiesta al server e stampa il saluto ricevuto. Nulla di complicato, ma si riescono ad intuire le potenzialità di questo protocollo.

I due script in php come vedrete fra poco, sono molto semplici, e non richiedono più di una decina di righe di codice ciascuno, la parte fondamentale di questo esempio è però il file wsdl, ossia il file xml di dichiarazione dei metodi messi a disposizione dal server. Questo file non è indispensabile ma è utile a chi implementa i client per sfruttare tutte le funzioni messe a disposizione da SOAP.

Come vi dicevo dal lato server il codice php è semplice:

function sayHello($name){
    $salutation="Ciao ".$name.", sarai lieto di sapere che sto funzionando!";
    return $salutation;
}
$server= new SoapServer("test.wsdl");
$server->addFunction("sayHello");
$server->handle();

Il file xml per le dichiarazioni è invece un po’ più complesso ma con un po’ di attenzione non sarà difficile comprenderne il significato:

<?xml version="1.0" encoding="UTF-8" ?>
<definitions name="test"
targetNamespace="http://URL_CARTELLA_SERVER_SOAP"
xmlns:tns="http://URL_CARTELLA_SERVER_SOAP"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

    <message name="sayHelloRequest">
        <part name="name" type="xsd:string"/>
    </message>
    <message name="sayHelloResponse">
        <part name="salutation" type="xsd:string"/>
    </message>

    <portType name="sayHelloPortType">
        <operation name="sayHello">
            <input message="tns:sayHelloRequest" />
            <output message="tns:sayHelloResponse" />
        </operation>
    </portType>

    <binding name="sayHelloBinding" type="tns:sayHelloPortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sayHello">
            <soap:operation soapAction="" />

            <input>
                <soap:body use="encoded" namespace="" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
            </input>
            <output>
                <soap:body use="encoded" namespace="" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
            </output>
        </operation>
    </binding>
    <documentation>UNA VOSTRA DESCRIZIONE DEL SERVIZIO</documentation>
    <service name="sayHelloService">
        <port name="sayHelloPort" binding="sayHelloBinding">
            <soap:address location="http://URL_SCRIPT_PHP_SERVER_SOAP" />
        </port>
    </service>
</definitions>

Fatto ciò, non ci resta che scrivere il codice per il client (tralascio il banale form html, ma metto a disposizione lo zip con tutto dentro):

$wsdl_url="http://URL_AL_DEL_FILE_WSDL";
if(isset($_POST['name'])){
    if($_POST['name']!=null){
        $client=new SoapClient($wsdl_url);
        print_r($client->sayHello(htmlentities($_POST['name'])));
    }
}

La cosa fondamentale per poter utilizzare SOAP è che il vostro php abbia l’estensione php_soap abilitata, ma se ho capito bene, da php6 viene inclusa di default tra le funzioni del core di php. Tra l’altro essendo inclusa nelle funzioni default di php è scritta in C per cui molto più veloce rispetto a scrivere codice php, di conseguenza, in queste applicazioni l’unico collo di bottiglia potrebbe essere la lentezza della linea o i tempi di ping/risposta di client e/o server.

Se siete pigri e volete testare il codice senza dovervi sbattere, eccolo qui, pronto per voi già impacchettato ;-)

Prossimamente testerò anche REST un altro metodo per realizzare web service con meno rogne, non serve infatti nessun file di dichiarazione, ma anche meno rigido e controllato.

Servire dinamicamente le immagini con php, Mysql e Imagick

Nessuno se ne sarà accorto ed effettivamente è un po’ impossibile notarlo se uno non lo sa ;-)

Ormai da una settimana però ho messo in funzione uno strumento “self-made” per la gestione delle immagini del mio blog.

Spulciando un po’ più attentamente google webmaster tools tra le funzioni sperimentali ho trovato un prestazioni del sito

In questa sezione google presenta un’analisi delle prestazioni del proprio sito (in termini di tempo e kilobyte di dati) e fornisce consigli su come ridurre i tempi di caricamento.

Siccome il mio hosting mi limita lo spazio web, utilizzavo un piccolo escamotage per caricare le foto del blog: le caricavo su picasa e sfruttavo i server di google per fornirle. Webmaster tools mi consigliava però di ridurre al minimo le ricerche DNS e infatti molto spesso le foto venivano reperite da diversi server di picasa. Tutto ciò contribuiva ad incrementare il tempo di caricamento del sito per cui ho preso la decisione di darmi da fare :-P

Come detto, il mio hosting mi limita lo spazio web, ma non ho limiti per quanto riguarda il db MySQL, per cui ho deciso di realizzare una mini-applicazione in php per salvare le immagini su database e servirle dinamicamente. Tutto questo con due funzioni che sfruttano la classe Imagick (installata sul server dove risiede il mio dominio).

Il concetto si basa in sostanza su tre pagine.

La prima sarà un form per far scegliere all’utente l’immagine da uploadare:

<form action="....." method="POST" enctype="multipart/form-data">.....</form>

La seconda, una pagina che riceve il parametro con l’immagine e la salva nel database:

il file è contenuto nell’array

$_FILES['image']['tmp_name']

dove image è il nome del parametro ricevuto via POST.

Qui ci servirà la prima funzione che sfrutterà la classe Imagick:

        public static function prepare_image($file){
		$img=new Imagick();
		$handler=fopen($file,'r');
		$ret=null;
		if(!feof($handler)){
			$fstr=fread($handler,filesize($file));
			$img->readImageBlob($fstr);
			/*Richiamo il metodo resize*/
			$img->setFormat('jpg');
			$img->setImageCompression(Imagick::COMPRESSION_JPEG);
			$img->setImageCompressionQuality(100);
			$ret=addslashes($img->getImageBlob());
			$img->destroy();
		}
		fclose($handler);
		return $ret;
	}

Dove la variabile $file sarà il contenuto dell’array di cui parlavamo prima. A questo punto potremo salvare il valore restituito dalla funzione nel database (il campo dovrà essere di tipo blob o meglio ancora longblob).

Fatto ciò possiamo passare alla terza pagina, quella che servirà l’immagine letta dal db.

Quest’ultima dovrà ricevere come parametro l’id dell’immagine da leggere sul db. La query la lascio fare a voi, io vi spiego la seconda funzione che ci servirà ;-) (molto più “leggera” della prima):

        public static function get_image_from_blob($blob){
		 $img=new Imagick();
		 $img->readImageBlob($blob);
		 $img->setFormat('jpg');
		 return $img;
	}

In questo caso il parametro $blob sarà il valore blob letto dal db, e la funzione ci restituirà l’immagine. A questo punto non vi resta che stampare l’immagine:

header('Content-Type: image/jpeg);
echo $img;

Abbastanza semplice no?
E dopo la prima settimana di utilizzo lascio a voi valutare i risultati ;-) :

Ovviamente tramite Imagick potete anche fare il resize dinamico delle immagini, ma non vorrei confondervi troppo le idee :-P

Nato di domenica…

Ho scoperto oggi un trick davvero interessante, che permette in php di creare delle variabili con nomi dinamici… :-O Cosa vorra mai dire tutto ciò? Semplice:
Se io voglio che una variabile prenda il nome del contenuto di un’altra variabile, posso farlo così:

$a="pippo";
${$a}="pluto";
echo $pippo;//a video verrà visualizzato: pluto

Mi sono trovato di fronte a questo problema, quando stamattina dovevo leggere dei records da un db, e creare delle variabili che si chiamassero come i campi del db.

Siccome sono pigro (e da qui il titolo di questo post, infatti mio papà dice che sono nato di domenica :-P), per semplificarmi la vita i campi letti dal db sono all’interno di un array associativo del tipo $array[0][‘campodb’] e scorro i risultati con un:

foreach(array_keys($array[0]) as $k){ ... }

Quindi sfruttando questa piccola scoperta in poche righe mi sono letto i valori dal db e creato le variabili contenenti i valori letti dal db.

Il codice finito risulta una cosa del tipo:

$q="SELECT campo1,campo2,...,campo20 FROM tabella WHERE condizioni";
$db->query($q); /**/
if($db->numRows>0){
   foreach(array_keys($db->rows[$i]) as $k){
      ${$k}=$db->rows[$i];
   }
}

Alla fine della fiera avrò una variabile per ogni campo presente nella SELECT con all’interno il valore letto dal db ($campo1, $campo2, … , $campo20).
In alternativa qui sotto ci sono alcuni esempi dell’utilizzo di questa features in php (direttamente da php.net)

define('ONE', 1);
function one() {
    return 1;
}
$one = 1;

${"foo$one"} = 'foo';
echo $foo1; // foo
${'foo' . ONE} = 'bar';
echo $foo1; // bar
${'foo' . one()} = 'baz';
echo $foo1; // baz

Sapevate già tutto? D’oh! Sono sempre l’ultimo a scoprire le cose interessanti! ;-(
[ad]

Indici Full Text in MySQL

Post da nerd, astenersi perditempo… :-P

[NERD MODE ON]

Alcuni giorni fa sul lavoro mi sono cimentato in un nuovo esperimento.

Tutto è partito dal problema di dover indicizzare dei contenuti, sui quali poi eseguira una ricerca “generica” in stile google (ovvero un unico campo che riceve un insieme di parole, che possono corrispondere a diversi campi del db).

Bene non voglio stare qui a spiegare nei dettagli la soluzione che abbiamo deciso di adottare, ma in sostanza mi sono ritrovato con un campo testuale con dentro tutte le parole che corrispondono ad una chiave.

Ok, e ora? L’idea iniziale è stata quella di eseguire una query con ” LIKE ‘%parola%’ ” in OR con altre LIKE simili, per ogni parola inserita dall’utente. Una volta implementata, tutto funzionava (avevo 3 record in quella tabella). Siccome per scrupolo avevo anche implementato un timer che misurasse il tempo di esecuzione, ho provato a fare un test pesante… Con python mi sono creato un programma per scrivere su file qualche centinaia di query di inserimento su quella tabella, lo eseguo e popolo il db. Ora ci sono 1000 record (non sono ancora abbastanza ma rende già l’idea), rilancio la ricerca e il timer segna 0,3 secondi. Buono no? sì ma sono 1000 record, se mai i record dovessero essere 100K o ancora di più? Mmm non mi soddisfa questa cosa. *Googleing*….*Googleing*….*Googleing* FULL TEXT INDEX

Cos’è? In MySQL è possibile definire su un campo di una tabella, un indice FULL TEXT, ovvero una struttura che indicizza in maniera ottimale tutte le parole presenti in uno o più campi. Ci sono alcune restrizioni però. Quella più importante è che la tabella non può essere creata con il motore InnoDB (il più comune perchè supporta query multiple in una singola transazione). Inoltre le stringhe da cercare con le query dovranno avere più di 3 caratteri (altrimenti bisogna andare a modificare le impostazioni di MySQL, cosa impossibile se non si ha accesso al server, e comunque relativamente complicata da fare).

Detto ciò i passi da fare sono i seguenti:
Definiamo la tabella e l’indice:

CREATE TABLE test (
id INT UNSIGNED   AUTO_INCREMENT NOT NULL PRIMARY KEY,
keywords TEXT,
[keywords2] VARCHAR(255),
FULLTEXT (keywords[,keywords2])
);

[Tra le parentesi quadre ho messo il secondo campo, spero si capisca che è opzionale…]

Fatto questo e dopo aver popolato la tabella, possiamo provare la nostra ricerca con la seguente query:

SELECT * FROM test WHERE MATCH(keywords) AGAINST('pippo')

In questo modo andremo a dire a MySQL di cercarci l’esatta parola ‘pippo’ all’interno del campo keywords.

Una cosa che non si trova facilmente nelle guide che ho seguito (ho fatto perciò riferimento alla documentazione ufficiale di MySQL) è che così facendo si effettua una ricerca in linguaggio naturale, che è ottimale per indicizzare frasi di senso (più o meno) compiuto. Il “difetto” se può definirsi così, è che con questo tipo di ricerca alcune parole non verranno trovate, infatti MySQL utilizza un array di stopwords che verranno saltate a piè pari dalla ricerca. Per effettuare una ricerca su tutte le parole e utilizzando operatori logici (ad esempio “+” significa OR mentre “-” esclude la parole che segue questo simbolo) bisogna effettuare una ricerca IN BOOLEAN MODE, più lenta, ma più efficace. La nostra query dovrà quindi essere modificata in:

SELECT * FROM test WHERE MATCH(keywords) AGAINST('*pippo*,*pluto*' IN BOOLEAN MODE)

In questo modo si effettua una ricerca all’interno del campo keywords, delle parole che contengono al loro interno “pippo” o “pluto”. Il carattere “*” funziona un po’ come il “%” nelle LIKE.

A questo punto manca un ultima chicca. La funzione MATCH (..) AGAINST(..) ritorna un valore che rappresenta la pertinenza delle parole cercate nel campo. Nel BOOLEAN MODE sarà il numero di parole trovate nel campo, mentre con la ricerca in linguaggio naturale ci sarà un algoritmo che calcola la pertinenza con delle regole strane ma che funzionano ;)

Quindi per sfruttare al massimo questo algoritmo bisognerà scrivere la query con:

SELECT *, MATCH(keywords) AGAINST('*pippo*,*pluto*' IN BOOLEAN MODE) as pertinenza FROM test WHERE MATCH(keywords) AGAINST('*pippo*,*pluto*' IN BOOLEAN MODE) ORDER BY pertinenza DESC

Così facendo ordiniamo i risultati dal più pertinente, a quello meno.

Risultato? sui miei 1000 records il tempo si è dimezzato, nel caso peggiore… Risultato accettabile no? :-)

Non vi resta che provare per credere, buon divertimento!

[NERD MODE OFF]
[ad#ad-1]