مسیری به سمت php 8.1

برنت (Brent) تهیه کننده ی سایت sitcher.io که مقالات و ویدئوهای جالبی درباره ی پی اچ پی منتشر می کند، یک خبرنامه‌ی ایمیلی 10 قسمتی درباره ی پی اچ پی 8.1 دارد. با عضویت در این خبرنامه طی 10 روز، 10 ایمیل درباره ی ویژگی های جدید این نسخه دریافت می کنید و سپس به صورت اتوماتیک عضویت شما لغو می شود و دیگر ایمیلی دریافت نخواهید کرد. من در این نوشته نگاهی به این ویژگی های جدید php 8.1 که در خبرنامه نام برده شده اند می اندازم.همچنین دقت کنید که این ها تنها ویژگی های جدید این نسخه نیستند. بلکه مواردی هستند که از نظر نویسنده مهم تر بوده اند. در زمان نوشتن این مطلب چون می خواستم مفاهیم ارائه شده را بهتر یاد بگیرم منابع دیگر را هم می دیدم و به نظرم یکی از بهترین منابع برای دیدن تغییرات php 8.1 سایت php.watch است که در این مطلب هم از آن استفاده کرده ام و ویژگی های بیشتری را هم توضیح داده است.پیشنهاد من این است که اگر می خواهید این مفاهیم را عمیق تر یاد بگیرید در خبرنامه عضو شوید یا سایتی را که معرفی کردم بخوانید.نکته: نسخه ی جدید خبرنامه، 6 قسمتی و مربوط به php 8.2 است.1. ویژگی Enumsفرض کنید متغیری دارید که چند مقدار مشخص می گیرد. مثلن یک مقاله می تواند در حالت «پیش نویس»، «منتشر شده» یا «بایگانی شده» باشد. برای چنین موردی می توانید از enum استفاده کنید:enum Status { case draft; case published; case archived; }شما می توانید به enum متد هم اضافه کنید:enum Status { case DRAFT; case PUBLISHED; case ARCHIVED; public function color(): string { return match($this) { Status::DRAFT => 'grey', Status::PUBLISHED => 'green', Status::ARCHIVED => 'red', }; } }می توانید مقادیر string یا integer به enum نسبت دهید:enum Status: string { case draft = 'draft'; case published = 'published'; case archived = 'archived'; }و در نهایت در کلاس از آنها استفاده کنید:class BlogPost { public function __construct( public Status $status, ) {} } $post = new BlogPost(Status::draft);2. ویژگی First-Class Callablesکاربرد این ویژگی زیاد نیست. با این حال خوب است که در موردش بدانیم. فرض کنید می‌خواهیم کارکرد این تابع را به یک متغیر نسبت دهیم. تا الان باید چنین کاری می کردیم:$strlen = Closure::fromCallable('strlen'); echo $strlen('foo') //3حالا راه ساده تری وجود دارد:$strlen = strlen(...) echo $strlen('foo') //3عبارت «...» جزئی از کد است (برای نوشتن توضیحات این بخش از این مقاله استفاده کردم).3. ویژگی Array Unpackingکاربرد این ویژگی مانند array_merge در ترکیب آرایه ها است. این ویژگی قبلن (از نسخه ی 7.4) فقط برای آرایه هایی با کلیدهای عددی وجود داشته است:$arrayA = [1, 2, 3]; $arrayB = [4, 5]; $result = [0, ...$arrayA, ...$arrayB, 6, 7]; // [0, 1, 2, 3, 4, 5, 6, 7]و حالا برای آرایه هایی با کلیدهای رشته ای هم اضافه شده است:$arrayA = ['a' => 1]; $arrayB = ['b' => 2]; $result = ['a' => 0, ...$arrayA, ...$arrayB]; // ['a' => 1, 'b' => 2]4. ویژگی Pure Intersection Typesاز نسخه ی 8.1 پی اچ پی به بعد می توانید مشخص کنید که نوع (type) یک پارامتر، پراپرتی یا مقدار بازگشتی (return value) چه باشد و آن مقدار را برای همه ی تایپ های کلاس ها یا اینترفیس های مرتبط اجباری کنید. این ویژگی با Union Type (که در نسخه ی 8.0 معرفی شده بود) متفاوت است. اجازه بدهید کمی بیشتر توضیح بدهم:از نسخه ی 7.4 می توان برای پراپرتی ها تایپ مشخص کرد (سن باید از نوع عدد صحیح باشد):public int $age;از نسخه ی 8.0 می توان چند نوع تایپ ممکن را مشخص کرد (سن می تواند عدد صحیح یا اعشاری باشد):public int|float $age;اما ویژگی ای که از نسخه ی 8.1 اضافه شده مشخص کردن همزمان چند نوع تایپ است (ورودی تابع تست باید هم از نوع A و هم از نوع B باشد):function test(A&B $foo) { /* … */ }اما چنین چیزی چطور ممکن است؟ فرض کنید دو اینترفیس داریم که یکی uuid دریافت می کند و یکی slug:interface WithUuid { public function getUuid(): Uuid; } interface WithSlug { public function getSlug(): string; }حالا فرض کنید تابعی دارید که مقادیر ورودی آن آبجکت هایی هستند که هم uuid و هم slug دارند:function url($object): string { /* … */ }چطور می توانید مطمئن شوید آبجکت ورودی هم اینترفیس WithUuid و هم اینترفیس WithSlug را پیاده سازی کرده است؟ در نسخه های قبلی باید اینترفیس جدیدی می ساختید که هر دو اینترفیس قبلی را اکستند کرده باشد و بعد از تایپ آن برای آبجکت ورودی استفاده می کردید:interface WithUrl extends WithUuid, WithSlug {}حالا نیازی به این کار نیست و می توانید همزمان هر دو نوع اینترفیس را به عنوان تایپ آبجکت ورودی مشخص کنید:function url(WithUuid&WithSlug $object): string { /* … */ }5. ویژگی Readonly Propertiesاز نسخه ی 8.1 به بعد اگر به پراپرتی یک کلاس ویژگی readonly را اضافه کنید، آن پراپرتی فقط یک بار می تواند مقداردهی اولیه شود و بعد از آن مقدارش را نمی توان تغییر داد:public readonly int $uid;چنین ویژگی ای می تواند باعث شود کدها کوتاه تر و تمیزتر نوشته شوند. برنت این موضوع را این گونه توضیح می دهد. قبل از نسخه ی 7.4 با ویژگی Data Transfer Object (DTO) می توانستیم چنین کلاسی بنویسیم:class BlogData { /** @var string */ private $title; /** @var State */ private $state; public function __construct(string $title, State $state) { $this->title = $title; $this->state = $state; } public function getTitle(): string { return $this->title; } public function getState(): State { return $this->state; } }از نسخه ی 7.4 ویژگی پراپرتی های تایپ دار شده را داشتیم:class BlogData { private string $title; private State $state; public function __construct(string $title, State $state) { $this->title = $title; $this->state = $state; } public function getTitle(): string { return $this->title; } public function getState(): State { return $this->state; } }از نسخه ی 8.0 می توانستیم پراپرتی ها را در خود سازنده تعریف کنیم:class BlogData { public function __construct(private string $title, private State $state){} public function getTitle(): string { return $this->title; } public function getState(): State { return $this->state; } }و در نهایت از نسخه ی 8.1 می توانیم بدون نوشتن پراپرتی های private و توابع getter هدف مورد نظرمان را با ویژگی readonly پیش ببریم:class BlogData { public function __construct(public readonly string $title, public readonly State $state) {} }می بینید که کد در عین خوانایی چقدر کوتاه تر شد.6. ویژگی New in Initializersتا قبل از این نسخه می شد برای پراپرتی های کلاس مقدار پیش فرض تعریف کرد. کلاس قبلی را در نظر بگیرید. می توانیم بگوییم پیش فرض پراپرتی «عنوان» یک استرینگ خالی باشد:class BlogData { public function __construct(public readonly string $title = '', public readonly State $state) {} }از نسخه ی 8.1 می توانیم برای پراپرتی هایی از نوع آبجکت هم مقدار پیش فرض تعیین کنیم. مثلن همین کلاس را با مقدار پیش فرض Draft() برای state بنویسیم:class BlogData { public function __construct(public readonly string $title = '', public readonly State $state = new Draft()) {} }7. بهبود Performanceدر نسخه ی 8.1 نسبت به نسخه های قبلی تعداد درخواست بیشتری را در هر ثانیه می توان دریافت کرد. مثلن گفته اند یک اپلیکیشن ساده ی Hello, World! در سیمفونی با این نسخه 8% سریعتر از نسخه ی قبلی لود می شود. یکی از دلایل این بهبود عملکرد کامپایل و کش شدن کلاس های پی اچ پی به صورت جداگانه و کش شدن وراثتی (inheritance cache) است (گرچه توضیحات فنی این تغییر را نمی دانم).8. ویژگی Fibersاین ویژگی در سطوح عمیق تر پی اچ پی جای داده شده و برای همین ممکن است خیلی از اپلیکیشن ها به صورت مستقیم با آن درگیر نباشند. کتابخانه هایی که از asynchronicity (غیرهمزمانی) استفاده می کنند با فیبرها بیشتر درگیرند.فیبرها روشی برای انجام پردازش موازی روی یک پردازش فیزیکی یکسان هستند. در هنگام استفاده از فیبرها از پردازش های چند تردی (multiple threads) یا موازی واقعی استفاده نمی کنیم که یعنی چند بخش از کد شما همزمان اجرا نمی شود بلکه اجازه داده می شود که توسعه دهنده ها وقتی بخشی از کدشان در حال اجراست کار دیگری را انجام دهند. مثلن وقتی بخشی از کد منتظر یک فایل برای خواندن است می توانید دریافت فایل دیگری را شروع کنید.مثلن می توانید 10 درخواست http بفرستید و همزمان برای منتظر پایان همه ی آنها باشید به جای این که یکی یکی دریافتشان کنید. این ویژگی پیش از این هم با generator ها و yield وجود داشته اما فیبرها API راحت تری را ارائه داده اند. فیبرها مجموعه توابع سطح پایینی (low level) هستند که به پی اچ پی اجازه می دهند اجرای کدش را همزمان با انتظار I/O در پس زمینه انجام دهد. برنت توضیحات دقیق تری را در نوشته ای جداگانه داده است.9. ویژگی Never Typeنوع جدید اضافه شده به پی اچ پی (never) اجازه می دهد که برای یک تابع قانونی تعریف کنیم که هیچ وقت نتواند مقدار بازگشتی (return) داشته باشد. این ویژگی فرق ظریفی با تعریف بازگشت void دارد. با تایپ بازگشتی void می گوییم تابع نمی تواند چیزی برگرداند اما می توان در تابع return را به تنهایی تعریف کرد. اما در تابعی که تایپ بازگشتی never دارد به هیچ وجه امکان استفاده از کلمه ی کلیدی return وجود ندارد. در این تابع یا باید یک استثنا (exception) ایجاد کرد (throw) یا از کد خارج شد (exit):function redirect(string $uri): never { header(&quotLocation: {$uri}&quot); exit; }10. چند ویژگی دیگرتابع جدید array_is_list بررسی می کند که آرایه فقط شامل کلیدهای عددی و مرتب شده است یا نه.از این نسخه ثابت های کلاس (const) هم می توانند ویژگی final داشته باشند و کلاس های زیرمجموعه نمی توانند این ثابت ها را مقداردهی مجدد کنند.class Foo { final public const TEST = '1'; } class Bar extends Foo { public const TEST = '2'; }تابع fsync تغییرات را در فایل همزمان می کند.در سیستم عددی هشت تایی (octal) از این به بعد دو مقدار 0o و 0O هم وجود خواهد داشت.echo 077; // 63 echo 0o77; // 63, PHP 8.1 echo 0O77; // 63, PHP 8.1دیگر نمی توان آرگیومنت های غیر قابل نال شدن را با null مقداردهی کرد.strlen(null); //Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in ... on line ...و ویژگی های دیگری که می توانید در این مطلب ببینید.