سلام خدمت همه شما
مدت زیادی بود میخواستم این پست رو منتشر کنم ولی کار پیش میومد و وقت نمیشد بشینم و این کار رو تمومش کنم اما بالاخره پس از مدت ها نشستم و کارشو تموم کردم :)
توی این پست تمرکز اصلی من روی psr-12 بود و psr-7 و psr-11 رو به صورت اجمالی توضیح دادم که فقط باهاشون آشنا باشید. اگر عمری باقی باشه اینا رو هم مفصل توضیح میدم بهتون.
اینکه خیلی روی psr-12 وقت گذاشتم دلیلش اینه که خیلی توی کار تیمی کمکم کرده و وجود این استاندارد باعث میشه که کل تیم یه حداقل هایی رو رعایت کنند تا کدها یکدست باشه و موقع خوندن پروژه، سردرگمی کمتر بشه.
خب دیگه بریم سراغشون...
همونطور که میدونید psr میاد و یه سری قوانین و استاندارد برای کارهای مختلف وضع میکنه. داخل این سند هم اومدن و یه استاندارد برای چگونگی کار کردن با HTTP Message ها ارائه دادن. یه پکیج هم براش ارایه دادن که یه سری اینترفیس داخلش هست تا با استفاده از اون ها بتونیم ساختار متناسب با PSR 7 رو پیاده سازی کنیم. این پکیج شامل دو اینترفیس RequestInterface و ResponseInterface که به ترتیب برای دریافت پارامتر ها و برای ارسال پارامتر ها با استفاده از پروتکل Http میباشد.
در صورتی که با فریمورک لاراول کار کرده باشید، کار کردن با این ساختار رو تا حدودی تجربه کردید.
توضیح دادن این استاندارد پیچیدگی خاصی داره که توی این سری مقالات نمیگنجه. ایشالا این مدل استاندارد ها رو توی پستهای جداگانهای بهتون معرفی خواهم کرد.
حتما تا حالا اسم کانتینر یا IoC Container به گوشتون خورده! اگر با این مفاهیم آشنا نیستید بهتره اول اینجا رو مطالعه کنید.
ما توی php ، تعداد زیادی پکیج برای پیادهسازی این سیستم داریم. برای مثال میشه موارد زیر رو نام برد:
کار psr هم اینه که بیاد و برای ما استانداردهایی رو بیاره که برنامهنویسها راحتتر بتونن با کدهای همدیگه ارتباط بگیرن. خب بسمالله! اینهم یه زمین عالی برای بازیِ بچههای PHP-FIG !
داخل psr-11 یه ساختاری بیان شده که همه بیان و از اون برای ساخت کانتینرهاشون استفاده کنند.
این psr چندتا اینترفیس رو برای ما میاره که که در ادامه با مهمترین اونها آشنا میشیم.
<?php namespace Psr\Container; /** * Describes the interface of a container that exposes methods to read its entries. */ interface ContainerInterface { /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @throws NotFoundExceptionInterface No entry was found for **this** identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. * * @return mixed Entry. */ public function get($id); /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * `has($id)` returning true does not mean that `get($id)` will not throw an exception. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. * * @param string $id Identifier of the entry to look for. * * @return bool */ public function has($id); }
در تمام IoC Container هاییکه این استاندارد رو رعایت کردند (از جمله کانتینر لاراول)، ما این متدها رو داریم و میتونیم ازشون استفاده کنیم.
دو اینترفیس دیگهای که این psr داره برای اکسپشنهایی هستش که این متدها دارند.
این psr یکی از مهمترین استانداردها هستش که رعایت اون میتونه کلی دعای خیر براتون بههمراه داشته باشه :)
توی پستهای قبلی با psr-1 آشنا شدیم. آشنایی با PSR-1
حالا توی این PSR میخوایم یه سری قراردادهای حرفهای تر برای کدنویسی رو یاد بگیریم.
این PSR خیلی جزئی تر از PSR-1 هستش. با رعایت این استانداردها برنامهنویسها کمتر سردرگم میشن و همکاری بین گروههای مختلف توسعه راحتتر انجام میشه.
اولین قانون PSR-12 اینه که از قوانین PSR-1 تبعیت کنیم.
بعد از این مورد میریم سراغ یه سری قوانین عمومی.
تمام فایل های PHP باید از Unix LF به عنوان line ending استفاده کنند. کاراکتر LF (مخفف Line Feed) کاراکترهایی برای کنترل پایان خطوط در یک فایل هستن. در واقع کامپیوترها برای اینکه بفهمن یک خط کجا تموم شده و باید به خط بعدی برن از این کاراکتر ها استفاده می کنن اما شما نمیتونید اونا رو ببینید. ما برای اعلام پایان خط دو نوع کاراکتر CR و LF داریم که به ترتیب کدهای 0x0D و 0x0A هستند. ویندوز برای اعلام انتهای خط از هر دو کاراکتر CR LF، سیستم های Unix مانند لینوکس از LF و سیستم های مک (کمپانی اپل) از CR استفاده می کنن. احتمالا شما این مقادیر را به شکل r\ برای CR و n\ برای LF دیده باشید (ویندوز از r\n\ استفاده میکنه). PSR-12 میگه که فایل های PHP شما باید از LF استفاده کنن.
تگ پایانی PHP که به شکل <? هستش، باید در فایلهایی که فقط کد PHP دارند حذف بشه. همچنین در انتهای خطوط نباید فضای خالی (whitespace) باشه.
هر خط نباید از ۸۰ کاراکتر بیشتر باشه و اگر اینطور شد باید اون خط رو به دو یا چند خط تقسیم کرد. همچنین هر خط فقط باید یک statement داشته باشه. برای indent کردن (تورفتگی خطوط) باید از چهار اسپیس استفاده بشه و استفاده از tab ممنوعه.
تمام کلمات کلیدی و رزرو شده در PHP باید با حروف کوچک نوشته بشن. همچنین استفاده از کلمات مشخص کننده نوع داده باید با حروف کوچک باشه؛ به طور مثال به جای boolean از bool و به جای integer از int استفاده کنید.
زمانی که تگ های آغازین PHP را مینویسین باید اونها رو در خط خودشون بنویسید و هیچ اطلاعات دیگهای توی اون خط نباشه مگر اینکه کدهای PHP شما با کدهای دیگهای مثل HTML ترکیب شده باشن. در واقع ابتدای فایل PHP یا header آن باید به ترتیب لیست زیر باشه. البته اگه اسکریپت شما به یکی از این موارد نیازی نداشت، اون رو نادیده بگیرید اما ترتیب باید حفظ شود:
دستورات import (وارد کردن منابع در فایل) نباید با علامت backslash شروع بشن بلکه همیشه آدرس باید به صورت کامل نوشته بشه. کد زیر یه مثال کامل از تمام این دستورات هستش:
<?php /** * This file contains an example of coding styles. */ declare(strict_types=1); namespace Vendor\Package; use Vendor\Package\{ClassA as A, ClassB, ClassC as C}; use Vendor\Package\SomeNamespace\ClassD as D; use Vendor\Package\AnotherNamespace\ClassE as E; use function Vendor\Package\{functionA, functionB, functionC}; use function Another\Vendor\functionD; use const Vendor\Package\{CONSTANT_A, CONSTANT_B, CONSTANT_C}; use const Another\Vendor\CONSTANT_D; /** * FooBar is an example class. */ class FooBar { // ... additional PHP code ... }
namespace های ترکیبی (compound namespace) که بیشتر از دو سطح داشته باشن مجاز نیستن. به طور مثال کد زیر یه نمونه غلط و غیر مجاز از وارد کردن namespace ها هستش:
<?php use Vendor\Package\SomeNamespace\{ SubnamespaceOne\AnotherNamespace\ClassA, SubnamespaceOne\ClassB, ClassZ, };
کد زیر هم نمونه یه کد صحیح و مجاز از وارد کردن namespace هاست:
<?php use Vendor\Package\SomeNamespace\{ SubnamespaceOne\ClassA, SubnamespaceOne\ClassB, SubnamespaceTwo\ClassY, ClassZ, };
توجه داشته باشید که این قانون مخصوص namespace های ترکیبی (compound namespace) هستش و ربطی به namespace های ساده نداره. ینی اگر میخواستید یه نیماسپیس معمولی بنویسید، نگران چیزی نباشید و راحت کارتون رو بکنید.
در صورتی که کدهای PHP شما با کدهای دیگهای مثل HTML ترکیب شدن و میخواید strict_type رو هم فعال کنید باید اون رو توی خط اول و در کنار تگ آغازین PHP قرار بدید. همچنین حتما باید تگ PHP رو ببندید. مثال:
<?php declare(strict_types=1) ?> <html> <body> <?php // ... additional PHP code ... ?> </body> </html>
دستورات declare نباید دارای فضای خالی (whitespace) باشن. همچنین اگر میخواید از دستورات block declare استفاده کنید حتما باید فرمتش به شکل زیر باشه (به مکان کروشه ها و فضای خالی دقت کنید):
declare(ticks=1) { // some code }
مثل همیشه کلمه class به معنی class و trait و interface و موارد مشابه هستش. قانون اول اینه که بعد از کروشه پایانی (علامت }) نباید هیچ کامنت یا کدی (توی همون خط) قرار بگیره. برای ساخت یک نمونه/شیء از یک کلاس همیشه باید پرانتزها رو هم بزنید حتی اگر نیازی به هیچ آرگومانی نداشته باشیم:
new Foo();
دستورات extends و implements باید حتما توی همون خطی باشن که نام کلاس هستش و اجازه ندارن در خطوط بعدی نوشته بشن. کروشه آغازین برای باز کردن کلاس (علامت {) باید حتما در خط خودش قرار بگیره. همچنین قبل یا بعد از این کروشهها نباید خطی خالی وجود داشتهباشه. کروشه پایانی هم باید بعد از بدنه کلاس و در خط دیگهای قرار بگیره و قبل از اون خطی خالی وجود نداشتهباشه. کد زیر یه نمونه ساده از این قوانین هستش:
<?php namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; class ClassName extends ParentClass implements \ArrayAccess, \Countable { // constants, properties, methods }
لیستهای دستورات implements و دستورات extends (در صورتی که برای interface ها باشه) میتونن به چند خط تقسیم بشن، البته به شرطی که indent (تورفتگی) بشن. در چنین حالتی اولین مورد باید از یه خط جدید شروع بشه و هر interface هم فقط توی یه خط قرار بگیره:
<?php namespace Vendor\Package; use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass; class ClassName extends ParentClass implements \ArrayAccess, \Countable, \Serializable { // constants, properties, methods }
اگر از دستور use برای استفاده از trait ها درون یک کلاس استفاده میکنین، باید اونها رو توی یه خط جدا و دقیقا بعد از کروشه آغازین بنویسین:
<?php namespace Vendor\Package; use Vendor\Package\FirstTrait; class ClassName { use FirstTrait; }
در کد بالا توجه داشته باشید که حتی اگه یه کلاس فقط یه دستور use داشته باشه باید کروشه پایانی توی یه خط جدا قرار بگیره. همچنین اگه چندین دستور use داشته باشیم، نمیتونیم اونها رو توی یه خط بیاریم بلکه باید هر کدوم رو توی یه خط جداگونه قرار بدیم:
<?php namespace Vendor\Package; use Vendor\Package\FirstTrait; use Vendor\Package\SecondTrait; use Vendor\Package\ThirdTrait; class ClassName { use FirstTrait; use SecondTrait; use ThirdTrait; }
در صورتی که کلاس شما علاوه بر use، کدهای دیگهای هم داشته باشه، باید یک خط بین این کدها و آخرین use فاصله بذارید:
<?php namespace Vendor\Package; use Vendor\Package\FirstTrait; class ClassName { use FirstTrait; private $property; }
همچنین هنگام استفاده از اپراتورهای insteadof و as باید دقیقا به شکل زیر عمل کنید (به فضای خالی، رفتن به خط جدید و تو رفتگی ها توجه کنید):
<?php class Talker { use A; use B { A::smallTalk insteadof B; } use C { B::bigTalk insteadof C; C::mediumTalk as FooBar; } }
visibility باید برای تمام خصوصیات مشخص بشه و نباید به مقادیر پیش فرض اتکا کنیم. همچنین اگه از PHP 7.1 به بعد استفاده می کنین، یعنی قابلیت مشخص کردن visibility برای ثابتها رو هم دارید بنابراین باید این کار را انجام بدید. استفاده از کلیدواژه var برای ساخت خصوصیات مجاز نیست.
در هر statement فقط باید یک خصوصیت را تعریف کنید و نباید همه رو با یک دستور بسازین. در برخی از زبان ها مانند جاوا اسکریپت مبحث visibility تعریف نشده بنابراین از قراردادهایی مثل شروع نام خصوصیات با آندرلاین (علامت _) برای مشخص کردن خصوصیات private استفاده میشه. توسعهدهندهها اجازه ندارن از این قراردادها در زبان PHP استفاده کنن و اگه نام خصوصیتی با آندرلاین شروع بشه نباید معنی خاصی داشته باشه.
همیشه باید بین تعیین تایپ (نوع داده) و خصوصیت یه اسپیس فاصله باشد. تعیین تایپ یک خصوصیت به شکل زیر انجام میشه:
<?php namespace Vendor\Package; class ClassName { public $foo = null; public static int $bar = 0; }
همانطور که می بینید int به ما میگه که این خصوصیت یه عدد هستش.
Visibility باید برای تمام متدها تعریف شود. قراردادهایی مانند شروع نام متد با آندرلاین برای مشخص کردن private بودنشان پذیرفته نیست (درست مثل پراپرتیها) و توسعهدهندگان نباید از چنین قراردادهایی استفاده کنند. البته نام متدهای شما میتواند با _ شروع بشه اما نباید به قصد مشخص کردن private یا protected باشه.
نام متدها و توابع نباید بعد از خودشان اسپیس داشته باشن، بلکه کروشه آغازین باید به خط بعدی بره و کروشه پایانی نیز باید پس از بدنه تابع/متد و در خط خودش قرار بگیره. همچنین در نظر داشته باشید که نباید هیچ اسپیسی بعد از پرانتز آغازین و قبل از پرانتز پایانی وجود داشته باشه. با این حساب اگر بخواهیم یک مثال ساده از تعریف متدها بزنیم، کد زیر را نشان خواهیم داد (به محل پرانتزها، کاما ها (ویرگول انگلیسی)، اسپیس ها و کروشه ها دقت کنید):
<?php namespace Vendor\Package; class ClassName { public function fooBarBaz($arg1, &$arg2, $arg3 = []) { // method body } }
این قوانین برای توابع هم صدق میکنن:
<?php function fooBarBaz($arg1, &$arg2, $arg3 = []) { // function body }
در لیست آرگومانها نباید قبل از ویرگولها اسپیس داشته باشیم بلکه یه اسپیس بعد از هر ویرگول قرار میگیره. همچنین آرگومان هایی که دارای مقدار پیش فرض هستن همیشه باید در آخر لیست قرار بگیرن (حتی در php8) :
<?php namespace Vendor\Package; class ClassName { public function foo(int $arg1, &$arg2, $arg3 = []) { // method body } }
همچنین میشه لیست آرگومان ها را بشکنیم به طوری که هر آرگومان در یک خط قرار بگیره و یک indentation (تو رفتگی) داشته باشه. توجه کنید که در این حالت باید آرگومان اول رو هم در یک خط جدید قرار بدید نه اینکه نقطه شکست از آرگومان دوم باشه.
بنابراین نباید هیچ آرگومانی در کنار هیچ کدام از پرانتزهای آغازین و پایانی داشته باشیم:
<?php namespace Vendor\Package; class ClassName { public function aVeryLongMethodName( ClassTypeHint $arg1, &$arg2, array $arg3 = [] ) { // method body } }
اگر متد/تابع شما دارای return type باشه (مشخص کرده باشید که چه نوع داده ای را بر میگردونه) باید بعد از علامت دو نقطه یک اسپیس قرار بدید و بعدش return type رو مشخص کنید. حواستون باشه که return type و علامت دو نقطه باید توی همون خطی باشن که پرانتز پایانی لیست آرگومانها در اون قرار داره به طوری که بین علامت دونقطه و پرانتز پایانی هیچ اسپیسی نباشه:
<?php declare(strict_types=1); namespace Vendor\Package; class ReturnTypeVariations { public function functionName(int $arg1, $arg2): string { return 'foo'; } public function anotherFunction( string $foo, string $bar, int $baz ): string { return 'foo'; } }
در صورتی که میخواید nullable type declaration انجام بدید (اجازه بدهید یک آرگومان null یا دلخواه باشد) نباید بین علامت سوال و نام آرگومان هیچ فاصلهای وجود داشتهباشه:
<?php declare(strict_types=1); namespace Vendor\Package; class ReturnTypeVariations { public function functionName(?string $arg1, ?int &$arg2): ?string { return 'foo'; } }
همچنین زمانی که از اپراتور reference (علامت &) برای یک آرگومان استفاده میکنید، نباید بعد از اون هیچ فاصلهای باشه. این موضوع رو میتونید داخل کد بالا مشاهده کنید.
آیا با variadic three dot operator یا به زبان ساده تر همون اپراتور splat در PHP آشنا هستید؟ این اپراتور اجازه میده که تعداد نامشخصی داده رو به یک آرگومان ارسال کنید. بهطور مثال:
unction concatenate($transform, ...$strings) { $string = ''; foreach($strings as $piece) { $string .= $piece; } return($transform($string)); } echo concatenate("strtoupper", "I'd ", "like ", 4 + 2, " apples"); // This would print: // I'D LIKE 6 APPLES
به عبارتی اولین آرگومان پاس داده شده به تابع concatenate، رشته strtoupper هستش اما از آرگومان اول به بعد (دوم و سوم و چهارم الی بی نهایت - مقادیر I'd و like و 4+2 و apples) همه در رشته string$ قرار می گیرند یا به عبارتی جزئی از آرگومان دوم محسوب میشن. زمانی که از این اپراتور استفاده میکنید نباید بین اون و نام آرگومان هیچ فاصلهای وجود داشتهباشه:
public function process(string $algorithm, ...$parts) { // processing }
همچنین زمانی که می خواهید از اپراتور reference و splat با هم استفاده کنید، نباید هیچ فاصله ای بینشون وجود داشتهباشه:
public function process(string $algorithm, &...$parts) { // processing }
اگر قصد استفاده از abstract یا final رو دارید، باید اون ها رو قبل از دستورات visibility بنویسید اما static همیشه بعد از visibility میاد. کد زیر این مسئله رو به خوبی نشون میده:
<?php namespace Vendor\Package; abstract class ClassName { protected static $foo; abstract protected function zim(); final public static function bar() { // method body } }
زمانی که متد یا تابعی رو فراخوانی می کنید، نباید بین نام تابع/متد و پرانتز آغازین فاصله وجود داشته باشه. همچنین نباید هیچ فاصلهای بعد از پرانتز آغازین و قبل از پرانتز پایانی وجود داشته باشه. در لیست آرگومانها هم نباید قبل از ویرگولها فاصلهای باشه بلکه این فاصله بعد از ویرگولها میاد. مثال:
<?php bar(); $foo->bar($arg1); Foo::bar($arg2, $arg3);
اگر آرگومانهای زیادی دارید، اجازه دارید اونها را بشکنید به طوری که هر آرگومان در خط خودش قرار گرفته و یک indentation (تورفتگی) داشته باشه. در چنین حالتی اولین آرگومان در خطی جدید قرار میگیره:
<?php $foo->bar( $longArgument, $longerArgument, $muchLongerArgument );
در صورتی که میخواید فقط یک آرگومان رو بشکنید (مثلا آرگومان شما یک anonymous function یا آرایه هستش) مجاز به انجام این کار هستین اما این شکستن به معنی شکستن خود لیست آرگومانها نیست بلکه میتونید فقط همون تک آرگومان رو بشکنید:
<?php somefunction($foo, $bar, [ // ... ], $baz); $app->get('/hello/{name}', function ($name) use ($app) { return 'Hello ' . $app->escape($name); });
دستورات کلی برای استفاده از ساختارهای کنترل کننده (هر ساختاری که اجرای کد رو کنترل میکنه - مثلا شرط if) اینطور هستش:
توسعهدهندگان اجازه ندارند که از ساختارهای کنترلی بدون کروشه استفاده کنند. استفاده از کروشهها باعث میشه که با اضافه شدن خطهای جدید، ساختار خوانا و قابل خواندن باشه.
ساختار یک شرط if باید به شکل زیر باشه. در این ساختار به محل پرانتزها و دستورات else و elseif توجه داشته باشید:
<?php if ($expr1) { // if body } elseif ($expr2) { // elseif body } else { // else body; }
دستورات درون پرانتزهای ساختارهای کنترلی رو میشه شکست. برای شکستن این دستورات باید از قوانین ذکر شده در قسمت قبلی (قرار گرفتن هر دستور در یک خط و indent شدن یک واحدی) پیروی کنید. در چنین حالتی اپراتورهای boolean همیشه باید یا ابتدای خط و یا در انتهای خط باشند و هر کدوم از این دو مورد رو که انتخاب کردید باید در کل کدها رعایت کنید:
<?php if ( $expr1 && $expr2 ) { // if body } elseif ( $expr3 && $expr4 ) { // elseif body }
یک دستور switch باید به شکل زیر نوشته بشه. دستور case همیشه باید یک واحد نسبت به switch تورفتگی داشته باشه اما دستور break (یا هر دستور دیگری که switch رو متوقف میکنه) باید نسبت به case فرورفتگی نداشته باشه. در صورتی که یک case داشته باشیم که خالی نباشه اما رد شدن از آن به صورت عمدی نوشته شده باشه (دستوری نداشته باشه که ما رو از switch خارج کنه)، باید یک کامنت به شکل no break براش بنویسید تا توسعهدهندههای دیگه مطمئن بشن که شما دچار اشتباه نشدید. شما می توانید تمام این موارد رو در کد نمونه زیر مشاهده کنید:
<?php switch ($expr) { case 0: echo 'First case, with a break'; break; case 1: echo 'Second case, which falls through'; // no break case 2: case 3: case 4: echo 'Third case, return instead of break'; return; default: echo 'Default case'; break; }
آرگومانهای دستور switch قابلیت شکسته شدن دارند. اگر بخواید چنین کاری رو انجام بدید باید از تمام قوانین گفتهشده برای این کار در ساختارهای شرطی نیز تبعیت کنید؛ یعنی هر آرگومان/دستور در یک خط جداگانه قرار گرفته و یک واحد indent میشه. پرانتز پایانی و کروشه آغازین باید در یک خط باشن و بینشون یک اسپیس باشه. همینطور اپراتورهای boolean هم باید همیشه یا در ابتدای خط و یا در انتهای آن باشند، شما باید یکی از این دو حالت رو انتخاب کنید و تمام کدهای خودتون رو دقیقا به همین شکل بنویسید:
<?php switch ( $expr1 && $expr2 ) { // structure body }
یک دستور while دقیقا به شکل زیر نوشته میشه. به محل قرار گرفتن پرانتزها و اسپیس ها دقت کنید:
<?php while ($expr) { // structure body }
در مورد شکستن آرگومانها هم دقیقا همون قوانینی رو پیروی میکنید که در قسمت switch توضیح دادم و برای تکراری نشدن موضوع دوباره اونها رو ذکر نمیکنم.
همچنین یک ساختار ساده do while به شکل زیر هستش:
<?php do { // structure body; } while ($expr);
و اگر بخواید آرگومانها رو بشکنید باید از تمام قوانینی که ذکر شد تبعیت کنید:
<?php do { // structure body; } while ( $expr1 && $expr2 );
از اونجایی که همه این قوانین شبیه هم هستند برای اینکه سرتون رو درد نیارم سعی میکنم مثال بزنم و از تکرار بیش از حد پرهیز کنم :) .
<?php for ($i = 0; $i < 10; $i++) { // for body }
برای شکستن آرگومان های for هم به این شکل عمل میکنیم:
<?php for ( $i = 0; $i < 10; $i++ ) { // for body }
همچنین استفاده از ساختار foreach به ای شکله:
<?php foreach ($iterable as $key => $value) { // foreach body }
در این ساختار شکستن آرگومان مجاز نیست.
ساختار try-catch-finally آخرین ساختار در این دسته هستش و شکل صحیحش به صورت زیر هستش:
<?php try { // try body } catch (FirstThrowableType $e) { // catch body } catch (OtherThrowableType | AnotherThrowableType $e) { // catch body } finally { // finally body }
قوانین استایلینگ در اپراتورها بر اساس مفهوم arity اونهاست؛ یعنی چه تعداد operand یا «عَمَلوَند» دریافت می کنند. عملوند همان عنصریه که اپراتور یا «عملگر» روی آن اجرا میشه. توی این قسمت وقتی میگیم استفاده از whitespace (اسپیس یا همان فضای خالی) در کنار یک اپراتور مجازه، میتونید از چند اسپیس استفاده کنید تا خوانایی کد بهتر بشه.
اپراتورهای افزایش و کاهش واحد نباید هیچ اسپیسی را بین خود و عملوند داشته باشند:
$i++; ++$j;
همچنین اپراتورهای Type casting نباید درون پرانتزهای خودشون هیچ اسپیسی داشته باشند:
$intValue = (int) $input;
تمام اپراتورهای حسابی، مقایسه ای، انتسابی، bitwise، منطقی، رشته ای و type باید قبل و بعد از خود حداقل یک اسپیس داشته باشند. به مثال زیر توجه کنید:
if ($a === $b) { $foo = $bar ?? $a ?? $b; } elseif ($a > $b) { $foo = $a + $b * $c; }
اپراتور شرطی ternary operator باید قبل و بعد از کاراکتر های ؟ و : حداقل یک اسپیس داشته باشند:
$variable = $foo ? 'foo' : 'bar';
زمانی که عملوند میانی این اپراتور حذف می شه باید از همون قوانین ذکر شده برای اپراتورهای مقایسهای پیروی کنید:
$variable = $foo ?: 'bar';
همونطور که می دونید closure ها در زبانهای برنامه نویسی یک هدف اصلی دارند: شلوغ نکردن global namespace یا global scope. در واقع closure ها راهی برای نوشتن یک تابع به صورت محلی هستند که فقط هدف خاص و کوچکی را دارد و نیازی به وجود آن در scope سراسری نیست. یک مثال ساده از اون ها رو در کد زیر می بینید:
function replace_spaces ($text) { $replacement = function ($matches) { return str_replace ($matches[1], ' ', ' ').' '; }; return preg_replace_callback ('/( +) /', $replacement, $text); }
در PSR-12 هم قوانینی مربوط به closure ها داریم که باید رعایت بشن. هر closure باید بعد از کلیدواژه function یک اسپیس داشته باشه. همچنین باید قبل و بعد از کلیدواژه use هم یک اسپیس داشته باشه. کروشه آغازین نباید به خط بعدی بره و در خط فعلی باقی میمونه اما کروشه پایانی باید بعد از بدنه تابع و در خط جداگانه خودش قرار داشته باشه. نباید بعد از پرانتز آغازین آرگومان ها یا متغیرها، اسپیسی وجود داشته باشه. این مسئله برای پرانتز پایانی هم صدق میکنه. در لیست آرگومان ها نباید قبل از ویرگولها اسپیس داشته باشیم بلکه باید پس از هر ویرگول یک اسپیس قرار بدید. آرگومانهای closure با مقادیر پیش فرض باید به انتهای لیست آرگومان ها منتقل بشن. اگر return type رو مشخص کردید باید به قوانین اون پایبند باشید: در صورت وجود کلیدواژه use، علامت دو نقطه باید پس از بسته شدن پرانتز use و بدون هیچ فاصلهای بیاد. در صورتی که تمام قوانین ذکر شده رو در کنار هم بگذارید چنین نتیجه ای میگیریم:
<?php $closureWithArgs = function ($arg1, $arg2) { // body }; $closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) { // body }; $closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool { // body };
همچنین شکستن لیست متغیرها/ آرگومانها تابع قوانینی هستش که قبلا توضیح داده بودیم:
<?php $longArgs_noVars = function ( $longArgument, $longerArgument, $muchLongerArgument ) { // body }; $noArgs_longVars = function () use ( $longVar1, $longerVar2, $muchLongerVar3 ) { // body }; $longArgs_longVars = function ( $longArgument, $longerArgument, $muchLongerArgument ) use ( $longVar1, $longerVar2, $muchLongerVar3 ) { // body }; $longArgs_shortVars = function ( $longArgument, $longerArgument, $muchLongerArgument ) use ($var1) { // body }; $shortArgs_longVars = function ($arg) use ( $longVar1, $longerVar2, $muchLongerVar3 ) { // body };
توجه داشته باشید که قوانین ذکر شده در تمام حالات برقرار هستند، حتی در زمانی که closure مستقیما در یک متد یا در فراخوانی تابع استفاده بشه:
<?php $foo->bar( $arg1, function ($arg2) use ($var1) { // body }, $arg3 );
کلاسهای ناشناس از قوانین ذکر شده در قسمت closure ها پیروی میکنن:
<?php $instance = new class {};
کروشه آغازین میتونه روی همون خطی باشه که کلیدواژه class در آن قرار داره البته به شرطی که لیست interface آنقدر طولانی نباشه که از حد مجاز طول خط رد شده و به خط بعدی بریم. اگر به خط بعدی بریم باید کروشه آغازین رو در خط جدیدی قرار بدیم:
<?php // Brace on the same line $instance = new class extends \Foo implements \HandleableInterface { // Class content }; // Brace on the next line $instance = new class extends \Foo implements \ArrayAccess, \Countable, \Serializable { // Class content };
خب دیگه باید بهتون یه خسته نباشید بگم که تا اینجا رو مطالعه کردید. اگر قابل میدونید بنده رو فالو کنید چون در آینده میخوام یه سری مطالب جالب در مورد php بهتون بگم و احتمالا ضبط ویدئو هم داشته باشم برای بعضی از مطالب.
براتون آرزوی موفقیت میکنم.