PHP 8.0: Νέα στους τύπους δεδομένων (2/4)
Μόλις κυκλοφόρησε η έκδοση 8.0 της PHP. Είναι γεμάτη από νέα χαρακτηριστικά, όπως καμία άλλη έκδοση πριν. Η εισαγωγή τους αξίζει τέσσερα ξεχωριστά άρθρα. Στο δεύτερο μέρος, θα ρίξουμε μια ματιά στους τύπους δεδομένων.
Ας πάμε πίσω στην ιστορία. Η εισαγωγή των
υποδείξεων κλιμακωτών τύπων ήταν μια
σημαντική καινοτομία στην PHP 7. Παραλίγο να
μην συμβεί. Ο Andreu Faulds, ο
συγγραφέας της έξυπνης λύσης, η οποία ήταν
πλήρως συμβατή προς τα πίσω και προαιρετική
χάρη στο declare(strict_types=1)
, απορρίφθηκε
σκληρά από την κοινότητα. Ευτυχώς, ο Anthony Ferrara
υπερασπίστηκε εκείνη την εποχή την ίδια και
την πρόταση, ξεκίνησε μια εκστρατεία και το
RFC πέρασε πολύ κοντά. Ουφ. Οι περισσότερες
από τις ειδήσεις στην PHP 8 είναι ευγενική
προσφορά του θρυλικού Nikita Popov και όλες πέρασαν
την ψηφοφορία σαν το μαχαίρι στο βούτυρο. Ο
κόσμος αλλάζει προς το καλύτερο.
Η PHP 8 φέρνει τους τύπους στην τελειότητα.
Η συντριπτική πλειοψηφία των σχολίων phpDoc
όπως @param
, @return
και @var
θα
αντικατασταθεί από εγγενή συμβολισμό. Αλλά
το πιο σημαντικό είναι ότι οι τύποι θα
ελέγχονται από τη μηχανή της PHP. Μόνο οι
περιγραφές δομών όπως το string[]
ή οι
πιο σύνθετες επισημάνσεις για το PHPStan θα
παραμείνουν στα σχόλια.
Ενωσιακοί τύποι
Οι ενωτικοί τύποι είναι μια απαρίθμηση δύο ή περισσότερων τύπων που μπορεί να δεχτεί μια μεταβλητή:
class Button
{
private string|object $caption;
public function setCaption(string|object $caption)
{
$this->caption = $caption;
}
}
Ορισμένοι τύποι ένωσης έχουν εισαχθεί
στην PHP και στο παρελθόν. Μηδενιζόμενοι
τύποι, για παράδειγμα. Όπως το ?string
,
το οποίο είναι ισοδύναμο με τον τύπο ένωσης
string|null
. Ο συμβολισμός με ερωτηματικά
μπορεί να θεωρηθεί συντομογραφία. Φυσικά,
λειτουργεί και στην PHP 8, αλλά δεν μπορείτε
να τη συνδυάσετε με κάθετες γραμμές. Έτσι,
αντί για ?string|object
πρέπει να γράψετε
string|object|null
. Επιπλέον, το iterable
ήταν πάντα ισοδύναμο με το array|Traversable
.
Ίσως εκπλαγείτε από το γεγονός ότι το
float
είναι επίσης ένας τύπος ένωσης,
καθώς δέχεται και το int|float
, αλλά
ρίχνει την τιμή στο float
.
Δεν μπορείτε να χρησιμοποιήσετε τους
ψευδότυπους void
και mixed
σε
ενώσεις, διότι αυτό δεν θα είχε νόημα.
Η Nette είναι έτοιμη για τύπους ένωσης. Στο
Schema, το Expect::from()
τους δέχεται, και οι παρουσιαστές τους
δέχονται επίσης. Μπορείτε να τους
χρησιμοποιήσετε σε μεθόδους render και action,
για παράδειγμα:
public function renderDetail(int|array $id)
{
...
}
Από την άλλη πλευρά, η αυτόματη καλωδίωση στο Nette DI απορρίπτει τους ενωτικούς τύπους. Μέχρι στιγμής, δεν υπάρχει καμία περίπτωση χρήσης όπου θα είχε νόημα ο κατασκευαστής να δέχεται είτε το ένα είτε το άλλο αντικείμενο. Φυσικά, αν εμφανιστεί μια τέτοια περίπτωση χρήσης, θα είναι δυνατόν να προσαρμοστεί ανάλογα η συμπεριφορά του δοχείου.
Οι μέθοδοι getParameterType()
,
getReturnType()
και getPropertyType()
στο
Nette\Utils\Reflection
ρίχνουν μια εξαίρεση
στην περίπτωση του τύπου union (δηλαδή στην
έκδοση 3.1– στην προηγούμενη έκδοση 3.0, αυτές
οι μέθοδοι επιστρέφουν για να διατηρήσουν
τη συμβατότητα με null).
mixed
Ο ψευδότυπος mixed
δέχεται
οποιαδήποτε τιμή.
Στην περίπτωση των παραμέτρων και των
ιδιοτήτων, έχει ως αποτέλεσμα την ίδια
συμπεριφορά με αυτή που θα είχαμε αν δεν
προσδιορίζαμε κανέναν τύπο. Ποιος είναι
λοιπόν ο σκοπός του; Να διακρίνει πότε ένας
τύπος απλώς λείπει και πότε είναι σκόπιμα
mixed
.
Στην περίπτωση των τιμών επιστροφής
συναρτήσεων και μεθόδων, ο μη προσδιορισμός
του τύπου διαφέρει από τη χρήση mixed
.
Σημαίνει το αντίθετο του void
, καθώς
απαιτεί η συνάρτηση να επιστρέψει κάτι. Μια
ελλιπής επιστροφή παράγει τότε ένα μοιραίο
σφάλμα.
Στην πράξη, θα πρέπει να το χρησιμοποιείτε σπάνια, επειδή χάρη στους τύπους ένωσης, μπορείτε να καθορίσετε την τιμή με ακρίβεια. Επομένως, είναι κατάλληλη μόνο σε μοναδικές περιπτώσεις:
function dump(mixed $var): mixed
{
// print variable
return $var;
}
false
Σε αντίθεση με το mixed
, μπορείτε να
χρησιμοποιήσετε το νέο ψευδότυπο false
αποκλειστικά σε τύπους ένωσης. Προέκυψε από
την ανάγκη περιγραφής του τύπου επιστροφής
των εγγενών συναρτήσεων, οι οποίες ιστορικά
επιστρέφουν false σε περίπτωση αποτυχίας:
function strpos(string $haystack, string $needle): int|false
{
}
Ως εκ τούτου, δεν υπάρχει τύπος true
.
Ούτε μπορείτε να χρησιμοποιήσετε μόνο το
false
, ούτε το false|null
, ούτε το
bool|false
.
static
Ο ψευδότυπος static
μπορεί να
χρησιμοποιηθεί μόνο ως τύπος επιστροφής
μιας μεθόδου. Λέει ότι η μέθοδος επιστρέφει
ένα αντικείμενο του ίδιου τύπου με το ίδιο
το αντικείμενο (ενώ το self
λέει ότι
επιστρέφει την κλάση στην οποία ορίζεται η
μέθοδος). Είναι εξαιρετικός για την
περιγραφή ρευστών διεπαφών:
class Item
{
public function setValue($val): static
{
$this->value = $val;
return $this;
}
}
class ItemChild extends Item
{
public function childMethod()
{
}
}
$child = new ItemChild;
$child->setValue(10)
->childMethod();
resource
Αυτός ο τύπος δεν υπάρχει στην PHP 8 και δεν
θα εισαχθεί στο μέλλον. Οι πόροι είναι ένα
κατάλοιπο από την εποχή που η PHP δεν είχε
αντικείμενα. Σταδιακά, οι πόροι πρόκειται
να αντικατασταθούν από αντικείμενα. Τελικά,
θα εξαφανιστούν εντελώς. Για παράδειγμα, η
PHP 8.0 αντικαθιστά τον πόρο image με το
αντικείμενο GdImage
και τον πόρο curl με
το αντικείμενο CurlHandle
.
Stringable
Είναι μια διεπαφή που υλοποιείται
αυτόματα από κάθε αντικείμενο με μια μαγική
μέθοδο __toString()
.
class Email
{
public function __toString(): string
{
return $this->value;
}
}
function print(Stringable|string $s)
{
}
print('abc');
print(new Email);
Είναι δυνατόν να δηλωθεί ρητά το
class Email implements Stringable
στον ορισμό της
κλάσης, αλλά δεν είναι απαραίτητο.
Nette\Utils\Html
αντικατοπτρίζει επίσης
αυτό το σχήμα ονοματοδοσίας υλοποιώντας τη
διεπαφή Nette\HtmlStringable
αντί της
προηγούμενης IHtmlString
. Αντικείμενα
αυτού του τύπου, για παράδειγμα, δεν
διαφεύγουν από το Latte.
Τύπος διακύμανσης, συνδιακύμανσης, συνδιακύμανσης
Η αρχή υποκατάστασης Liskov (LSP) δηλώνει ότι οι κλάσεις επέκτασης και οι υλοποιήσεις διεπαφών δεν πρέπει ποτέ να απαιτούν περισσότερα και να παρέχουν λιγότερα από τον γονέα. Δηλαδή, η μέθοδος-παιδί δεν πρέπει να απαιτεί περισσότερα ορίσματα ή να δέχεται στενότερο εύρος τύπων για παραμέτρους από τη γονέα και αντίστροφα, δεν πρέπει να επιστρέφει μεγαλύτερο εύρος τύπων. Μπορεί όμως να επιστρέφει λιγότερους. Γιατί; Επειδή διαφορετικά, η κληρονομικότητα θα έσπαγε. Μια συνάρτηση θα δεχόταν ένα αντικείμενο συγκεκριμένου τύπου, αλλά δεν θα είχε ιδέα ποιες παραμέτρους μπορεί να περάσει στις μεθόδους της και ποιους τύπους θα επέστρεφαν. Οποιοδήποτε παιδί θα μπορούσε να τη σπάσει.
Έτσι, στην OOP, η κλάση παιδί μπορεί:
- να δέχεται ένα ευρύτερο εύρος τύπων στις παραμέτρους (αυτό ονομάζεται αντιπαραβολή)
- να επιστρέφει ένα στενότερο εύρος τύπων (συνδιακύμανση)
- και οι ιδιότητες δεν μπορούν να αλλάξουν τύπο (είναι αναλλοίωτες)
Η PHP είναι σε θέση να το κάνει αυτό από την έκδοση 7.4 και όλοι οι νεοεισαχθέντες τύποι στην PHP 8.0 υποστηρίζουν επίσης contravariance και covariance.
Στην περίπτωση του mixed
, το παιδί
μπορεί να περιορίσει την τιμή επιστροφής σε
οποιοδήποτε τύπο, αλλά όχι το void
,
καθώς δεν αναπαριστά μια τιμή, αλλά μάλλον
την απουσία της. Το παιδί δεν μπορεί επίσης
να παραλείψει μια δήλωση τύπου, καθώς αυτό
επιτρέπει επίσης την απουσία τιμής.
class A
{
public function foo(mixed $foo): mixed
{}
}
class B extends A
{
public function foo($foo): string
{}
}
Οι ενωσιακοί τύποι μπορούν επίσης να επεκταθούν στις παραμέτρους και να περιοριστούν στις τιμές επιστροφής:
class A
{
public function foo(string|int $foo): string|int
{}
}
class B extends A
{
public function foo(string|int|float $foo): string
{}
}
Επιπλέον, το false
μπορεί να
επεκταθεί στο bool
στην παράμετρο ή
αντίστροφα το bool
στο false
στην
τιμή επιστροφής.
Όλες οι παραβάσεις κατά της συνδιακύμανσης/συνδιακύμανσης οδηγούν σε μοιραίο σφάλμα στην PHP 8.0.
Στα επόμενα μέρη αυτής της σειράς, θα δείξουμε ποια είναι τα χαρακτηριστικά, ποιες νέες συναρτήσεις και κλάσεις εμφανίστηκαν στην PHP και θα παρουσιάσουμε τον Just in Time Compiler.
Για να υποβάλετε ένα σχόλιο, συνδεθείτε