Un errore PHP piuttosto comune quando si utilizza la libreria cURL all’interno di un comune piano hosting è il seguente:
PHP Warning: curl_setopt(): CURLOPT_FOLLOWLOCATION cannot be activated when an open_basedir is set in...
Questo errore è dovuto a delle comuni implementazioni di sicurezza presenti sulla maggior parte degli shared hosting, impostazioni che confliggono con l’opzione CURLOPT_FOLLOWLOCATION utilizzata in diversi script che ricorrono alla libreria cURL per accedere a risorse remote.
Ma come ovviare a questo problema? La soluzione "da manuale" prevede un intervento sulla configurazione di PHP per le direttive safe_mode e open_basedir, tuttavia, per ovvie ragioni, all’interno di un hosting condiviso la strada appare difficilmente percorribile.
Per questo motivo può essere utile cercare una soluzione alternativa che è possibile implementare da soli, senza dover necessariamente agire sulle impostazioni del web server.
Ma andiamo per gradi…
Il problema: CURLOPT_FOLLOWLOCATION
Di seguito un esempio di codice che potrebbe generare l’errore oggetto del nostro articolo:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.sito.com/pagina.php');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
L’opzione colpevole dell’errore, come già anticipato, è la seguente:
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1);
Grazie a questa opzione, cURL può seguire gli eventuali redirect della risorsa remota (in alter parole: se la risorsa remota è stata spostata, lo script è in grado di seguire il/i redirect eventualmente implementati).
Per ragioni di sicurezza, quando la direttiva safe_mode è attiva e/o open_basedir è settato esplicitamente (cioè il suo valore non è "null"), questa opzione di cURL produce un errore in quanto il sistema blocca questo tipo di richiesta.
Soluzione n.1 – contattare l’hosting provider
Per risolvere il problema sarebbe sufficiente agire sul file php.ini settando safe_mode su "off" e open_basedir su "null" (è anche sufficiente commentare la riga).
Purtroppo la maggior parte degli hosting provider vi risponderà negativamente, in quanto queste direttive sono piuttosto importanti per questioni di sicurezza, quindi questa strada appare difficilemnte percorribile.
Soluzione n.2 – disattivare CURLOPT_FOLLOWLOCATION
Se non è possibile modificare le impostazioni del web server, la cosa più facile da fare per risolvere il nostro problema sarebbe disattivare l’opzione incriminata. Per farlo è sufficiente rimuovere o commentare la riga, oppure settare esplicitamente l’opzione su 0 o false in questo modo:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
Se la risorsa remota non effettua alcun redirect non avremo alcun problema, in caso contrario questa soluzione non è risolutiva.
Soluzione n.3 – implementare una funzione per simulare CURLOPT_FOLLOWLOCATION
Quando entrambe le soluzioni precedenti non sono praticabili, la risposta al nostro problema può essere trovata nell’implementazione di una funzione in grado di simulare il comportamento di CURLOPT_FOLLOWLOCATION.
Questa funzione è disponibile online in diverse varianti. La versione che vi propongo è stata reperita direttamente tra i commenti sul sito PHP.NET:
function curl_exec_follow($ch, &$maxredirect = null) {
$mr = $maxredirect === null ? 5 : intval($maxredirect);
if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0);
curl_setopt($ch, CURLOPT_MAXREDIRS, $mr);
}else{
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
if ($mr > 0) {
$newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
$rch = curl_copy_handle($ch);
curl_setopt($rch, CURLOPT_HEADER, true);
curl_setopt($rch, CURLOPT_NOBODY, true);
curl_setopt($rch, CURLOPT_FORBID_REUSE, false);
curl_setopt($rch, CURLOPT_RETURNTRANSFER, true);
do {
curl_setopt($rch, CURLOPT_URL, $newurl);
$header = curl_exec($rch);
if (curl_errno($rch)) {
$code = 0;
}else{
$code = curl_getinfo($rch, CURLINFO_HTTP_CODE);
if ($code == 301 || $code == 302) {
preg_match('/Location:(.*?)\n/', $header, $matches);
$newurl = trim(array_pop($matches));
}else{
$code = 0;
}
}
}
while ($code && --$mr);
curl_close($rch);
if (!$mr) {
if ($maxredirect === null) {
trigger_error('Too many redirects.', E_USER_WARNING);
}else{
$maxredirect = 0;
}
return false;
}
curl_setopt($ch, CURLOPT_URL, $newurl);
}
}
return curl_exec($ch);
}
Una volta aggiunta la funzione all’interno del sorgente del nostro script, possiamo modificare il codice originale in questo modo:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.sito.com/pagina.php');
curl_setopt($ch, CURLOPT_POST, 1);
$res = curl_exec_follow($ch);
curl_close($ch);
echo $res;
Come potete notare abbiamo rimosso la linea relativa all’opzione CURLOPT_FOLLOWLOCATION ed abbiamo sostituito l’invocazione della funzione nativa curl_exec() con curl_exec_follow().
In questo modo otteniamo lo stesso risultato che avremmo ottenuto con CURLOPT_FOLLOWLOCATION, senza incorrere in limitazioni o errori dovuti alla configurazione di PHP.