Nel linguaggio di scripting lato server PHP è riscontrabile una "pecca" che potrebbe passare inosservata allo sguardo degli sviluppatori meno "esperti" o meno attenti, ma che è decisamente importante conoscere ed aggirare: le funzioni facenti parte del core di PHP, nel caso in cui qualcosa vada storto al momento dell'esecuzione delle stesse, restituiscono il valore booleano false
invece di generare un'eccezione.
Un esempio pratico
Per capire di cosa stiamo parlando, niente è meglio di un esempio pratico. Nel seguente snippet utilizziamo la funzione file_get_contents()
, che si occupa di leggere il contenuto di un file (nel nostro esempio un file JSON) e di restituirlo come variabile stringa. Successivamente, chiamiamo la funzione json_decode()
che si occuperà di scomporre la nostra stringa JSON in un oggetto lavorabile con PHP:
$string = file_get_contents('myfile.json');
$myvar = json_decode($string);
A prima vista sembrerebbe tutto corretto, giusto? In realtà, se ci avvaliamo di uno strumento di analisi del codice PHP, come l'ottimo PHPStan che si occupa di trovare eventuali bug prima ancora che il codice venga eseguito, otterremo un "check" che indica la presenza di un errore. Il suddetto strumento compone i risultati delle sue analisi in livelli, dove il livello 7 è indicativo di un codice privo di qualsivoglia bug. Nel nostro caso, il warning generato da PHPStan dichiara che:
Parameter #1 $json of function json_decode expects string, string|false given.
json_decode()
si aspetta di ricevere come primo parametro una stringa (che rappresenta il contenuto JSON da scomporre) e noi le abbiamo fornito un valore booleano false
. In effetti file_get_contents()
deve restituire false
nel caso in cui qualcosa vada storto nella lettura del file indicato come primo parametro. Ad esempio, il file potrebbe non essere leggibile da parte dello script a causa dell'impostazione dei permessi, il file stesso potrebbe non esistere nemmeno (questo è il caso del nostro esempio) il disco potrebbe essere danneggiato e cosi via.
Un'opzione migliore: check con generazione di eccezioni
Possiamo dunque correggere il nostro codice e migliorarlo drasticamente nella maniera seguente:
$filename = 'myfile.json';
$string = file_get_contents('myfile.json');
if ($string === false) {
throw new FileLoadingException('Impossibile caricare il file ' . $filename);
}
$myvar = json_decode($string);
if ($myvar === null) {
throw new FileLoadingException($filename . 'non contiene JSON valido' . json_last_error());
}
In questo caso controlliamo quale sia il valore restituito da file_get_contents()
: se è false
generiamo un'eccezione di tipo FileLoadingException
indicante che qualcosa è andato storto, altrimenti proseguiamo con lo script chiamando json_decode()
sul parametro corretto. Anche in questo caso effettuiamo un check per verificare che il valore restituito non sia NULL
. Se cosi fosse generiamo un'altra eccezione dello stesso tipo con un messaggio consono.
Il codice è corretto ma decisamente più prolisso. Se i controlli da effettuare fossero numerosi la leggibilità e la manutenibilità dello script ne risentirebbe pesantemente.
La transizione di PHP 7.3
Ma come mai le funzioni del Core di PHP in caso di errori si limitano a restituire valori quali false
e NULL
invece di generare delle eccezioni? La risposta potrebbe essere radicata nella storia di PHP: è possibile che molte delle funzioni core siano state aggiunte prima che il supporto alle eccezioni fosse implementato nel motore di PHP. Per questo motivo, molte delle funzioni di PHP utilizzano il vecchio sistema di gestione degli errori che si avvale dei messaggi di tipo E_WARNING
, E_ERROR
.. al posto delle più recenti (e potenti) eccezioni.
Attualmente, gli sviluppatori del Core di PHP si stanno adoperando per sistemare questa caratteristica tenendo d'occhio la compatibilità con i sistemi di gestione dei vecchi script. Ad esempio, nella versione 7.3, la funzione core json_decode()
accetta come ultimo parametro un'opzione flag JSON_THROW_ERROR
che consente alla funzione in questione di generare una nuova eccezione al posto di restituire false
/NULL
:
// PHP 7.3+
$json = json_decode($jsonString, $assoc = true, $depth = 512, JSON_THROW_ON_ERROR);
Il problema è che, dato che sviluppatori di PHP sono obbligati a mantenere la compatibilità con le precedenti versioni, il comportamento migliore deve attualmente essere opzionale. Se dimentichi di passare JSON_THROW_ON_ERROR
sei al punto di partenza: la funzione restituirà false
/NULL
. Non possiamo dunque sperare che le funzioni Core di PHP cambino radicalmente comportamento nelle prossime release, ma questa è una cosa anche positiva, dato che la retrocompatibilità è una caratteristica fondamentale per un linguaggio maturo.