PAGE (רפרנס)

כל המתודות והמאפיינים הציבוריים של המחלקה PAGE: רישום נכסי CSS ו-JS, תגיות meta, אירועים, זיהוי נייד, רינדור העמוד ומטמון עמוד שלם.

⏱ 15 דק' קריאה 2780 מילים ערוך דף זה ב-GitHub

PAGE (system/collections/PAGE.php) היא מחלקה סטטית שמחזיקה את מצב העמוד הנוכחי ובונה ממנו את ה-HTML הסופי: אילו קבצי CSS ו-JS לטעון ובאיזה אזור, אילו תגיות meta להדפיס, איזו ערכת נושא לעטוף בה, ומה לשמור במטמון. אתר מתעסק איתה בעיקר מ-application/includes, מקונטרולרים ומ-theme. הדף הזה הוא הרפרנס המלא; הסבר מושגי מופיע ב-PAGE: נכסים, מטא ואירועים וב-צינור הנכסים.

system/ בדף הזה הוא תיקיית הליבה הפרוסה (api/core בריפו). כל החתימות מועתקות מהקוד.

מאפיינים סטטיים#

מאפייןברירת מחדל אחרי reset()משמעות
PAGE::$is_404falseדגל 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_pagefalseהאם לשמור את העמוד במטמון עמוד שלם
PAGE::$cache_this_page_hours24כמה שעות לשמור
PAGE::$include_system_jstrueהאם load() מוסיף את כל system/js/*.js אוטומטית
PAGE::$parse_page_funcfalsecallable שרץ על ה-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 הסופי של הבקשה (מחרוזת). אתר לא קורא לה; חשוב להכיר את הסדר שלה כדי לדעת מתי הגדרות שלכם נקלטות:

  1. reset(), ואם $isMobile הוא bool הוא נשמר כתוצאת הזיהוי (ובכך עוקף את is_mobile()).
  2. PARAMS::init(), SEO::init(), ROUTER::parse_friendly_url($url).
  3. בצד לקוח (אלא אם CONFIG::$ignore_domain_check === true): בדיקת דומיין. SERVER_NAME חייב להיות platform_data["domain"] או אחד מ-allowed_domains, אחרת die("URL ERROR"). הקונטרולרים minify ו-Tools ו-CRM.js פטורים.
  4. בצד לקוח: ROUTER::redirections() ואז FILES::include_dir(base_path/application/includes). כאן רצים קבצי ה-include של האתר.
  5. אם $include_system_js: כל system/js/*.js נרשם ב-add_asset, כש-system.js ב-head והשאר ב-body_end.
  6. בצד לקוח: טעינת minify_files ו-minify_groups מהמטמון (30 יום) ורישום כל קבוצה שה-theme שלה ריק או שווה ל-PAGE::$theme.
  7. באדמין: טעינת ספריות הפאנל ו-application/admin/includes.
  8. LANGS::init_words() ואז PAGE::trigger("SYSTEM_LOADED").
  9. בצד לקוח: אם לא נקבע theme, deftheme של הפלטפורמה; ואז MODULE::get_html(...). באדמין: AdminModule או מסך התחברות.
  10. 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> בזמן הפלט
$musttrue ⇒ נכלל גם כש-IncludeJsCss(true) נקרא (ראו למטה). ברירת מחדל false
$areahead (ברירת מחדל), 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
asyncfalseJS: מאפיין async="async". CSS: טעינה לא חוסמת, <link rel="preload" as="style"> ועוד <link media="print" onload="this.media='all'">
deferfalseJS בלבד: defer="defer"
printfalseCSS בלבד: הקובץ עובר FILES::scss() ומודפס inline בתוך <style>; אם ההמרה נכשלה (מחזירה false), נופל ל-<link> רגיל
after_user_intractionfalsetrue ⇒ הערך נדחף ל-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]);
after_user_intraction מריץ eval
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");   // לפני שמוסיפים גרסה אחרת
נכסי קבוצת minify
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 מותאם.

פרמטרמשמעות
$onlyMusttrue ⇒ רק נכסים שנרשמו עם $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");
לא רק meta

למרות השם, זה המנגנון להזרקת כל קטע 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_LOADEDPAGE::load(), אחרי טעינת מילים ולפני הרינדור[]
storage.updateadmin/storage.php["id" => <storage id>]
words_updateפאנל המילים הישן (ui_langs_legacy.php)[]
delete.cacheTools ו-agent_mcp אחרי ניקוי מטמון[]
bind חייב לרוץ לפני trigger
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");
}
תלות ב-Composer ועלות חוזרת

הפונקציה מבצעת 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.

מצב מלא. סדר ההדפסה:

  1. <!DOCTYPE html><html lang="he" dir="<direction>"> ו-<meta charset>. השפה בתגית ה-html קשיחה he, בלי קשר לשפת האתר.
  2. 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.
  3. באדמין: <title> מ-seo_title של הפלטפורמה. בצד לקוח: SEO::get_schema("WebPage") וסקריפטים פעילים מטבלת scripts עם area = head (מטמון 48 שעות).
  4. <body class="<direction> <MISC::$GLOBALS["body_class"]>">, נכסי body_start, סקריפטים של body_start.
  5. תוכן ה-theme (או התוכן הישיר).
  6. נכסי body_end ו-IncludeAfterIntercation().
  7. כל תגיות <script> inline שה-theme או הקונטרולר הדפיסו (חוץ מאלה שמכילות את המחרוזת nominify) מוסרות ממקומן, מוקטנות עם JShrink ומודפסות כאן, בסוף ה-body.
  8. writeLoadedFile(), סקריפטים של body_end מטבלת scripts, print_meta_tags("body_end") ואם קיים system/system_tags.json גם tag_manager.js.

בצד לקוח ל-SeoBots::track() נקראת פעם אחת בכל רינדור מלא (שגיאה שלה לא תפיל עמוד).

הזזת סקריפטים inline

כל <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 חייב להיות מוצהר

הפונקציה קוראת ל-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);
};
משבית הגשה ישירה של gzip

כש-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;) כדי לבדוק מה נרשם.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.