הדף הזה מיועד למפתח PHP שקיבל אתר WIZZO CMS לעבודה ורוצה להגיע ליום הראשון עם קוד שרץ. הוא לא מלמד את כל המערכת. הוא נותן מסלול קצר: מה יש לכם ביד, איך מרימים סביבה מקומית, איפה כותבים, ומה בונים כדי לראות את כל החלקים עובדים יחד. בכל שלב יש קישור לדף שמעמיק.
system/ בדף הוא תיקיית הליבה הפרוסה באתר, system/core/. {admin} הוא הערך של CONFIG::$admin_url, כתובת הניהול של האתר.
מה מקבלים#
WIZZO CMS מופץ ברישיון מסחרי ובאישור של Wizzo בלבד. אין הורדה חופשית ואין מאגר קוד ציבורי. מפתח שמקבל גישה מקבל:
- אתר מאושר, או את אתר הפתיחה והמתקין שלו כדי להקים אחד בעצמכם. אחרי ההקמה הליבה יושבת ב-
system/core/וההגדרות של האתר ב-system/config.php. - משתמש מנהל להיכנס אל
{admin}ולהגדיר פאנלים, מודולים והרשאות. - גישה לתיעוד הזה, שמתאר את הקוד שרץ באתר שלכם.
אתם עובדים על האתר: ערכות נושא ב-themes/, קונטרולרים, מודלים, תבניות ופאנלי ניהול ב-application/, וכלי MCP של האתר. אתם לא עובדים על הליבה. היא מגיעה אליכם בהתקנה ובעדכון, ועדכון הליבה הבא מחליף את system/core/ כולה (ראו "מה לא עורכים" בהמשך).
מגרסה 5.0.159 אתר חדש מוקם במתקין של אתר הפתיחה, install.php, שמגיע עם אישור הרישיון. הוא מוריד את הליבה, בודק את השרת, מקים את הטבלאות, יוצר מנהל ראשון וכותב את ההגדרות. כך מרימים גם סביבה מקומית: מסד נתונים ריק משלכם, ואז php install.php עם הדומיין המקומי (למשל --domain=mysite.test). כל האפשרויות ב-התקנת אתר חדש. מה שכתוב כאן על כתיבת קוד באתר לא תלוי בדרך ההקמה.
סביבה מקומית#
המטרה: אתר שרץ על המחשב שלכם מול מסד נתונים משלכם, כך שאפשר לכתוב קוד ולבדוק בלי לגעת באתר החי. פירוט הדרישות ב-דרישות מערכת.
| רכיב | מה צריך |
|---|---|
| PHP | 8.3 ומעלה. composer.json של הליבה נועל platform.php = 8.3, ועדכון ליבה מסרב לרוץ על PHP ישן יותר |
| הרחבות | pdo_mysql, mbstring, curl, zip, zlib, openssl, json, ctype, dom, gd. הרשימה המלאה ב-דרישות מערכת |
| מסד נתונים | MySQL או MariaDB. הליבה פותחת חיבור utf8mb4 ומכבה strict mode |
| שרת | Apache עם mod_rewrite, או nginx עם PHP-FPM |
exec() | חייבת להיות פעילה: משימות הרקע (cron) מופעלות דרכה |
Apache. ה-.htaccess שבשורש האתר מנתב כל בקשה שאינה קובץ אמיתי אל index.php. אם האתר שקיבלתם כולל אותו, אין מה לעשות. אם לא, הנוסח המלא נמצא ב-התקנת אתר חדש. בסביבה בלי תעודה מקומית הסירו ממנו את כללי ההפניה ל-https.
nginx. nginx לא קורא .htaccess, ולכן ההגנות על cache/ ועל קבצי ההגדרות ב-system/ נכתבות בו ידנית. הליבה מגיעה עם קובץ כללים מוכן, server/nginx.conf, שמוסיפים פעם אחת בראש בלוק ה-server של האתר, לפני כל location ~ אחר:
server {
server_name mysite.test;
root /var/www/mysite;
include /var/www/mysite/system/core/server/nginx.conf;
location / {
try_files $uri /index.php?$query_string;
}
location = /index.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
אחרי nginx -t && nginx -s reload הקובץ פעיל. הוא מחזיר 404 לקבצי הלוג והמצב שבתוך cache/, ולקבצי ההגדרות עם מפתחות ב-system/. הוא נשלח עם הליבה ומתעדכן איתה. הקטע המלא של שרת nginx, כולל הגשת נכסים סטטיים, ב-התקנת אתר חדש.
הגדרות פיתוח ב-system/config.php. בסביבה מקומית כבו את מטמון העמודים כדי לראות כל שינוי מיד:
static $allow_cache = false;
שאר המפתחות (חיבור למסד, $admin_url, $default_module) מתוארים ב-קונפיגורציה וב-CONFIG_USER.
מלכודות של סביבה מקומית#
| תסמין | סיבה |
|---|---|
| ההתחברות לניהול לא נשמרת | COOKIES::set() שולח עוגיות עם secure=true. על http:// או על localhost הדפדפן עלול לא לשמור אותן. עדיף HTTPS מקומי ושם מארח כמו mysite.test |
| האתר מציג תוכן של פלטפורמה אחרת | CONFIG::init() בוחר שורה ב-CRM_platforms לפי SERVER_NAME. שם שלא נמצא, גם לא ב-allowed_domains, נופל לשורה הראשונה בטבלה. הוסיפו את המארח המקומי ל-allowed_domains של הפלטפורמה |
שינוי ב-CRM_platforms או ב-CRM_params לא נראה | הטבלאות נשמרות במטמון (פלטפורמות ל-30 יום). נקו מטמון מ-{admin}/clear_cache |
| קריאה לשירות חיצוני מחזירה שגיאה | חלק מהכלים פונים לשירותי Wizzo Market (כתובות השירותים). על אתר מקומי זה צפוי |
בדיקת עשן אחרי שהאתר עלה:
curl -s http://mysite.test/system/core_ping
# {"ok":true,"version":"5.0.x","db":true,"php":"8.3.x"}
שלד האתר ומה יושב איפה#
<שורש האתר>/
├── index.php נקודת הכניסה, טוען את system/config.php ואת הליבה
├── system/
│ ├── core/ הליבה. לא עורכים. מוחלפת בכל עדכון
│ ├── js/ קבצי ה-JS המשותפים של הליבה
│ └── config.php class CONFIG_USER: ההגדרות של האתר
├── application/
│ ├── controllers/ קונטרולרים של האתר
│ ├── models/ מודלים
│ ├── views/ תבניות .tpl של הקונטרולרים
│ ├── includes/ PHP שנטען בכל בקשה בצד הלקוח (init.php)
│ └── admin/ פאנלי הניהול של האתר
│ └── includes/ PHP שנטען באדמין, וכלי MCP של האתר
├── themes/<name>/ ערכות עיצוב
├── cache/ מטמון (נכתב בזמן ריצה)
└── media/ קבצים שהועלו (נכתב בזמן ריצה)
הכלל הפשוט: כל מה שכתבתם יושב ב-application/, ב-themes/ וב-system/config.php. מה שמגיע עם המערכת יושב ב-system/. טבלת המיקומים המלאה, כולל מי קורא כל תיקייה ואילו תיקיות נכתבות בזמן ריצה, ב-מבנה התיקיות של אתר. רשימת ההגדרות ב-קונפיגורציה וב-CONFIG_USER.
Hello world בארבעה שלבים#
נבנה פיצ'ר קטן שעובר דרך כל השכבות: עמוד שמציג הערות מטבלה, פאנל ניהול לערוך אותן, וכלי MCP שמחזיר אותן. כל שלב נשען על הקודם. את הכללים שכל קוד באתר חייב לקיים קראו קודם ב-כללי יסוד.
שלב 1: טבלה ומודל#
הטבלאות של האתר בקידומת CRM_. צרו אותה במסד:
CREATE TABLE CRM_notes (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL DEFAULT '',
body TEXT NULL,
published TINYINT(1) NOT NULL DEFAULT 0
) DEFAULT CHARSET=utf8mb4;
המודל נטען מ-CONFIG::$models_folder (ברירת מחדל application/models). שם הקובץ הוא שם המחלקה:
<?php
// application/models/m_notes.php
class m_notes extends wz_model
{
function latest($count)
{
return DB::query("notes", ["published" => 1], "id DESC", "", "0," . (int)$count)->get_all();
}
function add($title, $body)
{
return DB::update("notes")
->set("title", $title)
->set("body", $body)
->set("published", 1)
->insert();
}
}
שימו לב שאין DB::insert: הוספה נעשית עם DB::update("table")->set(...)->insert(). ה-builder מקבל שם טבלה בלי CRM_, ואילו SQL גולמי (DB::get_all, DB::get_row) מחייב את השם המלא ו-DB::escape() לכל ערך חיצוני. הפירוט ב-שאילתות, ב-בוני שאילתות וב-רפרנס DB.
שלב 2: קונטרולר ותבנית#
קונטרולר הוא קובץ ב-application/controllers/ שמגדיר מחלקה באותו שם:
<?php
// application/controllers/hello.php
class hello extends wz_controller
{
// GET /hello
function index()
{
$this->load->model("m_notes");
SEO::set("title", "הערות");
return $this->view("hello/index", [
"notes" => $this->m_notes->latest(10),
]);
}
}
התבנית, application/views/hello/index.tpl. Smarty באתר לא עושה escape אוטומטי, ולכן כל ערך שמקורו במסד עובר |escape:
<section class="notes">
<h1>הערות</h1>
{foreach $notes as $note}
<article>
<h2>{$note.title|escape}</h2>
<p>{$note.body|escape}</p>
</article>
{foreachelse}
<p>אין הערות עדיין.</p>
{/foreach}
</section>
הכתובת /hello עדיין תחזיר 404, כי קונטרולר חדש חייב שורה פעילה ב-CRM_modules:
INSERT INTO CRM_modules (moduleName, active) VALUES ('hello', 1);
אחרי כתיבה ישירה למסד נקו את מטמון המודולים, מ-{admin}/clear_cache או מהקוד:
cache_engine::remove("modules");
אפשר גם להוסיף את המודול מהפאנל {admin}/Modules, והוא מנקה את המטמון בעצמו. עכשיו /hello מחזיר את העמוד. הסבר מלא על שלושת התנאים (קובץ, שורה, מתודה ציבורית) ב-קונטרולרים של אתר וב-העמוד הראשון שלך, ועל התבניות ב-תבניות Smarty. קונטרולר שנכשל ב-404 מאובחן ב-דיבוג ניתוב.
שלב 3: פאנל ניהול#
פאנל הוא קובץ ב-application/admin/ עם מחלקה ADMINMODULE_<name>:
<?php
// application/admin/notes.php
class ADMINMODULE_notes extends bgl_controller
{
public function index()
{
MISC::breadcrumbs([["name" => "הערות"]]);
$tbl = new panel_table("notes");
$tbl->title = "הערות";
$tbl->query = DB::query("notes", null, "id DESC");
$tbl->btn_add_url = CONFIG::$admin_url . "/notes/insert";
$tbl->delete = function ($id) {
DB::delete("notes", ["id" => $id]);
};
$tbl->titles = [
["title" => "ID", "field" => "id"],
["title" => "כותרת", "field" => "title"],
];
$tbl->actions = function ($rows) {
return [["title" => "עריכה", "link" => "EDIT", "icon" => '<i class="fas fa-edit"></i>']];
};
return $tbl->get_html();
}
public function insert()
{
$frm = new AJAXForm("frmNotes");
$frm->table = "notes";
$frm->successFunc = "adminMoveToLastPanel()";
if (isset($_GET["id"])) $frm->update_mode((int)$_GET["id"]);
$title = new FormInput_Text("title");
$title->title = "כותרת";
$title->Must();
$frm->push($title);
$published = new FormInput_Switch("published");
$published->title = "מפורסם";
$frm->push($published);
if (!$frm->IsFormSent()) return $frm->get_html();
return $frm->save();
}
}
גם כאן הקובץ לא מספיק: פאנל בלי שורה ב-CRM_adminPanel_panels הוא 404 ולא מופיע בתפריט. רושמים אותו מ-{admin}/PanelList, מ-API ה-panel_registry, או בקוד התקנה. שלוש הדרכים, ההרשאות ובדיקת קבוצה בלי הרשאה ב-בניית פאנל. הרשימות ב-panel_table, הטפסים ב-טפסים וב-קטלוג שדות הטופס.
כל מתודה ציבורית בפאנל היא כתובת. מתודת עזר חייבת להיות private או protected, ומתודה שמוחקת או משנה נתונים בודקת הרשאה בעצמה עם ADMIN::has_perms(). פירוט ב-הרשאות וקבוצות.
שלב 4: כלי MCP של האתר#
כלי MCP נותן ללקוח AI, או לסוכן של האתר, פעולה על הנתונים שלכם. הוא נרשם בקובץ שנטען מ-application/admin/includes/:
<?php
// application/admin/includes/mcp_tools.php
if (!class_exists("AGENT_TOOLS")) return;
AGENT_TOOLS::add_tool([
"name" => "notes_list",
"label" => "רשימת הערות",
"description" => "List the latest published notes (id, title). Call before editing notes.",
"input" => (object)[],
"handler" => function (array $args, array $ctx) {
return ["notes" => DB::get_all("SELECT id, title FROM CRM_notes WHERE published = 1 ORDER BY id DESC LIMIT 20")];
},
]);
כלי קריאה לא צריך write. כלי שכותב נתונים מסמן "write" => true ובודק הרשאות בעצמו, כי ה-handler ניגש ל-DB ישירות. הבדיקה היא קריאת tools/list ל-/system/mcp וראיית השם ברשימה. החוזה המלא, כלי כתיבה ובדיקה ב-כתיבת כלי MCP לאתר, והחיבור ללקוח ב-MCP ב-WIZZO CMS.
חובה לקרוא: כללי יסוד#
לפני שמעבירים קוד לאתר חי, קראו את כללי יסוד. הוא מרכז מה שחייב לחול על כל קוד שנכתב באתר: גישה למסד, אבטחה, מה לא עושים בקוד שרץ בכל בקשה, ואיך מתנהגים מול מטמון והרשאות. טעויות שמתגלות רק באתר חי, כמו $_GET שנדבק לשאילתה או פאנל בלי בדיקת הרשאה, מכוסות שם. את התקן של הכתיבה (שמות, סגנון, DB, אבטחה, JS ו-CSS) ראו ב-תקן הקוד.
שכבת התוכן#
אם הקוד שלכם יוצר או משנה תוכן באתר (כתבות, דפים, פריטים עם סטטוס, תזמון פרסום והיסטוריית גרסאות), עשו זאת דרך שכבת התוכן ולא בכתיבה ישירה לטבלאות התוכן. כך נשמרים ההיסטוריה, הסטטוס, התצוגה המקדימה והתזמון. הדף אירועי תוכן מתאר את הנקודות שאליהן אפשר להתחבר ואת הדרך הנכונה לכתוב תוכן מקוד.
עדכוני ליבה ומה לא עורכים#
הליבה מתעדכנת מתוך האתר, מ-{admin}/wizzo_update. התהליך מוריד גרסה, מחליף את התיקייה system/core/ בשלמותה, בודק עם core_ping שהגרסה החדשה עולה, וחוזר אוטומטית לקודמת אם לא. הוא מסנכרן גם מבנה טבלאות, פאנלים ומשימות מערכת. פירוט ב-עדכון ליבה ובמספור ב-גרסאות.
מכאן הכלל החשוב ביותר בדף:
כל שינוי שתעשו בתוך system/core/ יימחק בעדכון הבא. אין מנגנון patch או overlay. אותו דבר חל על system/js/. כל התאמה שייכת ל-application/, ל-themes/ או ל-system/config.php, שעדכון הליבה לא נוגע בהם. אל תוסיפו משימות cron משלכם לקטגוריה "מערכת": העדכון מוחק מקטגוריה זו כל משימה שאינה קיימת במקור.
מה שעוזר לעבוד בשלום עם עדכונים:
- בדקו את
system/core/version.txtכדי לדעת על איזו גרסה אתם. מה שהגיע בכל גרסה מופיע ב-היסטוריית גרסאות. - אל תסתמכו על פונקציה שלא מתועדת. מה שמתועד כאן הוא ההתנהגות שהליבה מתחייבת לשמר.
- אחרי כל עדכון בדקו
core_pingואת עמודי האתר המרכזיים. - חומר צד שלישי שהגיע עם הליבה (
system/core/addons/) אינו חלק מה-API שלכם. הוסיפו ספריות משלכם עם Composer של האתר: חבילות Composer.
כשמשהו נשבר#
| תסמין | איפה מסתכלים |
|---|---|
| 404 על כתובת של קונטרולר | ?__router_trace=1 מראה איזה כלל נתפס ואם המודול נמצא ב-CRM_modules: דיבוג ניתוב. אחר כך 404, 503 ושגיאות |
| עמוד לבן או 500 | הפאנל {admin}/error_log ולוג השגיאות של PHP: אבחון ולוגים |
| שאילתה שנכשלה או איטית | DB::$print_sql, הפרמטר log_sql והקובץ sqllog.txt: דיבוג SQL |
| הליבה לא עולה אחרי עדכון | /system/core_ping: אם db הוא false, הליבה עלתה אבל המסד לא ענה |
| שינוי שלא נראה | מטמון: ניקוי ותחזוקה ו-מטמון עמודים מלא |
בדיקה לפני העברה לאתר חי ורשימת בדיקה ידנית ב-בדיקות.
קבלת עזרה#
העזרה מגיעה דרך Wizzo. אין מאגר קוד ציבורי ולכן אין מערכת issues פתוחה. שאלה על הקוד של האתר שלכם, תקלה שנראית כבאג בליבה או בקשה לשינוי בליבה עוברות לצוות Wizzo בערוץ התמיכה שקיבלתם עם הרישיון. מה לצרף לפנייה, ואיך מפרידים בין קוד האתר לליבה כדי שהעדכון לא ישבור כלום, ב-עבודה עם צוות Wizzo.
מה הלאה#
- העמוד הראשון שלך: אותו מסלול בפירוט, צעד אחרי צעד.
- ארכיטקטורה ו-מחזור חיי בקשה: איך בקשה הופכת לעמוד.
- סביבת עבודה לאתר: זרימת העבודה של מפתח אתר.
- מילון מונחים: המונחים שחוזרים בתיעוד.