מטמון העמודים המלא מגיש עמוד ציבורי ישירות מקובץ, לפני שהליבה טוענת נתב, תבניות או מסד נתונים. הוא מה שמאפשר לאתר לשרת הרבה תנועה בעלות נמוכה. בדף הזה תראו את מחזור החיים של עמוד במטמון, את ההגנות מפני עומס, ואיך מבטלים ובונים מחדש עמוד.
בעמוד הזה system/ הוא תיקיית הליבה הפרוסה (= api/core בריפו) ו-{admin} הוא CONFIG::$admin_url של האתר.
תנאי להפעלה#
עמוד נשמר רק כששלושת התנאים מתקיימים (core.php):
CONFIG::$system_type == "client"(עמודי אדמין לא נשמרים לעולם).CONFIG::$allow_cache == true(הערך נקבע בקונפיגורציית האתר, ולא בליבה).PAGE::$cache_this_page === true, כלומר הבקר של האתר קרא ל-cache_engine::cache_all_page().
ברירת המחדל של PAGE::$cache_this_page היא false: עמוד נכנס למטמון רק אם הבקר ביקש זאת במפורש.
// בתוך בקר של האתר (application/controllers/news.php)
class news extends bgl_controller
{
function index()
{
cache_engine::cache_all_page(24); // העמוד הזה יישמר ל-24 שעות
return $this->view("news_index.tpl");
}
}
הארגומנט השני, $set_page, שומר את העמוד תחת כתובת אחרת מה-REQUEST_URI (דרך set_page_url).
מחזור החיים#
שלב 1: בדיקה בתחילת הבקשה#
core.php קורא ל-cache_engine::cache_php_check() בראשית הבקשה:
- כש-
allow_cacheכבוי, חוזרים מיד. - מסירים פרמטרי מעקב מה-
REQUEST_URI(ראו למטה).$_GETנשאר כמו שהוא. - בקשת
POSTעםreload_cacheמדלגת על הגשה ומכריחה בנייה מחדש (כך עובדping_cache). - הכתובת מקודדת לשם קובץ (
encode_url) ומחפשיםpages/<url>.html.gz, ובטלפוןpages_mobile/. ההפרדה היא לפיPAGE::is_mobile(). - קובץ שתוקפו עתידי מוגש, והבקשה מסתיימת ב-
exit. - אין קובץ תקף אבל יש קובץ שפג ומישהו אחר בונה אותו כרגע: מוגש הקובץ הישן.
- אם אין עמוד, נבדק גם
scss/<url>.css(קומפילציית SCSS) ומוגש כ-text/css.
ב-HIT נשלח Content-Type לפי הכתובת (.json, /rss, /feed, .xml) ולפי תחילת התוכן.
שלב 2: הבקר מבקש מטמון#
cache_all_page($hours = 24) מסמנת PAGE::$cache_this_page = true ואת משך התוקף, ותופסת נעילת בנייה. אם הנעילה תפוסה, הבקשה ממתינה קצרות לתוצאה של הבונה (ראו הנעילה).
שלב 3: שמירה#
אחרי שהעמוד רונדר ועבר MASK::out (מצב הסוואה), cache_all_page_save דוחסת אותו (gzip רמה 6), כותבת אטומית, ומציבה תפוגה של now + hours. בקשה ש"הפסידה" במרוץ הבנייה ופג זמנה לא כותבת: הבונה האמיתי יכתוב.
הגשה ישירה של קובץ gzip#
כדי לא לפתוח ולדחוס מחדש בכל HIT, try_serve_gz_direct שולחת את הבייטים הדחוסים כמו שהם עם Content-Encoding: gzip, Vary: Accept-Encoding ו-Content-Length. היא פועלת רק כשכל התנאים מתקיימים:
- לא הוגדרה
PAGE::$parse_page_func. zlib.output_compressionכבוי.- הלקוח שולח
Accept-Encoding: gzip. - כל ה-output buffers הפתוחים הם
ob_gzhandlerאו ברירת המחדל. - סוג התוכן זוהה, והקובץ באורך 18 בתים לפחות.
אחרת הקובץ נפתח, מועבר דרך PAGE::parse_page() אם הוגדרה, ומוגש רגיל. אתר שמשנה את הפלט דרך buffer משלו נופל אוטומטית למסלול הזה ($serve_gz_direct = false מכבה את האופטימיזציה כולה).
נעילת בנייה והגשת עמוד ישן#
כשעמוד פופולרי פג, בלי הגנה כל מי שנמצא באתר היה בונה אותו בו-זמנית. שתי הגנות מונעות זאת:
| מנגנון | מה הוא עושה |
|---|---|
נעילת בנייה ($use_build_lock) | רק בקשה אחת תופסת flock על locks/<sha256(url)>.lock ובונה. האחרות ממתינות עד $build_lock_wait_ms (750 מ"ש), בדיקה כל 50 מ"ש. הקובץ הופיע: מגישות אותו. הבונה נכשל: אחת משתלטת. פג הזמן: בונות לעצמן בלי לכתוב |
הגשת עמוד ישן ($serve_stale_while_building) | קובץ שפג לא נמחק ונשאר זמין. כל עוד מישהו בונה, שאר המבקרים מקבלים אותו |
הנעילה "נכשלת פתוח": אם flock לא זמין או שקובץ הנעילה לא נפתח, הבקשה בונה כרגיל.
פרמטרי מעקב#
פרמטרים שמזהים מקור תנועה בלבד לא משנים את העמוד, ולכן הם מוסרים מהכתובת לפני חישוב המפתח. כך קליק עם ?utm_source=... משתמש באותו קובץ של הכתובת הנקייה, ולא יוצר קובץ חדש לכל קמפיין. הרשימה ב-cache_engine::$tracking_params כוללת בין השאר utm_*, fbclid, gclid, gbraid, wbraid, msclkid, ttclid, twclid, mc_cid, mc_eid, _ga, _gl.
cache_engine::strip_tracking_params("/shop?utm_source=fb&color=red");
// "/shop?color=red"
פרמטר GET שאינו ברשימה הוא חלק ממפתח המטמון, ולכן כל ערך שונה יוצר קובץ חדש בלי תקרה. encode_url מסיר את תו ה-?, ולכן page?a=1 ו-pagea=1 חולקים אותו קובץ. אם הבקר שלכם מתעלם מפרמטר מסוים, מוטב להוסיף אותו ל-$tracking_params.
ביטול עמוד ובנייה מחדש#
cache_engine::cache_all_page_remove("news/123"); // מסמן כפג תוקף
cache_engine::cache_all_page_remove("news/123", true); // מחיקה פיזית
cache_engine::cache_all_page_update("news/123"); // בנייה מחדש בפועל
cache_all_page_remove($url, $hard_delete = false)מטפלת ב-pagesוב-pages_mobile, בקובצי.htmlו-.html.gz, ובווריאציות קידוד של הכתובת. כשהמפתח אינו דף הבית היא מבטלת גם כל עמוד תחת אותו נתיב:remove("news")מבטל גםnews/123.- ברירת המחדל היא ביטול, לא מחיקה.
drop_page_fileמזיזה את זמן השינוי של הקובץ לעבר, כך שהקובץ ממשיך לשמש כעמוד ישן בזמן הבנייה. אם ה-touchנכשל, היא מוחקת.trueמוחק מיד, ומתאים להסרות שחייבות להיעלם מיד (הסרה משפטית). cache_all_page_update($url)קוראת ל-ping_cache, ששולחPOSTעםreload_cache=trueלכתובת המלאה (CONFIG::$env_url . $url), פעם בדסקטופ ופעם כטלפון, וכך הקובץ נבנה מחדש. כשקייםexec, זה נעשה ברקע עםcurl, אחרת עםcurl_*ו-timeout.
הקריאה היחידה ל-cache_all_page_remove בליבה היא בשמירת עמוד בפאנל Pages (admin/Pages.php). שמירה בפאנל אחר, למשל חדשות או מוצרים של האתר, לא מנקה את המטמון אוטומטית, וזו אחריות הפאנל שלכם.
בקשת POST עם reload_cache מכריחה בנייה מחדש ושומרת את התוצאה במטמון המשותף, ללא כל אימות. כל מי שמכיר את הכתובת יכול לגרום לבנייה מחדש. ו-ping_cache מעביר את ה-cookies של בקשת המקור, ולכן אם הבקר מרנדר אחרת למנהל מחובר, זה מה שיישמר לכל המבקרים. הקפידו שהבקרים של עמודים ציבוריים לא מציגים תוכן שונה למנהל.