אלה שאלות ה-PHP שעולות הכי הרבה בראיונות, עם תשובות קצרות שנכונות ל-PHP 8.3 וקוד במקומות שבהם הקוד מוכיח את הנקודה. נחשו את הפלט של כל בלוק לפני שאתם לוחצים על Run: זה מה שמראיין בודק.
שאלות ראיון PHP למתחילים
מה זה PHP ואיך היא רצה?
PHP היא שפת סקריפטים בצד השרת. שרת web (Nginx או Apache) מעביר את הבקשה ל-PHP (בדרך כלל PHP-FPM), PHP מריצה את הסקריפט מההתחלה, ומה שהיא מדפיסה הופך לתשובה, בדרך כלל HTML. כל בקשה מתחילה מחדש: משתנים לא שורדים בין בקשות אלא אם שומרים אותם ב-session, במסד נתונים או במטמון. PHP מריצה סקריפטים גם בשורת הפקודה (php script.php).
מה ההבדל בין == ל-===?
== משווה אחרי המרת טיפוסים, === משווה ערך וטיפוס בלי המרה. החימום שלמעלה מראה את == בפעולה: מחרוזות מספריות מושוות כמספרים, ולכן '10' == '1e1' הוא true. מאז PHP 8, מספר שמושווה למחרוזת לא מספרית מושווה כמחרוזות, ולכן 0 == 'a' הוא false (ב-PHP 7 זה היה true). הקישו על התאים כדי להשוות בין שני האופרטורים:
| $a == $b | true | false | 0 | 1 | "0" | "1" | "" | null | [] | "a" | "10" | "1e1" |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| true | ||||||||||||
| false | ||||||||||||
| 0 | ||||||||||||
| 1 | ||||||||||||
| "0" | ||||||||||||
| "1" | ||||||||||||
| "" | ||||||||||||
| null | ||||||||||||
| [] | ||||||||||||
| "a" | ||||||||||||
| "10" | ||||||||||||
| "1e1" |
השתמשו ב-=== כברירת מחדל. in_array() ו-array_search() משתמשות ב-== אלא אם מעבירים true כארגומנט השלישי.
מה ההבדל בין echo ל-print?
שתיהן מדפיסות מחרוזות. echo מקבלת כמה ארגומנטים (echo $a, $b;) ולא מחזירה כלום; print מקבלת אחד ומחזירה 1, ולכן אפשר להשתמש בה בתוך ביטוי. אף אחת מהן אינה פונקציה, ולכן סוגריים אופציונליים. בפועל כולם משתמשים ב-echo.
גרשיים בודדים מול מירכאות כפולות?
מחרוזות במירכאות כפולות משלבות משתנים ורצפי escape: "Hi $name\n". מחרוזות בגרשיים בודדים מילוליות חוץ מ-\' ו-\\: 'Hi $name\n' מדפיסה בדיוק את התווים האלה. הבדל המהירות זניח, ולכן הבחירה היא עניין של קריאות: מירכאות כפולות כשמשלבים, גרשיים בודדים כשלא.
מה הם סוגי הנתונים ב-PHP?
ארבעה טיפוסים סקלריים (int, float, string, bool), שני טיפוסים מורכבים (array ו-object), ושניים מיוחדים (null ו-resource). callable, iterable, mixed ו-void קיימים רק כהצהרות טיפוס, אף פעם לא כטיפוס של ערך. gettype() ו-get_debug_type() אומרות לכם את הטיפוס של ערך בזמן ריצה, ו-var_dump() מדפיסה אותו עם הערך.
מה ההבדל בין isset, empty ו-is_null?
isset($x) היא true כשהמשתנה קיים ואינו null. empty($x) היא true כשהמשתנה לא קיים או שקרי (0, '0', '', [], false, null). is_null($x) היא true עבור null, אבל מזהירה על משתנה לא מוגדר. isset ו-empty אף פעם לא מזהירות, ולכן משתמשים בהן על מפתחות מערך.
| $x | isset | empty | is_null |
|---|---|---|---|
| null | |||
| false | |||
| 0 | |||
| "0" | |||
| "" | |||
| " " | |||
| [] | |||
| "a" | |||
| 0.0 |
השורה שאנשים טועים בה היא "0": מחרוזת האפס ריקה, בעוד " " (רווח) לא. בעמוד isset יש את הטבלה המלאה.
מה ההבדל בין include ל-require?
שתיהן מכניסות ומריצות קובץ אחר. אם הקובץ חסר, include מוציאה אזהרה והסקריפט ממשיך, בעוד require זורקת Error ("Failed opening required"), שעוצר את הסקריפט אלא אם משהו תופס אותו. גרסאות ה-_once (include_once, require_once) מדלגות על קובץ שכבר נטען, וזה מונע שגיאות "cannot redeclare function". השתמשו ב-require (או ב-require_once) לקבצים שהסקריפט לא יכול לרוץ בלעדיהם, וזה כמעט כולם; ה-autoloader של Composer הופך את רוב ה-includes הידניים למיותרים.
מה ההבדל בין GET ל-POST?
GET שולח נתונים ב-query string של כתובת ה-URL ($_GET), ולכן הם גלויים, אפשר לשמור אותם כסימנייה, והאורך שלהם מוגבל; השתמשו בו לקריאה, כמו חיפוש וחלוקה לדפים. POST שולח נתונים בגוף הבקשה ($_POST); השתמשו בו לכל דבר שמשנה מצב, כמו התחברות, שמירה או תשלומים. בקשת GET לעולם לא אמורה לשנות נתונים, כי קישורים נטענים מראש ונסרקים.
מה זה אופרטור ה-null coalescing?
$a ?? $b מחזיר את $a אם הוא קיים ואינו null, ואחרת את $b, בלי אזהרה על משתנה או מפתח לא מוגדרים: $page = $_GET['page'] ?? 1;. ??= מציב רק כשהצד השמאלי null או חסר. הוא שונה מ-?:, שעובר לערך החלופי בכל ערך שקרי, ולכן 0 ?: 5 הוא 5 אבל 0 ?? 5 הוא 0.
שאלות ראיון PHP לרמת ביניים
מה קורה כשעוברים בלולאה לפי הפניה ב-foreach?
foreach ($arr as &$v) מאפשרת לשנות איברים במקום, אבל $v נשאר הפניה לאיבר האחרון אחרי הלולאה. שימוש חוזר בשם בלולאה שנייה דורס אז את האיבר הזה:
האיבר האחרון הוא 20, לא 30: במהלך הלולאה השנייה, $v (שעדיין הוא $arr[2]) מקבל 10, אחר כך 20, ואז את עצמו. הסירו את ה-// שלפני unset($v) והריצו שוב. עמוד foreach מכסה הפניות בפירוט.
מה ההבדל בין מחלקה מופשטת לממשק? ומה זה trait?
ממשק הוא חוזה: חתימות מתודות (וקבועים) בלי מימוש, ומחלקה יכולה לממש הרבה. מחלקה מופשטת יכולה להחזיק מאפיינים, בנאים ומתודות גמורות, ועוד מתודות מופשטות שהבנות שלה חייבות לכתוב; מחלקה מרחיבה רק אחת. trait הוא קבוצת מתודות שמועתקת לתוך מחלקה עם use, לשימוש חוזר בקוד בין מחלקות שאין להן אב משותף.
תשובה קצרה שמראיינים אוהבים: השתמשו בממשק כדי לומר מה משהו יודע לעשות, במחלקה מופשטת כדי לשתף איך זה נעשה, וב-trait כשמחלקות לא קשורות צריכות את אותן כמה מתודות.
מה ההבדל בין self ל-static?
self מתייחס למחלקה שבה הקוד כתוב. static מתייחס למחלקה שנקראה בפועל בזמן ריצה, וזה נקרא late static binding. זה חשוב במתודות של אב שיוצרות או מתארות "את הסוג הזה של אובייקט":
User::createStatic() בונה User. User::createSelf() בונה Model, וזה שובר את טיפוס ההחזרה static, ולכן PHP זורקת TypeError. זו הסיבה שמתודות factory משתמשות ב-new static().
מה הן מתודות קסם?
מתודות ש-PHP קוראת להן בשבילכם, וכולן מתחילות בשני קווים תחתונים. הנפוצות: __construct() כשאובייקט נוצר, __destruct() כשהוא מושמד, __toString() כשמשתמשים בו כמחרוזת, __get() ו-__set() למאפיינים לא נגישים, __call() ו-__callStatic() למתודות חסרות, __invoke() כשקוראים לאובייקט כמו לפונקציה, ו-__clone() אחרי clone.
מה ההבדל בין array_merge() לאופרטור +?
array_merge() מוסיפה: מפתחות מספריים ממוספרים מחדש ומפתחות מחרוזת מאוחרים דורסים מוקדמים. + הוא איחוד: הוא שומר על המערך הראשון ומוסיף רק מפתחות שחסרים, ולכן במפתח כפול הערך הראשון מנצח.
+ הוא הכלי הנכון לברירות מחדל: $options + ['timeout' => 30] שומר על מה שהקורא העביר ומשלים את השאר.
מה עושה declare(strict_types=1)?
בלעדיה, PHP ממירה ארגומנטים סקלריים לטיפוס הפרמטר המוצהר כשהיא יכולה: העברה של "5" לפרמטר int נותנת 5. עם declare(strict_types=1); כפקודה הראשונה בקובץ, קריאות שנעשות מהקובץ הזה חייבות להעביר את הטיפוס המדויק, אחרת נזרק TypeError (int שמועבר לפרמטר float הוא ההרחבה היחידה שמותרת).
מחקו את שורת ה-declare והריצו שוב: הקריאה השנייה מחזירה 10 במקום לזרוק.
מה ההבדל בין session לעוגייה?
עוגייה נשמרת בדפדפן ונשלחת עם כל בקשה, ולכן המבקר יכול לקרוא ולשנות אותה. session שומר נתונים בשרת ומחזיק רק מזהה אקראי בעוגייה (PHPSESSID). שימו כל דבר שחייבים לסמוך עליו, כמו מזהה המשתמש המחובר, ב-$_SESSION; אחרי התחברות, קראו ל-session_regenerate_id(true). בעמוד ה-sessions והעוגיות יש את הקוד.
איך מונעים SQL injection?
לעולם אל תבנו SQL על ידי שרשור קלט. השתמשו ב-prepared statements, כך שהערכים נשלחים בנפרד מהשאילתה:
<?php
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
ממלאי מקום עובדים רק לערכים. שמות טבלאות, שמות עמודות ו-ASC/DESC חייבים להיבדק מול רשימת היתר לפני שהם נכנסים ל-SQL. ראו PDO.
איך מונעים XSS?
בצעו escape לפלט, לא לקלט: עטפו כל ערך שמודפס לתוך HTML ב-htmlspecialchars($value, ENT_QUOTES, 'UTF-8'), או השתמשו במנוע תבניות שעושה את זה אוטומטית (Twig, Blade). בתוך <script>, השתמשו ב-json_encode() עם JSON_HEX_TAG. הוסיפו כותרת Content-Security-Policy כשכבה שנייה.
איך צריך לשמור סיסמאות?
עם password_hash($password, PASSWORD_DEFAULT) ולבדוק עם password_verify($password, $hash). הן משתמשות ב-bcrypt (או ב-Argon2id) עם salt אקראי ששמור בתוך ה-hash ו-cost מכוון. md5() ו-sha1() שגויות לסיסמאות כי הן מהירות ובלי salt. השתמשו ב-password_needs_rehash() בהתחברות כדי לשדרג hashes ישנים.
שאלות ראיון PHP מתקדמות
מה הם גנרטורים ומתי הייתם משתמשים בהם?
פונקציה שמכילה yield מחזירה Generator שמפיק ערכים אחד אחד, רק כשמבקשים. הוא מאפשר לעבור בלולאה על רצף גדול או אינסופי בלי לבנות מערך בזיכרון:
שימושים טיפוסיים הם קריאת קובץ גדול שורה אחר שורה, הזרמת שורות ממסד נתונים, וצינורות עצלים. גנרטורים תומכים גם במפתחות (yield $key => $value), בהאצלה (yield from) ובקבלת ערכים עם send().
מה הם closures, ומה use עושה?
פונקציה אנונימית (function () { ... }) היא אובייקט Closure. היא לא רואה משתנים חיצוניים אלא אם רושמים אותם ב-use, שמעתיקה את הערך ברגע שה-closure נוצר; use (&$x) לוכדת במקום זאת לפי הפניה. פונקציות חץ (fn ($x) => $x * $factor) לוכדות משתנים חיצוניים אוטומטית, לפי ערך, ומכילות ביטוי יחיד.
מה ההבדל בין abstract, final ו-readonly?
abstract מסמן מחלקה שאי אפשר ליצור ממנה מופע, או מתודה שבת חייבת לממש. final אוסר דריסה של מתודה או הרחבה של מחלקה. readonly (PHP 8.1) הופך מאפיין לניתן לכתיבה פעם אחת בלבד, מתוך המחלקה, בדרך כלל בבנאי; PHP 8.2 הוסיפה מחלקות readonly, שבהן כל מאפיין הוא readonly.
מה זה Composer, ומה זה PSR-4?
Composer הוא מנהל התלויות של PHP: composer.json מפרט את החבילות, composer.lock מקבע גרסאות מדויקות, ו-vendor/autoload.php טוען הכול. PSR-4 הוא תקן הטעינה האוטומטית שממפה קידומת של namespace לתיקייה, כך ש-App\Mail\Mailer נמצאת ב-src/Mail/Mailer.php ונטענת בשימוש הראשון. הסבירו את install (מקובץ ה-lock) מול update (גרסאות חדשות, קובץ lock חדש).
מה השתנה ב-PHP 8?
התכונות המרכזיות: ב-8.0, ארגומנטים בשם, match, האופרטור nullsafe ?->, טיפוסי union, קידום מאפיינים בבנאי, attributes, str_contains(), והשוואה שפויה יותר בין מחרוזת למספר; ב-8.1, enums, מאפייני readonly, תחביר first-class callable (strlen(...)) ו-fibers; ב-8.2, מחלקות readonly וטיפוסי null/true/false עצמאיים; ב-8.3, קבועי מחלקה עם טיפוס, ה-attribute #[\Override] ו-json_validate(); ב-8.4, property hooks, רמת גישה אסימטרית (public private(set)), array_find() ו-new Foo()->method() בלי סוגריים עוטפים. הרבה פונקציות גם זורקות עכשיו TypeError או ValueError במקום להחזיר false עם אזהרה.
איך PHP מנהלת זיכרון?
לכל ערך יש מונה הפניות; כשהוא יורד לאפס, הזיכרון משתחרר מיד. אובייקטים שמפנים זה לזה במעגל אף פעם לא מגיעים לאפס, ולכן אוסף מעגלים רץ בכל פעם שהבאפר שלו של שורשי מעגל אפשריים מתמלא (10,000 רשומות כברירת מחדל) ומשחרר אותם (gc_collect_cycles() מריצה אותו לפי דרישה). מערכים ומחרוזות הם copy-on-write: הצבה $b = $a חולקת את הנתונים עד שאחד מהם משתנה. כיוון שכל בקשה מתחילה ומסתיימת בצורה נקייה, דליפות זיכרון חשובות בעיקר ב-workers שרצים זמן רב ובצרכני תורים.
איך מערכים ב-PHP ממומשים?
מערך ב-PHP הוא hash map מסודר: הוא שומר על סדר ההכנסה וממפה מפתחות int או string לערכים, ולכן אותו טיפוס משמש כרשימה, כמילון וכמחסנית. חיפוש לפי מפתח (isset($arr[$key])) לוקח זמן קבוע בממוצע, בעוד in_array() סורקת כל ערך. מפתחות מחרוזת שנראים כמו מספרים שלמים מומרים: $a['1'] ו-$a[1] הם אותו איבר.
משימות קוד נפוצות בראיונות
תרגילים קצרים שנשאלים לעיתים קרובות בזמן אמת. כל פונקציה היא תשובה נפוצה אחת; הריצו אותן, הוסיפו קלטים משלכם (כולל קלט ריק), והיו מוכנים להסביר את סיבוכיות הזמן, שבדרך כלל היא מה שהמראיין בודק.
שאלות נפוצות
איך מתכוננים לראיון עבודה כמפתחי PHP?
ודאו שאתם יכולים להסביר ולכתוב קוד ליסודות בלי לחפש אותם: השוואה וטיפוסים, מערכים והפונקציות שלהם, OOP (ממשקים, מחלקות מופשטות, traits, static), חריגות, ואבטחה (prepared statements, htmlspecialchars, password_hash). אחר כך הכירו framework אחד (Laravel או Symfony) ואת Composer מספיק טוב כדי לדבר על פרויקט שבניתם.
איזו גרסת PHP צריך להכיר לראיון?
PHP 8. מראיינים מצפים שתכירו תכונות שנוספו ב-8.0 עד 8.3: ארגומנטים בשם, match, האופרטור nullsafe ?->, טיפוסי union, קידום מאפיינים בבנאי, enums, מאפייני readonly ותחביר first-class callable. הזכירו את ההתנהגות של PHP 7 רק במקומות שבהם היא השתנתה, כמו 0 == "a".
אילו שאלות PHP שואלים מתחילים?
בעיקר את יסודות השפה: == מול ===, echo מול print, גרשיים בודדים מול מירכאות כפולות, isset מול empty, include מול require, GET מול POST, sessions מול עוגיות, ומשימות מערך פשוטות כמו היפוך, סינון או ספירת ערכים. צפו לפחות לתרגיל קוד קצר אחד.
אילו שאלות PHP שואלים מפתחים מנוסים?
תכנון והתנהגות מתחת למכסה המנוע: ממשקים מול מחלקות מופשטות, traits, late static binding, הפניות, גנרטורים וזיכרון, חריגות וטיפול בשגיאות, SQL injection וטרנזקציות, caching, טעינה אוטומטית עם Composer, ואיך הייתם בונים או בודקים פיצ'ר. מצפים שהתשובות יבואו עם שיקולים בעד ונגד, לא עם הגדרות.