לכל אתר WIZZO CMS יש שני עולמות Composer נפרדים: ה-composer.json של הליבה (system/core), שמתעדכן יחד עם הליבה, וה-composer.json של האתר בשורש, שבו אתם מוסיפים חבילות צד שלישי. פאנל "ניהול Composer" ({admin}/composer) מנהל את השני, בלי SSH. בדף הזה: מה הפאנל עושה, החתימות של ComposerManager, איך מחברים את ה-autoload לאתר, ומה אסור לעשות.
הפאנל והספרייה עובדים רק מול CONFIG::$base_path (השורש שבו יושב index.php), ולא מול CONFIG::$core_path. את ה-Composer של הליבה אי אפשר לשנות מכאן: הוא מתעדכן עם הליבה ב-עדכון עצמי.
שני עולמות#
| הליבה | האתר | |
|---|---|---|
| קובץ | system/core/composer.json | composer.json בשורש האתר |
| מי מנהל | צוות Wizzo, יחד עם הגרסה | מפתח האתר, מהפאנל |
vendor/ | system/core/vendor, נטען ב-cron_runner וב-cron_manager | vendor/ בשורש האתר |
| טעינה | require_once CONFIG::$core_path . '/vendor/autoload.php' | חייבים לחבר ידנית (ראו למטה) |
החבילות של הליבה בענף הזה: phpoffice/phpspreadsheet, mobiledetect/mobiledetectlib, verot/class.upload.php, guzzlehttp/guzzle, dragonmantank/cron-expression ו-smalot/pdfparser, עם config.platform.php של 8.3. בריפו של הליבה אין composer.lock ואין vendor/, ולכן בסביבת פיתוח חדשה צריך להריץ composer install בתיקיית הליבה לפני שהריצה של cron עולה.
דרישות סביבה#
הפאנל מריץ פקודות מעטפת, ולכן הוא תלוי בשרת:
| דרישה | פירוט |
|---|---|
exec() | חייב להיות פתוח (לא ב-disable_functions). בלעדיו אפשר לקרוא מצב, אבל לא להתקין |
| PHP CLI | ComposerManager::php_cli() מחפש בינארי ובודק כל מועמד עם -r 'echo PHP_SAPI;': רק cli עובר. Composer לא יכול לרוץ תחת lsphp או php-fpm |
| כתיבה | לשורש האתר (או לתיקיית העבודה, כשהשורש לקריאה בלבד) |
| רשת יוצאת | curl או allow_url_fopen, להורדת Composer וחבילות |
| הרשאה | ADMIN::is_developer() ושורת פאנל עם perm_developer=1 |
כל פעולת קריאה בלבד (summary, installed, autoload_status) עובדת גם כש-exec() חסום, והפאנל מציג מה בדיוק חוסם.
זרימת עבודה בפאנל#
- מצב סביבה: הפאנל קורא
statusומציג אתenv, איזה Composer נמצא, מצבcomposer.json,composer.lockו-vendor/, ומצב ה-autoload. - התקנת Composer אם חסר.
- יצירת
composer.jsonאם אין לאתר. - חיבור autoload, כדי שהחבילות ייטענו.
- הוספה, עדכון והסרה של חבילות, עם חיפוש ב-Packagist.
- בדיקת חבילות מיושנות (
outdated).
חיבור ה-autoload#
הליבה טוענת רק את ה-vendor של עצמה, ולכן התקנת חבילה לאתר לא מועילה עד שמשהו עושה require ל-vendor/autoload.php שלו. המקום הנכון הוא application/includes/init.php, ש-core.php טוען מיד אחרי אתחול CONFIG. הפאנל מוסיף לקובץ (ויוצר אותו אם אינו קיים) את הקטע הבא:
$__wz_site_autoload = CONFIG::$base_path . '/vendor/autoload.php';
if (file_exists($__wz_site_autoload)) require_once($__wz_site_autoload);
אם הקטע כבר קיים באחד מקבצי application/includes/*.php או ב-index.php (נבדק לפי הטקסט vendor/autoload.php), הפעולה לא עושה כלום.
hook_autoload כותב קוד PHP לקובץ application/includes/init.php. אם קובץ ה-init כבר עובר ניהול ב-git, זה יוצר שינוי שלא נמצא בריפו. הוסיפו את הקטע לקובץ בריפו ידנית, ובדקו שהוא אכן נטען, במקום להריץ את הפעולה על שרת חי.פעולות ה-API של הפאנל#
כל הפעולות הן POST עם גוף JSON אל {admin}/composer/<action>, ומחזירות JSON. כולן עוברות guard(), נעילה שנייה מעל שורת הפאנל: מי שאינו מפתח מקבל {"ok": false, "error": ...}.
| פעולה | גוף | מה היא עושה |
|---|---|---|
status | env, composer (מה נמצא), summary, autoload | |
packages | רשימת החבילות המותקנות, מתוך vendor/composer/installed.json, ואם אין, מתוך composer.lock | |
outdated | מריצה composer outdated --format=json --all סינכרונית (עד 180 שניות) ומחזירה map של version, latest, status, outdated | |
search | q | חיפוש ב-Packagist דרך השרת (עד 20 תוצאות) |
install_composer | מורידה ומתקינה composer.phar | |
create_manifest | יוצרת composer.json מינימלי, אם אינו קיים | |
hook_autoload | מחברת את ה-autoload, ראו למעלה | |
run | action, package?, constraint?, dev? | פקודת Composer כמשימה מנותקת. מחזירה {ok, id, mode, label} |
job | id | מצב משימה ולוג שגדל, לשאילתה חוזרת |
פעולות run#
רק הפעולות הבאות מגיעות למעטפת, ושם החבילה והמגבלה עוברים ולידציה:
action | פקודת Composer | הערה |
|---|---|---|
install | install | |
update | update | כל החבילות |
update_package | update <package> --with-dependencies | דורש package |
require | require <package>[:<constraint>] | dev: true מוסיף --dev |
remove | remove <package> | דורש package |
dump_autoload | dump-autoload -o | |
validate | validate --no-check-publish | |
clear_cache | clear-cache | |
diagnose | diagnose | |
self_update | self-update | רק כש-Composer הוא composer.phar של האתר. Composer גלובלי נדחה |
כל פקודה מקבלת אוטומטית --no-interaction --no-ansi --working-dir=<root>, והסביבה: COMPOSER_HOME ו-COMPOSER_CACHE_DIR בתיקיית העבודה, COMPOSER_ALLOW_SUPERUSER=1, COMPOSER_MEMORY_LIMIT=-1, ו-COMPOSER_PROCESS_TIMEOUT=900.
{"action": "require", "package": "monolog/monolog", "constraint": "^3.0"}
משימות מנותקות#
פקודה ארוכה לא יכולה למות עם בקשת ה-HTTP, ולכן start_job מריצה אותה כ-nohup sh -c ברקע, כותבת ל-jobs/<id>.log, ומסמנת סיום בקובץ jobs/<id>.exit עם קוד היציאה. job מחזיר {running, exit, success, output, elapsed}. משימה שלא כתבה .exit תוך 20 דקות מסומנת כנכשלה. קבצי משימה בני יותר מיממה נמחקים. במארח שאי אפשר להתנתק בו (Windows) הפקודה רצה סינכרונית, והתשובה mode היא sync.
תיקיית העבודה היא CONFIG::$cache_folder/composer/ עם home/ ו-jobs/.
ComposerManager: הספרייה#
libraries/ComposerManager.php היא מחלקה סטטית. נטענת עם $this->load->library("ComposerManager"). מתודות ציבוריות:
| חתימה | ערך החזרה |
|---|---|
root(): string | שורש האתר (CONFIG::$base_path) |
manifest_path(), lock_path(), vendor_path(), autoload_path(), phar_path(): string | נתיבי composer.json, composer.lock, vendor/, vendor/autoload.php, composer.phar |
work_dir(), composer_home(), jobs_dir(): string | תיקיות העבודה, נוצרות לפי הצורך |
is_windows(): bool | |
is_function_disabled(string $name): bool | האם הפונקציה ב-disable_functions |
can_exec(): bool, can_proc_open(): bool | |
php_cli(): ?string | נתיב ל-PHP CLI תקין, או null |
env(): array | מצב הסביבה: exec, proc_open, php_cli, root_writable, outbound ועוד |
detect(bool $fresh = false): array | {found, source, path, cmd, version, raw, error}. source הוא phar, phar_work, path או absolute |
install_composer(): array | {ok, log, version?} |
http_get(string $url): ?string | GET יוצא, או null |
build_cmd(array $args): string | הפקודה המלאה, כל ארגומנט עם escapeshellarg |
exec_cmd(string $cmd, int $timeout = 90): array | {ok, exit, output, timed_out} |
run_sync(array $args, int $timeout = 90): array | פקודת Composer קצרה, בתוך הבקשה |
start_job(string $label, array $args): array | {ok, id, mode, label} או {ok: false, error} |
job_status(string $id): array | מצב משימה. id חייב להיות 12 תווי hex |
prune_jobs(): void | מוחק קבצי משימה בני יותר מיממה |
manifest(): ?array, lock(): ?array | תוכן composer.json ו-composer.lock, או null |
content_hash(array $manifest): string | ה-hash שבו נבדק אם ה-lock מסונכרן |
installed(): array | {source, packages} |
summary(): array | מצב הפרויקט: manifest_exists, lock_synced, vendor_count, required_php, php_ok ועוד |
autoload_status(): array | {hooked, hooked_in, init_file, init_exists, init_writable, ...} |
hook_autoload(): array | {ok, message} |
create_manifest(): array | {ok, message} |
packagist_search(string $q): array | {ok, results} |
valid_package(string $name): bool, valid_constraint(string $c): bool | ולידציה לפי PACKAGE_RE ו-CONSTRAINT_RE |
$this->load->library("ComposerManager");
$env = ComposerManager::env();
if (!$env["exec"] || !$env["php_cli_ok"])
{
return "Composer cannot run on this host";
}
$summary = ComposerManager::summary();
$synced = $summary["lock_synced"]; // true, false, or null when there is no lock
אבטחה#
הפאנל מתקין לשרת הפרודקשן כל חבילה מ-Packagist: PACKAGE_RE בודק רק את צורת השם (vendor/package) ולא את מי שמפרסם אותה, ואין allowlist. חבילה זדונית או שנפרצה מריצה קוד על האתר מיד כשה-autoload נטען. הוסיפו רק חבילות שבדקתם, נעלו גרסה ב-composer.lock, והעבירו את composer.json ו-composer.lock לבקרת גרסאות.
בהתקנת Composer המסלול המועדף מאמת את חתימת המתקין (SHA-384). אם הוא נכשל (למשל חסימת רשת), מסלול הגיבוי מוריד composer-stable.phar ובודק רק שהקובץ גדול מ-400,000 בתים, בלי אימות חתימה. התקינו Composer ידנית בשרת רגיש, ואז הפאנל ישתמש בו דרך PATH.
vendor/ ו-composer.json יושבים בשורש האתר הציבורי. ודאו שהשרת חוסם גישה ישירה אליהם, וקבצי הלוג של המשימות נכתבים תחת CONFIG::$cache_folder, שעשוי להימצא בתוך ה-webroot, תלוי בהגדרת האתר. ראו הקשחה.ראו גם#
- עדכון עצמי, שמעדכן את ה-Composer של הליבה יחד איתה
- אבחון קוד ו-lint, לאבחון
execו-cron - מנהל קבצים, לעריכת
application/includes/init.php - הקשחה ו-מודל האבטחה
- דרישות מערכת