مقاله

استانداردهای PSR در PHP — PSR-4، PSR-12 و نقش در Laravel

استانداردهای PSR در PHP — PSR-4، PSR-12 و نقش در Laravel

PSR چیست، مهم‌ترین PSRها برای Laravel developer و autoloading با Composer — زبان مشترک ecosystem.

وقتی composer require spatie/laravel-permission می‌زنید، پکیج با قراردادهای مشترک کار می‌کند — بخشی از آن PSR (PHP Standards Recommendations) از PHP-FIG است. Laravel برای autoload از PSR-4 استفاده می‌کند و چند interface استاندارد را پیاده‌سازی می‌کند: container آن با PSR-11، Log با PSR-3 و Cache با PSR-16 سازگار است؛ HTTP Client آن هم روی Guzzle ساخته شده که PSR-7 و PSR-18 را پیاده‌سازی می‌کند. سبک کدنویسی خود Laravel بر پایهٔ PSR-12 است، اما قواعد اختصاصی خودش را هم دارد.

خلاصه: PSR توصیه‌نامه‌ای از PHP-FIG است، نه بخشی از زبان PHP؛ مفسر PHP هیچ PSRی را اجبار نمی‌کند. در کار روزمرهٔ Laravel دو مورد بیشترین کاربرد را دارند: PSR-4 که Composer بر اساس آن کلاس‌ها را از روی namespace پیدا می‌کند، و PSR-12 (و جانشین آن، PER Coding Style) که قالب‌بندی کد را یکدست می‌کند. بقیهٔ PSRها interfaceهای مشترک‌اند تا پکیج‌ها بتوانند با پیاده‌سازی‌های مختلف کار کنند.

PHP-FIG چیست؟

PHP-FIG (PHP Framework Interop Group) گروهی از نمایندگان framework و library authorهاست — هدف interoperability یعنی اینکه کد یک پروژه بتواند بدون وابستگی به جزئیات پروژهٔ دیگر با آن کار کند. PSR پیشنهاد است نه اجبار — اما ecosystem عملاً از مهم‌ترین‌ها پیروی می‌کند.

هر PSR وضعیتی دارد و فقط PSRهای Accepted نهایی‌اند. طبق فهرست رسمی php-fig.org/psr:

  • Accepted: PSR-1، 3، 4، 6، 7، 11، 12، 13، 14، 15، 16، 17، 18 و 20
  • Deprecated: PSR-0 (autoload قدیمی، جایگزین: PSR-4) و PSR-2 (coding style قدیمی، جایگزین: PSR-12)
  • Draft: مثل PSR-5 و PSR-19 (PHPDoc) — هنوز ممکن است تغییر کنند

PSRها سه دستهٔ کلی‌اند: autoload (PSR-4)، سبک کدنویسی (PSR-1 و PSR-12) و interfaceهای مشترک (مثل PSR-3، PSR-7، PSR-11 و PSR-16). رعایت یک interface یعنی کلاس شما همان متدها را با همان امضا دارد؛ ربطی به اینکه زبان PHP چه چیزی را مجاز می‌داند ندارد.

PSR-4 — Autoloading

"autoload": {
    "psr-4": {
        "App\\": "app/",
        "Database\\Factories\\": "database/factories/"
    }
}

namespace به پوشه map — Composer. با این تنظیم، کلاس App\Http\Controllers\PostController باید در فایل app/Http/Controllers/PostController.php باشد: پیشوند namespace به پوشهٔ پایه نگاشت می‌شود، بقیهٔ namespace به زیرپوشه‌ها و نام کلاس به نام فایل.

  • کلاس جدید در مسیری که از قبل در PSR-4 تعریف شده معمولاً بدون کار اضافه پیدا می‌شود؛ بعد از تغییر بخش autoload در composer.json: composer dump-autoload.
  • بزرگی و کوچکی حروف مهم است: روی Windows ممکن است postController.php کار کند، اما روی سرور Linux خطای class not found می‌دهد.
  • برای تست‌ها از autoload-dev استفاده کنید تا namespace تست در autoload production نباشد.

PSR-12 — Coding Style

PSR-12 جایگزین PSR-2 (اکنون deprecated) است و رعایت PSR-1 را لازم می‌داند — قواعد brace، indent، declare، import و فاصله‌گذاری. PHP-FIG بعداً PER Coding Style را منتشر کرده که طبق متن خودش PSR-12 را گسترش می‌دهد و جایگزین آن می‌شود تا قابلیت‌های جدیدتر PHP را هم پوشش دهد.

Laravel Pint به‌طور پیش‌فرض preset laravel را اجرا می‌کند که بر پایهٔ PSR-12 است اما با آن یکسان نیست. اگر پروژه یا پکیج شما دقیقاً PSR-12 یا PER می‌خواهد، preset را در pint.json عوض کنید:

{
    "preset": "psr12"
}

presetهای per و symfony هم وجود دارد. مهم انتخاب یک style و اجرای خودکار آن در PR است — Clean Code.

PSR-3 — Logger Interface

interface LoggerInterface
{
    public function emergency(string|\Stringable $message, array $context = []): void;
    public function info(string|\Stringable $message, array $context = []): void;
    // ...
}

امضای بالا مربوط به نسخهٔ ۳ پکیج psr/log است. Laravel Log facade — LogManager خودش LoggerInterface را پیاده‌سازی می‌کند و در پشت صحنه از Monolog (که آن هم PSR-3 است) استفاده می‌کند. پس پکیجی که یک PSR-3 logger می‌خواهد، می‌تواند logger لاراول را بگیرد.

PSR-7 — HTTP Message

PSR-7 interfaceهای Request/Response/Stream را تعریف می‌کند — Guzzle آن را پیاده‌سازی می‌کند و HTTP Client لاراول روی Guzzle ساخته شده. اما Illuminate\Http\Request و Response در Laravel بر پایهٔ Symfony HttpFoundation‌اند، نه PSR-7. اگر واقعاً لازم باشد، با نصب symfony/psr-http-message-bridge و یک پیاده‌سازی PSR-7 می‌توانید در route از ServerRequestInterface استفاده کنید.

PSR-11 — Container

Psr\Container\ContainerInterface با دو متد get() و has(). contract container لاراول این interface را extend می‌کند؛ پس کتابخانه‌ای که PSR-11 container می‌خواهد با container لاراول کار می‌کند.

PSR-16 — Simple Cache

get/set/delete ساده. contract Illuminate\Contracts\Cache\Repository خودش PSR-16 CacheInterface را extend می‌کند؛ یعنی Cache::store() را می‌توان به پکیجی داد که PSR-16 می‌خواهد — و در کنارش متدهای بیشتری مثل remember هم دارد.

چرا مهم است؟

  • swap implementation — logger، cache
  • خواندن doc پکیج‌ها
  • contribute به open source
  • code review سریع‌تر — diffها درگیر فاصله و brace نمی‌شوند

یکدستی همکاری را راحت می‌کند، اما رعایت PSR به‌تنهایی معماری خوب نمی‌سازد: کدی با قالب‌بندی کاملاً PSR-12 هم می‌تواند controller چندصدخطی و وابستگی‌های درهم داشته باشد.

PSRهای کمتر روزمره

PSR-6 cache pool (نسخهٔ مفصل‌تر cache نسبت به PSR-16)، PSR-14 event dispatcher، PSR-15 middleware و request handler، PSR-17 factoryهای HTTP و PSR-20 clock. این‌ها در ecosystem Symfony و frameworkهای مبتنی بر PSR-15 بیشتر دیده می‌شوند. Laravel برای event و middleware abstraction خودش را دارد: امضای middleware لاراول ($request و Closure $next) PSR-15 نیست و event dispatcher آن هم PSR-14 را پیاده‌سازی نمی‌کند.

PSR، قرارداد پروژه و قرارداد framework

سه لایه را از هم جدا کنید:

  • PSR: چیزی که بین پروژه‌ها مشترک است — مثل PSR-4 و interfaceها. «سازگار با PSR-3» یعنی دقیقاً همان interface را پیاده‌سازی کرده‌اید.
  • قرارداد framework: namespace App، محل controllerها، نام‌گذاری migration و model در Laravel — این‌ها PSR نیستند، قرارداد Laravel‌اند.
  • قرارداد تیم: نام‌گذاری متدها، ساختار پوشهٔ domain، preset انتخابی Pint — در README یا pint.json مستند شود.

لازم نیست هر پروژه همهٔ PSRها را پیاده کند؛ PSRهای interface فقط وقتی به کار می‌آیند که بخواهید پیاده‌سازی را عوض کنید یا پکیج قابل استفاده در frameworkهای مختلف بنویسید. برای تصمیم‌های معماری به بهترین شیوه‌های Laravel در پروژهٔ واقعی نگاه کنید.

اشتباهات

  • namespace اشتباه — class not found
  • تفاوت حروف بزرگ و کوچک بین نام فایل و کلاس — روی Windows کار می‌کند، روی Linux نه
  • ignore Pint — style ناهمگون
  • classmap و PSR-4 قاطی بدون دلیل
  • ارجاع به PSR-2 یا PSR-0 به‌عنوان استاندارد فعلی — هر دو deprecated‌اند
  • فرض اینکه PHP یا Composer رعایت PSR-12 را اجبار می‌کند — فقط ابزارهایی مثل Pint آن را بررسی می‌کنند

جمع‌بندی

PSR-4 و PSR-12 (یا PER Coding Style) روزانه؛ بقیه برای درک ecosystem و نوشتن پکیج قابل جابه‌جایی. Composer: نصب Composer. Error: Handling.

ساختار پکیج استاندارد

اشتراک‌گذاری