Anophel-آنوفل کار با Enums در PHP: راهنمای کدنویسی ایمن تر

کار با Enums در PHP: راهنمای کدنویسی ایمن تر

انتشار:
2

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


آیا خوب نیست که راهی برای اعلام این مجموعه محدود از مقادیر ممکن به شیوه ای واضح و مستند داشته باشیم؟ دقیقاً همان جایی است که Enumerations یا Enums، همانطور که اغلب نامیده می شود، به کمک می آیند.


Enumerations سال هاست که بخشی از بسیاری از زبان های برنامه نویسی بوده است. آنها به شما این امکان را می دهند که نوع خاصی را تعریف کنید که به مجموعه ای خاص از مقادیر محدود می شود و وضوح و ایمنی را افزایش می دهد. برای توسعه دهندگان PHP، خبر خوب این است که در PHP 8.1 از Enums اکنون بخشی از هسته PHP است. بله، درست شنیدید! PHP اکنون پشتیبانی داخلی برای Enums فراهم می کند.

تاریخچه مختصری از Enums در PHP

قبل از PHP 8.1 در PHP از Enums پشتیبانی داخلی نداشت. در حالی که این فقدان Enums مانع از نوشتن کد توسعه‌دهندگان نمی‌شود، اما به این معنی است که PHP فاقد ابزاری است که در بسیاری از زبان‌های دیگر یافت می‌شود که بتواند کدنویسی را ایمن‌تر و کارآمدتر کند.


بدون Enum های داخلی، توسعه دهندگان مجبور بودند از ساختارهای دیگری برای نمایش مجموعه ای از مقادیر ممکن استفاده کنند، که اغلب به ثابت های کلاس، آرایه ها یا گاهی اوقات فقط رشته ها یا اعداد صحیح متوسل می شوند.


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


در PHP، Enum نوع خاصی از شی است، با مواردی که Enum اشیاء تک نمونه ای از آن کلاس است. این بدان معناست که شما می توانید Enums را در هر جایی که می توانید از یک شی استفاده کنید، استفاده کنید و آنها را بسیار متنوع می کند.


افزودن Enums به PHP نشان‌دهنده تکامل مداوم زبان برای ترکیب مفاهیم و پارادایم‌های برنامه‌نویسی مدرن‌تر است، کارایی آن را افزایش می‌دهد و توسعه‌دهندگان را قادر می‌سازد کد امن‌تر و تمیز تری بنویسند.

آشنایی با اصول Enums در PHP

برای کشف اصول اولیه Enums در PHP، تصمیم گرفتم از دنیای جادویی هری پاتر استفاده کنم. تصور کنید ما در حال کدنویسی برای مدرسه جادوگری هاگوارتز هستیم، جایی که هر دانش آموز جدید باید توسط کلاه مرتب سازی مجازی در یک خانه طبقه بندی شود.


در هاگوارتز، تنها چهار خانه وجود دارد: گریفیندور، هافلپاف، ریونکلاو و اسلیترین.


بنابراین، ما می توانیم این را به عنوان Enum در PHP نشان دهیم.


در اینجا نحوه اعلام Enum آمده است:

<?php

enum House
{
    case Gryffindor;
    case Hufflepuff;
    case Ravenclaw;
    case Slytherin;
}

در این مثال، یک Enum به نام House ایجاد کرده‌ایم که می‌تواند چهار مقدار ممکن داشته باشد - گریفیندور، هافلپاف، ریونکلاو و اسلیترین.


بیایید یک کلاس Student ایجاد کنیم، جایی که هر دانش آموز دارای یک house$ است.

class Student
{
    public ?House $house = null;
}

همچنین یک کلاس SortingHat با متد sort داریم. این روش یا پیشنهاد خانه را می پذیرد یا به صورت تصادفی خانه ای را به دانش آموز اختصاص می دهد.

درست مانند هری پاتر، کلاه مرتب سازی انتخاب هری را در نظر گرفت!

class SortingHat
{   
    public function sort(Student $student, ?House $suggestedHouse = null)
    {
        if ($suggestedHouse) {
            $student->house = $suggestedHouse;
            
            return;
        }

        $houses = [
            House::Gryffindor,
            House::Hufflepuff,
            House::Ravenclaw,
            House::Slytherin,
        ];

        $index = array_rand($houses);
        
        $student->house = $houses[$index];
    }
}

همانطور که می بینید، ما مقادیر متغیر suggestedHouse$ و ویژگی house$ را محدود کرده ایم تا تنها یکی از چهار خانه هاگوارتز باشد که از House Enum استفاده می کنند.


اگر بخواهید دانش آموزی را به خانه ای که وجود ندارد طبقه بندی کنید، PHP متوجه این موضوع می شود و اجرا نمی شود. این جادوی Enums است!

در این سناریو، خانه‌های هاگوارتز بدون داده‌های مرتبط «Pure Cases» نامیده می‌شوند و Enum که فقط شامل موارد Pure است، مانند House Enum ما، به عنوان «Pure Enum» نامیده می‌شود.


Pure Enums در مقابل Backed Enums

Enum های پشتیبان شده Enum هایی هستند که دارای یک مقدار اسکالر هستند. به همین دلیل است که آنها "not pure" در نظر گرفته می شوند.


برای یادآوری، یک مقدار اسکالر در PHP از نوع bool، float، int یا string است.

کد گاهی اوقات بیشتر از کلمات صحبت می کند، بنابراین در اینجا یک Backed Enum آمده است:

<?php

enum House: string
{
    case Gryffindor = 'Gryffindor';
    case Hufflepuff = 'Hufflepuff';
    case Ravenclaw = 'Ravenclaw';
    case Slytherin = 'Slytherin';
}

همانطور که می بینید، نوع Backed Enum (رشته در آن حالت) را تعریف کردیم و به هر مورد یک مقدار اختصاص می دهیم. ساده تر از این نمی تواند باشد. اما چرا از Backed Enums استفاده می کنید؟


در اینجا یک مورد استفاده عالی وجود دارد:

// Let's pretend we fetched this value from the database.
$house = 'Gryffindor';

$student = new Student(
    House::from($house)
);

// object(Student)#1 (1) {
//   ["house"]=>
//   enum(House::Gryffindor)
// }
var_dump($student);

در این مثال:


ما وانمود می کنیم که داده ها را از یک پایگاه داده واکشی کرده ایم و سعی می کنیم یک شی Student جدید ایجاد کنیم.
ویژگی house$ را با متد استاتیک ()House::from از یک مقدار رشته مقداردهی می کنیم (زیرا House اکنون یک Backed Enum از نوع رشته است).
اگر این کار انجام نشد، یک استثنا throw می کند. (Uncaught ValueError: "Foo" is not a valid backing value for enum "House")
 

در برخی موارد، به جای ایجاد یک استثنا، ممکن است بخواهید به مقدار پیش فرض برگردید. در اینجا نحوه انجام این کار با متد استاتیک ()House::tryFrom  آمده است که در صورت شکست، null را برمی‌گرداند.

$student = new Student(
    House::tryFrom('Slytherin') ?? House::Gryffindor
);

لیست کردن مقادیر Enum

در مثال قبلی دیدید که بدون پیشنهاد دانش آموز، خانه ای به صورت تصادفی اختصاص داده می شود.


با این حال، آنچه در مثالی که ارائه دادم مرا آزار می‌دهد، این است که ما به صورت دستی مقادیر احتمالی House Enum را برای ساخت آرایه خود لیست کرده‌ایم.


خوشبختانه، Enums همگی یک متد ()case دارند که می تواند کد ما را انعطاف پذیرتر کند!

class SortingHat
{   
    public function sort(Student $student, ?House $suggestedHouse = null)
    {
        if ($suggestedHouse) {
            $student->house = $suggestedHouse;
            
            return;
        }

	    // [tl! remove:1,6]
        $houses = [
            House::Gryffindor,
            House::Hufflepuff,
            House::Ravenclaw,
            House::Slytherin,
        ];
	    $houses = House::cases(); // [tl! ++]

        $index = array_rand($houses);
        
        $student->house = $houses[$index];
    }
}

خیلی تمیز، درسته؟

بررسی عمیق PHP Enums و مقایسه آنها با کلاس ها

در سفری که تاکنون با PHP Enums داشته ایم، ممکن است متوجه شده باشید که آنها مشابه کلاس ها هستند. آنها می توانند فضای نامی داشته باشند، رابط ها را پیاده سازی کنند، از ویژگی ها استفاده کنند، و همچنین به همان روش قابل بارگیری خودکار هستند.


اما، البته، تفاوت های قابل توجهی نیز بین کلاس های PHP و Enums وجود دارد.


برای درک این تفاوت ها، بیایید نمونه جادویی هری پاتر خود را دوباره مرور کنیم.


هنگامی که دانش آموزی با کلاه مرتب سازی در خانه طبقه بندی می شود، می دانیم که خانه همیشه یکی از چهار گزینه از پیش تعریف شده خواهد بود: گریفیندور، هافلپاف، ریونکلاو یا اسلیترین.


هیچ احتمال دیگری در زمینه هاگوارتز وجود ندارد. این سناریو برای Enums عالی است.


Enum، مانند House، یک نوع داده منحصر به فرد است که شامل مجموعه ای از ثابت های از پیش تعریف شده است.

این نشان می دهد که یک متغیر می تواند تنها یکی از این ثابت های از پیش تعریف شده را داشته باشد و هیچ چیز دیگری ندارد.


برای مثال، ویژگی house$ کلاس Student فقط می تواند یکی از چهار گزینه house تعریف شده در House Enum را در خود جای دهد.

class Student
{
    public ?House $house = null;
}

در مقابل، کلاس‌ها در PHP، مانند کلاس Student ما، می‌توانند ویژگی‌های مختلفی را در خود جای دهند و می‌توانند چندین بار با مقادیر ویژگی‌های مختلف نمونه‌سازی شوند.


از سوی دیگر، Enum ها را نمی توان نمونه سازی کرد و برای تعریف مجموعه ای ثابت و محدود از نمونه ها استفاده می شود.


تفاوت دیگر در نحوه مقایسه کلاس ها و Enums است.


در پی اچ پی، Enum ها با هویت خود مقایسه می شوند، نه با مقدار هایشان.


بیایید به یک مثال نگاه کنیم:

$a = House::Gryffindor;
$b = House::Gryffindor;

var_dump($a === $b); // bool(true)

در این مثال، a$ و b$ همان نمونه House Enum هستند، بنابراین a === $b$ درست است.


مقایسه‌هایی که از عملگرهای کمتر از < یا بزرگتر از > استفاده می‌کنند برای اشیاء Enum معنی‌دار نیستند و همیشه false را برمی‌گردانند.


موارد Enum در PHP دارای یک ویژگی خاص به نام name هستند که نام case-sensitive به حروف بزرگ و کوچک خود case است. این می تواند زمانی مفید باشد که می خواهید نام Enum را چاپ کنید.

echo House::Gryffindor->name; // Prints "Gryffindor".

کار با متد های Enumeration 

اکنون که نحوه مقایسه Enum ها و تفاوت های آنها با کلاس ها را بررسی کردیم، زمان آن رسیده است که عمیق تر غوطه ور شویم و متد های Enumeration را در PHP Enums بررسی کنیم.


دقیقاً مانند کلاس ها، Enums در PHP می تواند حاوی متدهایی باشد.


بیایید ببینیم چگونه می توانیم با استفاده از سناریوی مرتب سازی هری پاتر از این ویژگی استفاده کنیم.

enum House
{
    case Gryffindor;
    case Hufflepuff;
    case Ravenclaw;
    case Slytherin;

    public function getHouseColors() : array
    {
        return match($this) {
            House::Gryffindor => ['Red', 'Gold'],
            House::Hufflepuff => ['Yellow', 'Black'],
            House::Ravenclaw => ['Blue', 'Bronze'],
            House::Slytherin => ['Green', 'Silver'],
        };
    }
}

// array(2) {
//   [0]=>
//   string(3) "Red"
//   [1]=>
//   string(4) "Gold"
// }
var_dump(House::Gryffindor->getHouseColors());

در دنیای جادویی هاگوارتز، هر خانه ای رنگ های خود را دارد. در مثال بالا، یک متد ()getHouseColor به House Enum خود اضافه کرده ایم تا رنگ هر خانه را برگردانیم.


هنگامی که یک متد در یک Enum تعریف می شود، متغیر this$ تعریف می شود و به نمونه case اشاره می کند.


Enum ها می توانند از Traits و پیاده سازی اینترفیس ها استفاده کنند

مانند کلاس ها، Enums می تواند از Traits استفاده کند. این زمانی عالی است که متد های زیادی وجود دارد و باید آنها را در چندین فایل تقسیم کنید تا کد خود را مرتب‌تر نگه دارید.


هرچند محدودیت هایی وجود دارد:


شما نمی توانید properties داشته باشید.
نمی‌توانید متد های Enums را نادیده بگیرید (مانند ()values).

trait Colors
{
    public function getHouseColors() : array
    {
        return match($this) {
            House::Gryffindor => ['Red', 'Gold'],
            House::Hufflepuff => ['Yellow', 'Black'],
            House::Ravenclaw => ['Blue', 'Bronze'],
            House::Slytherin => ['Green', 'Silver'],
        };
    }
}

enum House
{
    use Colors;
  
    case Gryffindor;
    case Hufflepuff;
    case Ravenclaw;
    case Slytherin;
}

مانند Traits در کلاس ها، رابط ها نیز می توانند در Enums پیاده سازی شوند. یافتن یک مثال ملموس برای این مورد کار دشواری است، اما به هر حال می‌توانید ادامه دهید:

interface HasColors
{
    public function getHouseColors() : array;
}

enum House implements HasColors
{
    case Gryffindor;
    case Hufflepuff;
    case Ravenclaw;
    case Slytherin;
  
    public function getHouseColors() : array
    {
        return match($this) {
            House::Gryffindor => ['Red', 'Gold'],
            House::Hufflepuff => ['Yellow', 'Black'],
            House::Ravenclaw => ['Blue', 'Bronze'],
            House::Slytherin => ['Green', 'Silver'],
        };
    }
}

Enums در PHP 7 یا حتی PHP 5 چطور؟

قبل از معرفی Enumerations بومی در PHP 8.1، enums به طور معمول در PHP به روش‌های مختلف مدیریت می‌شد که هیچ‌کدام به‌ویژه ظریف یا قابل اعتماد نبودند. در اینجا برخی از استراتژی های رایج آورده شده است:


ثابت‌های کلاس: شاید رایج‌ترین راه برای پیاده‌سازی enum‌ها از طریق ثابت‌های کلاس باشد. در اینجا یک مثال است:

class House
{
    const Gryffindor = 'Gryffindor';
    const Hufflepuff = 'Hufflepuff';
    const Ravenclaw = 'Ravenclaw';
    const Slytherin = 'Slytherin';
}

به این ترتیب، به عنوان مثال، می توانید به یک مقدار enum با House::Gryffindor مراجعه کنید. این رویکرد راهی برای گروه‌بندی ثابت‌های مرتبط ارائه می‌کند، اما هیچ نوع ایمنی یا عملکردی را که enums واقعی ممکن است ارائه دهد ارائه نمی‌کند (زیرا ثابت‌های کلاس را نمی‌توان تایپ کرد و به‌عنوان فقط خواندنی علامت‌گذاری کرد).

آرایه ها: روش رایج دیگر استفاده از آرایه ها برای نگهداری مقادیر ممکن بود.

$houses = [
    'Gryffindor', 
    'Hufflepuff',
    'Ravenclaw',
    'Slytherin',
]

با این حال، این روش همچنین فاقد مزایای Enum های واقعی مانند ایمنی نوع و تکمیل خودکار است.


SplEnum: در PHP قبلاً دارای یک کلاس داخلی SplEnum بود که بخشی از کتابخانه استاندارد PHP (SPL) بود. با این حال، به دلیل مشکلات عملکرد آن به طور گسترده مورد استفاده قرار نگرفت و به پسوند SPL Types نیاز داشت که با PHP همراه نبود و آزمایشی در نظر گرفته شد.

class House extends SplEnum
{
    const Gryffindor = 'Gryffindor';
    const Hufflepuff = 'Hufflepuff';
    const Ravenclaw = 'Ravenclaw';
    const Slytherin = 'Slytherin';
}

این کلاس در PHP 7.0 حذف شده است، بنابراین دیگر استفاده نمی شود.


پکیج های شخص ثالث: همچنین پکیج های متعددی وجود دارند که عملکرد enum را ارائه می دهند، مانند myclabs/php-enum. اینها اغلب ویژگی‌های بیشتری نسبت به ثابت‌های کلاس یا آرایه‌های ساده ارائه می‌کنند، مانند متد هایی برای لیست  کردن همه مقادیر ممکن، تبدیل به/از رشته‌ها و غیره.

use MyCLabs\Enum\Enum;

class House extends Enum
{
    const Gryffindor = 'Gryffindor';
    const Hufflepuff = 'Hufflepuff';
    const Ravenclaw = 'Ravenclaw';
    const Slytherin = 'Slytherin';
}

با وجود این راه‌حل‌ها، هیچ‌کدام از آنها نمی‌توانند مجموعه کاملی از ویژگی‌هایی را که Enumerations واقعی می‌تواند ارائه دهد، مانند ایمنی نوع، بهینه‌سازی عملکرد، و عملکردی مانند دریافت همه مقادیر ممکن، ارائه دهد.


به همین دلیل است که افزودن Enumerations بومی در PHP 8.1 ارتقای مهمی برای این زبان بود. برای آشنایی با ویژگی های جدید PHP 8.3 این مقاله را بررسی کنید.

نتیجه

ما در این مقاله با Enum ها آشنا شدیم و قدرت آن را در کد نویسی را نیز مشاهده کردیم. Enum ها در بهینه سازی عملکرد تاثیر خیلی زیادی دارند و باعث افزایش عملکرد حتی در کوئری زدن است، بزودی مقاله در این مورد منتشر خواهیم کرد.

#php#enum#php8#clean_code#php_enum#Enumerations
نظرات ارزشمند شما :

در حال دریافت...

مقاله های مشابه

در حال دریافت...