בעמוד הזה: מה גורם ל-404, איך בונים עמוד 404 מותאם, איך הליבה מפרידה בין "העמוד לא קיים" ל"בסיס הנתונים לא זמין" (404 מול 503), ומה כל אחד משאר מצבי הכשל מחזיר בפועל. ההבדל בין 404 ל-503 חשוב ל-SEO: גוגל מסיר מהאינדקס כתובות שענו 404, אבל מתייחס ל-503 כאל תקלה זמנית.
מה גורם ל-404#
הניתוב (ניתוב) לא יודע מראש אם עמוד קיים. הוא מנתח את הכתובת לבקר ולמתודה, ו-MODULE::get_html מחליט. הוא מחזיר את המחרוזת "ERRORPAGE" (ואז מופעלת MODULE::errorpage()) באחד המקרים האלה:
| המצב | דוגמה |
|---|---|
| אין מודול בכתובת | נתיב שלא הצליח להתפרש לשם מודול |
המודול לא קיים ב-CRM_modules, או שאינו פעיל (active != 1), ואין קובץ בקר | /blat |
| המודול פעיל, אבל קובץ הבקר או המחלקה חסרים | שורה ב-CRM_modules בלי application/controllers/<name>.php |
הבקר קיים אבל המתודה לא קיימת או לא ציבורית (is_callable) | /news/nothing |
| בקר מערכת לא קיים, או מתודה לא ציבורית בו | /system/nothing |
הבקר עצמו החזיר "ERRORPAGE" | return "ERRORPAGE"; כשהרשומה לא נמצאה |
המקרה האחרון הוא הדרך הנכונה לעשות 404 מתוך בקר: לא header(...) ולא die, אלא return "ERRORPAGE", ואז הליבה מציגה את עמוד ה-404 של האתר בתוך ה-theme כרגיל.
מודול שקיים כקובץ אבל לא הופעל ב-CRM_modules מציג לאדמין מחובר טופס "Click here to activate this module" (שנשלח אל /{admin}/Modules/insert) ולא 404. למבקר רגיל אותו מודול נותן 404. לכן "זה עובד לי אבל לא ללקוחות" פירושו כמעט תמיד מודול שלא הופעל (בקרים).
מה עושה MODULE::errorpage()#
errorpage()
1. DB::had_error()? כן -> MISC::service_unavailable(...) = 503, ונגמר
2. ROUTER::parse_friendly_url("404") מנתב מחדש אל כתובת 404
- שולח HTTP 404 ומסמן PAGE::$is_404 = true
3. PAGE::$cache_this_page = false עמוד 404 לא נשמר במטמון העמודים
4. אם המודול של עמוד ה-404 פעיל והמתודה קיימת: מריץ אותה ומחזיר את התוצאה
5. אחרת: מחזיר <h1>404 - Page not found</h1>
כלומר כותרת ה-HTTP 404 נשלחת בשלב 2, אחרי שכבר נבחר שהעמוד לא קיים, והעמוד שמוצג בשלב 4 הוא תוצר רגיל של בקר, עטוף ב-theme של האתר (PAGE::$is_404 זמין ל-theme או לקוד שרוצה להתאים את התצוגה).
הגדרת עמוד 404 משלכם#
עמוד ה-404 הוא כלל seoUrl רגיל עם sysname = '404'. ה-pageurl שלו הוא <module>/<method>, והמודול חייב להיות פעיל ב-CRM_modules, כי errorpage() בודקת זאת לפני שהיא מריצה אותו.
INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('404', 'errors/not_found', 0, 0);
הבקר, application/controllers/errors.php:
<?php
class errors extends bgl_controller
{
public function not_found()
{
SEO::set("title", "העמוד לא נמצא");
PAGE::$robots = "NOINDEX, FOLLOW";
return $this->view("errors/not_found"); // application/views/errors/not_found.tpl
}
}
אחרי הוספת הבקר מפעילים אותו ב-CRM_modules (או בפאנל המודולים) ומנקים את מטמון modules, ראו בקרים. את הכלל אפשר גם להוסיף מפאנל {admin}/Seo (כתובות SEO).
אם לא הגדרתם כלל 404, המסך הוא ה-<h1>404 - Page not found</h1> הגולמי, בתוך ה-theme של האתר, וסטטוס 404 נשלח כרגיל.
ב-ROUTER::parse_friendly_url יש ענף שמדפיס "page not found. please set 404 page." כשאין כלל 404. הוא קוד מת: ברגע שאין כלל, $pageUrl מקבל את הכתובת עצמה ("404") לפני הבדיקה, ולכן התנאי אף פעם לא מתקיים (ממצא CORE-34). ההתנהגות בפועל היא ה-<h1> שלמעלה.
503 במקום 404: כשבסיס הנתונים נכשל#
הניתוב תלוי בשתי טבלאות מהמסד: seoUrl ו-modules. שאילתה שנכשלת מחזירה תוצאה ריקה ולא שגיאה, ולכן מסד עמוס ריקן את modules, וכל עמוד תקין נראה "לא קיים". כדי שזה לא ייחשב ל-404 (שמוחק את העמוד מגוגל), הליבה סופרת כשלים בבקשה: DB::$error_count עולה על כל שאילתה שנכשלה, ו-DB::had_error() מחזיר true אם נספר אחד.
MODULE::errorpage() בודקת את זה ראשונה: אם הייתה שגיאת מסד בבקשה הנוכחית, היא קוראת ל-MISC::service_unavailable($reason = "", $retry_after = 120):
| פעולה | פירוט |
|---|---|
| ניקוי | סוגר את כל ה-output buffers, כך שלא יוצא עמוד חלקי |
| כותרות | HTTP/1.1 503 Service Unavailable, Retry-After: 120 (או הערך שהועבר), Cache-Control: no-store, no-cache, must-revalidate, max-age=0 |
| מטמון | PAGE::$cache_this_page = false, כך שהתשובה לא נשמרת כעמוד |
| גוף | עמוד HTML קטן וקבוע, noindex, "השירות אינו זמין כרגע" |
| לוג | error_log("503 service_unavailable: <סיבה> | <REQUEST_URI>") |
| סיום | exit. בקריאה חוזרת באמצע הקריסה עצמה היא פשוט יוצאת |
באותה דרך, כשל בחיבור למסד (PDO, mysql או pg) קורא ישירות ל-MISC::service_unavailable. ב-CLI (php_sapi_name() === "cli") הפונקציה כותבת את הסיבה ל-STDERR ויוצאת עם קוד 1 במקום לשלוח כותרות.
אפשר לקרוא לה גם מהקוד שלכם, כשאתם יודעים שהכשל הוא תשתיתי:
$rows = DB::query("products", ["active" => 1])->get_all();
if (DB::had_error()) MISC::service_unavailable("products query failed: " . DB::$last_error, 60);
DB::had_error() נספר על כל הבקשה, לא רק על השאילתה שלכם. שאילתה כושלת אחת, גם לא קשורה, שגורמת בהמשך ל-ERRORPAGE, תהפוך את ה-404 ל-503. זה מכוון (עדיף 503 מיותר על 404 שמוחק עמוד), אבל כשחוקרים 503 בלתי צפוי מחפשים בלוג השגיאות שורה 503 service_unavailable ואת השאילתה שנכשלה לפניה (אבחון).המטמון עצמו מוגן באותו עיקרון: cache_engine::get לא שומר ערך שחושב בזמן שנספרה שגיאת מסד (מטמון).
שאר מצבי הכשל#
| מה רואים | מי מייצר | מתי |
|---|---|---|
URL ERROR (טקסט פשוט) | PAGE::load | הדומיין של הבקשה לא ב-platform_data["domain"] וגם לא ב-allowed_domains. אפשר לכבות עם CONFIG::$ignore_domain_check = true. לא חל על minify ו-Tools |
router error (טקסט פשוט) | ROUTER::parse_friendly_url | אף תבנית לא התאימה (אחרי שהקודמות נכשלו), נדיר מאוד |
ERROR | בקר ספציפי | בקרים כמו pdf_viewer מחזירים את המחרוזת הזו כשהמזהה לא תקין |
false / true | Tools::delete_cache | false כשאין הרשאת אדמין |
scss compile failed (500, no-store) | minify::scss | שגיאת קומפילציה ב-SCSS (צנרת נכסים) |
| דף אדמין "Click here to activate" | MODULE::get_html | מודול עם קובץ אבל בלי הפעלה, לאדמין בלבד |
הראשונות הן תשובות עם סטטוס 200 ולא 404, ולכן נראות לחיפוש כ"עמוד תקין עם טקסט קצר". אם אתם רואים אותן בדוחות SEO, מקורן ברוב המקרים בנכס או בבקשה לדומיין לא מוגדר.
ראו גם#
- ניתוב ו-כתובות SEO: איך הכתובת נפתרת ואיך מוגדר כלל 404.
- בקרים: הפעלת מודול והחזרת
"ERRORPAGE". - מעקב ניתוב: הצגת שלבי
MODULE → ERRORPAGEו-errorpage() → re-route. - קודי שגיאה ו-אבחון.