FILES (רפרנס)

כל המתודות הציבוריות של המחלקה FILES: רינדור תבניות (Smarty, plain, eval), טעינת תיקיות, קומפילציית SCSS, טעינת bundle של Vue ועזרי קבצים ותיקיות.

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

FILES (system/collections/FILES.php) היא מחלקה סטטית קטנה עם שלושה תפקידים: רינדור תבניות (html_template, string_template), טעינת נכסים (scss, vue, include_dir) ועזרי מערכת קבצים (make_dir, remove_dir, is_img, get_extension). רוב האתרים פוגשים אותה בעקיפין: $this->view() בקונטרולר וה-theme קוראים ל-html_template, ו-PAGE קורא ל-scss ול-include_dir.

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

תבניות#

FILES::html_template()#

public static function html_template($tmpl_file, $varsArr = array(), $settings = array())

קוראת קובץ תבנית, מציבה בו משתנים, ומחזירה את ה-HTML כמחרוזת. זו הפונקציה שמאחורי $this->view("x", [...]) (קורא ל-html_template(CONFIG::$base_path . "/application/views/x.tpl", $vars)), $this->system_view() (תיקיית views של הליבה) ושל ה-theme (themes/<name>/index.tpl, ובמקרה של admin_login / admin_panel מתיקיית הליבה).

פרמטרמשמעות
$tmpl_fileנתיב לקובץ. יחסי ל-CWD של התהליך (שורש האתר בבקשת web) או מוחלט. נתיב שמתחיל ב-assets/ מתורגם ל-system/assets/
$varsArrמערך משתנים לתבנית (["title" => "..."])
$settings["cache"]false (ברירת מחדל). true מפעיל caching של Smarty ל-120 שניות. תקף רק במצב Smarty
$settings["use_smarty"]null (ברירת מחדל) ⇒ משתמשים ב-CONFIG::$use_smarty. ערך אחר דורס את ההגדרה הגלובלית לקריאה הזו

שלושה מצבי רינדור, לפי הערך האפקטיבי של use_smarty:

ערךמצבמה קורה
trueSmartySmarty::fetch($tmpl_file). מוצבים GLOBALS (= MISC::$GLOBALS) וכל מפתחות $varsArr
"plain"קובץ גולמימחזירה את תוכן הקובץ כמות שהוא, בלי עיבוד ובלי משתנים. התוכן נשמר ב-$GLOBALS["imp_<נתיב>"] לשארית הבקשה
כל ערך אחר (false)evalראו אזהרה למטה
// בתוך קונטרולר: application/views/news/item.tpl
return FILES::html_template(
    CONFIG::$base_path . "/application/views/news/item.tpl",
    ["row" => $row, "title" => $row["title"]],
    ["cache" => false]
);
// זהה ל:  return $this->view("news/item", ["row" => $row, "title" => $row["title"]]);
מצב eval (
use_smarty שאינו true או "plain") במצב הזה התבנית נעטפת במחרוזת PHP עם addslashes ומורצת ב-eval: כל $var, {$expr} או ${...} שבתוכן הקובץ מפוענח כ-PHP. מקבלים גם אובייקט $savedWords (מילים של LANGS) ומשתנים בשם המפתחות של $varsArr. המצב הזה שמור לתאימות לאחור: אל תפעילו אותו באתר חדש, והשאירו CONFIG_USER::$use_smarty = true.

CONFIG::$use_smarty חייב להיות מוצהר

הנכס נקרא ללא isset. אתר שלא הגדיר אותו ב-CONFIG_USER יקבל שגיאת undeclared static property בכל רינדור תבנית. ראו CONFIG_USER.

FILES::string_template()#

public static function string_template($tmpl_string, $varsArr = array(), $settings = array())

מרנדרת מחרוזת (לא קובץ) כתבנית Smarty, דרך המשאב string:. תמיד Smarty, בלי קשר ל-CONFIG::$use_smarty. $settings מכיל רק cache (כמו ב-html_template). משמשת את mail_tpl לרינדור נושא, שם שולח וגוף של תבניות מייל שמאוחסנות ב-DB.

$html = FILES::string_template('שלום {$name}, ההזמנה {$order.id} התקבלה', [
    "name"  => "דנה",
    "order" => ["id" => 1042],
]);
מחרוזת שמקורה במשתמש היא קוד

ל-Smarty של הליבה אין enableSecurity(), וההרחבה המותאמת מחזירה כל פונקציית PHP קיימת כ-modifier. לכן מי שיכול לשלוט בטקסט של התבנית (לדוגמה, עורך תבניות מייל בפאנל) יכול להפעיל פונקציות PHP בשרת. העבירו ל-string_template רק טקסט שנכתב על ידי מנהלים מהימנים, ולעולם לא קלט של גולש. משתנים שהגולש שולט בהם מועברים דרך $varsArr (ערך המשתנה אינו מפוענח כתבנית).

הרחבות Smarty (מה זמין בתבנית)#

מופע ה-Smarty נוצר מחדש בכל קריאה (create_smarty היא private), עם התצורה הבאה:

הגדרהערך
תיקיית תבניות./ (ה-CWD)
תיקיית קומפילציהcache/Smarty/templates_c/ (יחסית ל-CWD, עם use_sub_dirs)
תיקיית cachecache/Smarty/cache/
GLOBALSMISC::$GLOBALS מוצב אוטומטית בכל תבנית

חיפוש פונקציות, modifiers ובלוקים:

  • פונקציות ({crm_storage_url ...}): מתוך תוסף שנטען מ-system/addons/Smarty/libs/plugins (קבצים function.<name>.php עם smarty_function_<name>), או כל פונקציה גלובלית בשם smarty_function_<name>.
  • בלוקים: קבצי block.<name>.php, או פונקציה גלובלית smarty_block_<name>.
  • Modifiers ({$x|name}): modifier שנרשם מקובץ modifier.<name>.php, ואם אין, כל פונקציית PHP גלובלית בשם הזה.

הפונקציות שהליבה מספקת: crm_get_banner, crm_get_google_banner, crm_live_edit, crm_storage_tumb, crm_storage_url.

// application/includes/smarty_helpers.php (נטען על ידי PAGE::load)
function smarty_function_money($params, &$smarty)
{
    return number_format((float)$params["amount"], 2) . " ₪";
}
<p>מחיר: {money amount=$row.price}</p>
תיקיית plugins של האתר

אין תיקיית plugins ייעודית לאתר. התיקיות שהוגדרו ב-system/Smarty/ (configs, plugins) אינן קיימות בפועל, ומחפשים בהן רק אם נוצרו. הדרך הנקייה להרחבה מהאתר היא פונקציית smarty_function_* / smarty_block_* ב-application/includes. ראו תבניות Smarty.

טעינת קבצים#

FILES::include_dir()#

public static function include_dir($directoryincl)

עושה require_once לכל קובץ *.php ישירות בתיקייה (לא רקורסיבי), ממוינים לפי שם קובץ. תיקייה נטענת פעם אחת בלבד לבקשה: הנתיב נרשם ב-MISC::$GLOBALS["arrIncDirs"] והשוואה היא למחרוזת המדויקת (a/b ו-a/b/ נחשבות שונות). תיקייה שלא קיימת מדולגת בשקט. ערך החזרה: "" אם הנתיב כבר נטען, אחרת אין (null).

FILES::include_dir(CONFIG::$base_path . "/application/includes");
FILES::include_dir("application/admin/includes");

PAGE::load() קורא ל-include_dir על application/includes (צד לקוח) ועל application/admin/includes (אדמין), ולכן כל קובץ .php שתניחו שם ירוץ בכל בקשה, לפי סדר שמות הקבצים. אם יש תלות בין קבצים, קבעו את הסדר בשם (01_config.php, 02_helpers.php). ראו מבנה התיקיות של אתר.

FILES::scss()#

public static function scss($file)

מקבלת נתיב או כתובת של קובץ style, ומחזירה מחרוזת CSS מקומפל (או false). משמשת את PAGE::IncludeJsCss לנכסי print (CSS inline), ואת הקונטרולר minify לקבצי .scss.css.

קלטמה קורה
x.scssקומפילציה עם ScssPhp (פלט COMPRESSED)
x.scss.cssאותו דבר: מקור הקומפילציה הוא x.scss (הסיומת .css היא רק כדי שהבקשה תנותב אל PHP). אם x.scss לא קיים אבל x.scss.css פיזי כן, הקובץ הפיזי מוחזר כמו שהוא
x.cssקריאה ישירה מהדיסק, בלי קומפילציה, בלי cache ובלי שכתוב url()
כתובת http(s):// של האתר עצמומנורמלת לנתיב יחסי (site_url / original_site_url / env_url)
כתובת חיצונית, נתיב ריק, סיומת אחרת, קובץ שלא נמצא, או שגיאת קומפילציהfalse

פרטים שחשוב להכיר:

  • ?query ו-#fragment נחתכים לפני העיבוד.
  • איתור הקובץ הפיזי לפי הסדר: נתיב מוחלט קיים, assets/... ⇒ system/assets/..., CONFIG::$base_path/<נתיב>, DOCUMENT_ROOT/<נתיב>.
  • ה-@import נפתרים מתיקיית הקובץ הראשי בלבד.
  • בתוצאה, כל url(...) יחסי (שאינו http, data: או שמתחיל ב-/) משוכתב לנתיב מוחלט מהשורש, כדי שהוא יעבוד גם כשה-CSS מוזרק inline.
  • התוצאה נשמרת ב-cache/<SERVER_NAME>/scss/<file>ver=<attaches_version>.css (כתיבה אטומית דרך קובץ זמני, ו-touch ל-24 שעות קדימה). קובץ cache ריק נחשב פגום ומקומפל מחדש.
$css = FILES::scss("themes/main/critical.scss.css");
if ($css !== false) {
    PAGE::add_meta("<style>" . $css . "</style>", "head");
}
ה-cache מתבטל לפי גרסה, לא לפי שינוי קובץ

התיעוד בקוד מספר שה-cache מתחדש לפי filemtime, אבל בפועל המתודה לא משווה זמני שינוי: אם קובץ cache תקין קיים היא מחזירה אותו. עריכת x.scss לא תשפיע עד שמעלים את attaches_version (ראו PARAMS ו-LANGS) או מוחקים את תיקיית cache/<domain>/scss. ראו צינור הנכסים.

שגיאות קומפילציה נבלעות

חריגה מ-ScssPhp נתפסת ומוחזר false, בלי לוג ובלי הודעה. אם סגנון "נעלם", בדקו את ה-SCSS בנפרד. כמו כן, אין נרמול של .. בנתיב: העבירו רק נתיבים שאתם שולטים בהם ולא קלט של גולש.

FILES::vue()#

public static function vue($url, $m_settings)

טוענת bundle של Vue שנבנה ל-HTML (דף index.html של Vite, לדוגמה) ומחזירה מחרוזת HTML שאפשר להחזיר מקונטרולר. $m_settings הוא מערך (חובה, אין ברירת מחדל) ומכיל מפתח אחד: script (קוד JS שיתווסף בסוף בלוק ה-module, ברירת מחדל "").

מה היא עושה:

  1. טוענת את הקובץ ב-html_template($url, [], ["use_smarty" => false]) (מצב eval, ראו למעלה), ושולפת את תוכן ה-<head>.
  2. מתוך ה-head: <script src> מקומי הופך ל-import, <script src> חיצוני (CDN) נשמר כתגית רגילה, ו-<link rel="stylesheet"> מקומי נרשם עם PAGE::add_asset_advanced(..., ["area" => "body_end", "defer" => true]); stylesheet חיצוני נכנס ל-PAGE::add_meta באזור head. תגיות <link> אחרות (icon, manifest) מתעלמים מהן.
  3. מחזירה: ה-HTML של ה-body, תגיות הסקריפטים החיצוניים, ובלוק <script type='module'> שמייבא כל סקריפט מקומי כ-import '/<src>?ver=<time()>'; ואחריו $m_settings["script"].
// application/controllers/dashboard.php
class dashboard extends wz_controller
{
    function index()
    {
        return FILES::vue("application/vue/dist/index.html", [
            "script" => "window.APP_CONFIG = " . json_encode(["apiBase" => "/dashboard/"]) . ";",
        ]);
    }
}
שתי נקודות חשובות

(1) ?ver=time() משתנה בכל בקשה, ולכן הדפדפן לא שומר את ה-bundle ב-cache. (2) הקובץ עובר במצב eval, ולכן כל רצף $ או ${ בתוך ה-HTML (למשל template literal בקוד inline) יפוענח כ-PHP ועלול לשבור את הדף. bundle שמכיל ${ בתוך ה-HTML עצמו אינו מתאים לפונקציה הזו. ראו פאנלים מבוססי Vue.

קבצים ותיקיות#

FILES::get_extension()#

public static function get_extension($filename)

מחזירה את הסיומת לפי pathinfo($filename)['extension'], באותיות כפי שנכתבו (בלי lowercase), ובלי הנקודה. לקובץ בלי סיומת אין המפתח, ולכן מתקבלת אזהרת Undefined array key והערך null.

FILES::get_extension("media/Report.PDF");   // "PDF"

FILES::make_dir()#

public static function make_dir($path, $mode = 0777)

יוצרת תיקייה רקורסיבית, כולל תיקיות אב חסרות. מחזירה true אם התיקייה קיימת או נוצרה, false אם היצירה נכשלה. ההרשאות נקבעות לפי $mode תוך איפוס umask, כך שבפועל התיקייה היא 0777 (כתיבה לכולם), אלא אם ביקשתם אחרת.

if (!FILES::make_dir(CONFIG::$base_path . "/media/exports/2026", 0755)) {
    throw new Exception("cannot create exports folder");
}
0777 כברירת מחדל

עצי cache ו-media/Storage שנוצרים דרך הפונקציה הזו פתוחים לכתיבה לכל משתמש במערכת. באחסון משותף העבירו 0755 במפורש.

FILES::_make_dir()#

public static function _make_dir($path, $mode = 0777)

יוצרת תיקייה אחת (לא רקורסיבית) עם mkdir שקט ואיפוס זמני של umask. מחזירה את תוצאת mkdir (bool). זה עוזר פנימי של make_dir; השתמשו ב-make_dir.

FILES::remove_dir()#

public static function remove_dir($directory, $empty = FALSE)

מוחקת תיקייה ואת כל תוכנה בצורה רקורסיבית.

פרמטרמשמעות
$directoryנתיב. לוכסן סופי מוסר
$emptyFALSE (ברירת מחדל): מוחקת גם את התיקייה עצמה. TRUE: מרוקנת אותה ומשאירה אותה

מחזירה true בהצלחה. מחזירה false אם הנתיב אינו קיים, אינו תיקייה, אינו קריא, או אם rmdir נכשל. תיקיות-משנה נמחקות תמיד לגמרי (גם כש-$empty = TRUE).

FILES::remove_dir(CONFIG::$base_path . "/cache/" . $_SERVER["SERVER_NAME"] . "/scss", true);  // מרוקנת ומשאירה
אין הגנה על הנתיב

הפונקציה לא בודקת שהנתיב נמצא בתוך האתר, ו-is_dir עוקב אחרי קישורים סימבוליים לתיקיות: תוכן היעד של symlink יימחק. אל תעבירו לה קלט שמקורו בגולש או בטופס, ובנו את הנתיב מקבוע ידוע. הקריאות unlink אינן שקטות: קובץ שאי אפשר למחוק יפיק אזהרה והמחיקה תמשיך.

FILES::is_img()#

public static function is_img($str)

מחזירה true אם המחרוזת מסתיימת באחת מהסיומות gif, jpg, jpeg, png, tiff, jfif, webp (אותיות קטנות בלבד). null או מחרוזת ריקה מחזירים false. אפשר להעביר סיומת ("png") או שם קובץ שלם.

FILES::is_img("png");          // true
FILES::is_img("photo.webp");   // true
FILES::is_img("PHOTO.PNG");    // false (רגיש לאותיות)
FILES::is_img("logo.svg");     // false (svg, avif ו-bmp אינם ברשימה)
לא בודק נקודה

ההתאמה היא לסוף המחרוזת בלבד, ולכן גם "mypng" נחשב תמונה. השתמשו בה על סיומת שכבר חולצה (get_extension עם strtolower), לא כבדיקת אבטחה להעלאת קבצים (ראו כללי העלאת קבצים).

ראו גם#

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