برنامه نویسی 236 بازدید

در نسخه هشتم از زبان برنامه‌نویسی PHP امکان استفاده از خصوصیت‌ها (Attributes) فراهم آمده است. هدف این خصوصیت‌ها که در بسیاری از زبان‌های دیگر به نام «حاشیه‌نویسی» (Annotations) نیز شناخته می‌شوند، آن است که داده‌های متا را به کلاس‌ها، متدها، متغیرها و موارد مشابه به روش ساخت‌یافته اضافه کنیم. ما در این مقاله با مفهوم و کاربرد Attribute در PHP 8 آشنا خواهیم شد.

مفهوم خصوصیت به هیچ وجه جدید نیست و ما سال‌ها است که از DocBlock برای شبیه‌سازی آن بهره می‌گیریم. با این حال با اضافه شدن خصوصیت‌ها اکنون ابزاری اختصاصی برای بازنمایی این نوع از فراداده‌ها داریم و دیگر لازم نیست DocBlock-ها را به صورت دستی تحلیل کنیم.

اینک احتمالاً سوال‌های جدیدی برای شما پیش آمده است. ظاهر خصوصیت چگونه است؟ چطور می‌توان خصوصیت سفارشی ساخت؟ آیا هیچ نقطه ضعفی دارند؟ در ادامه این مقاله با عنوان آشنایی با Attribute در PHP 8 به همه این پرسش‌ها پاسخ خواهیم داد.

آشنایی مقدماتی با مفهوم Attribute در PHP 8

قبل از هر چیز باید بدانید که ظاهر کلی یک خصوصیت به شکل زیر است:

در ادامه این مقاله مثال‌های دیگری را معرفی خواهیم کرد، اما این مثال از «مشترک رویداد» (Event Subscriber)، مثالی خوبی برای تشریح استفاده اصلی خصوصیت‌ها محسوب می‌شود.

احتمالاً این ساختاری نیست که شما دوست داشتید یا امیدوار بودید ببینید. شاید استفاده از @ یا ‎‎@: یا docblocks یا موارد دیگر را بیشتر ترجیح می‌دادید، اما به هر حال ساختار آن در PHP این گونه نیست. تنها نکته‌ای که در خصوص ساختار، ارزش اشاره را دارد، این است که همه گزینه‌ها مورد بررسی قرار گرفته‌اند و دلایل خوبی برای انتخاب این ساختار وجود دارد. برای مشاهده این مباحث و استدلال‌ها می‌توانید به این صفحه (+) ‌مراجعه کنید.

طرز کار داخلی

اینک نوبت آن رسیده که روی موارد جالبی مانند طرز کار داخلی ListensTo تمرکز کنیم. قبل از هر چیز باید اشاره کنیم که خصوصیت‌های سفارشی، کلاس‌های ساده‌ای هستند که خود را با خصوصیت [Attribute]# حاشیه‌نویسی کرده‌اند. این [Attribute]# مبنا در مستندات معمولاً PhpAttribute نامیده می‌شد، اما در ادامه عنوان آن تغییر یافت. ظاهر آن چنین است:

کافی است به خاطر بسپارید که هدف از خصوصیت‌ها افزودن فراداده به کلاس‌ها و متدها است و نه چیز دیگر. برای مثال نمی‌توان و نباید از آن‌ها برای اعتبارسنجی ورودی آرگومان استفاده کرد. به بیان دیگر شما نباید به پارامترهای ارسال شده به یک متد درون خصوصیت‌هایش دسترسی داشته باشید. قبلاً این کار امکان‌پذیر بود، اما اینک روش کار تغییر یافته تا از پیچیدگی‌های غیرضروری اجتناب کنیم.

اگر به مثال مشترکان رویداد خود بازگردیم، متوجه می‌شویم که همچنان نیاز داریم که فراداده را بخوانیم و مشترکان خود را در جایی ثبت کنیم. اگر سابقه کار با لاراول را داشته باشید، در این موارد از یک «ارائه‌دهنده سرویس» (Service Provider) به عنوان محلی برای انجام این کار استفاده می‌کنید، اما استفاده از راه‌کارهای دیگر هم مشکلی ندارد. برای نمونه در کد زیر یک کد تکراری ملال‌آور را می‌بینید که صرفاً برای ارائه زمینه‌ای آمده است:

اینک نوبت آن رسیده که نگاهی به resolveListeners بیاندازیم که همه کارهای مهم در آن اتفاق می‌افتند:

نکات مهم

چنان که می‌بینید خواندن فراداده‌ها به این روش در قیاس با تحلیل رشته‌های docblock آسان‌تر است. با این حال دو نکته ظریف وجود دارد که باید به آن‌ها توجه داشته باشیم.

نکته نخست این است که یک فراخوانی ()attribute->newInstance$ وجود دارد. این جا در عمل مکانی است که کلاس خصوصیت سفارشی «وهله‌سازی» (instantiate) می‌شود. این متد پارامترهای لیست شده در تعریف خصوصیت در کلاس مشترک را دریافت کرده و آن‌ها را به «سازنده» (constructor) ارسال می‌کند.

این بدان معنی است که از لحاظ فنی حتی نیاز به ساخت خصوصیت سفارشی نیز وجود ندارد. شما می‌توانستید ()attribute->getArguments$ را به صورت مستقیم فراخوانی کنید. به علاوه وهله‌سازی کلاس به آن معنی است که انعطاف‌پذیری سازنده برای تحلیل هر چیزی که دوست دارید را به دست می‌‌آورید. در نهایت باید اشاره کنیم که همواره بهتر است خصوصیت را با استفاده از ()newInstance وهله‌‌سازی کنیم.

نکته دوم که در این مقاله با عنوان آشنایی با Attribute در PHP 8 باید مورد اشاره قرار دهیم، استفاده از ReflectionMethod::getAttributes()‎ است. این تابعی است که همه خصوصیت‌های یک متد را بازگشت می‌دهد. شما می‌توانید دو آرگومان را به آن ارسال کنید تا خروجی‌اش را فیلتر نمایید.

با این حال برای درک این فیلترینگ یک نکته دیگر را نیز باید در مورد خصوصیت‌ها بدانید. شاید این نکته برای شما بدیهی باشد، اما اجازه بدهید اشاره گذرایی به آن داشته باشیم. امکان اضافه کردن چند خصوصیت به متد، کلاس، مشخصه یا ثابت وجود دارد. برای نمونه می‌توانید به صورت زیر عمل کنید:

با توجه به این نکته، کاملاً روشن است که چرا ()Reflection*::getAttributes یک آرایه بازگشت می‌دهد. در بخش بعدی با روش فیلتر کردن خروجی آن آشنا می‌شویم.

فیلتر کردن خروجی

فرض کنید مشغول تحلیل مسیرهای کنترلر هستید و صرفاً به خصوصیت Route علاقه دارید. به این ترتیب می‌توانید فقط آن کلاس را به عنوان یک فیلتر ارسال کنید:

پارامتر دوم شیوه اجرای فیلترینگ را تغییر می‌دهد. شما می‌توانید ReflectionAttribute::IS_INSTANCEOF را ارسال کنید که همه خصوصیت‌های پیاده‌سازی شده یک اینترفیس مفروض را بازگشت می‌‌دهد.

برای نمونه فرض کنید تعاریف کانتینر را تحلیل می‌کنید که روی چند خصوصیت متمرکز است. در این حالت باید کاری مانند زیر انجام دهید:

چنان که می‌بینید این یک میانبر زیبا است که در هسته مرکزی این زبان تعبیه شده است.

Attribute در PHP 8

تئوری فنی

اکنون که ایده‌ای از چگونگی کارکرد عملی خصوصیت‌ها یافته‌اید، نوبت آن رسیده که کمی هم به مباحث تئوری Attribute در PHP 8 بپردازیم تا مطمئن شویم که مفهوم خصوصیت‌ها را در PHP به طور کامل یاد گرفته‌ایم. قبل از هر چیز قبلاً به اجمال اشاره کردیم که خصوصیت‌ها می‌توانند در چند نقطه اضافه شوند. برای نمونه امکان افزودن آن‌ها در کلاس‌ها و کلاس‌های بی‌نام وجود دارد:

همچنین می‌توانیم آن‌ها را به مشخصه‌ها و ثابت‌ها اضافه کنیم:

برای اضافه کردن خصوصیت به متدها و تابع‌ها نیز به روش زیر عمل می‌کنیم:

در مورد کلوژر‌ها می‌توانید از ساختار زیر کمک بگیرید:

و پارامترهای متدها و تابع‌ها نیز به صورت زیر خواهد بود:

توجه کنید که امکان اعلان خصوصیت‌ها پیش و پس از docblock-ها وجود دارد:

همچنین باید اشاره کنیم که خصوصیت‌ها می‌توانند صفر، یک یا چند آرگومان داشته باشند که از سوی سازنده خصوصیت تعریف می‌شود:

همانند پارامترهای مجاز که می‌توان به یک خصوصیت ارسال کرد، قبلاً دیدیم که ثابت‌های کلاس، نام‌های ‎::class و نوع‌های اسکالر نیز مجاز هستند. با این حال چند نکته دیگر نیز در این خصوص وجود دارند که باید مورد اشاره قرار دهیم. خصوصیت‌ها تنها عبارت‌های ثابت را به عنوان آرگومان ورودی می‌پذیرند.

این بدان معنی است که عبارت‌های اسکالر و حتی شیفت بیت‌ها (bit shifts) مجاز هستند. همچنین امکان استفاده از class::، ثابت‌ها، آرایه‌ها و باز کردن آرایه‌ها، عبارت‌های بولی و عملگر null coalescing وجود دارد.

پیکربندی خصوصیت

چنان که در بخش قبل این مقاله با عنوان آشنایی با Attribute در PHP 8 دیدیم، خصوصیت‌ها را به صورت پیش‌فرض می‌توان در مکان‌های متعددی اضافه کرد. با این حال، امکان پیکربندی آن‌ها به صورتی که تنها در مکان‌های خاصی بتوانند استفاده شوند نیز وجود دارد. برای نمونه می‌توانیم کاری کنیم که ClassAttribute تنها بتواند در کلاس‌ها و نه جای دیگر استفاده شود. برای بهره‌گیری از این رفتار باید یک فلگ به خصوصیت Attribute روی کلاس خصوصیت ارسال کنیم. به مثال زیر توجه کنید:

استفاده از فلگ‌های زیر ممکن است:

این‌ها فلگ‌های bitmask هستند و از این رو می‌توانید آن‌ها را با استفاده از یک عملگر OR باینری ترکیب کنید.

فلگ پیکربندی بندی دیگر به بحث تکرارپذیری مربوط است. به طور پیش‌فرض یک خصوصیت نمی‌تواند دو بار مورد استفاده قرار گیرد، مگر این که به صورت صریح به صورت تکرارپذیر مشخص شده باشد. این کار به همان روش پیکربندی هدف با یک فلگ بیت انجام می‌پذیرد:

توجه کنید که همه این فلگ‌ها تنها زمانی که ()attribute->newInstance$ فراخوانی شود و نه قبل‌تر، اعتبارسنجی می‌شوند.

سخن پایانی

زمانی که قابلیت خصوصیت به صورت ابتدایی به این زبان اضافه شد، زمینه افزودن خصوصیت‌های داخلی دیگر نیز به آن مهیا شد. برای نمونه یکی از این موارد خصوصیت [Deprecated]# که مثال رایجی از آن خصوصیت [Jit]# است. ما در آینده شاهد خصوصیت‌های داخلی بیشتری خواهیم بود. به عنوان نکته نهایی در این مقاله آشنایی با Attribute در PHP 8 باید اشاره کنیم که اگر در مورد ژنریک‌ها نگران هستید، ذکر این نکته ضروری است که این ساختار تداخلی با آن‌ها نخواهد داشت، هر چند هنوز ساختار ژنریک به زبان PHP اضافه نشده است.

اگر این مطلب برای شما مفید بوده است، آموزش‌ها و مطالب زیر نیز به شما پیشنهاد می‌شوند:

«میثم لطفی» دانش‌آموخته ریاضیات و شیفته فناوری به خصوص در حوزه رایانه است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار نیز با مجله فرادرس همکاری دارد.

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *