Novità in PHP 7

In questo articolo riassumerò le principali funzionalità introdotte in PHP 7.

Maico Orazio
weBeetle
8 min readMar 21, 2023

--

PHP 7.x offre numerosi miglioramenti e nuove funzionalità che toccano tutti gli aspetti del linguaggio, incluso un migliore supporto per la programmazione orientata agli oggetti, estensioni a classi e interfacce, miglioramenti al sistema dei tipi, gestione degli errori e altro ancora. In questo articolo, discuteremo delle nuove funzionalità nelle varie versioni di PHP 7.x.

Classi anonime

RFC https://wiki.php.net/rfc/anonymous_classes

PHP 7.0 ha aggiunto il supporto per le classi anonime, adeguate per creare un’istanza anche per uso singolo. Le classi anonime sono proprio come le classi a tutti gli effetti in quanto possono estendere altre classi, implementare interfacce, definire argomenti nel costruttore, etc.

Di seguito un caso d’uso di classe anonima per gestire i messaggi per un log del server: definiamo una interfaccia LogMsg contenente una funzione setMsg(string $msg) che permette di impostare un messaggio di log. Inoltre, creiamo una classe ServerLog con i metodi getter / setter getLogMsg() e setLogMsg() per impostare un messaggio di log:

interface LogMsg {
public function setMsg(string $text);
}

class ServerLog {
private $logMsg;
public function getLogMsg(): LogMsg {
return $this->logMsg;
}
public function setLogMsg(LogMsg $logMsg) {
$this->logMsg = $logMsg;
}
}

$serverLog = new ServerLog;
$serverLog->setLogMsg(new class implements LogMsg {
public function setMsg(string $msg) {
echo $msg;
}
});
var_dump($serverLog->getLogMsg());

L’esempio appena fatto, crea un’istanza della classe ServerLog e invoca il metodo setLogMsg che ha come argomento una classe anonima che implementa l’interfaccia LogMsg. La funzione var_dump visualizza un riferimento all'oggetto della classe anonima:

object(class@anonymous)#2 (0) { }

Se non avessimo usato una classe anonima, avremmo dovuto fornire una definizione di classe completa che implementasse l’interfaccia LogMsg.

Tutti gli oggetti istanziati della stessa dichiarazione di classe anonima sono istanza di quella stessa classe e identici tra loro.

Come accennato in precedenza, una classe anonima può estenderne un’altra usando la parola chiave extends. Come ci si può aspettare, un argomento può essere passato a un costruttore della classe anonima.

class LogMsg {
private $msg;
public function getMsg() {
return $msg;
}
}
class ServerLog {
private $logMsg;
public function getLogMsg(): LogMsg {
return $this->logMsg;
}
public function setLogMsg(LogMsg $logMsg) {
$this->logMsg = $logMsg;
}
}
$serverLog = new ServerLog;
$serverLog->setLogMsg(new class('Log Message') extends LogMsg {
public function __construct($msg) {
$this->msg = $msg;
}
public function setMsg(string $msg) {
$this->msg = $msg;
}
});
var_dump($serverLog->getLogMsg());

La funzione var_dump stampa il messaggio di log passato al costruttore della classe anonima e restituito da getLogMsg().

object(LogMsg@anonymous)#2 (2) {
["msg":"LogMsg":private]=>
NULL
["msg"]=>
string(11) "Log Message"
}

Una classe anonima può essere nidificata all'interno di un’altra classe ma non può utilizzare le funzioni o le proprietà protette o private della classe esterna. Per poterle utilizzare nella classe interna anonima, e’ necessario passare le proprietà come argomenti al costruttore della classe anonima.

class Outer {
private $a = 1;
public function inner() {
return new class($this->a) {
private $a;
public function __construct($a) {
$this->a = $a;
}
public function getFromOuter() {
echo $this->a;
}
};
}
}

Per stampare il valore della proprietà privata passato alla classe anonima interna, creare un’istanza della classe Outer e invocare la funzione inner(), che, a sua volta, crea la classe anonima e invoca la funzione getFromOuter() della classe anonima che restituisce il valore della proprietà privata:

echo (new Outer)->inner()->getFromOuter(); // 1

Per invocare una delle funzioni protette definite nella classe esterna, la classe interna anonima deve estendere la prima.

Operatore spread

RFC https://wiki.php.net/rfc/spread_operator_for_array

Fin dalla versione 5.6 abbiamo avuto la possibilità di usare la notazione a 3 punti per ottenere gli argomenti dalla funzione; in PHP 7.4 possiamo usare la stessa notazione per decomprimere e unire gli array.

// PHP 5.6
function dumpNames(...$names) {
var_dump($names);
}
dumpNames('Maico', 'Giovanni', 'Giuseppe');

L’ RFC incoraggia l’utilizzo in sostituzione di array_merge, in quanto dovrebbe essere più veloce perché l’operatore spread è un costrutto del linguaggio piuttosto che una funzione.

// PHP 5.6
$colors = ['red', 'blue'];
$newColors = array_merge(['yellow'], $colors);

// PHP 7.4
$newColors = ['yellow', ...$colors];

Funziona anche con più array:

$colors = ['red', 'blue'];
$colors2 = ['black'];
$newColors = ['yellow', ...$colors, ...$colors2];
var_dump($newColors); // ['yallow','red','blue', 'black']

e su array restituiti da una funzione:

function getColors() {
return ['red', 'blue'];
}
$newColors = ['yellow', ...getColors()];
var_dump($newColors); // ['yallow','red','blue']

Arrow functions

RFC https://wiki.php.net/rfc/arrow_functions_v2

Quando vogliamo usare la funzione array_map(), array_filter() o qualsiasi altra funzione che accetta una callback, dobbiamo creare una funzione e dare la callback come stringa o fornire una funzione anonima come argomento.
In PHP 7.4 abbiamo una nuova sintassi: le arrow functions, dette anche “short closures”:

// PHP 7.3
$aNames = array_map(function($oUser) {
return $oUser->name;
}, $aUsers);

// PHP 7.4
$aNames = array_map(fn($oUser) => $oUser->name, $aUsers);

La sintassi è la seguente:

fn(params) => expression

Possono contenere solo un’espressione, che è sempre una istruzione di ritorno, anche se la parola chiave return non è presente.

Quando abbiamo una variabile nell'ambito corrente e vogliamo accedervi nella funzione anonima, abbiamo bisogno di utilizzare l’operatore use; in PHP 7.4 possiamo magicamente accedere alla variabile grazie alle arrow functions, il cui ambito è ereditato dalla funzione padre: la variabile verrà acquisita in modo implicito per valore.

$PI = 3.14;

// PHP 7.3
$numbers = [1, 2, 3];
$result = array_map(function($n) use ($PI) {
return $PI * $n * $n;
}, $numbers);

// PHP 7.4
$result = array_map(fn($n) => $PI * $n * $n, $numbers);

Tipi annullabili

PHP 7.1 ha aggiunto il supporto per i tipi di parametro e tipi restituiti nullable. È necessario prefissare un parametro o restituire un tipo con un ? per renderlo annullabile.

Utilizzo di void come tipo restituito per le funzioni

In PHP 7.0 è stato aggiunto il supporto per le dichiarazioni del tipo di ritorno, mentre PHP 7.1 ha introdotto il tipo void di ritorno.

In una funzione che restituisce void, l’istruzione return dovrebbe essere vuota o omessa del tutto. NULL non va confuso con il tipo void. NULL è un valore per il tipo null, mentre void implica l’assenza di un valore.

Nuovo tipo iterable

Il tipo iterable è un nuovo tipo composto introdotto in PHP 7.1 e può essere utilizzato come tipo di parametro o restituito. Accetta un array o un oggetto che implementa l’interfaccia Traversable, entrambi possono essere iterati usando foreach.

// array iterable
function iterator(iterable $iter) {
foreach ($iter => $value) {
echo $value;
}
}

$languages = ['Java', 'PHP', 'JavaScript'];
iterator($languager);

// object iterable
class Languages implements IteratorAggregate {
public $lang1 = 'Java';
public $lang2 = 'PHP';
public $lang3 = 'JavaScript';

public function __constructor() {
}

public function getIterator() {
return new ArrayIterator($this);
}
}

Proprietà tipizzate

RFC (https://wiki.php.net/rfc/typed_properties_v2)

PHP ha introdotto per la prima volta il suggerimento sui tipi nella versione 5.0 per funzioni e metodi di classe, e ha continuato a migliorare la funzionalità. PHP 7.0 ha introdotto le dichiarazioni del tipo restituito. PHP 7.4 consente agli sviluppatori di dichiarare i tipi per le proprietà delle classi.

Se hai una proprietà age dichiarata all'interno della classe User e assegni una stringa a questa proprietà, riusciamo a farlo senza problemi in PHP 7.3 perché non è specificato che dovrebbe essere un intero.
In PHP 7.4 possiamo specificare il tipo quando dichiariamo una proprietà e se provi ad assegnare una stringa genera un errore di tipo fatal error.

Ora PHP 7.4 supporta i seguenti tipi sulle proprietà, che richiedono un modificatore di accesso (public, protected, private):

  • boolean
  • int
  • float
  • string
  • array
  • iterable
  • object
  • ? (nullable)

Alcune delle loro caratteristiche sono:

  • i tipi possono essere dichiarati anche sulle proprietà static
  • sono supportati i riferimenti
  • sono interessate dalla direttiva strict_type
  • i tipi possono essere usati con la notazione var
  • è possibile dichiarare i valori predefiniti
  • il tipo di più proprietà può essere dichiarato in un’unica dichiarazione
  • il tipo per una proprietà annullabile può essere dichiarato
  • una proprietà di classe di tipo callable o void non può essere dichiarata.

Per dimostrare l’uso delle proprietà insieme alla modalità di tipo rigoroso, considera lo script seguente che imposta la direttiva strict_types a 1 e dichiara una proprietà di classe chiamata $age di tipo int.

declare(strict_types=1);

class User {
public int $age;
}

$user = new User();
$user->age = "39";

Se proviamo, come da codice sopra, ad assegnare una stringa alla proprietà dichiarata di tipo intero, viene generato un TypeError.

Non inizializzata

Un’altra cosa da notare è che esiste un nuovo stato di variabile uninitialized.

class User {
public string $name;
}
$oUser = new User;

Anche se il valore di name non è una stringa, dopo aver creato un oggetto di User, PHP non genera un errore, lo fa solo quando si prova ad accedere alla proprietà name:

echo $oUser->name;
Fatal error: Uncaught Error: Typed property User::$name must not be accessed before initialization

Se $name non avesse un tipo, il suo valore sarebbe semplicemente null. Tuttavia, i tipi, come abbiamo visto sopra, possono essere annullabili, quindi non è possibile determinare se una proprietà tipizzata annullabile è stata impostata o semplicemente dimenticata.
Ecco perché è stato aggiunto lo stato “non inizializzato”.

L’utilizzo di unset su una proprietà tipizzata la renderà uninitialized, mentre la disimpostazione di una proprietà non tipizzata la renderà null.

Operatore di assegnazione di coalescenza nullo

RFC (https://wiki.php.net/rfc/null_coalesce_equal_operator)

Quando voglio verificare se una chiave è presente in un array di dati, o una proprietà di classe è null, possiamo utilizzare l’operatore di coalescenza nullo. Con PHP 7.4 é diventato ancora più interessante il suo utilizzo perché abbiamo una sintassi ancora più breve chiamata operatore di assegnazione di coalescenza nullo, che combina un controllo isset() con un operatore ternario.

$name = $data['name'] ?? 'Maico';
$data['name'] = $data['name'] ?? 'Maico';
$data['user']['name'] = $data['user']['name'] ?? 'Maico';

// PHP 7.4
$name = $data['name'] ?? 'Maico';
$data['name'] ??= 'Maico';
$data['user']['name'] ??= 'Maico';

Tutte le istruzioni sopra fanno la stessa cosa. Controllano se $data[‘name’] è impostato, se sì, recupera il valore e lo assegna a sinistra dell’operatore di assegnazione; altrimenti assegna il valore Maico.

Può essere molto utili quando la chiave è nidificata nell’array di dati:

$data['group']['users'][1]['name'] = $data['group']['users'][1]['name'] ?? 'Maico';

// PHP 7.4
$data['group']['users'][1]['name'] ??= 'Maico';

Eccezioni consentite in __toString()

RFC (https://wiki.php.net/rfc/tostring_exceptions)

In PHP 7.3 non è permesso lanciare eccezioni sotto il metodo __toString(), generando un errore fatale in php. Se ad esempio si cerca di convertire l’istanza di una classe in stringa, anche se l’istruzione si inserisce in un try/catch per catturate l’eccezione, l’errore generato non cambia:

class User {
public function __toString() {
throw new Exception("User can not be converted into string");
}
}

echo new User();

try {
echo new User();
} catch(Exception $e) {
echo $e->getMessage();
}

// Fatal error: Method User::__toString() must not throw an exception, caught Exception: ....

In PHP 7.4 vedi il messaggio dell’eccezione lanciata nel metodo __toString() e catturata dal try/catch.

In questo articolo, abbiamo discusso delle principali funzioni di PHP 7.

Ci sono molte altre funzionalità in questo aggiornamento: (https://www.php.net/manual/en/migration74.new-features.php)

Buon lavoro 👨‍💻

--

--

Maico Orazio
weBeetle

Senior Web Application Developer. I'm a software engineer, a passionate coder, and a web developer. I am a fan of technology. #php #symfony #javascript #reactjs