Nuovi operatori, classi anonime, type-hinting-scalare, EngineException e jsond parser lo rendono due volte più veloce

Alla fine del 2015 è uscito PHP 7, sviluppato in seguito allo sviluppo del 6, che non è mai uscito grazie a delle feauture introdotte, risultate troppo dispendiose in termini di risorse. La nuova versione del linguaggio di sviluppo lato-server più popolare è molto più performante delle precedenti. Quanto emerge dagli studi fatti su alcune piattaforme è impressionante.

  • Applicato a Drupal (versione 8-beta4), PHP 7 ha raggiunto le 185 richieste soddisfatte al secondo (r/s), contro le 101 della 5.6, superando anche quelle della HHVM di Facebook (una macchina virtuale che converte gli script PHP in C++ per i server di compilazione ed esecuzione, per rendere più preformante l’elaborazione dei dati e consentire ai server di ospitare dalle cinque alle sei volte in più di traffico rispetto alla norma), 159;
  • Su WordPress 4.1 non è ancora arrivato a battere l’HHVM, a quota 619 r/s, ma le prestazioni sono comunque interessanti: PHP 7 raggiunge le 545 r/s, un numero più che doppio rispetto alle 262 della 5.6.
  • In phpBB le r/s arrivano a 912 contro le 483 della versione precedente.
  • Con Wardrobe PHP 7 è sempre a 912 r/s mentre PHP 5.6 si attesta a 532.

Questi dati parlano chiaro: PHP 7 ha migliorato notevolmente le prestazioni di un linguaggio di sviluppo non proprio noto per la sua performance.

Le novità dal punto di vista del codice

PHP 7 ha introdotto interessanti novità dal punto di vista del codice, inserendo nuovi operatori e introducendo nuovi modi di dichiarare funzioni e di istanziare oggetti.

Vediamo le principali novità con una serie di esempi, alcuni dei quali possono essere testati copiando e incollando i codici nella textarea presente su Online PHP Functions. Per testarne altri può essere meglio avere un server locale, come XAMPP, la cui versione di Apache supporta PHP 7.

Operatore Spaceship (<=>)

Spaceship è utile per confrontare due valori e sapere se il primo è maggiore o minore del secondo, piuttosto che equivalente.

  • “a”<=>”b” //restituisce -1
  • “b”<=>”a” //restituisce 1
  • “a”<=>”a” //restituisce 0

A differenza di altri operatori di confronto, come >, <, ==, ===, ecc… , <=> restituisce un valore intero, anziché booleano. Pertanto non può essere usato in un’espressione come quella di una struttura if, per esempio, ma possiamo invece utilizzarlo in uno switch, come sotto:

<?php

$n1=2;
$n2=5;

switch($n1<=>$n2){
   case 1:
     echo "n1>n2";
     break;

   case 0:
     echo "n1==n2";
     break;

   case -1:
     echo "n1<n2";
}
//stamperà n1<n2

?>

Spaceship è molto utile quando si ha la necessità di ordinare un array, anche multidimensionale, con la funzione usort(), la quale accetta, come secondo argomento, una funzione di comparazione che stabilisca il criterio da seguire nel mettere in ordine gli elementi in questione.
Nell’esempio sottostante c’è un array composto da alcuni array associativi, di cui ciascuno ha due elementi, nome e cognome, riferiti a una persona.

$people=array();

$people[]=array('nome'=>'andrea','cognome'=>'ronchetti');
$people[]=array('nome'=>'giuseppe','cognome'=>'verdi');
$people[]=array('nome'=>'mario','cognome'=>'rossi');
$people[]=array('nome'=>'fabio','cognome'=>'bianchi');

L’operazione da compiere è ordinare questo array, tenendo conto del valore assegnato alla chiave “cognome” di ciascun elemento, nel classico ordine alfabetico. Sarà la funzione di comparazione a fare in modo che i cognomi risultino in fila dalla a alla z

function compareSurname($a, $b){
    return $a['cognome'] <=> $b['cognome'];
}

Ora posso invocare usort

usort($people,'compareSurname');

Ora, dato che il mio array è in ordine in base al valore della chiave “cognome”, posso effettuare un ciclo foreach per stampare gli elementi di una lista di persone

echo '<ul>';
foreach ($people as $arr){
    echo '<li>'.$arr['nome']." ".$arr['cognome'].'</li>';
}
echo '</ul>';

Nel browser vedrò questo

  • fabio bianchi
  • andrea ronchetti
  • mario rossi
  • giuseppe verdi

Operatore null (??)

L’operatore null ha lo scopo di controllare se una variabile è settata. Viene usato in modo analogo a un operatore ternario e verifica la stessa condizione di isset().
$a=$b ?? false;
PHP tenta di assegnare a $a il valore di $b. Nel caso che $b sia null o inesistente $a prenderà il valore false. L’espressione significa “a $a assegna il valore di $b, se $b non esiste o è nulla, assegna false”.

In questo esempio troviamo un codice di una pagina web, contenente un form comunissimo di inserimento dati anagrafici, preceduto dal codice php per la validazione e la registrazione dei dati in un database. Il motivo per cui questa parte precede la stampata dell’html sta nella possibilità di compilare, eventualmente, i campi del form con i valori inseriti.

<?php
 //variabili per i dati dell'utente
 $nome="";
 $cognome="";
 $ragione="";
 $sesso="";

//messaggio con esito dopo elaborazione dati
 $messaggio="";

//verifica di un eventuale invio dei dati
 if(isset($_POST['action'])){

    //presa dei dati
    $nome=trim($_POST['nome']);
    $cognome=trim($_POST['cognome']);
    $ragione=trim($_POST['ragione']);
    $sesso=($_POST['sesso'] ?? $sesso);

//validazione dei dati prima di inserimento nel db
    if($nome==""){
        $messaggio.='<p>Inserire il nome.</p>';
    }
    if($cognome==""){
        $messaggio.='<p>Inserire il cognome.</p>';
    }
    if($sesso==""){
       $messaggio.='<p>Indicare il sesso.</p>';
    }

//se $messaggio è vuota non ci sono stati errori
    if($messaggio==""){
       //registrazione nel db dei dati
       $messaggio='Registrazione completata correttamente.';
    }

}

?>
 
<form method="post" action="">

    <p>Nome <input type="text" name="nome" value="<?php echo $nome; ?>"></p>
    <p>Cognome <input type="text" name="cognome" value="<?php echo $cognome; ?>"></p>
    <p>Ragione sociale <input type="text" name="ragione" value="<?php echo $ragione; ?>"></p>
    <p> 
        Sesso
        M <input type="radio" name="sesso" value="M" <?php if($sesso=="M"){ echo "checked";} ?> >
        F <input type="radio" name="sesso" value="F" <?php if($sesso=="F"){ echo "checked";} ?> >
    </p>

    <input type="submit" name="action" value="invia">

</form>

<?php echo $messaggio; ?>

Se son stati inviati dei dati saranno memorizzati nelle variabili preposte.

Per $nome, $cognome e $ragione non è necessario verificare il settaggio delle relative chiavi in $_POST, visto che quei valori saranno comunque inviati dal browser, che può prenderli in ogni caso da input testuali, anche se non compilati; discorso diverso per ‛sesso’, che viene impostata solamente se l’utente ha selezionato uno dei due radio-button che impostano quella chiave. Sarà quindi essenziale controllarne il settaggio, onde evitare la stampa di errori, e ci si serve dell’operatore null per dire al sistema di impostare a $sesso il suo valore se $_POST[‛sesso’] non esiste.

Successivamente si può procedere alla validazione dei dati obbligatori, prima del loro inserimento nel database, che avverrà in assenza di messaggi di errore generati da ogni assenza di dato obbligatorio.

Il messaggio con l’esito della procedura verrà stampato subito dopo il form.

Tipi di ritorno e argomenti tipizzati delle funzioni e dei metodi

Con PHP 7 lo sviluppatore ha la facoltà di definire i tipi di dati, passati come argomenti, e di ritorno delle funzioni e dei metodi (type hinting scalare).
In altre parole è possibile dichiarare un argomento o un tipo di ritorno con int, float, bool, string, per stabilire, rispettivamente, se questo valore deve essere un numero intero, un numero a virgola mobile, una stringa oppure booleano.
Tuttavia, affinché Apache non permetta un ritorno o un settaggio diverso da quello stabilito, occorre impostare, prima di qualsiasi istruzione da eseguire, la modalità di controllo delle variabili in strict-mode, con questa dichiarazione: declare(strict_types=1);
In mancanza di questa direttiva PHP continuerà ad accettare tipi di ritorno e di argomenti diversi da quelli previsti.
Guardiamo questo codice:

class Computer{

    private $type, $system, $nYears, $used;

    function __construct(string $type, string $system, int $nYears, bool $used){
        $this->type=$type;
        $this->system=$system;
        $this->nYears=$nYears;
        $this->used=$used;
    }

}

La classe Computer definisce un computer con quattro proprietà, ossia $type, $system, $nYears, $used. $type definisce se si tratta di un PC o di un Mac, $system il sistema operativo, $nYears in numero di anni che ha e $used se il computer è usato.
Questi valori andranno passati tassativamente mediante il costruttore, che si serve di argomenti tipizzati.

Il tipo di computer va settato con una stringa, così come il sistema operativo, mentre per il numero di anni è necessario un intero, invece lo stato di usato necessita di true o false. Si può facilmente capire che con un numero non posso definire il tipo di computer, così come il numero di anni è più utile averlo come intero, per poter fare eventuali calcoli, che non come stringa.

In un altro file viene incluso quello con la classe Computer, così da poter istanziare un oggetto relativo.

<?php
declare(strict_types=1);
include 'computerclass.php';

$mycomputer=new Computer('Mac','Finder',1,true);
?>

$mycomputer diventa un nuovo oggetto di tipo Computer; si tratta di un Mac, con sistema operativo Finder, con un anno di vita e già usato.

Se, ponendo, come primo argomento passo 1, essendo impostata la modalità strict-type, il sistema generà errore, cosa che non avviene senza la prima istruzione. Questo garantisce che non possa essere impostato un numero intero come tipo di computer.

Dicevamo che le funzioni possono anche avere un tipo di ritorno.
In quest’altro caso c’è una semplice funzione scritta per stabilire se un numero intero è pari o dispari. Restituisce true, se il numero passato è pari, altrimenti false.

<?php

function isPar(int $n):bool{
    return $n%2==0;
}

echo isPar(4);
//stamperà 1

?>

Questa funzione restituisce un valore booleano.

Una precisazione va fatta sui tipi di variabile: nell’esempio dell’oggetto abbiamo detto che se come primo argomento arriva un numero, anziché una stringa, senza la modalità strict non verranno generati errori; nel secondo caso, invece, se passa una stringa, con caratteri differenti dalle cifre, l’errore ci sarà comunque. Questo avviene perché PHP, pur essendo debolmente tipizzato, può, ad esempio, prendere un valore intero e trattarlo come stringa ma non viceversa. Una stringa può essere trasformata in un intero solo se contiene esclusivamente cifre, quindi un numero trattabile come tale.

Altra considerazione. isPar() restituisce un booleano in ogni caso; quindi anche qualora debba restituire una stringa o un numero darà sempre 1 o 0 perché, in generale, tutti i valori possono essere considerati true o false: 0 “0” “” saranno false; gli atri true.
Per capire meglio questo concetto consultare la tabella dei tipi di variabile sulla documentazione ufficiale di PHP (capitolo “Behaviour of weak type checks”).

Classi anonime

Fino alla versione 5.6 in PHP, dove sono previsti oggetti class-based, per istanziare un oggetto era obbligatorio creare una classe a cui questo doveva appartenere.
Con la 7, invece, abbiamo la possibilità di creare l’oggetto e assegnarlo a una classe anonima, contemporaneamente.
Per creare, ad esempio, un oggetto di tipo Penna, prima di PHP 7 avremmo dovuto creare una classe penna, in questo modo

class Penna{
    public function write($w){
        echo $w;
    }
}
//Codice per scrivere con la penna
$penna=new Penna();
$penna->write(‛Ho scritto con una penna’);

Ora è ammessa anche questa procedura

$penna=new class{
    public function write($w){
        echo $w;
    }
};

$penna->write(‛Ho scritto con una penna’);

A $penna viene assegnato un oggetto appartenente a una classe, creata al momento, che prevede il metodo write(), avente lo scopo di stampare una stringa.

Anche le classi anonime possono estendere altre classi e implementare interfacce.

Rimanendo nel caso della penna essa è uno strumento per scrivere, quindi la sua classe estende StrumentoPerScrivere; una classe genitore avente come proprietà il colore del tratto generato, di default nero.

class StrumentoPerScrivere{
    public $color="#000";
}

Adesso viene istanziato l’oggetto penna, avente una classe anonima che estende StrumentoPerScrivere.

$penna=new class extends StrumentoPerScrivere{
    public function write($w){
        echo '<span style="color:'.$this->color.'">'.$w.'</span>';
    }
};

Il metodo write(), in questo caso, stampa una porzione di stringa tinteggiata col colore della proprietà color, ereditata dalla classe genitore. Ovviamente il colore può essere cambiato tranquillamente, settando la proprietà in questione, che è pubblica.

Engine exception per la gestione dell’incoerenza dei tipi di variabile

Nel caso in cui a una variabile venga passato un valore di tipo diverso da quello previsto, ma comunque compatibile per PHP, si genera un catchable fatal error, un errore grave, ma gestibile tramite try-catch, l’EngineException error.

Personalmente sono convinto che è sempre meglio gestire queste operazioni in altri modi, controllando esplicitamente i tipi di dati passati, con le funzioni adatte. Anche perché se al posto di un intero, ad esempio, arriva una stringa contenente anche un solo carattere differente dalle cifre, l’errore non è catturabile e quest’eccezione non può essere sollevata.

Nuovo JSON parser

Il Json parser non ha un codice sorgente aperto, così Linux e Debian, rimasti esclusi, si sono dotati di altri parser, come Json-C (Per approfondimento PHP7 replaces non-free JSON extension).
Per uniformare l’elaborazione dei dati, inviati con Json, gli sviluppatori di PHP hanno pensato di adottare un nuovo parser open-source, Jsond-RFC, creato da Jakub Zelenka.
In questo modo dovrebbe essere garantita la compatibilità tra i vari sistemi operativi.

Share This