Negli articoli precedenti, dedicati rispettivamente ad un'introduzione alla programmazione Object-Oriented in WordPress e alle linee guida per la scrittura di codice Orientato agli Oggetti nel noto Blog engine Open Source, abbiamo discusso dell'importanza dell'OOP nel contesto di WordPress e delle convenzioni con cui scrivere il nostro codice adeguandoci alle best practices ed agli standard correnti. In questo nuovo articolo proseguiremo il discorso affrontando l'argomento relativo all'inclusione delle nostre classi nel flusso dell'applicazione.
Inclusione manuale
L'inclusione manuale delle classi in WordPress avviene in due modi diversi, a seconda del fatto che stiamo sviluppando un tema o un plugin. In un tema le classi vanno richiamate nel file functions.php
tramite la funzione di PHP require_once()
. In questa funzione andrebbe sempre usata la costante TEMPLATEPATH
di WordPress che conserva il percorso fisico sul server alla directory del tema in uso.
require_once( TEMPLATEPATH . '/framework/MyClass.php' );
Le classi possono essere sia istanziate nel file di origine che nel file functions.php
, tenendo presenti ovviamente le necessità del design del vostro codice. In un plugin invece l'inclusione avviene sempre nel file principale del plugin. Il metodo di inclusione è analogo a quello usato per i temi, ad eccezione del fatto che si utilizza la funzione di WordPress plugin_dir_path()
con la costante "magica" di PHP __FILE__
per ottenere il percorso assoluto alla directory del plugin.
$dir = plugin_dir_path( __FILE__ );
require_once( $dir . '/framework/MyClass.php' );
Ovviamente, in entrambi i casi potrete anche creare un file apposito per l'inclusione manuale delle classi e quindi richiamarlo all'interno del vostro tema o plugin.
Inclusione automatica
Chi conosce il pattern MVC (Model-View-Controller) sa bene che in PHP è possibile eseguire l'autoloading delle classi, ossia il loro caricamento automatico. Il caricamento automatico ha l'indubbio vantaggio di evitarci la fatica di includere manualmente nel codice una nuova classe - ed un nuovo file - specificandone il nome e il percorso. PHP ha solo bisogno di un percorso iniziale da dove partire per cercare i file da includere; tutto il resto avviene in modo automatico.
Possiamo creare un file di bootstrap (secondo la terminologia MVC) da includere nel tema o nel plugin utilizzando la funzione di PHP spl_autoload_register()
(disponibile in tutte le versioni aggiornate di PHP) a cui passeremo una nostra funzione che eseguirà l'autoload a partire da un set di directory:
spl_autoload_register( 'my_autoload' );
function my_autoload( $class ) {
// se ho già caricato la classe esco dalla funzione
if( class_exist( $class, false ) ) return;
// altrimenti cerco di caricare la classe
$base_path = TEMPLATEPATH; // per i temi
$lookup_dirs = array( $base_path . '/framework/', $base_path . '/addons/' );
// scorro le directory
foreach( $lookup_dirs as $dir ) {
// se trovo il file che cerco smetto di cercare ed esco dal loop
if( file_exists( $dir . $class . '.php' ) ) {
require_once( $dir . $class . '.php' );
break;
}
}
}
Quindi possiamo semplicemente includere il file di bootstrap nel file 'functions.php' per i temi o nel file principale del nostro plugin. Ovviamente la variabile '$base_path' va modificata se si sta sviluppando un plugin come abbiamo visto nella sezione precedente.
Il problema della injection di classi nell'autoloading
Il maggiore problema insito nella soluzione di cui sopra è che l'inclusione delle classi avviene senza aver prima verificato che la classe corrente sia una classe legittima.
Tecnicamente parlando, l'autoloading delle classi in PHP usa gli stessi
principi alla base dell'inclusione di file esterni nel codice principale e come tale è soggetto alle stesse regole.Una soluzione a questa situazione è verificare che il nome della classe passato sia valido. Possiamo per questo creare la seguente espressione regolare:
\\?(?>[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*\
\)*[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*
\\?(?>[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*\
\?)+
Quindi possiamo usare questa espressione regolare con la funzione PHP 'preg_match()' all'interno della funzione di autoload:
$pattern = '(^\\?(?>[a-zA-Z_x7f-xff][a-zA- Z0-9_x7f-xff]*\\?)+$)';
$valid = (bool) preg_match($pattern, $class);
Di seguito alcuni riferimenti correlati per lo studio di questo argomento che abbiamo appena accennato.
- Dependency Injection: Huh? (NetTuts)
- Dependency Injection in PHP (NetTuts)
- PHP Object Injection
L'ultima risorsa elencata fa riferimento alla funzione di PHP 'unserialize()' e al fatto che l'input passato a tale funzione dovrebbe essere sempre validato prima di poter essere usato nel contesto delle classi. Di particolare utilità l'ultimo link (PDF) contenuto nell'ultima risorsa,
Shocking news in PHP exploitation.
Conclusioni
L'inclusione delle classi nei temi o nei plugin è un aspetto da non sottovalutare nello sviluppo in WordPress basato sul Paradigma Orientato agli Oggetti, ciò perché solo conoscendo questa caratteristica possiamo evitare problemi futuri; nel corso di questa trattazione abbiamo introdotto gli argomenti relativi alle inclusioni manuali ed automatiche (autoloading) senza ignorare le eventuali problematiche inerenti la sicurezza connesse a tali pratiche (Invalid Classname Injection nell'autoload in PHP).