In che modo i nuovi tipi di intersezione in PHP 8.1 offrono maggiore flessibilità

0
168

I tipi di intersezione sono una nuova funzionalità del sistema di tipi in arrivo in PHP 8.1. Consentono di digitare valori di suggerimento che devono soddisfare più di un vincolo di tipo. PHP’s ha già tipi di unione che combinano tipi con un “or” logico clausola; i tipi di intersezione offrono un “and” clausola invece.

Alcuni sviluppatori già digitano le intersezioni utilizzando le annotazioni PHPDoc. L'aggiunta del supporto nativo è un passo avanti in quanto i tipi verranno effettivamente applicati in fase di esecuzione. Sono inoltre completamente compatibili con l'ereditarietà delle classi, contribuendo a ridurre il rischio di errori dovuti ad annotazioni obsolete o incompatibili.

Sintassi di base

L'intersezione la sintassi del tipo è simile ai tipi di unione:

class DemoClass {   public Countable|Stringable $Union;   public numerabile e stringabile $Intersezione;   }

In questo esempio, $Union sarà soddisfatto da qualsiasi valore che sia Countable o Stringable. $Intersection accetterà solo valori che soddisfano entrambi i vincoli. Otterrai un TypeError se assegni alla proprietà un valore che implementa uno o zero dei tipi suggeriti.

I tipi di intersezione sono supportati ovunque funzionino i typehints. Puoi usarli con definizioni di proprietà, parametri di funzione e valori restituiti.

A differenza dei tipi di unione, le intersezioni possono solo indicare interfacce e classi. Un'intersezione scalare come int&string non ha senso in quanto non potrebbe mai essere soddisfatta. Anche il tipo misto è bandito, perché ogni valore soddisferà il suo vincolo.

Varianza del tipo

I tipi di intersezione rispettano le regole di varianza esistenti di PHP. I tipi restituiti dai metodi sottoposti a override devono essere covarianti, mentre i tipi di parametro sono controvarianti. Le proprietà della classe sono sempre invarianti, quindi i figli non possono modificare la definizione del tipo a meno che la nuova firma non sia sia un sottotipo che un supertipo di quella ereditata.

Annuncio

Per i valori restituiti, le regole di varianza significa che puoi aggiungere ulteriori tipi di intersezione nei metodi sovrascritti. I metodi possono anche rimuovere, ma non aggiungere, tipi alle intersezioni usate come parametri. Queste regole applicano il principio di sostituzione di Liskov.

interfaccia I1 { metodo di funzione pubblica1(A&B $demo) : X&Y; metodo di funzione pubblica2(A&B $demo) : X&Y; }   l'interfaccia I2 estende I1 {   //PERMESSO metodo di funzione pubblica1(A $demo) : X&Y&Z;   //NON CONSENTITO metodo di funzione pubblica2(A&B&C $demo) : X;   }

Nel caso del metodo1, i vincoli non sono effettivamente cambiati. Stai affermando che il metodo sottoposto a override può effettivamente funzionare con qualsiasi A, anche se non è anche una X. È meno specifico, il che si traduce in una variazione del parametro accettabile. La dichiarazione di ritorno è più specifica, affermando che il valore implementerà X, Y e Z; in questo scenario, l'aggiunta di specificità non interrompe il contratto, poiché il valore sarà comunque accettato da qualsiasi cosa che suggerisca I1.

L'override del metodo2 viene interrotto in entrambi i casi. Richiedendo che il parametro di input soddisfi A, B e C, non è più un sostituto drop-in per I1. Allo stesso modo, il metodo2 garantisce solo che il valore restituito sarà un'istanza di X. Ciò interrompe il contratto poiché qualsiasi suggerimento I1 richiede che il valore soddisfi l'intersezione di X e Y.

Le intersezioni invertono le regole pratiche di varianza dei tipi di unione. Poiché le unioni vengono combinate utilizzando “or,” i bambini possono aggiungere tipi di parametri e rimuovere i tipi restituiti. Il principio di sostituzione di Liskov è soddisfatto quando l'effetto di allargamento e restringimento dei vincoli di tipo è invertito.

Come per i tipi di unione, le regole di varianza si applicano anche ai tipi che costituiscono un'intersezione – queste sono le singole parti X e Y di X&Y. Puoi restringere un tipo quando lo restituisci – affermando che ’restituirai una sottoclasse – o ampliarlo come parametro, accettando una superclasse.

Pubblicità

Le intersezioni possiedono anche alcune regole speciali sull'aliasing e sulle implementazioni concrete. Se digiti X&Y ma scrivi un'interfaccia Z estende X, Y, è logico che Z soddisfi il vincolo. Di conseguenza, puoi digitare Z invece di X&Y ovunque sia consentita la covarianza:

interfaccia Il test estende X, Y {}   interfaccia Demo1 { demo di funzione pubblica() : X&Y; }   interfaccia Demo2 { demo di funzione pubblica() : Test; }

Questo ti consente di digitare un suggerimento su una classe o un'interfaccia concreta se dipendi da funzionalità aggiuntive. È accettabile purché tutti i vincoli nell'intersezione siano soddisfatti dal typehint finale.

Quando usare i tipi di intersezione?

I tipi di intersezione sono per le volte in cui si vuole essere sicuri che un valore soddisfi un'interfaccia composta senza definire effettivamente quell'interfaccia. Le versioni precedenti di PHP non fornivano alcun supporto nativo per questo, quindi era necessario aggiungere un pasticcio di boilerplate extra alla base di codice:

interfaccia CountableStringable estende Countable, Stringable { //… }   class FakeArray implementa CountableStringable {   array pubblico $items = [];   conteggio delle funzioni pubbliche() : int { conteggio restituito($this -> articoli); }   funzione pubblica __toString() : stringa { ritorno implode(", ", $this -> articoli); }   }   class DemoClass {   public CountableStringable $Value;   }

Ciò porta a un eccesso di interfacce superficiali simili a stub per ottenere un comportamento di intersezione di base. Sebbene gli sviluppatori PHP siano sopravvissuti fino ad oggi senza intersezioni, la loro presenza aiuta a consolidare le capacità composte del sistema di tipi. L'utilizzo di intersezioni native crea un codice più pulito e intuitivo.

E i tipi compositi?

Non è possibile combinare i tipi di intersezione e unione nello stesso typehint. Sebbene sarebbe tecnicamente possibile, è stato omesso dall'attuale RFC in quanto vi sono ambiguità riguardo a sintassi, precedenza e varianza.

I tipi compositi rimangono un'idea per il futuro. Se fossero stati aggiunti, potresti iniziare ad aggiungere suggerimenti tipografici complessi come questo:

class DemoClass {   public Countable&Stringable|CountableStringable $Intersezione;   } Advertisement

Questa classe di esempio combina le due implementazioni di cui sopra. Se funzionasse, ti consentirebbe di adottare tipi di intersezione nativi mantenendo la compatibilità con le vecchie classi utilizzando il “falso” approccio.

Tipi composti a tutti gli effetti completerebbero la gestione dei tipi multipli facilitata da unioni e intersezioni. Nel frattempo, dovrai continuare a scrivere le tue interfacce composite in questi scenari.

Riepilogo

I tipi di intersezione arriveranno in PHP 8.1 e sbloccare possibilità più avanzate all'interno del sistema di tipi. L'estensione delle opzioni intorno ai tipi compositi riduce la quantità di codice che devi scrivere quando sono supportate più interfacce.

Le intersezioni sono una funzionalità opzionale che non introdurrà alcuna incompatibilità con il codice esistente. La RFC è stata implementata nella terza build alpha di PHP 8.1. La versione finale arriverà alla fine di novembre entro la fine dell'anno.