آرگومان های نامدار در PHP 8 — به زبان ساده
در نسخه 8 PHP امکان پشتیبانی از پارامترها یا «آرگومانهای نامدار» (Named Arguments) اضافه شده است. در این مقاله به بررسی ورودی و خروجی آرگومان های نامدار در PHP 8 میپردازیم. اما قبل از آن با بررسی چند مثال با ساختار آن آشنا میشویم.
1setcookie(
2 name: 'test',
3 expires: time() + 60 * 60 * 2,
4);
در مثال فوق شاهد یک آرگومان نامدار هستیم که در یک تابع داخلی PHP استفاده شده است.
1class CustomerData
2{
3 public function __construct(
4 public string $name,
5 public string $email,
6 public int $age,
7 ) {}
8}
9
10$data = new CustomerData(
11 name: $input['name'],
12 email: $input['email'],
13 age: $input['age'],
14);
در مثال فوق یک DTO از مشخصههای پروموت شده به همراه آرگومانهای نامدار استفاده میکند.
1$data = new CustomerData(...$customerRequest->validated());
چنان که در مثال فوق میبینید آرگومانهای نامدار از اسپرد آرایه نیز پشتیبانی میکنند.
شاید از روی مثالهای فوق متوجه شده باشید که آرگومانهای نامدار امکان ارسال دادههای ورودی را به یک تابع بر مبنای نام آرگومان به جای ترتیب آرگومان فراهم میسازند.
بدیهی است که آرگومانهای نامدار قابلیتی عالی هستند که تأثیری چشمگیر روی زندگی روزانه هر برنامهنویسی میگذارند. شاید در مورد جزییاتی از این قبیل نگران باشید که اگر نام اشتباهی وارد کنید چه اتفاقی میافتد، یا نگران باشید که این ساختار اسپرد آرایه چه کار میآید. در ادامه این مقاله به این پرسشها پاسخ خواهیم داد.
چرا باید از آرگومانهای نامدار استفاده کنیم؟
ابتدا باید بیان کنیم که بحثهای گستردهای پیرامون این قابلیت مطرح است و حتی برخی استدلالها بر ضد آن مطرح شده است. با این حال باید گفت که کفه مزیتهای آن بسیار سنگینتر از مواردی مانند مشکلات سازگاری با نسخههای پیشین یا سنگین شدن API-ها است. این قابلیت به ما امکان خواهد داد که کدهای تمیز و با انعطافپذیری بیشتری بنویسیم.
یکی از مزیتهای آرگومان نامدار این است امکان حذف مقادیر پیشفرض را فراهم میسازد. در این بخش نگاهی دوباره به مثال کوکی قبلی میاندازیم:
1setcookie(
2 name: 'test',
3 expires: time() + 60 * 60 * 2,
4);
امضای این متد در واقع به صورت زیر است:
1setcookie (
2 string $name,
3 string $value = "",
4 int $expires = 0,
5 string $path = "",
6 string $domain = "",
7 bool $secure = false,
8 bool $httponly = false,
9) : bool
در مثالی که نشان دادیم، نیازی به تعیین یک کوکی $value وجود ندارد، اما باید یک «زمان انقضا» (Expiration Time) تعیین کنیم. آرگومانهای نامدار موجب شده که فراخوانی این متد کمی منسجمتر باشد. در ادامه متد setcookie را بدون آرگومان نامدار میبینید:
1setcookie(
2 'test',
3 '',
4 time() + 60 * 60 * 2,
5);
و در این بخش همین متد با آرگومان نامدار دیده میشود:
1setcookie(
2 name: 'test',
3 expires: time() + 60 * 60 * 2,
4);
علاوه بر این که با بهرهگیری از این قابلیت نیازی به استفاده از مقادیر پیشفرض نیست، این مزیت را نیز دارد که در مورد کاری که متغیر انجام میدهد، هیچ ابهامی وجود نخواهد داشت. این موضوع به طور خاص در تابعهایی با امضای متد بزرگ بسیار مفید است. در واقع میتوان گفت که اغلب آرگومانها معمولاً نوعی کد هستند و سوای این که چیستاند باید آنها را مدیریت کنیم. از این رو بهتر است که یک روش معقول برای انجام این کار داشته باشیم، تا این که هیچ چیزی در اختیار نداشته باشیم.
جزییات آرگومانهای نامدار
اینک که با کلیات آرگومانهای نامدار آشنا شدیم، نوبت آن رسیده که نگاهی تفصیلی به جزییات آن و کارهایی که میتوان یا نمیتوان با آن انجام داد داشته باشیم.
قبل از هر چیز باید اشاره کنیم که آرگومانهای نامدار میتوانند با آرگومانهای بینام یا همان آرگومانهای ترتیبی ترکیب شوند. در این حالت، آرگومانهای ترتیبی باید همواره اول آمده باشند.
مثال TDO زیر را که قبلاً نیز بررسی کردیم، در نظر بگیرید:
1class CustomerData
2{
3 public function __construct(
4 public string $name,
5 public string $email,
6 public int $age,
7 ) {}
8}
آن را میتوان به صورت زیر نیز نوشت:
1$data = new CustomerData(
2 $input['name'],
3 age: $input['age'],
4 email: $input['email'],
5);
با این حال داشتن یک آرگومان ترتیبی پس از آرگومان نامدار موجب بروز خطا میشود:
1$data = new CustomerData(
2 age: $input['age'],
3 $input['name'],
4 email: $input['email'],
5);
نکته دیگری که باید اشاره کنیم این است که امکان ترکیب اسپردینگ آرایه همراه با آرگومانهای نامدار وجود دارد:
1$input = [
2 'age' => 25,
3 'name' => 'Brent',
4 'email' => 'brent@stitcher.io',
5];
6
7$data = new CustomerData(...$input);
با این حال اگر برخی مداخل در آرایه مفقود باشند، یا اگر کلیدی باشد که به صورت یک آرگومان نامدار فهرست نشده باشد، خطایی ایجاد خواهد شد:
1$input = [
2 'age' => 25,
3 'name' => 'Brent',
4 'email' => 'brent@stitcher.io',
5 'unknownProperty' => 'This is not allowed',
6];
7
8$data = new CustomerData(...$input);
این امکان وجود دارد که آرگومانهای نامدار و ترتیبی را در یک آرایه ورودی ترکیب کرد، اما این کار تنها در صورتی امکانپذیر است که آرگومانهای ترتیبی از همان قاعده که قبلاً گفتیم پیروی کنند و در ابتدا آمده باشند.
1$input = [
2 'Brent',
3 'age' => 25,
4 'email' => 'brent@stitcher.io',
5];
6
7$data = new CustomerData(...$input);
اگر از تابعهای variadic استفاده میکنید، آرگومانهای نامدار با نام کلیدشان به آرایه آرگومانهای variadic ارسال میشوند. مثالهای زیر را در نظر بگیرید:
1class CustomerData
2{
3 public static function new(...$args): self
4 {
5 return new self(...$args);
6 }
7
8 public function __construct(
9 public string $name,
10 public string $email,
11 public int $age,
12 ) {}
13}
14
15$data = CustomerData::new(
16 email: 'brent@stitcher.io',
17 age: 25,
18 name: 'Brent',
19);
در این حالت، args$ در CustomerData::new شامل دادههای زیر خواهد بود:
1[
2 'age' => 25,
3 'email' => 'brent@stitcher.io',
4 'name' => 'Brent',
5]
«خصوصیتها» (Attributes) که به نام «حاشیهنویسی» (Annotations) هم شناخته میشوند نیز از آرگومانهای نامدار پشتیبانی میکنند.
1class ProductSubscriber
2{
3 #[ListensTo(event: ProductCreated::class)]
4 public function onProductCreated(ProductCreated $event) { /* … */ }
5}
امکان داشتن یک متغیر به عنوان نام آرگومان وجود ندارد:
1$field = 'age';
2
3$data = CustomerData::new(
4 $field: 25,
5);
در نهایت باید اشاره کنیم که آرگومانهای نامدار در زمان وراثت تغییرات نام را به روشی عملگرایانه مدیریت میکنند. به مثال زیر توجه کنید:
1interface EventListener {
2 public function on($event, $handler);
3}
4
5class MyListener implements EventListener
6{
7 public function on($myEvent, $myHandler)
8 {
9 // …
10 }
11}
PHP در پسزمینه امکان تغییر دادن نام event$ را به myEvent$ میدهد و handler$ نیز میتواند به myHandler$ عوض شود، اما اگر قصد دارید از آرگومانهای نامدار با استفاده از نام والد استفاده کنید، با خطای زمان اجرا مواجه خواهید شد:
1public function register(EventListener $listener)
2{
3 $listener->on(
4 event: $this->event,
5 handler: $this->handler,
6 );
7}
در مثال فوق در صورتی که listener$ وهلهای از MyListener باشد، شاهد یک خطای زمان اجرا خواهیم بود.
این رویکرد عملگرایانه برای اجتناب از بروز تغییرهای عمده در حفظ همه نامهای آرگومانها در زمان ارثبری اتخاذ شده است که راهحل مناسبی به نظر میرسد.
سخن پایانی
در این مقاله تلاش کردیم هر آن چه ممکن بود را در مورد آرگومانهای نامدار بیان کنیم.
شما نیز میتوانید دیدگاهها و پیشنهادهای خود را در این خصوص در بخش نظرات این نوشته بیان کنید.