اولین پروژه‌ای که به‌عنوان فریلنسر تحویل دادم، سه فایل PHP داشت که به‌ترتیب include می‌شدند و همه‌ی کارهایشان هم درست انجام می‌شد. اما وقتی دو سال بعد مشتری خواست یک کتابخانه‌ی ارسال ایمیل اضافه شود، مجبور شدم چهار فایل داخلی آن کتابخانه را دستی کپی کنم، وابستگی‌های داخلی‌اش را با require_once سرهم کنم، و دعا کنم با نسخه‌ی بعدی PHP نشکند. آن روز، اسم Composer را فقط شنیده بودم. چند ماه بعد که اولین بار جدی سراغش رفتم، فهمیدم چه درِ بزرگی را نادیده گرفته‌ام: کامپوزر در PHP نه یک ابزار جانبی، بلکه چیزی است که تفاوت پروژه‌ی آماتور و حرفه‌ای را مشخص می‌کند. در این مقاله، همان مسیری را می‌رویم که هر توسعه‌دهنده‌ی PHP دیر یا زود باید طی کند — از نصب و اولین composer.json تا رفتار در محیط تولید و استفاده در پروژه‌های وردپرسی.

Composer چیست و چه دردی را درمان می‌کند؟

اگر با مفاهیم پایه‌ی PHP تازه آشنا می‌شوید، اول آموزش PHP از صفر برای مبتدیان را بخوانید تا زمینه روشن باشد. اما تعریف دقیق: Composer یک مدیر وابستگی (Dependency Manager) برای PHP است. وظیفه‌اش این است که بگوید پروژه‌ی شما به چه کتابخانه‌هایی نیاز دارد، آن‌ها را از مخزن اصلی (Packagist) دانلود کند، نسخه‌ها را با هم هم‌خوان کند، و یک نقشه‌ی اتولود در اختیارتان بگذارد تا بدون نوشتن require دستی، کلاس‌ها را در کد فراخوانی کنید.

قبل از Composer، دنیای PHP شبیه شهری بود که هر خانه آب و برق خودش را جداگانه تولید می‌کرد. کتابخانه‌ها فایل‌های ZIP بودند، نسخه‌ها به هم نمی‌خوردند، و هر پروژه یک روش خاص برای مدیریت وابستگی داشت. Composer این آشفتگی را به یک قرارداد مشترک تبدیل کرد: یک فایل JSON استاندارد، یک پوشه‌ی مشترک (vendor/)، و یک فایل قفل (composer.lock) که تضمین می‌کند روی همه‌ی ماشین‌ها، نسخه‌های دقیقاً یکسانی نصب شود.

یک نکته‌ی مهم که گاهی اشتباه گرفته می‌شود: Composer با Package Manager سیستمی مثل apt یا npm تفاوت دارد. Composer در سطح پروژه کار می‌کند، نه در سطح سیستم. یعنی هر پروژه پوشه‌ی vendor/ خودش را دارد و نسخه‌ها بین پروژه‌ها مشترک نیستند. همین ویژگی باعث می‌شود دو پروژه با وابستگی‌های متناقض روی یک سرور کنار هم زندگی کنند.

Composer یک require هوشمند است، نه یک فروشگاه بسته؛ کسی که تفاوت این دو را نفهمد، در اولین به‌روزرسانی بزرگ، تیر می‌خورد.

نصب Composer روی سیستم و سرور

نصب Composer روی لینوکس و مک، یک خط دستور است:

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
composer --version

روی ویندوز، نصب‌کننده‌ی رسمی کار را در دو کلیک تمام می‌کند. اما یک نکته که خیلی‌ها از آن غافل می‌شوند: Composer را روی سرور تولید هم داشته باشید، حتی اگر فقط برای اجرای composer install --no-dev باشد. اگر سرور شما Composer ندارد و نمی‌توانید نصب کنید، می‌توانید پوشه‌ی vendor/ را از محیط محلی به سرور منتقل کنید — این کار قابل قبول است ولی بخشی از مزیت Composer (به‌روزرسانی یک‌خطی) را از دست می‌دهید.

برای پروژه‌های وردپرسی که روی هاست اشتراکی میزبانی می‌شوند، معمولاً Composer در دسترس نیست. در آن حالت، روی محیط محلی با Composer کار می‌کنید و پوشه‌ی نهایی را با یک استقرار ساده (FTP، SSH یا گیت) منتقل می‌کنید. این الگو، همان مسیری است که در گیت در وردپرس هم به آن رسیده‌ام: کد منبع با گیت، وابستگی‌ها با Composer.

composer.json: قلب پروژه

هر پروژه‌ی Composer یک فایل composer.json در ریشه دارد. این فایل مثل شناسنامه‌ی پروژه است: نام، توضیحات، نسخه‌ی PHP مورد نیاز، وابستگی‌ها، و تنظیمات اتولود. یک نمونه‌ی واقعی از پروژه‌ای که اخیراً ساختم:

{
    "name": "myvendor/myproject",
    "description": "A sample PHP project",
    "type": "project",
    "license": "MIT",
    "require": {
        "php": "^8.1",
        "monolog/monolog": "^3.0",
        "guzzlehttp/guzzle": "^7.5"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

سه بخش مهم این فایل که تجربه‌ام می‌گوید بیشترین تأثیر را روی سلامت پروژه دارند:

  • بخش require: وابستگی‌های تولیدی. نسخه‌ی PHP را همین اول محدود کنید تا وقتی کسی پروژه را کلون کرد، در همان مرحله‌ی اول بفهمد چه چیزی نیاز دارد.
  • بخش require-dev: وابستگی‌هایی که فقط در محیط توسعه لازم‌اند (مثل PHPUnit). این‌ها در سرور تولید نصب نمی‌شوند؛ نتیجه‌اش پوشه‌ی vendor/ سبک‌تر و سطح حمله‌ی کمتر.
  • بخش autoload: قاعده‌ی نگاشت نام‌فضا به پوشه. با PSR-4، یک خط تنظیم، جای ده‌ها require دستی را می‌گیرد.

یک نکته‌ی عملی: به‌جای تایپ کردن دستی این JSON، از composer init استفاده کنید. این دستور یک ویزارد تعاملی است که فایل پایه را برایتان می‌سازد و در پروژه‌های تازه، وقت زیادی صرفه‌جویی می‌کند.

دستورات ضروری که هر روز استفاده می‌کنم

Composer ده‌ها دستور دارد، ولی در پروژه‌های واقعی، هفت دستور ۹۰٪ کار را انجام می‌دهد:

# نصب وابستگی‌های اعلام‌شده در composer.json
composer install

# افزودن یک کتابخانه جدید
composer require monolog/monolog

# افزودن یک ابزار فقط برای توسعه
composer require --dev phpunit/phpunit

# به‌روزرسانی همه‌ی وابستگی‌ها با احترام به قیدهای نسخه
composer update

# به‌روزرسانی فقط یک بسته
composer update monolog/monolog

# حذف یک بسته
composer remove guzzlehttp/guzzle

# نمایش بسته‌های نصب‌شده و نسخه‌شان
composer show --direct

تفاوت install و update شایع‌ترین سوءتفاهم در کار با Composer است. composer install به فایل composer.lock نگاه می‌کند و نسخه‌های قفل‌شده را نصب می‌کند. اگر composer.lock وجود نداشته باشد، آن را از روی composer.json می‌سازد. composer update فایل قفل را نادیده می‌گیرد و با احترام به قیدهای composer.json، آخرین نسخه‌های سازگار را پیدا و قفل جدید را تولید می‌کند. در محیط تولید، هرگز update نزنید — همیشه install.

قیدهای نسخه‌گذاری

Composer از قیدهای معنایی پشتیبانی می‌کند و دانستن تفاوت‌شان جلوی بسیاری از دردها را می‌گیرد:

قیدمعنیکاربرد
^1.2هر نسخه‌ی 1.x که ≥ 1.2 باشدپیش‌فرض مطمئن برای اکثر پروژه‌ها
~1.2هر نسخه‌ی ≥ 1.2 و < 2.0وقتی 1.3 هم می‌تواند شکستن بیاورد
1.2.*فقط نسخه‌های 1.2.xپروژه‌های سخت‌گیر
dev-mainشاخه‌ی main مخزنفقط برای توسعه
در composer.json، قید ^ دوست شماست و * دشمن؛ تفاوت‌شان یک کاراکتر است، ولی در بودجه‌ی دیباگ، ماه‌ها فاصله دارند.

composer.lock و تفاوت محیط توسعه با تولید

بزرگ‌ترین هدیه‌ی Composer به تیم‌های چندنفره، فایل composer.lock است. وقتی اولین بار روی یک پروژه composer install می‌زنید، Composer نه‌فقط بسته‌ها را دانلود می‌کند، بلکه نسخه‌ی دقیق و هش commit هر بسته را در composer.lock ثبت می‌کند. از آن به بعد، هر کسی — همکار، CI، سرور تولید — با اجرای composer install، دقیقاً همان نسخه‌ها را می‌گیرد.

قاعده‌ی طلایی: composer.lock را همیشه در گیت نگه دارید. تنها استثنا، زمانی است که پروژه یک کتابخانه است (نه یک اپلیکیشن) — کتابخانه‌ها معمولاً فایل قفل را منتشر نمی‌کنند تا وابستگی‌هایشان به مصرف‌کننده تحمیل نشود. برای همه‌ی پروژه‌های نهایی (سایت‌ها، افزونه‌های درون‌سازمانی، سرویس‌ها) فایل قفل باید در مخزن باشد.

در سرور تولید، همیشه دو سوئیچ طلایی را اضافه کنید:

composer install --no-dev --optimize-autoloader

--no-dev جلوی نصب وابستگی‌های توسعه را می‌گیرد و --optimize-autoloader نقشه‌ی کلاس‌ها را فشرده می‌کند تا بارگذاری در محیط تولید سریع‌تر باشد. این دو سوئیچ، تفاوت محسوسی در مصرف حافظه و زمان پاسخ‌دهی دارند — همان چیزی که در بهینه‌سازی کدهای PHP روی اهمیتشان تأکید کرده‌ام.

اتولودینگ PSR-4: پایان دوران require_once

پیش از PSR-4، هر فایل PHP پر بود از require_once دستی — بعضی‌شان حتی داخل شرط، برای بارگذاری تنبل. این روش سه درد داشت: ترتیب include مهم می‌شد، مسیرها به هم می‌ریختند، و هر فایل جدید باید در چند جای پروژه اعلام می‌شد. PSR-4 این سه را با یک قاعده‌ی ساده حل کرد: نام‌فضای کلاس، تعیین می‌کند فایل کجاست.

// composer.json
"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
}

با این قاعده، کلاس App\Services\Mailer باید در مسیر src/Services/Mailer.php باشد، و کافی است در فایل اصلی پروژه، یک‌بار این را بگذارید:

require __DIR__ . "/vendor/autoload.php";

از این لحظه، هرجای پروژه که به یک کلاس نیاز داشته باشید، PHP خودش فایل مناسب را پیدا می‌کند. هیچ require_once دیگری لازم نیست. این سادگی، به‌تنهایی دلیل کافی برای پذیرش Composer است — حتی در پروژه‌ای که هیچ کتابخانه‌ی خارجی استفاده نمی‌کند. اگر با مفاهیم کلاس و نام‌فضا راحت نیستید، آموزش شی‌گرایی در PHP پیش‌نیاز خوبی است.

یک نکته‌ی ظریف که در پروژه‌های بزرگ به آن رسیده‌ام: بعد از هر بار اضافه کردن کلاس جدید به پوشه‌ی src/، لازم نیست چیزی را دستی به composer.json اضافه کنید — ولی بعد از تغییر تنظیمات خود autoload در JSON، باید یک بار composer dump-autoload بزنید تا نقشه‌ها بازسازی شوند. فراموش کردن این دستور، منبع کلاسیک خطای «Class not found» در پروژه‌های تازه است.

Composer در پروژه‌های وردپرسی

وردپرس خودش با Composer مدیریت نمی‌شود، ولی این به‌معنی غیبت Composer در پروژه‌های وردپرسی نیست. در عمل، من از Composer در سه جای مختلف پروژه‌های وردپرسی استفاده می‌کنم:

۱) توسعه‌ی افزونه‌ی اختصاصی

افزونه‌های جدی معمولاً به کتابخانه‌هایی مثل PHPMailer، Guzzle، یا Twig وابسته‌اند. به‌جای کپی دستی این کتابخانه‌ها در پوشه‌ی افزونه، composer.json می‌سازید و بعد از composer install، پوشه‌ی vendor/ را همراه افزونه منتشر می‌کنید. ساختار درست این کار را در ساختار فایل‌های یک افزونه استاندارد وردپرس باز کرده‌ام؛ اگر تازه با افزونه‌نویسی آشنا می‌شوید، افزونه وردپرس چیست نقطه‌ی شروع بهتری است.

۲) ابزارهای توسعه

ابزارهایی مثل PHPCS، PHPStan و PHPUnit با Composer نصب می‌شوند و کیفیت کد را در سطح تیم تضمین می‌کنند. در پروژه‌های وردپرسی جدی، این ابزارها را در بخش require-dev می‌گذارم تا کد از ابتدا با استانداردهای حرفه‌ای نوشته شود.

۳) مدیریت نسخه‌ی خود وردپرس و افزونه‌ها

با استفاده از بسته‌های جانبی مثل composer/installers و مخازن خصوصی، می‌توان وردپرس و افزونه‌ها را هم از طریق Composer مدیریت کرد. این رویکرد در تیم‌های بزرگ رایج است، ولی پیچیدگی خودش را دارد و برای سایت‌های تک‌نفره معمولاً ارزشش را ندارد. توصیه‌ی من: با Composer در سطح افزونه‌ی اختصاصی شروع کنید و بعد اگر تیم بزرگ شد، به سطح کل وردپرس گسترش دهید.

در هر سه حالت، یک نکته‌ی حیاتی وجود دارد: پوشه‌ی vendor/ را در گیت نگذارید، ولی در بسته‌ی نهایی محصول (افزونه، تم) قرار دهید. برای پروژه‌های درون‌سازمانی، معمولاً vendor/ را در گیت می‌گذارم تا استقرار روی هاست اشتراکی بدون Composer ممکن باشد. برای افزونه‌های عمومی که در مخزن وردپرس منتشر می‌شوند، معمولاً یک اسکریپت ساخت هست که composer install --no-dev --optimize-autoloader می‌زند و خروجی را بسته‌بندی می‌کند. اگر با نصب افزونه در وردپرس آشنا نیستید، آموزش نصب افزونه در وردپرس مسیر نصب خروجی نهایی را نشان می‌دهد.

امنیت وابستگی‌ها با composer audit

یکی از کم‌دیده‌شده‌ترین قابلیت‌های Composer، بررسی امنیتی وابستگی‌ها است. از نسخه‌ی ۲.۴ به بعد، دستور composer audit اضافه شده که وابستگی‌های پروژه را با پایگاه‌داده‌ی آسیب‌پذیری‌های شناخته‌شده مقایسه می‌کند:

composer audit

خروجی، فهرست بسته‌های آسیب‌پذیر با شناسه‌ی CVE و راه‌حل پیشنهادی را نشان می‌دهد. تجربه‌ام می‌گوید این دستور را در دو نقطه‌ی حیاتی اجرا کنید: اول در CI بعد از هر composer install، تا اگر وابستگی‌ای آسیب‌پذیر شد، همان روز خبردار شوید؛ دوم قبل از هر استقرار در تولید. امنیت وابستگی‌ها بخشی از همان چارچوبی است که در امنیت در PHP رویش تأکید کرده‌ام — کد خودتان ممکن است بی‌نقص باشد، ولی یک کتابخانه‌ی قدیمی می‌تواند کل پروژه را بی‌اعتبار کند.

در پروژه‌های PHP مدرن، بزرگ‌ترین ریسک امنیتی معمولاً کد خودتان نیست؛ وابستگی‌های نادیده‌گرفته‌شده است — همان‌هایی که ماه‌ها به‌روز نشده‌اند و کسی سراغشان نمی‌رود.

اشتباهاتی که در پروژه‌های واقعی دیده‌ام

فهرست کوتاهی از اشتباهاتی که در بازبینی پروژه‌های دیگران بارها دیده‌ام — هر کدام از آن‌ها یک ساعت یا یک روز دیباگ را در کدهای بعدی حذف می‌کند:

  • اجرای composer update روی سرور تولید: این کار می‌تواند ناگهان چند کتابخانه را به نسخه‌ی جدیدی ببرد که رفتارشان تغییر کرده و سایت را بشکند. در تولید همیشه composer install بزنید.
  • نادیده گرفتن فایل composer.lock: بعضی تیم‌ها این فایل را در .gitignore می‌گذارند به این بهانه که «فایل تولیدشده است». نتیجه: هر همکار و سرور، نسخه‌های متفاوتی می‌گیرد و باگ‌ها فقط روی بعضی محیط‌ها ظاهر می‌شوند.
  • نصب پکیج جهانی به‌جای پروژه‌ای: ابزارهایی مثل PHPUnit یا PHPStan باید در require-dev پروژه باشند، نه با composer global require. در غیر این صورت، هر پروژه نسخه‌ی خودش را نمی‌تواند داشته باشد.
  • فراموش کردن composer dump-autoload: بعد از افزودن کلاس جدید در بعضی شرایط (مثل autoload بهینه‌شده در تولید)، لازم است این دستور اجرا شود وگرنه «Class not found» می‌گیرید.
  • نسخه‌ی PHP متناقض در دو محیط: در composer.json بنویسید "php": "^8.1" ولی روی سرور PHP 7.4 باشد، Composer نصب نمی‌کند ولی کد شما در اجرا به مشکل می‌خورد. این تناقض را همان اول با php -v و composer check-platform-reqs چک کنید.
  • نادیده گرفتن خطاهای composer audit: اگر خروجی این دستور آسیب‌پذیری نشان می‌دهد، آن را به بعد موکول نکنید. راه‌حل معمولاً به‌روزرسانی یک نسخه‌ی کوچک است که چند دقیقه وقت می‌برد.

یک توصیه‌ی جانبی از تجربه: عادت کنید قبل از هر composer update روی محیط محلی، یک شاخه‌ی گیت بزنید. اگر بعد از به‌روزرسانی چیزی شکست، بازگشت به وضعیت قبل، یک دستور است. این عادت کوچک، بارها از بحران‌های نصفه‌شب جلوگیری کرده — همان اصلی که در گیت در پروژه‌های وردپرسی رویش تأکید کرده‌ام.

سخن آخر

Composer، در نگاه اول یک ابزار نصب کتابخانه به نظر می‌رسد. ولی در واقع، یک قرارداد است: قراردادی بین شما، همکارانتان، سرور تولید، و آینده‌ی پروژه. هر بار که یک وابستگی جدید اضافه می‌کنید، در حال امضای یک تعهد هستید که نسخه‌ی مشخصی از یک کتابخانه را در پروژه نگه می‌دارید؛ و هر بار که composer audit می‌زنید، در حال تجدید همان تعهد با معیارهای امنیتی امروز هستید.

اگر تا امروز Composer را در پروژه‌های PHP خود وارد نکرده‌اید، پیشنهاد می‌کنم از یک کار کوچک شروع کنید: در پروژه‌ی فعلی‌تان، یک پوشه‌ی src/ بسازید، composer.json با تنظیم PSR-4 بنویسید، و کدهای پراکنده‌ی پروژه را یکی‌یکی داخل کلاس‌های نام‌فضادار منتقل کنید. همین تمرین، حتی بدون نصب یک کتابخانه‌ی خارجی، کیفیت کد شما را یک پله بالا می‌برد. تجربه‌ی خودتان از پذیرش Composer در پروژه‌های قدیمی — مخصوصاً اگر با مقاومت همکار یا مشتری مواجه شده‌اید — در دیدگاه‌ها بنویسید؛ همین نکته‌های عملی، برای خواننده‌ی بعدی از هر مستند رسمی ارزشمندتر است. 📦