בנאי ב-PHP הוא מתודה בשם __construct(). PHP קוראת לה אוטומטית כשכותבים new ClassName(...), והארגומנטים שבסוגריים הופכים לפרמטרים שלה, כך שהאובייקט מתחיל את חייו כשהערכים שלו כבר מוגדרים.
בלי בנאי הייתם יוצרים אובייקט ריק ומגדירים כל מאפיין אחר כך, ושום דבר לא היה מונע מכם לשכוח אחד. עם בנאי, new User() בלי ארגומנטים הוא ArgumentCountError.
קידום מאפיינים בבנאי
הצהרה על מאפיין, רישום שלו כפרמטר והצבה של $this->name = $name חוזרות על כל שם שלוש פעמים. מאז PHP 8.0, מילת מפתח של רמת גישה לפני פרמטר בבנאי עושה את שלושתן בבת אחת:
פרמטר מקודם יכול להיות גם readonly (public readonly string $id), וזו הדרך הקצרה ביותר לכתוב אובייקט ערך שאינו ניתן לשינוי. אפשר לערבב פרמטרים מקודמים ורגילים, וגוף הבנאי עדיין יכול להכיל קוד שרץ אחרי שהמאפיינים המקודמים הוצבו.
ערכי ברירת מחדל וארגומנטים בשם
פרמטרים של בנאי מקבלים ברירות מחדל כמו כל פונקציה. פרמטרים עם ברירות מחדל באים אחרי פרמטרי החובה. עם ארגומנטים בשם (PHP 8.0) הקורא יכול לדלג ישר לאפשרות שהוא רוצה במקום לחזור על כל ברירת מחדל שלפניה:
הריצו את זה: הפלט מכיל HTML, ולכן הלשונית Page מציגה את שלושת הכפתורים, והלשונית Output מציגה את התגיות ש-PHP הדפיסה. שימו לב לסוגריים ב-(new Button('Save'))->render(): מאז PHP 8.4 הם אופציונליים, אבל PHP 8.3 צריכה אותם.
אימות ארגומנטים בבנאי
בנאי לא יכול להחזיר false או קוד שגיאה: מה שהוא מחזיר נזרק. כדי לסרב לקלט לא תקין, זרקו חריגה. האובייקט אז לא נוצר לעולם, ולכן שאר התוכנית יכולה לסמוך על כל Temperature שהיא מקבלת.
העמוד try catch מסביר בפירוט רב יותר איך תופסים את החריגות האלה.
כמה בנאים עם מתודות factory סטטיות
אין ב-PHP העמסת בנאים: מחלקה אחת, __construct אחד. כשאפשר לבנות אובייקט מקלטים שונים (מחרוזת תאריך, מערך, חותמת זמן), הוסיפו מתודות סטטיות עם שם שמכינות את הארגומנטים וקוראות ל-new static(...). הפיכת הבנאי ל-private מכריחה את כולם לעבור דרך ה-factories.
new Color(1, 2, 3) מחוץ למחלקה זורק עכשיו Error ("Call to private Color::__construct() from global scope"). העמוד static מסביר למה משתמשים כאן ב-new static ולא ב-new self.
קריאה לבנאי של האב עם parent::__construct
כשמחלקת בת מגדירה בנאי משלה, הבנאי של האב לא רץ מעצמו. קראו לו עם parent::__construct(...), בדרך כלל ראשון, כדי שהמאפיינים של האב יוגדרו לפני שהבת מוסיפה את שלה:
אם לבת אין בנאי בכלל, PHP משתמשת בזה של האב, ו-new Truck('Volvo') הייתה מעבירה את הארגומנטים שלה ישירות ל-Vehicle::__construct. עוד על כך בירושה.
שכחת parent::__construct
הטעות הזו לא מפיקה שגיאה בשורה שבה היא קורית. בנאי הבת רץ, המאפיינים עם הטיפוס של האב נשארים לא מאותחלים, והתוכנית נכשלת מאוחר יותר, בכל מקום שבו קורית הקריאה הראשונה:
הוסיפו parent::__construct(); כשורה הראשונה של Post::__construct והריצו שוב: זה מדפיס את התאריך. מתודה קשורה, __destruct(), רצה כשההפניה האחרונה לאובייקט נעלמת או כשהסקריפט מסתיים. בקוד יומיומי צריך אותה לעיתים רחוקות, כי PHP סוגרת קבצים ומשחררת זיכרון בעצמה בסוף כל בקשה.
שאלות נפוצות
מה זה __construct ב-PHP?
__construct() הוא הבנאי: מתודה ש-PHP קוראת לה אוטומטית מיד אחרי ש-new יוצר את האובייקט. הארגומנטים שמעבירים ל-new User('Ada', 36) מועברים ל-__construct(string $name, int $age), שבדרך כלל שומרת אותם במאפיינים.
מה זה קידום מאפיינים בבנאי ב-PHP?
מאז PHP 8.0, מילת מפתח של רמת גישה על פרמטר בבנאי מצהירה על המאפיין ומציבה אותו בצעד אחד: public function __construct(private string $name) {} זהה להצהרה private string $name; ולכתיבת $this->name = $name; בגוף.
האם למחלקה ב-PHP יכולים להיות כמה בנאים?
לא. אין ב-PHP העמסת מתודות, ולכן למחלקה יש __construct אחד. השתמשו בפרמטרים אופציונליים עם ברירות מחדל, או הוסיפו מתודות factory סטטיות כמו public static function fromArray(array $data): static שקוראות ל-new static(...) עם הארגומנטים הנכונים.
האם הבנאי של מחלקת האב נקרא אוטומטית ב-PHP?
רק כשלמחלקת הבת אין בנאי משלה. אם מחלקת הבת מגדירה __construct, הבנאי של האב לא רץ אלא אם קוראים ל-parent::__construct(...) בעצמכם, בדרך כלל בשורה הראשונה.
האם בנאי ב-PHP יכול להחזיר ערך?
לא. new תמיד נותן לכם את האובייקט, ומה ש-__construct מחזירה נזרק. כדי לדווח על בעיה, זרקו חריגה מהבנאי, למשל throw new InvalidArgumentException('Age must be positive').