Eseguire Joomla da script o linea di comando
La modalità migliore per schedulare l’esecuzione di attività amministrative sul nostro sito Joomla è di realizzare uno script che venga eseguito direttamente dal server, tramite crontab o altri sistemi di schedulazione lato server.
In questo articolo vediamo invocare una pagina Joomla direttamente da script o linea di comando. Si presuppone una buona conoscenza applicativa di Joomla e del sistema operativo Linux.
Nota: quanto riportato si basa sulla nostra esperienza specifica. Poco tempo fa abbiamo infatti realizzato un componente di importazione di dati esterni in VirtueMart. L’esigenza di schedulare l’esecuzione di tale componente ci ha portato a fare tutti i test e studi necessari, il cui risultato è questo articolo.
Attività amministrative
Esistono alcuni componenti per Joomla, in particolare tra quelli disponibili nel pannello ammministrativo, che compiono attività di manutenzione quali importazione di file, pulizia di tabelle, elaborazione di dati.
Sono attività che non producono un risultato specifico, se non un messaggio con il successo dell’operazione e/o eventuali avvisi ed errori.
Può ragionevolmente presentarsi l’esigenza di eseguire questa attività ad intervalli regolari. Certo, potremmo mettere un promemoria periodico sul cellulare che ci ricorda di eseguire a mano l’operazione. Ma non esiste un sistema più comodo?
Estensioni esistenti
Esistono già alcune estensioni per Joomla che ci permettono di schedulare l’esecuzione di attività amministrative.
Tuttavia, dopo averle esaminate, abbiamo visto che queste estensioni simulano soltanto la schedulazione. Si tratta solitamente di plugin che vengono eseguiti a qualunque visualizzazione di pagina, controllano l’orario, e – se l’orario corrisponde con una impostazione definita – eseguono determinate attività.
Il difetto di questo approccio è che, se per caso nessuno visita il nostro sito per qualche tempo, il plugin non viene mai eseguito e la schedulazione non avviene. Ecco perchè si tratta di una schedulazione simulata.
Scripting
Per effettuare una vera e propria schedulazione, la modalità corretta è quella di creare uno script.
Nell’approccio più semplice, tale script viene realizzato tramite un normale linguaggio di scripting, quale PHP o Perl, andando a effettuare operazioni direttamente sul database o sul file system, in sostanza duplicando le funzionalità offerte del componente di Joomla.
Tuttavia, un buon programmatore dovrebbe sempre evitare di avere diversi programmi che fanno le stesse cose. Troviamo quindi un modo per richiamare direttamente il componente di Joomla.
Problemi iniziali
La prima cosa che ci viene in mente è di richiamare direttamente da linea di comando la pagina index.php con i parametri necessari al nostro componente:
php /var/www/testsite/administrator/index.php?option=com_xvm
Non funzionerà: la gestione dei parametri GET e POST con il PHP al di fuori del web server non è possibile. E in ogni caso la pagina index.php funziona solo se siamo autenticati in Joomla, mentre noi non lo siamo dati che stiamo agendo da linea di comando. Per cui dobbiamo trovare una alternativa.
La seconda soluzione che ci viene in mente è richiamare direttamente lo script PHP corrispondente al nostro componente, così vengono saltati tutti i controlli inclusi nella index.php e non c’è bisogno di passare ulteriori parametri:
php /var/www/testsite/administrator/components/com_xvm/xvm.php
Non funzionerà comunque: lo script xvm.php avrà come prima riga il controllo della costante JEXEC. Se tale costante non è definita, lo script capisce che non è stato richiamato dalla index.php e rifiuta di eseguirsi.
Nota: la presenza di tale controllo non è fondamentale per il funzionamento del componente ma si tratta di un efficace controllo di sicurezza per cui ogni componente appena decente dovrebbe averlo. Il nostro XVM ce l’ha.
Va bene, direte voi, accettiamo il compromesso e rimuoviamo tale controllo, permettendo allo script di essere richiamato direttamente.
Ancora, niente da fare: xvm.php usa variabili, oggetti e metodi propri del framework Joomla; ma se lo richiamiamo direttamente da linea di comando il framework non è disponibile. Seguiranno valanghe di errori relative a variabili, oggetti e metodi non definiti.
Attivare il framework Joomla
L’unica soluzione che ci rimane è quella di ricostruire il framework minimo necessario per permettere l’esecuzione dello script. Ovvero, in altre parole, dobbiamo includere a mano tutte le classi e librerie di supporto che normalmente la pagina index.php recupera per noi.
Grazie a un post di Flavio Copes, che a sua volta aveva preso spunto da un post nel tracker di Joomla, siamo stati in grado di ricostruire il codice minimo necessario per creare il framework Joomla e poter quindi poi richiamare lo script.
Nota: le esatte classi e librerie di supporto da includere possono variare a seconda del componente che vogliamo richiamare, ma l’esempio riportato dovrebbe essere un buon punto di partenza..
Eccolo:
<?php // We are a valid Joomla entry point. define('_JEXEC', 1); // Setup the path related constants. define('DS', DIRECTORY_SEPARATOR); define('JPATH_BASE', dirname( __FILE__ )); define('JPATH_ROOT', JPATH_BASE); define('JPATH_CONFIGURATION', JPATH_BASE); define('JPATH_LIBRARIES', JPATH_BASE.DS.'libraries'); define('JPATH_METHODS', JPATH_ROOT.DS.'methods'); // Load the library importer. require_once (JPATH_LIBRARIES.'/joomla/import.php'); require_once (JPATH_CONFIGURATION.'/configuration.php'); // Import library dependencies. jimport('joomla.application.application'); jimport('joomla.application.component.controller'); jimport('joomla.application.component.helper'); jimport('joomla.html.parameter'); jimport('joomla.utilities.utility'); jimport('joomla.language.language'); jimport('joomla.utilities.string'); jimport('joomla.factory'); $conf =& JFactory::getConfig(); $conf->setValue('config.user', 'my_username'); $conf->setValue('config.password', 'my_password'); $conf->setValue('config.db', 'my_database'); $conf->setValue('config.host', 'localhost'); ?>
Se noi creiamo una nuova versione del nostro script xvm.php che contenga inizialmente le righe soprastanti, siamo a posto. Lo script richiamato da linea di comando con:
php /var/www/testsite/administrator/components/com_xvm/xvm.php
funzionerà correttamente.
Non è finita…
Ci sono delle ovvie limitazioni nella soluzione appena creata:
- lo script originale xvm.php non è più disponibile, e potremmo avere problemi quando cerchiamo di eseguirlo alla maniera tradizionale, ovvero a mano dal pannello di controllo di Joomla;
- se dovessimo ripetere il tutto per un altro componente, dovremmo copiare e incollare il codice in un altro script.
- lo script è eseguibile da chiunque, senza alcun controllo di sicurezza, bypassando del tutto il sistema di autenticazione di Joomla.
Si può fare di meglio.
Soluzione numero 1
Cominciamo a separare il codice di creazione del framework dal nostro componente, creando uno script separato. Riportiamo il file xvm.php al suo stato originale e copiamo invece le righe soprastanti in un nuovo script, ad esempio cli.php.
Alla fine del file cli.php includiamo il nostro componente aggiungendo queste due righe:
$file = JPATH_ROOT . DS . "administrator" . DS . "components" . DS . "com_xvm" . DS . "xvm.php"; include ( $file );
Il comando da richiamare diventa ora:
php /var/www/testsite/administrator/components/com_xvm/cli.php
Con questo il componente originale rimane invariato e il problema numero 1 è risolto.
Soluzione numero 2
Con un piccolo sforzo aggiuntivo possiamo rendere parametrico il componente da eseguire.
Se infatti cambiamo nello script cli.php le due righe inserite al passo 1 con queste tre:
$component = $_SERVER["argv"][1] $file = JPATH_ROOT . DS . "administrator" . DS . "components" . DS . "com_" . $component . DS . $component . ".php"; include ( $file );
possiamo richiamare qualsiasi componente passandolo come parametro del comando:
php /var/www/testsite/administrator/components/com_xvm/cli.php xvm
php /var/www/testsite/administrator/components/com_xvm/cli.php mycomponent
Va da se che a questo punto possiamo spostare il file cli.php in una directory comune o nella root del sito, dato che non si tratta più di uno script specifico per il componente XVM.
E con questo anche il problema numero 2 è risolto.
Soluzione numero 3
Per assicurarci che lo script possa essere eseguito solo da utenti autorizzati, possiamo inserire un controllo iniziale su una “password” che deve essere passata come secondo parametro. Quindi mettiamo all’inizio dello script cli.php:
$secret = "PasswordSegreta"; if ( $_SERVER["argv"][2] != $secret ) { echo "Non autorizzato\n"; exit; }
In questo caso la “PasswordSegreta” è definita direttamente nello script, ma potrebbe benissimo essere impostata nella configurazione del componente stesso (il noto componente AkeebaBackup utilizza un sistema simile per l’esecuzione remota di backup).
In questo modo il comando da richiamare diventa:
php /var/www/testsite/administrator/components/com_xvm/cli.php xvm PasswordSegreta
E con questo il problema numero 3 e risolto
Potete ora scaricare il file php risultante.
Rifiniture e conclusioni
Un’ultima rifinitura per il sistema è cercare di identificare il fatto che siamo in esecuzione da script piuttosto che da Joomla, in modo che i componenti possano adottare di conseguenza comportamenti specifici.
Nel file cli.php aggiungiamo quindi una riga:
define('_JCLI', 1);
definendo una costante, che potrà poi essere sfruttata dai nostri componenti. Ad esempio, potremmo restituire gli errori in maniera diversa inserendo nel nostro componente:
$error = "Qualcosa è andato storto"; if ( defined('_JCLI') ) { echo "*** Errore: $error\n"; } else { $message = "Errore: $error"; $app = JFactory::getApplication(); $app->enqueueMessage( JText::_( $message ), 'error' ); }
IMPORTANTE: tutto il sistema illustrato in questo articolo si basa sul fatto che il comando venga richiamato sullo stesso server che ospita il sito.
Se invece avete bisogno di richiamarlo da una macchina remota… abbiamo una soluzione anche per quello, ma la vedremo in un altro articolo! 🙂
Daniele C.
Ottimo Articolo!
Ho solo una perplessità: nel codice per ricostruire il framework Joomla! viene indicata come JPATH_BASE la directory di __FILE__, questo vuol dire la directory stessa del file php, ma per funzionare è necessario che sia la root del sito (nel caso indicato /var/www/testsite/ ), quindi se semplicemente copi le righe di codice nel file /var/www/testsite/administrator/components/com_xvm/xvm.php la JPATH_BASE sarà /var/www/testsite/administrator/components/com_xvm/, cosa che sballerà tutte le altre importazioni.
Se non mi sbaglio le righe di codice dovrebbero essere inserite in un file PHP che si trova in /var/www/testsite/administrator/ (infatti se guardi il codice dell’index.php nell’administrator il JPATH_BASE è settato li).
Francesco
@Daniele:
hai ragione a metà. Sei nel giusto quando dici che lo script non può essere piazzato ovunque, dato che fissa la JPATH_BASE nella directory corrente. Tuttavia, non va messo in /var/www/testsite/administrator ma direttamente nella root del sito, ovvero /var/www/testsite.
E’ da lì, infatti, che viene pescato il framework di Joomla. Tra l’altro, controllando meglio, mi viene il sospetto che l’inclusione della cartella “METHODS” non sia necessaria.
Correggerò l’articolo e il file allegato appena trovo una soluzione!
luca
Fantastico! Siete stati veramente chiari come pochi.. grazie mille!
Francesco
@Luca:
grazie, ma tieni presente che nel frattempo le modalità di esecuzione da CLI si sono evolute. In Joomla 2.5 e successivi, subito dopo l’installazione trovi una cartella “cli” che contiene degli esempi già esistenti di applicativi simili.
Consiglio di studiare quelli anzichè l’esempio riportato sopra, che nel frattempo è diventato un po’ obsoleto.