Composer

ניהול חבילות Composer של האתר מתוך פאנל הניהול, מה ההבדל בין ה-composer של האתר לזה של הליבה, איך מחברים autoload, ואילו מגבלות בטיחות מובנות

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

לכל אתר 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.jsoncomposer.json בשורש האתר
מי מנהלצוות Wizzo, יחד עם הגרסהמפתח האתר, מהפאנל
vendor/system/core/vendor, נטען ב-cron_runner וב-cron_managervendor/ בשורש האתר
טעינה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 CLIComposerManager::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() חסום, והפאנל מציג מה בדיוק חוסם.

זרימת עבודה בפאנל#

  1. מצב סביבה: הפאנל קורא status ומציג את env, איזה Composer נמצא, מצב composer.json, composer.lock ו-vendor/, ומצב ה-autoload.
  2. התקנת Composer אם חסר.
  3. יצירת composer.json אם אין לאתר.
  4. חיבור autoload, כדי שהחבילות ייטענו.
  5. הוספה, עדכון והסרה של חבילות, עם חיפוש ב-Packagist.
  6. בדיקת חבילות מיושנות (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": ...}.

פעולהגוףמה היא עושה
statusenv, composer (מה נמצא), summary, autoload
packagesרשימת החבילות המותקנות, מתוך vendor/composer/installed.json, ואם אין, מתוך composer.lock
outdatedמריצה composer outdated --format=json --all סינכרונית (עד 180 שניות) ומחזירה map של version, latest, status, outdated
searchqחיפוש ב-Packagist דרך השרת (עד 20 תוצאות)
install_composerמורידה ומתקינה composer.phar
create_manifestיוצרת composer.json מינימלי, אם אינו קיים
hook_autoloadמחברת את ה-autoload, ראו למעלה
runaction, package?, constraint?, dev?פקודת Composer כמשימה מנותקת. מחזירה {ok, id, mode, label}
jobidמצב משימה ולוג שגדל, לשאילתה חוזרת

פעולות run#

רק הפעולות הבאות מגיעות למעטפת, ושם החבילה והמגבלה עוברים ולידציה:

actionפקודת Composerהערה
installinstall
updateupdateכל החבילות
update_packageupdate <package> --with-dependenciesדורש package
requirerequire <package>[:<constraint>]dev: true מוסיף --dev
removeremove <package>דורש package
dump_autoloaddump-autoload -o
validatevalidate --no-check-publish
clear_cacheclear-cache
diagnosediagnose
self_updateself-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): ?stringGET יוצא, או 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, תלוי בהגדרת האתר. ראו הקשחה.

ראו גם#

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