SmartObject
Το SmartObject βελτίωνε για χρόνια τη συμπεριφορά των αντικειμένων στην PHP. Από την έκδοση PHP 8.4, όλες οι λειτουργίες του έχουν ενσωματωθεί στην ίδια την PHP, ολοκληρώνοντας έτσι την ιστορική του αποστολή ως πρωτοπόρος του σύγχρονου αντικειμενοστρεφούς προγραμματισμού στην PHP.
Εγκατάσταση:
Το SmartObject δημιουργήθηκε το 2007 ως μια επαναστατική λύση στις ελλείψεις του τότε μοντέλου αντικειμένων της PHP. Σε μια εποχή που η PHP αντιμετώπιζε πολλά προβλήματα με τον αντικειμενοστρεφή σχεδιασμό, έφερε σημαντικές βελτιώσεις και απλοποίησε την εργασία των προγραμματιστών. Έγινε θρυλικό μέρος του framework Nette. Πρόσφερε λειτουργικότητα που η PHP απέκτησε πολλά χρόνια αργότερα – από την επικύρωση πρόσβασης σε ιδιότητες αντικειμένων μέχρι τον εξελιγμένο χειρισμό σφαλμάτων. Με την έλευση της PHP 8.4, ολοκλήρωσε την ιστορική του αποστολή, καθώς όλες οι λειτουργίες του έγιναν εγγενή μέρη της γλώσσας. Προηγήθηκε της εξέλιξης της PHP κατά αξιοσημείωτα 17 χρόνια.
Τεχνικά, το SmartObject πέρασε από μια ενδιαφέρουσα εξέλιξη. Αρχικά
υλοποιήθηκε ως κλάση Nette\Object
, από την οποία άλλες κλάσεις
κληρονομούσαν την απαιτούμενη λειτουργικότητα. Μια σημαντική αλλαγή
ήρθε με την PHP 5.4, η οποία έφερε την υποστήριξη για traits. Αυτό επέτρεψε τη
μετατροπή του σε trait Nette\SmartObject
, προσφέροντας μεγαλύτερη
ευελιξία – οι προγραμματιστές μπορούσαν να χρησιμοποιήσουν τη
λειτουργικότητα ακόμη και σε κλάσεις που ήδη κληρονομούσαν από άλλη
κλάση. Ενώ η αρχική κλάση Nette\Object
έπαψε να υπάρχει με την PHP 7.2 (η
οποία απαγόρευσε την ονομασία κλάσεων με τη λέξη ‚Object‘), το trait
Nette\SmartObject
συνεχίζει να υπάρχει.
Ας εξερευνήσουμε τις λειτουργίες που προσέφεραν το Nette\Object
και
αργότερα το Nette\SmartObject
. Κάθε μία από αυτές τις λειτουργίες
αποτελούσε ένα σημαντικό βήμα προόδου στον αντικειμενοστρεφή
προγραμματισμό της PHP.
Συνεπής Διαχείριση Σφαλμάτων
Ένα από τα πιο επείγοντα προβλήματα της πρώιμης PHP ήταν η ασυνεπής
συμπεριφορά κατά την εργασία με αντικείμενα. Το Nette\Object
έφερε
τάξη και προβλεψιμότητα σε αυτό το χάος. Ας δούμε πώς συμπεριφερόταν
αρχικά η PHP:
Το Fatal error τερμάτιζε την εφαρμογή χωρίς δυνατότητα αντίδρασης. Η
σιωπηλή εγγραφή σε μη υπάρχοντα μέλη χωρίς προειδοποίηση μπορούσε να
οδηγήσει σε σοβαρά σφάλματα που ήταν δύσκολο να εντοπιστούν. Το
Nette\Object
έπιανε όλες αυτές τις περιπτώσεις και έριχνε μια
εξαίρεση MemberAccessException
, επιτρέποντας στους προγραμματιστές να
αντιδρούν και να χειρίζονται αυτά τα σφάλματα:
Από την PHP 7.0, η γλώσσα δεν προκαλεί πλέον μη πιάσιμα fatal errors και από την PHP 8.2, η πρόσβαση σε μη δηλωμένα μέλη θεωρείται σφάλμα.
Βοήθεια „Did you mean?“
Το Nette\Object
ήρθε με μια πολύ βολική λειτουργία: έξυπνες
προτάσεις για τυπογραφικά λάθη. Όταν ένας προγραμματιστής έκανε λάθος
στο όνομα μιας μεθόδου ή μεταβλητής, όχι μόνο ανέφερε το σφάλμα αλλά
πρόσφερε και βοήθεια προτείνοντας το σωστό όνομα. Αυτό το εμβληματικό
μήνυμα, γνωστό ως „did you mean?“, εξοικονόμησε στους προγραμματιστές ώρες
αναζήτησης τυπογραφικών λαθών:
Ενώ η PHP δεν έχει καμία μορφή „did you mean?“, αυτή η λειτουργία παρέχεται τώρα από το Tracy. Μπορεί ακόμη και να διορθώνει αυτόματα τέτοια σφάλματα.
Ιδιότητες με Ελεγχόμενη Πρόσβαση
Μια σημαντική καινοτομία που έφερε το SmartObject στην PHP ήταν οι ιδιότητες με ελεγχόμενη πρόσβαση (properties). Αυτή η έννοια, κοινή σε γλώσσες όπως η C# ή η Python, επέτρεψε στους προγραμματιστές να ελέγχουν κομψά την πρόσβαση στα δεδομένα αντικειμένων και να διασφαλίζουν τη συνέπειά τους. Οι ιδιότητες είναι ένα ισχυρό εργαλείο αντικειμενοστρεφούς προγραμματισμού. Λειτουργούν ως μεταβλητές αλλά στην πραγματικότητα αντιπροσωπεύονται από μεθόδους (getters και setters). Αυτό επιτρέπει την επικύρωση εισόδων ή τη δημιουργία τιμών τη στιγμή της ανάγνωσης.
Για να χρησιμοποιήσετε τις ιδιότητες, πρέπει να:
- Προσθέσετε στην κλάση την επισήμανση
@property <type> $xyz
- Δημιουργήσετε getter με όνομα
getXyz()
ήisXyz()
, setter με όνομαsetXyz()
- Διασφαλίσετε ότι ο getter και ο setter είναι public ή protected. Είναι προαιρετικοί – άρα μπορούν να υπάρχουν ως ιδιότητες μόνο-ανάγνωσης ή μόνο-εγγραφής
Ας δούμε ένα πρακτικό παράδειγμα χρησιμοποιώντας την κλάση Circle, όπου
θα χρησιμοποιήσουμε ιδιότητες για να διασφαλίσουμε ότι η ακτίνα είναι
πάντα μη αρνητική. Θα αντικαταστήσουμε το public $radius
με μια
ιδιότητα:
Από την PHP 8.4, η ίδια λειτουργικότητα μπορεί να επιτευχθεί χρησιμοποιώντας property hooks, που προσφέρουν πολύ πιο κομψή και συνοπτική σύνταξη:
Extension Methods
Το Nette\Object
έφερε στην PHP άλλη μια ενδιαφέρουσα έννοια
εμπνευσμένη από σύγχρονες γλώσσες προγραμματισμού – τις extension methods.
Αυτή η λειτουργία, δανεισμένη από τη C#, επέτρεψε στους προγραμματιστές
να επεκτείνουν κομψά υπάρχουσες κλάσεις με νέες μεθόδους χωρίς να τις
τροποποιούν ή να κληρονομούν από αυτές. Για παράδειγμα, θα μπορούσατε
να προσθέσετε μια μέθοδο addDateTime()
σε μια φόρμα που προσθέτει ένα
προσαρμοσμένο DateTimePicker:
Οι extension methods αποδείχθηκαν μη πρακτικές επειδή οι επεξεργαστές δεν πρότειναν τα ονόματά τους και αντίθετα ανέφεραν ότι η μέθοδος δεν υπάρχει. Επομένως, η υποστήριξή τους διακόπηκε. Σήμερα, είναι πιο συνηθισμένο να χρησιμοποιείται σύνθεση ή κληρονομικότητα για την επέκταση της λειτουργικότητας των κλάσεων.
Λήψη Ονόματος Κλάσης
Το SmartObject πρόσφερε μια απλή μέθοδο για τη λήψη του ονόματος της κλάσης:
Πρόσβαση σε Reflection και Annotations
Το Nette\Object
παρείχε πρόσβαση σε reflection και annotations μέσω των
μεθόδων getReflection()
και getAnnotation()
. Αυτή η προσέγγιση
απλοποίησε σημαντικά την εργασία με μετα-πληροφορίες κλάσεων:
Από την PHP 8.0, είναι δυνατή η πρόσβαση σε μετα-πληροφορίες μέσω attributes, τα οποία προσφέρουν ακόμη περισσότερες δυνατότητες και καλύτερο έλεγχο τύπων:
Method Getters
Το Nette\Object
πρόσφερε έναν κομψό τρόπο για να περνάμε μεθόδους
σαν να ήταν μεταβλητές:
Από την PHP 8.1, μπορείτε να χρησιμοποιήσετε το first-class callable syntax, το οποίο προχωράει αυτήν την έννοια ακόμη περισσότερο:
Συμβάντα (Events)
Το SmartObject προσφέρει απλοποιημένη σύνταξη για εργασία με συμβάντα. Τα συμβάντα επιτρέπουν στα αντικείμενα να ενημερώνουν άλλα μέρη της εφαρμογής για αλλαγές στην κατάστασή τους:
Ο κώδικας $this->onChange($this, $radius)
είναι ισοδύναμος με τον
ακόλουθο βρόχο:
Για λόγους σαφήνειας, συνιστούμε να αποφεύγετε τη μαγική μέθοδο
$this->onChange()
. Μια πρακτική εναλλακτική είναι η συνάρτηση Nette\Utils\Arrays::invoke: