PAGE היא המחלקה שמרכיבה את העמוד. בקר מחזיר מחרוזת, ו-PAGE מניחה אותה בתוך ה-theme, מוסיפה <head> עם הנכסים, תגי ה-SEO והסקריפטים, ומדפיסה מסמך HTML שלם. בעמוד הזה: מה קורה ב-PAGE::load לפי הסדר, איך נראה המסמך שיוצא, ומה כל מתודה ומשתנה ציבורי עושים. פירוט הנכסים בצנרת הנכסים, תגי ה-SEO בתגי SEO, ורשימת חתימות מקוצרת ברפרנס PAGE.
כל המתודות והמשתנים כאן הם static: קוראים להם PAGE::name(...) מכל מקום. המצב שלהם הוא פר-בקשה (PHP לא חולק זיכרון בין בקשות), ו-PAGE::reset() מאפס אותו בתחילת PAGE::load.
מחזור החיים: PAGE::load#
core.php קורא ל-PAGE::load($_SERVER['REQUEST_URI']). הסדר, כפי שהוא בקוד:
| # | שלב | הערה |
|---|---|---|
| 1 | PAGE::reset() | מאפס נכסים, meta, אירועים, theme, מטמון עמודים, favicon ו-is_404 |
| 2 | PARAMS::init() ו-SEO::init() | טוענים את פרמטרי המערכת ואת ברירות המחדל של ה-SEO |
| 3 | ROUTER::parse_friendly_url($url) | קובע בקר, מתודה ו-$_GET (ניתוב) |
| 4 | בדיקת דומיין (צד לקוח) | die("URL ERROR") כשהדומיין לא מותר. מדלגים כש-CONFIG::$ignore_domain_check === true, ועבור minify ו-Tools |
| 5 | ROUTER::redirections() | הפניות 301 (הפניות), צד לקוח בלבד |
| 6 | FILES::include_dir("application/includes") | כל קבצי ה-*.php שם, לפי סדר שמות, פעם אחת. צד לקוח בלבד |
| 7 | JS של המערכת | כל system/js/*.js נוסף כנכס: system.js ב-head, השאר ב-body_end. רק כש-PAGE::$include_system_js הוא true (ריצת צד לקוח) |
| 8 | קבוצות minify | לכל קבוצה ב-CRM_minify_groups שה-theme שלה ריק או שווה ל-PAGE::$theme: add_asset_group |
| 9 | אדמין | טעינת הספריות של האדמין ו-application/admin/includes |
| 10 | LANGS::init_words() ו-PAGE::trigger("SYSTEM_LOADED") | נקודת ההרחבה הראשונה שבה הכל טעון |
| 11 | בחירת theme והרצת הבקר | PAGE::set_theme(platform_data["deftheme"]) אם לא נבחר אחר, ואז MODULE::get_html |
| 12 | PAGE::output() | הרכבת המסמך |
שלוש משמעויות מעשיות:
- קוד ב-
application/includes/*.phpרץ אחריresetולפני הבקר. כאן מוסיפים נכסים גלובליים,PAGE::bind(...)ו-PAGE::set_theme(...). בקר שרץ אחר כך עדיין יכול להוסיף ולהסיר נכסים. - שלב 8 תלוי ב-theme בזמן הקריאה.
PAGE::set_themeשנקרא בבקר (שלב 11) מאוחר מדי כדי להשפיע על קבוצות ה-minify, שנבחרו בשלב 8 לפי ה-theme באותו רגע. בחירת theme שמשפיעה על קבוצות נעשית ב-application/includes. זה נכון גם הפוך: עד שלב 11PAGE::$themeריק, ולכן קבוצה שה-theme שלה מוגדר (למשלdefault) נטענת רק אםapplication/includesקרא ל-PAGE::set_theme("default"). קבוצה עם theme ריק נטענת תמיד. application/includes/init.phpהוא חריג.core.phpטוען אותו לפניPAGE::load, כלומר לפניreset, ולכןbindו-add_assetשבו נמחקים. מה שצריך לשרוד, כמוPAGE::$parse_page_func(ראו למטה), שייך לשם. נכסים ואירועים שייכים לקובץ אחר באותה תיקייה.
המסמך ש-PAGE::output מרכיבה#
אם $_GET["pmode"] הוא empg או inner, אין theme ואין מעטפת HTML: יוצא רק מה שהבקר החזיר, והנכסים נרשמים בכותרות התשובה (scripts ו-scripts_ver, שבהן system.js משתמש כדי לדעת מה כבר נטען). זו הדרך ל-JSON, לקטעי HTML שנטענים ב-AJAX ולכל פלט שאינו עמוד (בקרים).
אחרת המסמך נבנה כך:
| מקום | מה נכנס |
|---|---|
<html lang="he" dir="…"> | ה-lang קבוע ל-he. ה-dir הוא platform_data["direction"] |
<head> | SEO::tags_summary(), <base href>, נכסי CSS ואז JS של אזור head, print_meta_tags("head"), <meta name="ROBOTS"> מ-PAGE::$robots, <meta name="copyright"> (אלא אם CONFIG::$show_wizzo_credits === false), <link rel="shortcut icon"> מ-PAGE::$favicon, ובצד לקוח גם SEO::get_schema("WebPage") וסקריפטים מטבלת CRM_scripts שאזורם head |
<body class="…"> | המחלקה היא הכיוון ועוד MISC::$GLOBALS["body_class"] אם נקבע. אחריה: נכסי body_start, סקריפטי CRM_scripts של body_start, ו-print_meta_tags("body_start") |
| גוף העמוד | ה-HTML של ה-theme (בלי תגי <script>, ראו הערה) |
| סוף הגוף | נכסי body_end, ה-JS שנטען אחרי אינטראקציה, תגי ה-<script> של ה-theme (מכווצים ב-JShrink), writeLoadedFile(), סקריפטי CRM_scripts של body_end, print_meta_tags("body_end"), ולבסוף /tag_manager.js?id=system אם קיים system/system_tags.json |
כל המסמך עובר minify_html (כיווץ רווחים), אלא אם CONFIG::$minify_html === false.
<script> ב-theme ובתבניות מוצאים מהמקום שלהם ועוברים לסוף ה-<body>. תג <script> בגוף ה-theme נחתך מה-HTML ומודפס אחרי נכסי body_end. תג ששמו כולל את המילה nominify נשאר במקום ולא נגע בו. סקריפט שצריך לרוץ באמצע העמוד (כמו קוד שכותב ל-document.write) חייב להכיל nominify, למשל כתכונה (<script data-x="nominify">) או כהערה בתוכו.minify_html מבוסס regex ועלול לפגוע בתוכן. הוא מכווץ רווחים, מוחק סוף שורה שנראה כהערת // (גם //localhost בסוף כתובת), ומקצר רצפי whitespace גם בתוך <pre>, <textarea> ו-JS מוטמע (ממצא CORE-21). בעמוד עם בלוקי קוד או טקסט שהרווחים בו משמעותיים, כבו אותו עם CONFIG::$minify_html = false בקובץ ההגדרות (הגדרות).נכסים: JS ו-CSS#
הנכסים נאספים בזמן הבקשה בשני מערכים ($assets_css, $assets_js), והסוג נקבע לפי תת-מחרוזת בכתובת: מי שמכילה .css היא CSS, מי שמכילה .js היא JS, ומי שמכילה css בלי נקודה גם היא CSS. כתובת יחסית מקבלת את הדומיין של האתר, וכל נכס מקבל ?ver= עם הערך של הפרמטר attaches_version, כך שאחרי מחיקת מטמון הדפדפנים מורידים מחדש.
PAGE::add_asset($fileSrc, $must = false, $area = "head", $order = "append")#
הדרך הרגילה להוסיף קובץ.
| פרמטר | משמעות |
|---|---|
$fileSrc | נתיב יחסי לשורש האתר או כתובת מלאה |
$must | true מסמן נכס חובה, שמועבר גם כש-IncludeJsCss(true) מבקש רק חובה (שימוש פנימי) |
$area | head, body_start או body_end |
$order | append (ברירת המחדל) או prepend, שמכניס לתחילת הרשימה |
הוספה של קובץ שכבר נרשם מחליפה את הרישום הקודם: הוא עובר לסוף (או לתחילת) הרשימה, ולא נשלח פעמיים.
PAGE::add_asset("themes/default/style.scss.css", false, "head");
PAGE::add_asset("themes/default/app.js", false, "body_end");
PAGE::add_asset("https://cdn.example.com/lib.js", false, "body_end");
PAGE::add_asset_advanced($fileSrc, $m_settings = [])#
כשצריך שליטה בטעינה. המפתחות ב-$m_settings (ברירות המחדל בסוגריים):
| מפתח | משמעות |
|---|---|
area (head) | head, body_start או body_end |
async (false) | ב-JS: async="async". ב-CSS: טעינה לא חוסמת (preload ואז החלפה ל-stylesheet) |
defer (false) | ב-JS: defer="defer" |
after_user_intraction (false) | לא נטען עם העמוד, אלא בלחיצה או בלחיצת מקש הראשונה של המבקר (ריצת צד לקוח) |
print (false) | ב-CSS: ה-SCSS המקומפל נכנס inline בתוך <style>, בלי בקשה נוספת. אם הקומפילציה נכשלה חוזרים ל-<link> |
PAGE::add_asset_advanced("themes/default/chat.js", ["area" => "body_end", "after_user_intraction" => true]);
PAGE::add_asset_advanced("themes/default/critical.scss.css", ["print" => true]);
שימו לב: add_asset_advanced לא מקבל $order, והוא תמיד מוסיף לסוף. הערך after_user_intraction (כך כתוב בקוד, עם שגיאת כתיב) גם מבטל את כל שאר ההגדרות של הנכס.
שאר מתודות הנכסים#
| מתודה | פירוט |
|---|---|
PAGE::remove_asset($fileSrc) | מסיר נכס לפי הכתובת המדויקת שנרשם בה. שימושי כדי להוריד נכס של הליבה או של ה-theme בעמוד מסוים |
PAGE::add_asset_group($id) | מוסיף קבוצת minify לפי ה-id שלה. נקרא אוטומטית ב-load לכל הקבוצות. הקבצים שבקבוצה לא נשלחים בנפרד |
PAGE::IncludeJsCss($onlyMust = false, $type = "both", $area = "") | מחזיר מחרוזת HTML של התגים, לפי סוג (js, css, both) ואזור. נקראת מ-output, ורק אם בונים מסמך בעצמכם צריך אותה |
PAGE::IncludeAfterIntercation() | מחזיר את הסקריפט שמאזין ללחיצה ולמקש וטוען את נכסי after_user_intraction |
PAGE::JsIncludeJsCss() | כותב את רשימת הנכסים לכותרות scripts ו-scripts_ver (במצב pmode) |
PAGE::writeLoadedFile($onlyMust = false) | מחזיר <script> שמודיע ל-writeLoadedFile של system.js אילו קבצים כבר נטענו, כדי שלא יטענו שוב ב-AJAX |
בבקר, add_asset נקרא לפני ה-return: הנכסים נאספים בזמן הבקשה, ו-output רץ רק אחרי שהבקר חזר. בקר שנקרא ב-pmode=empg שולח את הנכסים רק בכותרות, ולכן קוד JS צריך לטעון אותם בעצמו.
תגי meta ו-head#
| מתודה | פירוט |
|---|---|
PAGE::add_meta($val, $area = "head") | מוסיף מחרוזת HTML גולמית (תג <meta>, <link>, כל דבר) לאזור head, body_start או body_end. הערך לא עובר escape |
PAGE::print_meta_tags($area = "") | מחזיר את כל המחרוזות של האזור. נקראת מ-output |
PAGE::$robots | תוכן תג ROBOTS. ברירת המחדל "ALL". לדוגמה PAGE::$robots = "NOINDEX, FOLLOW"; |
PAGE::$favicon | כתובת ה-favicon. ברירת המחדל "/favicon.ico". false מבטל את התג |
PAGE::add_meta('<meta name="viewport" content="width=device-width, initial-scale=1">');
PAGE::add_meta('<link rel="preconnect" href="https://fonts.gstatic.com">');
כותרת, תיאור ותגי Open Graph נקבעים דרך SEO::set, לא דרך add_meta, כדי שלא יופיעו פעמיים (תגי SEO).
אירועים#
מנגנון פשוט של hooks: PAGE::bind($event, Closure $func) רושם מאזין, ו-PAGE::trigger($event, $args = []) מפעיל את כולם לפי סדר הרישום, וכל אחד מקבל את $args. האירועים נמחקים ב-reset, ולכן רושמים אחרי תחילת load (ב-application/includes, לא ב-init.php).
// application/includes/hooks.php
PAGE::bind("SYSTEM_LOADED", function ($args) {
PAGE::add_asset("themes/default/extra.js", false, "body_end");
});
PAGE::bind("delete.cache", function ($args) {
// למשל: ניקוי מטמון חיצוני
});
האירועים שהליבה מפעילה בעצמה:
| אירוע | מתי | $args |
|---|---|---|
SYSTEM_LOADED | ב-PAGE::load, אחרי שהכל נטען ולפני הבקר | ריק |
delete.cache | אחרי "מחיקת מטמון" (Tools::delete_cache, וגשר ה-MCP) | ריק |
storage.update | אחרי עדכון קובץ באחסון | ["id" => <מזהה הקובץ>] |
words_update | אחרי עדכון מילים בפאנל ה-UI | ריק |
אתם מוזמנים להפעיל אירועים משלכם, כמו PAGE::trigger('order.created', $order), והם עובדים באותה דרך, אבל trigger ו-bind חייבים להיות באותה בקשה.
theme#
PAGE::$theme הוא שם ה-theme של העמוד. PAGE::set_theme($theme) מציבה אותו, ו-PAGE::set_theme(false) מוציאה את העמוד בלי theme (המסמך עדיין עטוף ב-<html>, אבל הגוף הוא הפלט של הבקר). כשאין בחירה אחרת בצד לקוח, load מציבה את platform_data["deftheme"]. הפרטים בתבניות ו-themes.
מטמון עמודים#
| חבר | פירוט |
|---|---|
PAGE::$cache_this_page | true שומר את העמוד המוגמר בקובץ (ברירת המחדל false) |
PAGE::$cache_this_page_hours | משך החיים בשעות (ברירת המחדל 24) |
בפועל לא כותבים אותם ישירות אלא קוראים ל-cache_engine::cache_all_page($hours = 24, $set_page = false) בבקר (מטמון עמודים). PAGE::$cache_this_page נשמר רק בצד לקוח וכש-CONFIG::$allow_cache דלוק, ו-MODULE::errorpage() מאפסת אותו כדי שעמוד 404 לא יישמר.
parse_page: עיבוד אחרי המטמון#
PAGE::$parse_page_func הוא callable אופציונלי שמקבל את ה-HTML המוגמר ומחזיר HTML. הוא רץ גם על עמוד שהוגש מהמטמון, ולכן הוא המקום לתוכן שחייב להיות טרי בכל בקשה (שם המשתמש בכותרת, מונה עגלה, nonce).
// application/includes/init.php (חייב להיות כאן כדי לרוץ גם בפגיעה במטמון)
PAGE::$parse_page_func = function ($html) {
return str_replace("{{year}}", date("Y"), $html);
};
PAGE::parse_page($content) מריצה את ה-callable ואחר כך את MASK::out על התוצאה (מצב הסוואה).
parse_page_func חייב להירשם ב-application/includes/init.php, לא ב-application/includes/<other>.php. בפגיעה במטמון, core.php מגיש את הקובץ לפני ש-PAGE::load רץ, ולכן קבצים אחרים ב-application/includes עוד לא נטענו וה-callable לא קיים. בנוסף, כל עוד הוא קיים, הליבה מוותרת על מסלול ההגשה המהיר של קבצי gzip ופותחת כל עמוד מהמטמון, מפענחת ומריצה אותו.מתודות נוספות#
| מתודה | פירוט |
|---|---|
PAGE::is_mobile() | true רק לטלפון (לא לטאבלט), לפי MobileDetect. התוצאה נשמרת לבקשה. מצריכה vendor/autoload.php של הליבה (Composer) |
PAGE::reset($params = false) | מאפסת את כל המצב. נקראת בתחילת load, ובדרך כלל לא צריך לקרוא לה |
PAGE::backup() | מחזירה מערך עם כל המצב הנוכחי, בפורמט ש-reset($params) מקבלת. שימושי כדי להריץ עמוד בתוך עמוד ולהחזיר את המצב |
PAGE::getAllScripts($html) | מחלצת מ-HTML את כל תגי <script> השלמים. משמשת את output להעברת הסקריפטים לסוף הגוף |
PAGE::minify_html($body) | כיווץ הרווחים, ראו האזהרה למעלה |
PAGE::output() | מרכיבה ומחזירה את המסמך |
PAGE::load($url, $isMobile = null) | המחזור המלא. $isMobile בוליאני מכריח את תוצאת is_mobile |
ראו גם#
- מחזור הבקשה: איך
PAGE::loadמתחבר ל-core.phpול-index. - צנרת הנכסים: minify, SCSS ו-
attaches_version. - תבניות ו-themes ו-Smarty.
- רפרנס PAGE: חתימות בלבד.