کاوش در PHP 8

بررسی نسخه هشتم PHP به همراه معرفی منابع یادگیری
بررسی نسخه هشتم PHP به همراه معرفی منابع یادگیری

در این مقاله در مورد PHP 8 صحبت می‌کنیم. به نظرم مطالعه تاریخچه تغییرات در کنار معرفی منابع آموزشی این فرصت رو به ما می‌ده تا با چیزی عمیق تر آشنا بشیم و نسبت بهش تسلط بیشتری پیدا کنیم.

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

بررسی *.۸.۰

۱. پشتیبانی از Named Arguments: به کمک این ویژگی مشخص می‌کنیم که آرگومان ها بر اساس نام پارامتر به جای موقعیت پارامتر به یک تابع پاس داده بشند. برای درک بهتر لازمه تا مثالی ببینیم:

array_fill(start_index: 0, count: 100, value: 50);

۲. اضافه شدن Attributes: راهی برای اضافه کردن متادیتا برای کلاس‌ها، متدها، پراپرتی‌ها و توابع هستند و برای افزایش کیفیت کدها خوبه که ازش استفاده کنیم.

۳. توانایی تعریف و تعیین پراپرتی‌ها در ورودی constructor: روشی کوتاه برای این است که پارامترهای سازنده به یک ویژگی در سازنده تخصیص داده شوند. برای درک بهتر لازمه تا مثالی ببینیم:

class User { 
    private string $name;
    public function __construct(string $name) {
        $this->name = $name; } 
}

مثال بالا میتونه به روش پایین هم نوشته بشه:

class User {
    public function __construct(private string $name) {}
}

۴. پشتیبانی از Union Types: با کمک این ویژگی امکانی بوجود اومده تا بتونیم برای یک متغیر تایپ‌های قابل قبول مختلفی رو تعیین کنیم، برای درک بهتر لازمه تا مثالی ببینیم:

class Example {
    private int|float $foo;
    public function squareAndAdd(float|int $bar): int|float {
        return $bar ** 2 + $foo; 
    }
}

۵. پشتیبانی از match expression: مثل switch expression، برای بررسی یک مقدار به کار میره؛ در switch تمرکز روی مقدار هست(==) اما در match تمرکز بر مقدار و جنس اون متغیر(===) هست. برای یادگیری این مطلب میتونید منابع مختلف موجود مثل(این مطلب یا این مطلب) رو مطالعه کنید.

۶. افزوده شدن قابلیت Nullsafe: با این قابلیت میتونیم به متد یا پراپرتی یک شی در صورتی که وجود داشته باشه دسترسی داشته باشیم ولی اگر وجود نداشته باشه null برگردونده میشه به جای Exception، برای درک بهتر لازمه تا مثالی ببینیم:

$address = $customer->getAddress();
$country = $address ? $address->getCountry() : null;

مثال بالا میتونه به روش پایین هم نوشته بشه:

$country = $customer->getAddress()?->getCountry(); 

۷. افزوده شدن WeakMap: کالکشنی از مجموعه‌ با جفت‌‌های Key/Value است که کلید آن باید یک شی باشد.

۸. افزوده شدن ValueError: نوع جدیدی از Error ها برای موقعی که نوع یک متغیر درست هست ولی مقدارش اشتباهه برای مثال پاس دادن یک عدد منفی به یک متغیر وقتی که متغیر انتظار عدد مثبت داشت.

۹. تغییر رفتار برای فراخوانی متدهای همنام در trait ها: فرض کنید داخل یک trait تابعی دارید که با یک تابع از trait دیگه هم‌نام هست. شما این 2 تا trait رو باهم داخل یک کلاس فراخوانی کردید، تو این نسخه باید به صورت دستی مشخص کنید که منظور شما دقیقا کدوم trait هست یعنی به این صورت: T2::func یا T1::func

۱۰. استفاده از spaceship operator(<=>) در مرتب‌سازی: برای مرتب کردن مقادیر استفاده از از عملگرspaceship operator (<=>) توصیه شده. برای درک بهتر لازمه تا مثالی ببینیم:

// Old
usort($array, fn($a, $b) => $a > $b);
// New
usort($array, fn($a, $b) => $a <=> $b);

۱۱. پشتیبانی از mixed type: زمانیکه متغیر ما قابلیت پذیرفتن انواع مختلف مقادیر رو داره برای مشخص کردن type به جای نام‌بردن از تک تک type ها خوبه که از mixed استفاده می‌کنیم. باتوجه به مستندات PHP، در واقع mixed معادل union type به این نوع است: object|resource|array|string|int|float|bool|null

۱۲. امکان تعیین static به‌عنوان نوع بازگشتی از متدها: در این نسخه این قابلیت وجود داره که بتوان مقدار static را از یک متد برگرداند، برای درک بهتر لازمه تا مثالی ببینیم:

class Test {
    public function create(): static {
        return new static(); 
    }
}

بررسی *.۸.۱

۱. پشتیبانی از رشته‌ها به عنوان کلید‌ در unpack کردن آرایه: هنگام unpack کردن یک آرایه (بازکردن آرایه با پیشوند "…") امکان پشتیبانی از رشته‌ها به عنوان کلید‌ آن آرایه نیز وجود دارد، برای درک بهتر لازمه تا مثالی ببینیم:

$arr1 = [1, 'a' => 'b'];
$arr2 = […$arr1, 'c' => 'd']; //[1, 'a' => 'b', 'c' => 'd']

۲. پشتیبانی از تعیین آرگومان‌های با نام پس از unpacking: ویژگی named argument از نسخه ۸ به PHP اضافه شده و در این نسخه امکانی وجود دارد تا پس از unpacking آرایه‌ها به عنوان ورودی یک تابع بتوان از named argument نیز استفاده کرد. برای درک بهتر لازمه تا مثالی ببینیم:

function printNumbers(int $a, int $b, int $c) {
    echo $a.' '.$b.' '.$c;
}
printNumbers(...[5, 7], c: 9); // Output: 5 7 9

۳. افزوده شدن Enumerations: مجموعه‌ای از چند آیتم مرتبط باهم به عنوان مقادیر محدود و مجاز برای یک چیز هستند. این ویژگی تو زبان‌های برنامه‌نویسی دیگه مثل Java هم وجود داره و پشتیبانی از اون در PHP یک اتفاق نسبتا بزرگ بوده.

۴. افزوده شدن Fibers: در اصل، فیبر یک بلاکی از کد هست که استک (متغیرها و...) خودش رو حفظ می کنه و جدا از برنامه اصلی اجرا میشه.

۵. افزوده شدن Intersection types: توسط این ویژگی میشه تعیین کرد تا متغیر تعیین شده توسط برنامه نویس به صورت همزمان از چه نوع class یا interface هایی لازم است باشند. این برعکس Union Types هست که اجازه میده تا متغیر متعلق به یکی از type های اعلام شده توسط برنامه‌نویس باشه. برای درک بهتر لازمه تا مثالی ببینیم:

function count_and_iterate(Iterator&\Countable $value) {
    foreach($value as $val) {} 
    count($value);
}

تو مثال بالا مقدار value$ باید به صورت همزمان از Iterator و Countable تاثیر گرفته باشه، وجود foreach نمایش تاثیرپذیری از Iterator و وجود ()count نمایش تاثیرپذیری از Countable هست.

۶. افزوده شدن نوع بازگشتی never: این type به عنوان نوع خروجی در توابع یا متدهایی استفاده میشه که در صورت اجرای اونها برنامه متوقف میشه(استفاده از die، exit یا Exception). برای درک بهتر لازمه تا مثالی ببینیم:

function redirect(string $url): never {
    header('Location: ' . $url); 
    exit();
}

تفاوت void با never در این هست که never تضمین میکنه برنامه پس از اجرای این تکه کد برنامه متوقف میشه یا Exception ایجاد میشه و چیزی از این تابع برگردانده نمیشه(حتی ;return) ولی یکی از علائم void این هست که برنامه پس از اجرای تکه کد ادامه پیدا می‌کنه.

۷. افزوده شدن کلمه کلیدی readonly: کلمه کلیدی که جلوگیری میکنه از تغییر مقدار یک پراپرتی پس از نمونه‌سازی اولیه. نمونه کد پایین این مسئله رو بهتر توضیح میده:

class User {
    public readonly string $username;
    public function __construct(string $username) {
        $this->username = $username; 
    }
}

$user = new User('joe','secure');
$user->username = 'john';
// Fatal error: Uncaught Error: Cannot modify readonly property User::$username

در واقع بعد از نمونه‌سازی اولیه از کلاس User دیگه امکانی برای تغییر مقادیرش وجود نداره و همونطور که در کد نشون داده شده بعد از اقدام برای تغییر، Fatal Error نمایش داده میشه.

۸. پشتیبانی از تعیین کلمه کلیدی final برای constant در class: از کلمه کلیدی final می‌توان برای تعیین اینکه مقدار ثابت و متد در کلاس غیرقابل تغییر باقی بماند استفاده کرد. با قرار دادن پیشوند final از تغییر یک متد یا ثابت توسط کلاس های فرزند جلوگیری می‌شود. اگر خود کلاس پیشوند final داشته باشد، نمی توان از آن ارث‌بری کرد.

۹. افزوده شدن تابع ()array_is_list: تابعی که بررسی میکنه آیا آرایه ما یک list هست یا نه. یک آرایه زمانی list در نظر گرفته میشه که کلیدهای اون به صورت عددی و متوالی از صفر(۰) شروع بشن.

۱۰. منسوخ شدن امکان تبدیل float به int: تبدیل ضمنی float به int که باعث از بین رفتن مقدار اعشاری میشه از این نسخه منسوخ شده. این روی کلیدهای آرایه، تعیین نوع int به عنوان مقدار ورودی یا خروجی و همینطور بر عملگرهایی که روی int کار میکنن تایر میذاره. نمونه کد پایین این مسئله رو بهتر توضیح میده:

$a = [];
$a[15.5]; // deprecated, as key value loses the 0.5 component
$a[15.0]; // ok, as 15.0 == 15

بررسی *.۸.۲

۱. پشتیبانی از readonly class: این ویژگی باعث میشه تا تمام پراپرتی‌های موجود در کلاس ویژگی readonly دریافت کنند.

class Post {
    public function __construct(
        public readonly string $title,
        public readonly Author $author,
        public readonly string $body,
        public readonly DateTime $publishedAt,
        ) {}
}

مثال بالا میتونه به روش پایین هم نوشته بشه:

readonly class Post {
    public function __construct(
        public string $title, 
        public Author $author,
        public string $body,
        public DateTime $publishedAt,
        ) {}
}

در صورتی امکان ارث‌بری از کلاس های readonly وجود داره که کلاس فرزند هم دارای این ویژگی باشه.

2. منسوخ شدن پراپرتی‌های داینامیک: استفاده از این ویژگی در این ورژن منسوخ شده و از نسخه ۹ زبان PHP استفاده از این ویژگی باعث ایجاد ErrorException میشه. در صورت استفاده از تکه کد زیر با پیامی در مورد منسوخ شدن این ویژگی مواجه میشیم.

class Post {
}
// …
$post = new Post();
$post->name= 'Name';
var_dump($post->name); // 'Name'
// Deprecated: Creation of dynamic property Post::$name is deprecated

در نظر داشته باشید که هنوز هم می‌تونیم از ()get__ و ()set__ برای کار با مقادیر داینامیک استفاده کنیم، به مثال زیر توجه کنید:

class Post {
    private array $properties = [];
    public function __set(string $name, mixed $value): void {
        $this->properties[$name] = $value;
    }
}
// …
$post->name = 'Name';

اگه هنوز هم میخواید که از مقادیر داینامیک استفاده کنید ولی تمایلی به پیاده‌سازی ()get__ و ()set__ ندارید راه بهتری هم وجود داره و اون استفاده از یک attribute با عنوان AllowDynamicPropertiesدر PHP هست، به مثال زیر توجه کنید:

#[AllowDynamicProperties]
class Post {
}
$post = new Post();
$post->name = 'Name'; // All fine

3. اضافه شدن اکستنشن Random: در این نسخه یک اکستنشن PHP جدید برای تولید عدد تصادفی اضافه شده است که مشکلات مربوط به توابع موجود رو برطرف میکنه. تمام کلاس‌های جدید معرفی شده در اکستنشن random تحت namespace با عنوان Random\ هستند.

کلاس Random\Randomizer به منظور ایجاد یک API شی گرا برای دسترسی به تمام عملکردهای تولید اعداد تصادفی ارائه شده است، به مثال زیر توجه کنید:

$r = new Random\Randomizer();
echo $r->getInt(1, 100);

4. پشتیبانی از انواع برگشتی true، false و null: از این نسخه امکان استفاده از مقادیر true false و null به عنوان نوع برگشتی یک تابع یا متد رو داریم. برای جلوگیری از افزونگی نمی‌تونیم از انواع true، false و bool در union type استفاده کنیم. به مثال زیر توجه کنید:

function alwaysReturnsFalse(): false {}
function alwaysReturnsNull(): null {}
function alwaysReturnsTrue(): true {}

5. پشتیبانی از DNF types: این ویژگی اجازه استفاده از union type و intersection type در کنار هم رو به توسعه‌دهنده میده. به مثال زیر توجه کنید:

function generateSlug((HasTitle&HasId)|null $post)  {
    if ($post === null) {
        return '';
    }
return strtolower($post->getTitle()) . $post->getId();
}

در صورت نوشتن مقادیری که در فرم DNF نباشن با parse error مواجه میشیم. به مثال زیر توجه کنید:

A&(B|D)
// Can be rewritten as (A&B)|(A&D) to avoid parse error
A|(B&(D|W)|null)
// Can be rewritten as A|(B&D)|(B&W)|null to avoid parse error

6. امکان تعیین constدر trait: از این نسخه توسعه‌دهنده‌ها امکان استفاده از constant در trait رو دارند و این constant ها میتونن همراه با final یا access modifier ها باشند، به مثال زیر توجه کنید:

trait FooBar {
    const FOO = 'foo';
    private const BAR = 'bar';
    final const BAZ = 'baz';
    final protected const QUX = 'qux';
}

class Test {
    use FooBar;
}

echo Test::BAZ; // 'bar'

7. پشتیبانی و حفاظت از مقادیر حساس: یک روش معمول در هر کدبیسی ارسال خطاها به سرویسی هست که اونها رو ردیابی میکنه و در صورت بروز مشکل به توسعه دهندگان اطلاع میده. توجه کنید که این پیغام میتونه شامل مقدار واقعی اطلاعات حساس مثل متغیرهای محیطی، گذرواژه‌ها یا نام‌های کاربری باشه که امکان داره در پیام‌های خطا، گزارش‌های خطا، گزارش‌های برنامه‌ها و غیره ختم نمایش داده بشه که ناامن و بسیار نامطلوب است. از این نسخه امکانی به وجود اومده تا بشه چنین پارامترهای حساسی رو با attribute جدیدی با نام SensitiveParameter\ علامت گذاری کرد. هر پارامتری که به عنوان پارامترحساس علامت‌گذاری شده باشه در قسمت خطاها نمایش داده نمیشه. بنابراین، می‌تونید اونها رو بدون نگرانی با سرویس‌های شخص ثالث برای ردیابی خطاها به اشتراک بگذارید.

function login(
    string $user,
    #[\SensitiveParameter] string $password
) {
    // …
    throw new Exception('Error');
}
 
login('root', 'root');
 
Fatal error: Uncaught Exception: Error in login.php:8
Stack trace:
#0 login.php(11): login('root', Object(SensitiveParameterValue))
#1 {main}
  thrown in login.php on line 8

با گوشه‌ای از ویژگی‌ها و تغییرات PHP 8 آشنا شدیم، خوشحال میشم که نظرات و پیشنهادات شما رو بدونم تا بتونم مطالب بهتری بنویسم. همونطور که گفتم بخاطر یکسری محدودیت‌ها نتونستم تمامی منابع رو معرفی کنم، لطفا برای دیدن منابع کامل لطفا به نسخه انگلیسی این مقاله تو لینکدین یا مدیوم سر بزنید، ولی اگر فرصت این‌کار رو ندارید این چندتا منبع رو داشته باشید.

تا زمانی که نسخه ۸ درحال توسعه باشه، این مقاله هم در حال کامل شدن هست و مطابق با ارائه نسخه جدید به‌روز میشه تا از آخرین تغییرات باخبر بشید.