Menu

ממשקים ב-PHP: implements, קבועים ודוגמאות

ממשק ב-PHP מפרט מתודות שמחלקה מבטיחה שיהיו לה: interface Shape { public function area(): float; }, ו-class Circle implements Shape חייבת אז להגדיר את area(). למדו רמזי טיפוס עם ממשקים, מימוש של כמה ממשקים, קבועי ממשק, הרחבת ממשקים, ממשקים מובנים כמו Countable, וממשק מול מחלקה מופשטת.

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

ממשק ב-PHP מצהיר על מתודות שמחלקה מבטיחה לספק, בלי שום קוד: interface Shape { public function area(): float; }. מחלקה שכותבת implements Shape חייבת להגדיר את area(), וכל פונקציה שמשתמשת ברמז הטיפוס Shape תקבל את המחלקה הזו.

describe() לא יודעת ולא אכפת לה איזו מחלקה היא מקבלת. היא מסתמכת רק על ההבטחה שהממשק נותן, ולכן Triangle implements Shape חדשה עובדת איתה בלי לשנות שורה.

תכנות מול ממשק

הסיבה הרגילה לכתוב ממשק היא לאפשר לקוד להיות תלוי ביכולת ולא במחלקה מסוימת. כאן שירות הזמנות מקבל כל Notifier, כך שאותו קוד יכול לשלוח אימייל בפרודקשן ולאסוף הודעות בבדיקה:

מימוש של כמה ממשקים

מחלקה מרחיבה לכל היותר אב אחד, אבל היא יכולה לממש כמה ממשקים שהיא צריכה. רשמו אותם אחרי implements, מופרדים בפסיקים, והגדירו את המתודות של כולם:

JsonSerializable הוא אחד הממשקים המובנים של PHP: מימוש שלו קובע מה json_encode() מפיקה עבור האובייקט.

קבועי ממשק

ממשק יכול להצהיר על קבועים. כל מחלקה שמממשת אותו יורשת אותם, ואפשר לקרוא אותם דרך שם הממשק או שם המחלקה:

לממשקים לא יכולים להיות מאפיינים ב-PHP 8.3, רק מתודות וקבועים. המתודות יכולות להיות סטטיות: public static function fromArray(array $data): static; בממשק מחייבת כל מחלקה להגדיר את המתודה הסטטית הזו. לקבוצה קבועה של ערכים קשורים, enum מתאים לעיתים קרובות יותר מרשימה של קבועי ממשק.

ממשק יכול להרחיב ממשקים אחרים

גם ממשקים משתמשים ב-extends, ובניגוד למחלקות הם יכולים להרחיב כמה בבת אחת. מחלקה שמממשת את ממשק הבת חייבת להגדיר את המתודות של כל השרשרת:

ממשקים מובנים: Countable, IteratorAggregate, Stringable

PHP מגיעה עם ממשקים שמחברים אובייקטים לשפה. Countable גורם ל-count($obj) לעבוד, IteratorAggregate גורם ל-foreach לעבוד, ו-Stringable ממומש אוטומטית על ידי כל מחלקה עם מתודת __toString():

שכחה של מתודה

אם מחלקה מצהירה שהיא מממשת ממשק אבל מחסירה מתודה, PHP נעצרת עם שגיאה קטלנית שמציינת את המתודה החסרה:

הוסיפו מתודת total() והסקריפט ירוץ. מחלקה מופשטת רשאית להשאיר מתודות של ממשק לא מוגדרות, וזה פתח המילוט שהודעת השגיאה מציעה.

ממשק מול מחלקה מופשטת

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

ממשקמחלקה מופשטת
גוף של מתודותאין (רק חתימות)מתודות מופשטות וגמורות
מאפייניםלאכן
בנאיאפשר להצהיר על אחד, לעיתים רחוקות שימושיכן, עם קוד
רמת גישה של מתודותpublic בלבדpublic, protected, private
קבועיםכןכן
כמה למחלקהכל מספר (implements A, B, C)אחת (extends Base)
שימוש טיפוסייכולת: Exportable, Countableקוד משותף למשפחה: Model, Controller

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

שאלות נפוצות

מה זה ממשק ב-PHP?

רשימה של חתימות מתודות ציבוריות בלי גוף: interface Logger { public function log(string $msg): void; }. מחלקה שכותבת implements Logger חייבת להגדיר כל מתודה שבו, וכל קוד שמשתמש ברמז הטיפוס Logger מקבל את המחלקה הזו.

האם מחלקה ב-PHP יכולה לממש כמה ממשקים?

כן. רשמו אותם אחרי implements, מופרדים בפסיקים: class File implements Readable, Writable, Countable. המחלקה חייבת להגדיר את המתודות של כולם. כך PHP מכסה את מה שירושה מרובה עושה בשפות אחרות.

מה ההבדל בין ממשק למחלקה מופשטת ב-PHP?

ממשק מצהיר רק על מתודות ציבוריות (ועל קבועים); אין בו מאפיינים ואין בו קוד של מתודות, ומחלקה יכולה לממש הרבה ממשקים. מחלקה מופשטת יכולה להחזיק מאפיינים, בנאים ומתודות גמורות ליד המופשטות שלה, אבל מחלקה יכולה להרחיב רק אחת. השתמשו בממשק ליכולת ("ניתן לייצוא"), ובמחלקה מופשטת לקוד משותף במשפחה של מחלקות.

האם לממשק ב-PHP יכולים להיות מאפיינים?

לא ב-PHP 8.3: ממשק יכול להכיל חתימות מתודות וקבועים (const VERSION = 2;), אבל לא מאפיינים. הצהרות על מאפיינים בממשקים (property hooks) הגיעו רק ב-PHP 8.4.

איך בודקים אם אובייקט מממש ממשק ב-PHP?

השתמשו ב-instanceof: $obj instanceof Countable הוא true כשהמחלקה של האובייקט מממשת את הממשק, ישירות או דרך מחלקת אב. class_implements($obj) מחזירה את השמות של כל הממשקים שהמחלקה מממשת.

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

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

להתחיל