פונקציה אנונימית, שנקראת גם closure, היא פונקציה בלי שם. כותבים function ($param) { ... } במקום שבו מצופה ערך: מציבים אותה במשתנה, מעבירים אותה לפונקציה אחרת או מחזירים אותה. היא יכולה לשאת משתנים מהקוד שמסביב עם use ($var).
כל פונקציה אנונימית היא אובייקט של המחלקה המובנית Closure, ולכן אפשר לשמור אותה, להעביר אותה הלאה ולקרוא לה מאוחר יותר עם $greet(...).
מילת המפתח use לוכדת לפי ערך
closure לא יכול לראות משתנים חיצוניים בעצמו. use ($x) מעתיקה את הערך של $x לתוך ה-closure ברגע שה-closure נוצר, לא כשהוא רץ:
use לפי הפניה: &$var
use (&$x) נותנת ל-closure את המשתנה עצמו ולא עותק. שינויים בתוך ה-closure נראים בחוץ, ושינויים בחוץ נראים בפנים. כך callback מוסיף לסכום או מחזיק מונה.
כל קריאה ל-makeCounter() יוצרת $count חדש, וה-closure שמוחזר שומר אותו בחיים אחרי שהפונקציה חזרה. לזה מתייחס המונח "closure": הפונקציה סוגרת על המשתנים שהיא צריכה.
העברת closure כ-callback
פונקציות כמו array_map(), array_filter(), usort() ו-array_walk() מקבלות פונקציה כארגומנט. closure מאפשר לכתוב אותה במקום, עם כמה פקודות שהעבודה צריכה:
כשהגוף הוא ביטוי יחיד, פונקציית חץ כמו fn($p) => $p['price'] קצרה יותר ולא צריכה use.
callables: שמות פונקציות, מתודות ו-strlen(...)
פרמטר עם הטיפוס callable מקבל יותר מ-closures. מחרוזת עם שם פונקציה, מערך עם אובייקט ושם מתודה, ומערך עם שם מחלקה ושם מתודה סטטית כולם נחשבים. מאז PHP 8.1, תחביר ה-first-class callable strlen(...) הופך כל אחד מהם ל-Closure ישירות.
הצורה (...) נבדקת כשהשורה רצה: שגיאת הקלדה זורקת Error בדיוק שם. מחרוזת כמו 'strtoupr' היא רק מחרוזת עד שמישהו מנסה לקרוא לה.
closures בתוך מחלקה: $this ו-static
ל-closure שנוצר בתוך מתודה יש $this מקושר אוטומטית, כך שהוא יכול להשתמש במאפיינים של האובייקט. סמנו אותו כ-static function כשהוא לא צריך את $this, וזה גם מונע ממנו להשאיר את האובייקט בחיים.
שכחת use
הבאג הנפוץ ביותר ב-closures הוא שימוש במשתנה חיצוני בלי לרשום אותו ב-use. בתוך ה-closure המשתנה לא קיים:
$minAge = 18;
$adults = array_filter($people, function ($p) {
return $p['age'] >= $minAge; // $minAge is not in use (...)
});
PHP prints: Warning: Undefined variable $minAge
$minAge הוא null בפנים, כל גיל שמושווה >= null הוא true, וכל אדם עובר את הסינון. בשרת שמסתיר אזהרות, שום דבר לא מודיע לכם. הוסיפו את המשתנה ל-use:
שאלות נפוצות
מה זה closure ב-PHP?
פונקציה בלי שם, שנוצרת עם function (...) { ... } ונשמרת במשתנה או מועברת כארגומנט. PHP מייצגת אותה כאובייקט של המחלקה Closure. היא יכולה לשאת משתנים מהטווח שבו היא נוצרה, שרשומים ב-use (...).
מה מילת המפתח use עושה ב-closure של PHP?
use ($x) מעתיקה את המשתנה החיצוני $x לתוך ה-closure כשה-closure נוצר. שינויים מאוחרים ב-$x בחוץ לא נראים. כתבו use (&$x) כדי לשתף את המשתנה במקום זאת, כך ששינויים עוברים לשני הכיוונים.
מה זה callable ב-PHP?
כל דבר ש-PHP יכולה לקרוא לו: closure, שם של פונקציה כמחרוזת ('strtoupper'), מערך [$object, 'method'] או [ClassName::class, 'staticMethod'], או אובייקט עם __invoke(). השתמשו בטיפוס callable כדי לקבל כל אחד מהם וב-is_callable() כדי לבדוק.
מה המשמעות של strlen(...) ב-PHP?
זה תחביר ה-first-class callable מ-PHP 8.1: strlen(...) יוצר Closure מהפונקציה בלי לקרוא לה. הוא עובד גם למתודות, $obj->method(...), ולמתודות סטטיות, Foo::bar(...), ונבדק כשהשורה רצה, בניגוד לשם במחרוזת.
צריך נקודה פסיק אחרי פונקציה אנונימית?
כן, כשמציבים אותה: $f = function () { ... }; היא פקודת הצבה, ולכן היא מסתיימת ב-;. השמטה שלה היא שגיאת תחביר. closure שמועבר ישירות כארגומנט לא צריך נקודה פסיק משלו.