# Phasers: `BEGIN`, `UNITCHECK`, `CHECK`, `INIT`, `END` חמישה בלוקים בעלי שם מיוחד מאפשרים לקוד להתחבר למעברים שתוכנית עושה בין היותה מהודרת לבין היותה מורצת. הם נראים כמו שגרות אך אינם כאלה: ניתן לכתוב כמה מכל סוג, לאף אחד אין שם הניתן לקריאה, וכל אחד מהם נורה אוטומטית ברגע שלו. ```perl BEGIN { ... } # during compilation, as soon as defined UNITCHECK { ... } # right after this compilation unit is compiled CHECK { ... } # after the whole program is compiled INIT { ... } # just before the runtime begins END { ... } # just before the interpreter exits ``` התחילית `sub` (`sub BEGIN { ... }`) מתקבלת אך אינה מוסיפה דבר ואין לעודד אותה. אלה בלוקים, לא שגרות בעלות שם; לא ניתן לקרוא להם, אלא רק לתת להם להירות. ## מתי כל אחד רץ תוכנית פרל חיה בשני שלבים: **שלב הידור** שמפרש את המקור לצורה פנימית, ו**שלב ריצה** שמבצע אותו. ה-phasers מסמנים את הגבול. - `BEGIN` רץ ברגע שסוגר הסוגר שלו מפורש, עוד לפני ששאר הקובץ בכלל נקרא. כך פועל [`use`](../perlfunc/use.md): `use` הוא [`require`](../perlfunc/require.md) עטוף ב-`BEGIN` בתוספת [`import`](../perlfunc/import.md), כך שהשמות המיובאים קיימים עבור הקוד שלאחר מכן. לאחר ש-`BEGIN` רץ, הוא אינו מוגדר וזיכרונו משוחזר. - `UNITCHECK` רץ מיד כש**יחידת ההידור** שמכילה אותו סיימה להתהדר. יחידה היא התוכנית הראשית, מודול שנטען על ידי [`require`](../perlfunc/require.md), [`eval`](../perlfunc/eval.md) של מחרוזת, [`do`](../perlfunc/do.md) FILE, או קוד שהודר על ידי מבנה ה-regex `(?{ })`. משום שיחידות מסיימות להתהדר בזמנים שונים, `UNITCHECK` הוא ה-phaser היחיד שתזמונו עוקב אחר הקובץ הבודד ולא אחר התוכנית בכללותה. - `CHECK` רץ פעם אחת, אחרי ששלב ההידור **הראשוני** של כל התוכנית מסתיים ולפני ששלב הריצה מתחיל. - `INIT` רץ פעם אחת, רגע לפני ששלב הריצה מתחיל, אחרי כל בלוקי ה-`CHECK`. - `END` רץ פעם אחת, מאוחר ככל האפשר: אחרי שהתוכנית סיימה (כולל אחרי [`die`](../perlfunc/die.md)), רגע לפני שהמפרש יוצא. `BEGIN` ו-`UNITCHECK` קשורים ל *פירוש*, לא לשלב המפרש, ולכן הם יכולים להירות במהלך כל שלב: `eval` של מחרוזת בזמן ריצה עדיין מהדר את גופו, וכל `BEGIN` או `UNITCHECK` שבתוכו רץ אז ושם. `CHECK` ו-`INIT`, לעומת זאת, מסמנים את גבול ההידור/הריצה החד-פעמי של התוכנית הראשית. ## ריבוי וסדר ביצוע ניתן לכתוב כל מספר של כל בלוק. כולם רצים, בסדר מוגדר: - `BEGIN`: ראשון נכנס, ראשון יוצא (FIFO) כפי שנתקלים בו במהלך ההידור. ה-`BEGIN` הראשון בסדר המקור רץ ראשון. - `UNITCHECK`: אחרון נכנס, ראשון יוצא (LIFO) בתוך כל יחידה, אחרי שאותה יחידה מתהדרת. - `CHECK`: LIFO, אחרי כל ההידור. - `INIT`: FIFO, רגע לפני זמן הריצה. - `END`: LIFO ביציאה. ה-`END` האחרון שהוגדר הוא הראשון לרוץ. המקרה המאלף הוא קובץ שמערבב משפטים רגילים עם כל חמשת סוגי הבלוקים. הבלוקים אינם רצים במקום שבו הם נכתבים; הם רצים בשלב שלהם, בסדר שלהם בכל שלב, בעוד המשפטים הרגילים רצים בין לבין בזמן ריצה. מבחינה רעיונית, עבור קובץ בודד: ```default 1. BEGIN blocks -- FIFO, during compilation 2. UNITCHECK blocks -- LIFO, after this unit compiles 3. CHECK blocks -- LIFO, after all compilation 4. INIT blocks -- FIFO, just before runtime 5. ordinary code -- run phase, in source order 6. END blocks -- LIFO, at exit ``` כך ש-`BEGIN` ו-`UNITCHECK` רצים לפני כל `CHECK`; `CHECK` רץ לפני `INIT`; `INIT` רץ לפני השורה הראשונה של קוד רגיל; וכל `END` רץ אחרי האחרונה. בתוך `BEGIN` ו-`INIT`, בלוקים שנכתבו מוקדם יותר רצים מוקדם יותר; בתוך `UNITCHECK`, `CHECK` ו-`END`, בלוקים שנכתבו מאוחר יותר רצים מוקדם יותר. ## בלוקי `END` בפירוט `END` הוא ה-phaser של הניקוי. הוא רץ LIFO כך שהפירוק מתפרק בסדר ההפוך לזה של ההתקנה, באופן שמשאבים מקוננים מצפים לו. בתוך בלוק `END`, [`$?`](../perlvar/error.md) מחזיק את הערך שהתוכנית עומדת להעביר ל-[`exit`](../perlfunc/exit.md). תוכל לקרוא אותו כדי ללמוד כיצד התוכנית מסתיימת, ותוכל להשים לתוכו כדי לשנות את קוד היציאה: ```perl END { $? = 0 if $? == 42; # rewrite one exit code on the way out } ``` היזהר מדריסה של `$?` בטעות. הרצת כל דבר באמצעות [`system`](../perlfunc/system.md) בתוך בלוק `END` דורסת את `$?` עם סטטוס תהליך-הבן, שאז הופך לקוד היציאה של התוכנית אלא אם תשמור ותשחזר אותו. בלוקי `END` **אינם** רצים בכמה מקרים: - תחת המתג `-c` (בדיקת תחביר בהידור-בלבד). הקוד הראשי אינו רץ, וגם בלוקי `END` אינם רצים. - כאשר ההידור נכשל. תוכנית שאינה מתהדרת לעולם אינה מגיעה לנקודה שבה בלוקי `END` היו נורים. - כאשר התהליך מחליף את עצמו באמצעות [`exec`](../perlfunc/exec.md). התמונה איננה; לא נותר דבר שירוץ את הבלוק. - כאשר התהליך נהרג על ידי אות קטלני שאינך תופס. תפוס את האות בעצמך (באמצעות [`%SIG`](../perlvar/signals.md)) אם דרוש לך שהניקוי ירוץ. בלוק `END` שנוצר בתוך [`eval`](../perlfunc/eval.md) של מחרוזת **אינו** רץ כש-`eval` זה מסתיים. הוא נרשם כמו כל בלוק `END` אחר של החבילה שלו ורץ בסדר LIFO רגע לפני שהמפרש יוצא. ## `${^GLOBAL_PHASE}` והשלבים המשתנה לקריאה בלבד [`${^GLOBAL_PHASE}`](../perlvar/interpreter.md) נוקב בשם השלב הנוכחי של המפרש. ערכיו מאפשרים לקוד שרץ ביותר משלב אחד לגלות היכן הוא נמצא. במהלך בלוק `CHECK` הוא קורא `CHECK`; במהלך `INIT`, `INIT`; במהלך קוד רגיל בזמן ריצה, `RUN`; במהלך בלוק `END`, `END`. ראה [`${^GLOBAL_PHASE}`](../perlvar/interpreter.md) לרשימה המלאה של הערכים ולציר הזמן של השלבים. ## בלוקי `defer` בלוק [`defer`](../perlfunc/defer.md) הוא בן-הדוד בקנה-מידה קטן של `END`. הוא רץ כש**תחום הבלוק** העוטף יוצא, מכל סיבה שהיא, ולא כשהתוכנית כולה יוצאת. פנה אל `defer` כדי לזווג רכישה עם שחרור בתוך שגרה או לולאה; פנה אל `END` עבור פירוק כלל-תהליכי שחייב לקרות פעם אחת, ממש בסוף. ## ראו גם - [`BEGIN` and `use`](../perlfunc/use.md) - `use` הוא טעינה-וייבוא עטופים ב-`BEGIN`; הסיבה הנפוצה ביותר להתעניין בתזמון שלב ההידור. - [`${^GLOBAL_PHASE}`](../perlvar/interpreter.md) - קרא את השלב הנוכחי מתוך כל בלוק כדי ללמוד היכן אתה נמצא. - [`defer`](../perlfunc/defer.md) - ניקוי בתחום הבלוק, הכלי הנכון כשהטווח הכלל-תוכניתי של `END` רחב מדי. - [`$?`](../perlvar/error.md) - סטטוס היציאה שבלוק `END` יכול לקרוא ולכתוב מחדש. - [`%SIG`](../perlvar/signals.md) - תפוס את האותות שאחרת היו עוקפים את בלוקי ה-`END` כליל. - [`exit`](../perlfunc/exit.md) - מה שבלוקי `END` רצים רגע לפניו; הערך שהוא נושא הוא ה-`$?` שבלוק `END` רואה.