Menu

ירושה ב-PHP: extends, parent:: ודריסת מתודות

ב-PHP מחלקה יורשת ממחלקה אחרת עם class Dog extends Animal: היא מקבלת את המאפיינים והמתודות הציבוריים והמוגנים של האב, יכולה לדרוס אותם, ועדיין יכולה לקרוא למקור עם parent::method(). למדו דריסה, protected, final, instanceof ואת המלכודת של מתודה פרטית.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

ב-PHP מחלקה יורשת ממחלקה אחרת עם extends: class Dog extends Animal נותנת ל-Dog כל מאפיין ומתודה ציבוריים ומוגנים של Animal. הבת יכולה להוסיף מתודות חדשות, לדרוס מתודות שירשה על ידי הצהרה על מתודה עם אותו שם, ולקרוא למקור עם parent::method().

ל-Dog אין בנאי, ולכן היא משתמשת בזה של Animal, וזו הסיבה ש-new Dog('Rex') עובד. מחלקת בת יכולה להרחיב רק אב אחד; אין ב-PHP ירושה מרובה של מחלקות.

קריאה למתודה של האב עם parent::

דריסה מחליפה את המתודה של האב לגמרי. כדי להרחיב אותה במקום להחליף, קראו ל-parent::method() מהבת ובנו על התוצאה שלה:

אותו הדבר עובד לבנאים עם parent::__construct(...), שמוסבר בעמוד הבנאי, כולל מה שקורה כששוכחים אותו.

protected מול private במחלקות בת

חבר protected מוסתר מקוד חיצוני אבל גלוי לכל מחלקה במשפחה. חבר private גלוי רק במחלקה שמצהירה עליו, ולכן מחלקת בת לא רואה אותו בכלל:

isset($this->pin) הוא false בבת כי לבת אין גישה ל-$pin הפרטי של האב, בעוד המתודה של האב עצמו, hasPin(), עדיין קוראת אותו. השתמשו ב-protected למה שמחלקות בת אמורות לבנות עליו, וב-private לפרטים שאתם רוצים להשאיר חופשיים לשינוי.

פולימורפיזם ו-instanceof

קוד שנכתב מול טיפוס האב עובד עם כל בת. כל אובייקט מריץ את הגרסה שלו של מתודה שנדרסה, ולכן לולאה אחת מטפלת בכולם. instanceof בודק אם אובייקט שייך למחלקה או לאחת מבנותיה:

כשאסור להשתמש באב לבדו (Shape רגיל עם שטח 0 לא הגיוני במיוחד), הפכו אותו למחלקה מופשטת והצהירו על area() כמופשטת.

כללי דריסה: החתימות חייבות להישאר תואמות

מתודה דורסת חייבת לקבל לפחות את מה שהאב קיבל ולהחזיר משהו שהקוראים של האב יכולים להשתמש בו. אפשר להוסיף פרמטר אופציונלי, להרחיב טיפוס של פרמטר או לצמצם טיפוס החזרה; אי אפשר להוסיף פרמטר חובה או להפוך מתודה ציבורית לפרטית.

שנו את string $sep = ',' ל-string $sep חובה ו-PHP תסרב להדר את המחלקה עם שגיאה קטלנית ("Declaration of CsvExporter::export(array rows,stringrows, string sep): string must be compatible with Exporter::export(array $rows): string").

מחלקות ומתודות final

final עוצר את הירושה בנקודה הזו. מתודה final אי אפשר לדרוס, ו-final class אי אפשר להרחיב. שניהם נבדקים כשהקובץ מהודר, ולכן הסקריפט לא מתחיל בכלל:

<?php
class Payment
{
    final public function charge(int $cents): string
    {
        return "charged $cents";
    }
}

class FreePayment extends Payment
{
    public function charge(int $cents): string
    {
        return 'free';
    }
}
PHP Fatal error:  Cannot override final method Payment::charge() in /home/index.php on line 12

למחלקה final ההודעה היא Class B cannot extend final class A. סמנו מתודה כ-final כשהאב מסתמך על כך שהיא תתנהג בדיוק בדרך אחת, למשל מתודה שבודקת הרשאות לפני שהיא עושה כל דבר אחר.

המלכודת של מתודה פרטית

מתודה פרטית לא עוברת בירושה, ולכן מתודה בבת עם אותו שם לא דורסת אותה. קוד באב ממשיך לקרוא לגרסה הפרטית שלו, וזה מפתיע אנשים שמצפים שהמתודה של הבת תרוץ:

greet() עדיין מדפיסה Hello: ה-$this->word() של האב מתפרש כמתודה הפרטית של האב. שנו את private ל-protected ב-Greeter והריצו שוב; עכשיו המתודה של הבת היא דריסה אמיתית ו-greet() אומרת Bonjour.

שאלות נפוצות

איך extends עובד ב-PHP?

class Dog extends Animal הופכת את Dog לבת של Animal. לאובייקט Dog יש כל מאפיין ומתודה ציבוריים ומוגנים של Animal, הוא יכול להוסיף משלו, והוא יכול להחליף (לדרוס) מתודות שירש על ידי הצהרה על מתודה עם אותו שם.

איך קוראים למתודה של האב ב-PHP?

השתמשו ב-parent::methodName() בתוך המתודה של הבת, למשל return parent::describe() . ' and more';. לבנאי זה parent::__construct(...).

האם PHP תומכת בירושה מרובה?

לא. מחלקה יכולה להרחיב רק מחלקת אב אחת. כדי לשתף התנהגות מכמה מקורות, ממשו כמה ממשקים (class A implements B, C) והשתמשו ב-traits (use Loggable, Timestamps;).

מה המשמעות של final ב-PHP?

מתודה final אי אפשר לדרוס במחלקת בת, ו-final class אי אפשר להרחיב בכלל. הפרה של אחד הכללים היא שגיאה קטלנית כשהמחלקה מהודרת, למשל Cannot override final method A::f().

האם מחלקת בת יכולה לגשת למאפיין פרטי של האב ב-PHP?

לא. חבר private שייך רק למחלקה שמצהירה עליו, ולכן בבת $this->pin הוא מאפיין לא מוגדר (אזהרה, ואז null) ומתודה בבת עם אותו שם לא דורסת מתודה פרטית. הצהירו על החבר כ-protected כשמחלקות הבת צריכות אותו.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל