כל HTML של האתר עובר דרך תבניות Smarty: ה-theme, תבניות הבקרים, ואפילו פאנלי הניהול. בעמוד הזה: הדרכים לעבד תבנית, מה הליבה מגדירה ב-Smarty, איזה תחביר ותוספים זמינים, איך כותבים תוסף, ואילו מלכודות כדאי להכיר לפני שמעבדים ב-Smarty תוכן שבא ממשתמש.
עיבוד תבנית#
| קריאה | פירוט |
|---|---|
$this->view($tpl, $vars = []) | מתוך בקר: מעבד את application/views/<tpl>.tpl ומחזיר HTML. $tpl בלי סיומת, למשל "news/list" |
$this->system_view($tpl, $vars = []) | כמו view, אבל מתוך core_path/views/ (לבקרים של הליבה ופאנלים) |
FILES::html_template($tmpl_file, $varsArr = [], $settings = []) | הפונקציה שמאחורי השתיים: מקבלת נתיב מלא מקובץ, מחזירה HTML |
FILES::string_template($tmpl_string, $varsArr = [], $settings = []) | מעבדת מחרוזת כתבנית (ראו אזהרה) |
class news extends bgl_controller
{
public function index()
{
$items = DB::query("news", ["active" => 1], "id DESC", "", "0,10")->get_all();
return $this->view("news/list", [
"items" => $items,
"title" => "חדשות",
]);
}
}
{* application/views/news/list.tpl *}
<h1>{$title|escape}</h1>
<ul>
{foreach $items as $item}
<li><a href="news/{$item.id}">{$item.title|escape}</a></li>
{foreachelse}
<li>אין חדשות</li>
{/foreach}
</ul>
$settings של html_template מכיל שני מפתחות:
| מפתח | ברירת מחדל | פירוט |
|---|---|---|
use_smarty | null (לפי CONFIG::$use_smarty) | דריסה למקרה אחד: true, false או "plain" |
cache | false | מדליק את המטמון של Smarty ל-120 שניות (ראו מלכודות) |
מה הליבה מגדירה ב-Smarty#
הליבה משתמשת ב-Smarty 5.8 (api/core/addons/Smarty) ויוצרת מופע חדש לכל קריאה:
| הגדרה | ערך |
|---|---|
| תיקיית התבניות | ./, כלומר שורש האתר. נתיב ב-{include} נמדד משם: {include file="application/views/partials/menu.tpl"} |
| תיקיית קומפילציה | cache/Smarty/templates_c/ (עם תתי-תיקיות), יחסית לשורש האתר |
| תיקיית מטמון | cache/Smarty/cache/ |
| משתנים שמוקצים תמיד | GLOBALS (כל MISC::$GLOBALS) ובנוסף כל מפתחות $vars |
| escape אוטומטי | כבוי (escape_html בברירת המחדל של Smarty 5, שהיא false) |
| מטמון של Smarty | כבוי (caching = 0), אלא אם ביקשתם cache |
{$title} מדפיס את הערך כמות שהוא, ולכן ערך שהגיע ממשתמש או מבסיס הנתונים וכולל <script> יתבצע בדפדפן. תמיד {$value|escape} בטקסט, ו-{$value|escape:'url'} בפרמטר של כתובת. nofilter, שמופיע בדוגמאות ({$content nofilter}), אינו משנה כאן כלום כי אין מה לבטל, אבל הוא מסמן בבירור שהערך הוא HTML מכוון (XSS).תחביר שימושי#
התחביר הוא Smarty 5 סטנדרטי, וכל ה-תיעוד של Smarty תקף. הקצרים הנפוצים:
{$user.name|escape} {* משתנה ומודיפייר *}
{if $items|count > 0} ... {else} ... {/if}
{foreach $items as $k => $item} ... {/foreach}
{include file="application/views/partials/card.tpl" item=$item}
{$GLOBALS.body_class} {* MISC::$GLOBALS *}
{$smarty.now|date_format:"%Y"}
{literal} function(){ return {a: 1}; } {/literal} {* סוגריים מסולסלים של JS/CSS *}
סוגריים מסולסלים של JS ו-CSS בתוך תבנית חייבים להיות בתוך {literal}...{/literal} (או עם רווח אחרי {), אחרת Smarty ינסה לפרש אותם.
מודיפיירים: כל פונקציית PHP#
ההרחבה של הליבה מאפשרת להשתמש כמודיפייר בכל פונקציית PHP בשמה, בלי להירשם:
{$price|number_format:2}
{$text|strtoupper}
{$name|trim}
זה נוח, אבל זו גם הסיבה לאזהרה על string_template למטה.
תוספים מובנים#
חמש פונקציות תבנית מגיעות עם הליבה (api/core/addons/Smarty/libs/plugins/):
| תג | פרמטרים | מה עושה |
|---|---|---|
{crm_storage_url id=…} | id | כתובת מלאה של קובץ מהאחסון (STORAGE::get_url) |
{crm_storage_tumb id=… width=… height=…} | id, width, height | כתובת תמונה ממוזערת (STORAGE::get_thumb) |
{crm_live_edit id=… table=… field=… tbType=…} | id, table, field, tbType (ברירת מחדל conf) | מדפיס תכונות contenteditable שמאפשרות עריכה מתוך העמוד, רק לאדמין מחובר ועם ?liveEdit=true בכתובת. לכל השאר מדפיס מחרוזת ריקה |
{crm_get_banner type=… tag=…} | type, tag | באנר מהמערכת, דורש application/models/m_banner.php באתר |
{crm_get_google_banner name=… tag=…} | name, tag | באנר Google, דורש application/models/m_google_ads.php באתר |
<img src="{crm_storage_tumb id=$item.image width=400 height=300}" alt="{$item.title|escape}">
<h2 {crm_live_edit id=$item.id table="news" field="title"}>{$item.title|escape}</h2>
{crm_live_edit} נכתב בתוך התג הפותח, כי הוא מדפיס תכונות ולא אלמנט. הוא גם מוסיף את נכסי CKEditor לעמוד, אבל רק כשהתנאי מתקיים.
תוסף משלכם#
הליבה מחפשת תוספים בשתי תיקיות: core_path/Smarty/plugins (שאינה קיימת) ו-addons/Smarty/libs/plugins. שתיהן חלק מהליבה שמוחלפת בעדכון גרסה, ולכן לא שמים בהן קבצים. הדרך באתר היא להגדיר פונקציה גלובלית עם השם smarty_function_<name> (וגם smarty_block_<name>) בקובץ שנטען תמיד, למשל application/includes/smarty.php. ההרחבה בודקת function_exists על השם הזה בכל תג שהיא לא מכירה.
<?php
// application/includes/smarty.php
function smarty_function_price($params, $smarty)
{
$amount = (float)($params["amount"] ?? 0);
return number_format($amount, 2) . " ₪";
}
{price amount=$item.price}
התוסף מקבל את מערך הפרמטרים של התג ואת אובייקט ה-Smarty, ומחזיר מחרוזת שנכנסת כמו שהיא (בלי escape). אם הפונקציה מדפיסה ערך שבא ממשתמש, אתם אחראים לעשות לו htmlspecialchars.
מצבי CONFIG::$use_smarty#
| ערך | התנהגות |
|---|---|
true (ברירת המחדל) | Smarty, כל מה שלמעלה |
"plain" | מחזיר את תוכן הקובץ כמות שהוא, בלי עיבוד |
| כל ערך אחר | מצב eval ישן: הקובץ נהפך למחרוזת PHP ו-eval מפענח אותה |
מצב ה-eval (כל ערך שאינו true ואינו "plain") מריץ קוד PHP מתוך קובץ התבנית ואינו בטוח (ממצא CORE-06). השאירו use_smarty על true. הסיבה היחידה לשנות היא אתר ישן מאוד.
מלכודות#
string_template או ב-Smarty תוכן שמשתמש יכול להשפיע עליו. אין enableSecurity ואין מדיניות מגבילה (ממצא CORE-43), ומודיפייר יכול להיות כל פונקציית PHP. תוכן ממשתמש או מעורך תוכן פנימי שמוזן ל-string_template יכול לקרוא לפונקציות שמריצות פקודות. תבניות שעוברות ב-Smarty צריכות להגיע מקבצי .tpl של הקוד, והתוכן הדינמי נכנס אליהן כמשתנה, לא כחלק מהתבנית.["cache" => true], הפלט נשמר ל-120 שניות (הערך קבוע בקוד), והקריאה לא מעבירה cache_id. פלט של תבנית שמקבלת משתנים שונים בכל קריאה (עמוד מוצר, משתמש) עלול לחזור זהה. להאטה אמיתית השתמשו ב-מטמון הפריטים ובמטמון העמודים ולא במטמון של Smarty.תיקיות הקומפילציה והמטמון (cache/Smarty/...) יחסיות לתיקיית העבודה, שהיא שורש האתר בבקשת HTTP (ממצא CORE-43). בהרצה מ-CLI או מ-cron, ודאו שתיקיית העבודה היא שורש האתר, אחרת Smarty ינסה לכתוב למקום אחר.