PAGE (system/collections/PAGE.php) היא מחלקה סטטית שמחזיקה את מצב העמוד הנוכחי ובונה ממנו את ה-HTML הסופי: אילו קבצי CSS ו-JS לטעון ובאיזה אזור, אילו תגיות meta להדפיס, איזו ערכת נושא לעטוף בה, ומה לשמור במטמון. אתר מתעסק איתה בעיקר מ-application/includes, מקונטרולרים ומ-theme. הדף הזה הוא הרפרנס המלא; הסבר מושגי מופיע ב-PAGE: נכסים, מטא ואירועים וב-צינור הנכסים.
system/ בדף הזה הוא תיקיית הליבה הפרוסה (api/core בריפו). כל החתימות מועתקות מהקוד.
מאפיינים סטטיים#
| מאפיין | ברירת מחדל אחרי reset() | משמעות |
|---|---|---|
PAGE::$is_404 | false | דגל 404 (בקוד הליבה אין מי שקורא אותו; הוא זמין לקוד האתר) |
PAGE::$favicon | "/favicon.ico" | כתובת ה-favicon של האתר. false מדלג על התגית. באדמין תמיד assets/themes/admin_panel/images/favicon.ico |
PAGE::$robots | "ALL" | הערך של <meta name="ROBOTS">. לא מאופס ב-reset() |
PAGE::$theme | "" | שם ערכת הנושא. ריק בצד לקוח ⇒ platform_data["deftheme"] |
PAGE::$assets_css / $assets_js | [] | מערכי AddedFile (ראו בסוף הדף) |
PAGE::$assets_after_intercation | [] | נכסים וקטעי קוד שייטענו באינטראקציה ראשונה (כתיב intercation הוא הכתיב בקוד) |
PAGE::$meta_tags | [] | מערך של ["value" => ..., "area" => ...] |
PAGE::$events | [] | מאזינים רשומים לפי שם אירוע |
PAGE::$cache_this_page | false | האם לשמור את העמוד במטמון עמוד שלם |
PAGE::$cache_this_page_hours | 24 | כמה שעות לשמור |
PAGE::$include_system_js | true | האם load() מוסיף את כל system/js/*.js אוטומטית |
PAGE::$parse_page_func | false | callable שרץ על ה-HTML הסופי בכל בקשה, גם מהמטמון (ראו parse_page) |
PAGE::$group_files ו-PAGE::$is_mobile_results הם private. הם נכללים ב-backup() ובמערך הפרמטרים של reset(), אבל אין להם גישה ישירה מהאתר.מחזור חיים: reset, backup, load#
PAGE::reset()#
public static function reset($params = false)
מאפס את כל המאפיינים הסטטיים. $params הוא מערך אופציונלי עם אותם מפתחות של backup(); מפתח שחסר מקבל את ברירת המחדל. הערך false (ברירת המחדל) מאפס הכול. PAGE::$robots, PAGE::$parse_page_func ו-MISC::$GLOBALS לא נוגעים כאן. PAGE::load() קורא ל-reset() בתחילתו, ולכן הגדרות שנעשו לפני load (למשל ב-CONFIG_USER) לא שורדות.
PAGE::backup()#
public static function backup()
מחזיר מערך אסוציאטיבי עם צילום מצב של כל מפתחות ה-reset(): is_404, favicon, theme, assets_css, assets_js, assets_after_intercation, meta_tags, events, group_files, is_mobile_results, cache_this_page, cache_this_page_hours, include_system_js. מיועד לשימוש ב-reset($backup) כדי לרנדר עמוד פנימי ואז לחזור למצב הקודם.
$saved = PAGE::backup();
PAGE::reset();
// ... רינדור משני (למשל תבנית מייל) ...
PAGE::reset($saved);
PAGE::load()#
public static function load($url, $isMobile = null)
נקראת פעם אחת מ-core.php עם $_SERVER['REQUEST_URI'], ומחזירה את ה-HTML הסופי של הבקשה (מחרוזת). אתר לא קורא לה; חשוב להכיר את הסדר שלה כדי לדעת מתי הגדרות שלכם נקלטות:
reset(), ואם$isMobileהואboolהוא נשמר כתוצאת הזיהוי (ובכך עוקף אתis_mobile()).PARAMS::init(),SEO::init(),ROUTER::parse_friendly_url($url).- בצד לקוח (אלא אם
CONFIG::$ignore_domain_check === true): בדיקת דומיין.SERVER_NAMEחייב להיותplatform_data["domain"]או אחד מ-allowed_domains, אחרתdie("URL ERROR"). הקונטרולריםminifyו-Toolsו-CRM.jsפטורים. - בצד לקוח:
ROUTER::redirections()ואזFILES::include_dir(base_path/application/includes). כאן רצים קבצי ה-include של האתר. - אם
$include_system_js: כלsystem/js/*.jsנרשם ב-add_asset, כש-system.jsב-headוהשאר ב-body_end. - בצד לקוח: טעינת
minify_filesו-minify_groupsמהמטמון (30 יום) ורישום כל קבוצה שה-themeשלה ריק או שווה ל-PAGE::$theme. - באדמין: טעינת ספריות הפאנל ו-
application/admin/includes. LANGS::init_words()ואזPAGE::trigger("SYSTEM_LOADED").- בצד לקוח: אם לא נקבע theme,
defthemeשל הפלטפורמה; ואזMODULE::get_html(...). באדמין:AdminModuleאו מסך התחברות. PAGE::output().
נכסים שחייבים להיות בכל עמוד שייכים ל-application/includes/*.php (רץ בשלב 4, אחרי reset ולפני הרינדור) או ל-theme. קוד שרץ לפני PAGE::load נמחק על ידי reset().
נכסי CSS ו-JS#
סוג הנכס נקבע לפי המחרוזת, לא לפי פרמטר: אם הכתובת מכילה .css זה CSS; אחרת אם מכילה .js זה JS; אחרת אם מכילה את הרצף css זה CSS; כל שאר המקרים JS. לכן https://fonts.googleapis.com/css2?family=Heebo נרשם כ-CSS, ו-style.scss.css (הצורה שבה מגישים SCSS) גם כן.
רישום חוזר של אותה כתובת מוחק את הרישום הישן ומוסיף מחדש בסוף הרשימה (או בהתחלה עם prepend ב-add_asset). ההשוואה היא על המחרוזת המדויקת: /x.js ו-x.js נחשבים לשני נכסים שונים.
PAGE::add_asset()#
public static function add_asset($fileSrc, $must = false, $area = "head", $order = "append")
| פרמטר | משמעות |
|---|---|
$fileSrc | נתיב יחסי לשורש האתר (themes/main/style.scss.css) או כתובת מלאה http(s)://. יחסי ⇒ מקבל קידומת CONFIG::$protocol://<original_site_url> בזמן הפלט |
$must | true ⇒ נכלל גם כש-IncludeJsCss(true) נקרא (ראו למטה). ברירת מחדל false |
$area | head (ברירת מחדל), body_start או body_end. ערך אחר נרשם אבל לא יודפס אף פעם, כי output() מבקש רק את שלושת האזורים האלה |
$order | "append" (ברירת מחדל) או "prepend", רק prepend מכניס לתחילת הרשימה |
ערך החזרה: אין (void).
// application/includes/assets.php
PAGE::add_asset("themes/main/style.scss.css");
PAGE::add_asset("themes/main/app.js", false, "body_end");
PAGE::add_asset("themes/main/vendor/jquery.js", false, "head", "prepend");
PAGE::add_asset_advanced()#
public static function add_asset_advanced($fileSrc, $m_settings = [])
כמו add_asset, עם אפשרויות טעינה. $m_settings ממוזג מעל ברירות המחדל:
| מפתח | ברירת מחדל | משמעות |
|---|---|---|
area | "head" | head / body_start / body_end |
async | false | JS: מאפיין async="async". CSS: טעינה לא חוסמת, <link rel="preload" as="style"> ועוד <link media="print" onload="this.media='all'"> |
defer | false | JS בלבד: defer="defer" |
print | false | CSS בלבד: הקובץ עובר FILES::scss() ומודפס inline בתוך <style>; אם ההמרה נכשלה (מחזירה false), נופל ל-<link> רגיל |
after_user_intraction | false | true ⇒ הערך נדחף ל-PAGE::$assets_after_intercation ומוחזר מיד, בלי סיווג CSS/JS ובלי בדיקת כפילות |
ערך החזרה: אין. אין פרמטר must, ולכן נכס שנרשם כאן אף פעם לא נכלל ב-IncludeJsCss(true).
PAGE::add_asset_advanced("themes/main/fonts.scss.css", ["area" => "head", "async" => true]);
PAGE::add_asset_advanced("themes/main/critical.scss.css", ["print" => true]);
PAGE::add_asset_advanced("themes/main/chat.js", ["area" => "body_end", "defer" => true]);
// נטען רק בלחיצה או בלחיצת מקש הראשונה של הגולש
PAGE::add_asset_advanced("https://widget.example.com/loader.js", ["after_user_intraction" => true]);
IncludeAfterIntercation() כותב ל-HTML קריאה ל-apply_after_interaction([...]) (מ-system/js/system.js). לכל ערך שנראה כמו HTML (מכיל תגית) הפונקציה מסירה את תגיות <script> ומריצה את התוכן ב-eval; כל ערך אחר נטען כקובץ דרך loadobjs. לכן אל תעבירו לשם קלט שמקורו במשתמש. הטעינה מתרחשת פעם אחת, באירוע click או keydown.PAGE::remove_asset()#
public static function remove_asset($fileSrc)
מוחק מרשימת ה-CSS או ה-JS (לפי אותו סיווג מחרוזת) כל נכס שה-src שלו שווה בדיוק ל-$fileSrc. אין החזרה. שימושי כדי להסיר נכס ש-PAGE::load הוסיף אוטומטית:
PAGE::remove_asset("system/js/jquery.min.js"); // לפני שמוסיפים גרסה אחרת
remove_asset עובד על PAGE::$assets_css ו-PAGE::$assets_js. קבצים ששייכים לקבוצת minify מוסתרים מההדפסה דרך $group_files פרטי, ואי אפשר להסיר אותם מכאן.PAGE::add_asset_group()#
public static function add_asset_group($id)
רושם קבוצת minify (טבלת minify_groups) כנכס יחיד minify/<id>.<type>, עם area ו-async מהקבוצה, ומסמן את כל הקבצים של הקבוצה (minify_files עם group_id זהה) כך שלא יודפסו בנפרד. נקראת אוטומטית מ-PAGE::load() בצד לקוח, לכל קבוצה שה-theme שלה ריק או שווה ל-theme הנוכחי. תלויה ב-MISC::$GLOBALS["minify_groups"] ו-["minify_files"], ולכן לא ניתן לקרוא לה לפני load. ראו צינור הנכסים.
PAGE::IncludeJsCss()#
public static function IncludeJsCss($onlyMust = false, $type = "both", $area = "")
מחזירה מחרוזת HTML עם תגיות <link> ו-<script> של הנכסים הרשומים. נקראת על ידי output() שש פעמים (CSS ו-JS, לכל אחד משלושת האזורים). אפשר לקרוא לה גם מ-theme מותאם.
| פרמטר | משמעות |
|---|---|
$onlyMust | true ⇒ רק נכסים שנרשמו עם $must = true |
$type | "js", "css" או "both" (CSS קודם) |
$area | "" = כל האזורים; אחרת רק נכסים עם אותו area |
כללי הפלט:
- נכס שנמצא ב-
$group_files(שייך לקבוצת minify) מדולג. - כתובת יחסית מקבלת קידומת
<protocol>://<original_site_url ללא http/https>. - לכל קישור מתווסף
?ver=<PARAMS attaches_version>דרךMISC::add_querystring_var. העלאתattaches_versionבפאנל מבטלת את המטמון של הדפדפן. - נכס עם
after_user_intractionלא מודפס כאן.
PAGE::IncludeAfterIntercation()#
public static function IncludeAfterIntercation()
אם PAGE::$assets_after_intercation לא ריק, מחזירה בלוק <script> (מחרוזת) שמגדיר applyHTML() ורושם אותה על click ו-keydown של ה-document (ומסיר את המאזינים אחרי הריצה הראשונה). אם הרשימה ריקה לא מוחזר דבר (null). output() קורא לה בסוף ה-body.
PAGE::JsIncludeJsCss()#
public static function JsIncludeJsCss()
מיועדת לעמודים שנטענים ב-AJAX (pmode=empg / pmode=inner). במקום להדפיס נכסים, שולחת שתי כותרות HTTP: scripts (מערך JSON של src של כל נכסי ה-JS ואחריהם ה-CSS, אחרי MASK::out) ו-scripts_ver (גרסת attaches_version). הקוד בצד הדפדפן משתמש בהן כדי לטעון נכסים חסרים. חייבת להיקרא לפני שנשלח פלט, אחרת header() ייכשל. output() קורא לה בעצמה במצבים האלה.
PAGE::writeLoadedFile()#
public static function writeLoadedFile($onlyMust = false)
מחזירה <script> עם קריאה ל-writeLoadedFile('a','b',...) שמסמנת בצד הדפדפן אילו נכסים כבר נטענו, כדי ש-loadobjs לא יטען אותם שוב. מחזירה מחרוזת ריקה אם אין נכסים רשומים. עם $onlyMust = true רק נכסי must. נקראת על ידי output() בסוף ה-body.
תגיות meta#
PAGE::add_meta()#
public static function add_meta($val, $area = "head")
מוסיפה מחרוזת HTML גולמית לרשימה. $val מודפס כמו שהוא, בלי escape. $area: head, body_start או body_end.
PAGE::add_meta('<meta name="theme-color" content="#0a2540">');
PAGE::add_meta('<script>window.APP = {lang: "he"};</script>', "head");
למרות השם, זה המנגנון להזרקת כל קטע HTML לאזור (סקריפט inline, <link rel="preconnect">, תגית analytics). תגיות הכותרת וה-Open Graph אינן כאן: הן נבנות ב-SEO::tags_summary() (ראו SEO (רפרנס)).
PAGE::print_meta_tags()#
public static function print_meta_tags($area = "")
מחזירה את הערכים של כל התגיות ב-PAGE::$meta_tags שמתאימות ל-$area (או את כולן כש-$area ריק), משורשרים. output() קורא לה עבור head, body_start ו-body_end.
ערכת נושא#
PAGE::set_theme()#
public static function set_theme($theme)
קובעת PAGE::$theme. output() טוען themes/<theme>/index.php מתיקיית האתר (CONFIG::$base_path), יוצר new TEMPLATE_<theme>(<theme>) וקורא ל-get_html(). הערכים admin_login ו-admin_panel נטענים מתיקיית הליבה (system/themes/). ערך ריק ⇒ ה-HTML של הקונטרולר מוצג בלי עטיפה. ראו ערכות נושא.
// בתוך מתודה של קונטרולר: עמוד נחיתה בלי header/footer של האתר
PAGE::set_theme("landing");
PAGE::load מציבה deftheme של הפלטפורמה רק אם PAGE::$theme ריק בשלב זה, לכן set_theme מ-application/includes מנצח.אירועים#
מנגנון אירועים פשוט בתוך הבקשה (לא נשמר בין בקשות). PAGE::$events מאופס בכל reset().
PAGE::bind()#
public static function bind($event, Closure $func)
רושמת מאזין. מאזינים של אותו אירוע רצים לפי סדר הרישום. חייב להיות Closure (לא שם פונקציה).
PAGE::trigger()#
public static function trigger($event, $args = array())
מריצה כל מאזין רשום בקריאה $func($args): המאזין מקבל את המערך כארגומנט יחיד. ערכי החזרה של מאזינים מתעלמים, וחריגה שמאזין זורק עוברת הלאה (אין try/catch).
PAGE::bind("order.paid", function ($args) {
error_log("order " . $args["id"] . " paid by " . $args["email"]);
});
PAGE::trigger("order.paid", ["id" => 42, "email" => "a@example.com"]);
אירועי מערכת#
| אירוע | נשלח מ- | args |
|---|---|---|
SYSTEM_LOADED | PAGE::load(), אחרי טעינת מילים ולפני הרינדור | [] |
storage.update | admin/storage.php | ["id" => <storage id>] |
words_update | פאנל המילים הישן (ui_langs_legacy.php) | [] |
delete.cache | Tools ו-agent_mcp אחרי ניקוי מטמון | [] |
SYSTEM_LOADED נשלח לפני שה-controller רץ, אבל אחרי application/includes. מאזין שנרשם בקונטרולר יפספס אותו. בנוסף, reset() מוחק את כל המאזינים, כך שרישום לפני load() אובד.זיהוי נייד#
PAGE::is_mobile()#
public static function is_mobile()
מחזירה true רק עבור טלפון. טאבלט ומחשב מחזירים false. הזיהוי נעשה עם Detection\MobileDetect (ספריית vendor/autoload.php של הליבה), והתוצאה נשמרת ב-$is_mobile_results לשארית הבקשה. PAGE::load($url, true|false) מאפשרת לקבוע את התוצאה מראש.
if (PAGE::is_mobile()) {
PAGE::add_asset("themes/main/mobile.scss.css");
}
הפונקציה מבצעת require (לא require_once) של system/vendor/autoload.php בכל קריאה. אם התיקייה vendor לא הותקנה (היא לא חלק מהריפו, ראו חבילות Composer) הקריאה תיכשל בשגיאה קטלנית. כמו כן, כשהתוצאה היא false היא לא נשמרת כ"מוכנת" ולכן הזיהוי רץ מחדש בכל קריאה.
רינדור ופלט#
PAGE::output()#
public static function output()
מרכיבה את מסמך ה-HTML ומחזירה אותו כמחרוזת (אחרי minify_html). נקראת מ-PAGE::load(); אתר לא אמור לקרוא לה. התנהגות:
מצב ללא עטיפה. אם $_GET["pmode"] הוא empg או inner: קורא ל-JsIncludeJsCss() ומחזיר רק את MISC::$GLOBALS["app_html"] (פלט הקונטרולר), בלי <html>. כך קונטרולר שמחזיר JSON, קובץ או קטע AJAX מונע עטיפה: הוא קובע $_GET["pmode"] = "empg"; לפני ה-return.
מצב מלא. סדר ההדפסה:
<!DOCTYPE html><html lang="he" dir="<direction>">ו-<meta charset>. השפה בתגית ה-htmlקשיחהhe, בלי קשר לשפת האתר.SEO::tags_summary(),<base href>, נכסיhead(CSS ואז JS), תגיות meta שלhead,<meta name="ROBOTS" content="<PAGE::$robots>">, וקרדיט<meta name="copyright">(מושבת עםCONFIG::$show_wizzo_credits = false), ו-favicon.- באדמין:
<title>מ-seo_titleשל הפלטפורמה. בצד לקוח:SEO::get_schema("WebPage")וסקריפטים פעילים מטבלתscriptsעםarea = head(מטמון 48 שעות). <body class="<direction> <MISC::$GLOBALS["body_class"]>">, נכסיbody_start, סקריפטים שלbody_start.- תוכן ה-theme (או התוכן הישיר).
- נכסי
body_endו-IncludeAfterIntercation(). - כל תגיות
<script>inline שה-theme או הקונטרולר הדפיסו (חוץ מאלה שמכילות את המחרוזתnominify) מוסרות ממקומן, מוקטנות עם JShrink ומודפסות כאן, בסוף ה-body. writeLoadedFile(), סקריפטים שלbody_endמטבלתscripts,print_meta_tags("body_end")ואם קייםsystem/system_tags.jsonגםtag_manager.js.
בצד לקוח ל-SeoBots::track() נקראת פעם אחת בכל רינדור מלא (שגיאה שלה לא תפיל עמוד).
כל <script> inline בתוך ה-HTML (גם כזה שנכתב באמצע התבנית) עובר לסוף ה-body. סקריפט עם src= מועבר כמו שהוא, וסקריפט inline מוקטן. קוד שתלוי במיקום ההגדרה בדף, או שמחזיר סדר ריצה מסוים מול jquery, ישתנה. כדי להשאיר סקריפט במקומו וללא כיווץ, הוסיפו למחרוזת התגית את המילה nominify (למשל <script nominify>). הזיהוי הוא regex ולא parser, ולכן <script> בתוך מחרוזת JS עלול להתפרש לא נכון.
PAGE::minify_html()#
static function minify_html($body)
כיווץ HTML ב-regex: הסרת רווחים וטאבים סביב תגיות, איחוד רווחים, הסרת שורות ריקות, וצמצום מעברי שורה אחרי ){ ו-,{ ב-JS. מוחזר המקור אם CONFIG::$minify_html === false. output() מפעיל אותה תמיד כשורה האחרונה.
הפונקציה קוראת ל-CONFIG::$minify_html ללא isset. אם הנכס לא מוגדר ב-CONFIG_USER מתקבלת שגיאה. כמו כן, הכיווץ פועל גם על תוכן <pre>, <textarea> ועל JS inline: רצפי רווחים בתוכם מתכווצים, והערות // שמורכבות מאותיות לטיניות, ספרות ורווחים בלבד נמחקות. אם יש לכם תוכן רגיש לרווחים, כבו את הכיווץ: CONFIG::$minify_html = false.
PAGE::parse_page()#
static function parse_page($content)
נקראת אחרונה, ב-core.php, ממש לפני ה-echo. אם PAGE::$parse_page_func הוא callable, מחזירה MASK::out(call_user_func(PAGE::$parse_page_func, $content)); אחרת מחזירה את $content כפי שהוא. הקריאה רצה בכל בקשה, גם כשהעמוד הוגש מהמטמון, ולכן זה המקום להזרקת תוכן דינמי (שם משתמש, עגלה) לעמוד שנשמר במטמון.
// application/includes/cache_hooks.php
PAGE::$parse_page_func = function ($html) {
return str_replace("{{is_logged}}", LOGIN::is_logged() ? "1" : "0", $html);
};
כש-parse_page_func מוגדר, מנגנון הגשת ה-.html.gz בלי PHP מבוטל, כי התוכן חייב לעבור דרך הפונקציה. ראו מטמון עמודים מלא.
מטמון עמוד שלם#
PAGE::$cache_this_page ו-PAGE::$cache_this_page_hours נשלטים בפועל על ידי cache_engine::cache_all_page($hours = 24, $set_page = false), שמציבה אותם (true ושעות). ב-core.php, אם CONFIG::$system_type == "client", CONFIG::$allow_cache == true ו-PAGE::$cache_this_page === true, הפלט (אחרי MASK::out) נשמר דרך cache_engine::cache_all_page_save. ה-MODULE ו-MISC מכבים את הדגל (false) במצבי שגיאה. פרטים ב-מטמון עמודים מלא וב-cache_engine API.
// בתחילת מתודת קונטרולר של עמוד סטטי יחסית
cache_engine::cache_all_page(6);
return $this->view("about", []);
פונקציות עזר#
PAGE::getAllScripts()#
static function getAllScripts($html)
מחזירה מערך של מחרוזות: כל בלוק <script ...>...</script> שלם שנמצא ב-$html, לפי סדר הופעתו (התאמת פתיחה לסגירה לפי מיקום, regex בלבד). משמשת את output() לבודד סקריפטים inline. אפשר להשתמש בה בעצמכם לניתוח HTML, בידיעה שהיא לא מתמודדת עם </script> בתוך מחרוזות.
המחלקה AddedFile#
class AddedFile
{
var $src = "";
var $must = false;
var $area = "head";
var $async = false;
var $after_user_intraction = false;
var $defer = false;
var $print = false;
function __construct($m_src, $m_must = false, $m_area = "head")
}
אובייקט שמייצג נכס רשום; הבנאי מציב רק src, must, area. שאר השדות נקבעים על ידי add_asset_advanced. אפשר לקרוא את הרשימות ישירות (foreach (PAGE::$assets_js as $f) echo $f->src;) כדי לבדוק מה נרשם.
ראו גם#
- PAGE: נכסים, מטא ואירועים: הסבר מושגי ודוגמאות לשימוש.
- צינור הנכסים (SCSS, Minify) ו-FILES
- ערכות נושא ו-תגיות SEO ו-Schema
- מחזור חיי בקשה ו-404, 503 ושגיאות
- מטמון עמודים מלא ו-cache_engine API
- מצב הסוואה (MASK)