سلام دوستان! ورژن 8 زبان PHP منتشر شد! سال قبل همین موقع بود که ورژن 7.4 رو داشتیم و پر از ویژگیهایی بود که امید رو توی دل طرفدارای این زبان زنده کرد. ورژن 8 این زبان با ویژگیهایی منتشر شده که نوید یک آینده روشن رو برای PHP میده. که توی این پست داغترین ویژگیهای اون رو بررسی میکنیم 😉
ویژگیهایی که قراره بررسی کنیم:
- Union Types
- JIT
- Nullsafe operator
- خروجیهایی از نوع static
- Named Arguments
- Constructor property promotion
- Match Expression
- نوعهایی از نوع mixed
- گرفتن اسم کلاس از آبجکت
- Catch بدون استفاده از متغیر
- ارثبری و متدهای private
- آخرین کاما توی لیست پارامترها
- سه تابع جدید
- Attribute ها
Union Types
با این ویژگی میتونیم برای ورودیها و خروجیهای توابع و متدها، نوعهای مختلفی تعریف کنیم. قبلاً فقط میتونستیم یک نوع برای ورودیها و خروجیها در نظر بگیریم. مثلاً فقط int. اما با این ویژگی میشه بگیم یک پارامتر یا خروجی متد/تابع هم میتونه از نوع int باشه و هم از نوع float:

JIT
همونطور که میدونیم PHP یک زبان تفسیری هست. یعنی کدهای برنامه در زمان اجرا (runtime) تبدیل به کد قابل فهم ماشین میشن. برعکسِ زبانهایی مثل جاوا و سی که کامپایلر دارن و کدهای اونها قبل از اجرای واقعی، تبدیل به کد قابل فهم ماشین میشن.
JIT که مخفف Just In Time هست، ابزاری هست که کد رو زمان اجرا (runtime) کامپایل میکنه و باعث میشه کد کامپایل شده مورد استفاده قرار بگیره. یعنی به نحوی، کد تفسیر شدهی ما کش میشه و بعد مورد استفاده قرار میگیره.
در حالت عادی JIT نوید افزایش سرعت و عملکرد قابل توجهی رو به زبانهایی میده که از این کامپایلر استفاده میکنن. اما توی دنیای وب قضیه مقداری متفاوت هست و به خاطر ماهیتی که برنامههای تحت وب دارن، این بهبودِ عملکرد - حداقل برای الان - جزئی خواهد بود.
Nullsafe operator
اگه با عملگر Null coalescing (??) که توی ورژن 7 این زبان معرفی شد آشنایی داشته باشید، میدونیم که استفاده از این عملگر برای متدهای یک آبجکت قابل استفاده نیست. کد زیر به درستی برای پراپرتیهای تعریف نشده کار میکنه:
$obj = new stdClass; echo $obj->foo->bar->baz ?? 12; // 12
اما اگه از این عملگر برای متدهای آبجکت استفاده کنیم خطا میگیریم که این متد تعریف نشده:
$obj = new stdClass; echo $obj->foo()->bar()->baz() ?? 12; // PHP Fatal error: Uncaught Error: Call to undefined method stdClass::foo()
توی ورژن 8 این زبان یک عملگر جدید به اسم Null safe operator معرفی شده که مشکل بالا رو حل میکنه و باعث میشه خطا نگیریم. این عملگر به صورت ?-> هست که توی کد زیر میبینیم:
$obj = new stdClass; echo $obj?->foo()?->bar()?->baz(); // null
خروجیهایی از نوع static
توی این ورژن میتونیم تعریف کنیم که خروجی یک تابع/متد از نوع static باشه. خط ۴ کد زیر رو ببینید:

Named Arguments
یکی از ویژگیهایی که توی این ورژن خواهیم داشت، Named Arguments هست. تابع زیر رو در نظر بگیرید:
function user( $name, $email = null, $age = null, $gender = null, $phoneNumber = null ) { }
بدون این ویژگی، اگه میخواستیم آرگومان ۱ و ۵ رو پاس بدیم، میبایست آرگومانهایی که توی این بین قرار گرفتن رو حتما پر میکردیم:
// older versions user('John', null, null, null, '0911');
اما با این ویژگی دیگه لازم نیست آرگومانهای یک تابع رو بر اساس ترتیبی که تعریفشده پاس بدیم:
user( phoneNumber: '0911', name: 'John' );
Constructor property promotion
کد زیر رو در نظر بگیرید. توی متد سازنده داریم آرگومانها رو به پراپرتیها نسبت میدیم و مقدار اونها رو تنظیم میکنیم:
class User { public $name; public $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } }
اسم پارامترهای متد __construct با پراپرتیهای کلاس یکسان هست. به PHP 8 یک ویژگی اضافه شده که بتونیم بصورت مستقیم و خودکار، از پارامتر به پراپرتیها مقدار بدیم:
class User { public function __construct( public string $name, public string $email ) { } }
همونطور که میبینیم، کافیه سطح دسترسی پارامترها (public - protected - private) رو مشخص کنیم تا به بطور خودکار به عنوان پراپرتی کلاس در نظر گرفته بشن.
Match Expression
یکی از جذابترین ویژگیهای این ورژن Match Expression هست که کاری مشابه switch انجام میده اما با ظاهر و خوانایی بهتر. کد زیر رو ببینید که با switch نوشته شده:

شاید به خوندن و کار کردن با این سبک عادت کرده باشیم، اما با معرفی شدن Match Expression، چشمها را باید شُست :) کد زیر دقیقاً همون کار کد بالا رو انجام میده:

نوعهایی از نوع mixed
میتونیم برای ورودی/خروجیهای توابع و متدها، خروجی از نوع mixed داشته باشیم. یعنی برای مثال وقتی از mixed برای نوع خروجی یک تابع استفاده میکنیم، این مفهوم رو انتقال میدیم که خروجی ممکنه نوعهای مختلفی مثل آرایه، رشته، بولین، عدد، نال و ... داشته باشه:

گرفتن اسم کلاس از آبجکت
قبلاً اگه میخواستیم از یک آبجکت، اسم کلاسش رو بدست بیاریم، از تابع get_class استفاده میکردیم. اما الان میتونیم با نوشتن ::class جلوی آبجکت، این کار انجام بدیم:
$bird = new Bird(); echo $bird::class;
Catch بدون استفاده از متغیر
قبلاً توی try/catch ها برای قسمت catch حتماً باید از یک متغیر استفاده میکردیم. خط ۳ کد زیر رو ببینید:
try { } catch(Exception $e) { DB::rollBack(); }
اینجا ما توی بلاک catch از متغیر $e استفاده نکردیم. ولی باید اون رو مینوشتیم. اما توی ورژن جدید PHP دیگه لازم نیست از یک متغیر استفاده کنیم:
try { } catch(Exception) { DB::rollBack(); }
ارثبری و متدهای private
توی ورژنهای قبلی وقتی میخواستیم یک متد کلاس والد که private بود رو توی کلاس فرزند هم تعریف کنیم، توی شرایط زیر خطا میگرفتیم:
- وقتی متد کلاس والد
finalباشه - وقتی متد کلاس والد
staticباشه - وقتی متد کلاس فرزند
abstractباشه
class Foo { final private function iAmPrivate() {} } class Bar extends Foo { private function iAmPrivate() {} } new Bar(); // PHP Fatal error: Cannot override final method Foo::iAmPrivate()
class Foo { private static function iAmPrivate() {} } class Bar extends Foo { private function iAmPrivate() {} } new Bar(); // PHP Fatal error: Cannot make static method Foo::iAmPrivate() non static in class Bar
class Foo { private function iAmPrivate() {} } abstract class Bar extends Foo { abstract public function iAmPrivate(); } class Baz extends Bar { public function iAmPrivate() {} } new Baz(); // PHP Fatal error: Cannot make non abstract method Foo::iAmPrivate() abstract in class Bar
برای این سه مورد دیگه توی ورژن 8 خطا نمیگیریم.
آخرین کاما توی لیست پارامترها
قبلاً اگه چنین کدی مینوشتیم، خطا میگرفتیم:
function Jump( $foo, $bar, $baz, ) {}
چون بعد از آخرین پارامتر یک کاما قرار گرفته. اما توی PHP 8 خطا نخواهیم گرفت.
سه تابع جدید
PHP 8 با سه تابع جدید str_contains و str_starts_with و str_ends_with منتشر شده.
str_contains
قبلاً برای اینکه بررسی کنیم آیا یک رشته شامل یک مقدار خاص هست یا نه، از تابع strpos به صورت زیر استفاده میکردیم:
if (strpos('I am John from the Moon', 'Moon') !== false) { }
ولی یک تابع اختصاصی جدید به اسم str_contains تعریف شده برای اینکه بررسی کنیم یه مقدار توی یک رشته وجود داره یا نه:
if (str_contains('I am John from the Moon', 'Moon')) { }
str_starts_with
با این تابع میتونیم بررسی کنیم که آیا یک رشته، با یک مقدار خاص شروع میشه یا نه:
str_starts_with('Downloads', 'Down'); // true
str_ends_with
این تابع بررسی میکنه که آیا یک رشته با یک مقدار خاص تموم میشه یا نه:
str_ends_with('Downloads', 'loads'); // true
Attribute ها
این ویژگی که اون رو توی زبانهای دیگه با عنوان annotation میشناسیم، یک راهی هست برای اضافه کردن metadata به کلاسها، متدها، پراپرتی و ثابتها. Attribute ها یک کلاس معمولی PHP هستن که نحوه نوشتن اونها بصورت زیر هست:
#[Attribute] class ExampleAttribute { public $value; public function __construct($value) { $this->value = $value; } }
و اونها رو به این صورت استفاده میکنیم (به #[ExampleAttribute] دقت کنین) :
use App\Attributes\ExampleAttribute; #[ExampleAttribute] class Foo { #[ExampleAttribute] public const FOO = 'foo'; #[ExampleAttribute] public $x; #[ExampleAttribute] public function foo(#[ExampleAttribute] $bar) { } }
اگه با سیمفونی کار کرده باشین، برای تعریف Route قبلاً از این روش استفاده میکردیم:
use Symfony\Component\Routing\Annotation\Route; class SomeController { /** * @Route("/path", name="action") */ public function someAction() { // ... } }
همونطور که میبینیم، بالای متد someAction و توی docblock کدی رو نوشتیم که مشخص کنیم برای این action چنین روتی رو میخوایم درنظر بگیریم. کدی که توی قسمت docblock نوشتیم، برای تجزیه و تحلیل شدن، به یک ابزار دیگه مثل doctrine/annotations نیاز داره تا اون رو پردازش کنه و Route ما ساخته بشه و PHP های قدیم چنین قابلیتی رو درون خودشون نداشتن. اما با قابلیت Attributes که از ورژن 8 اون رو خواهیم داشت، برای اضافه کردن metadata نیاز به ابزار خارجی نخواهیم داشت.
توی سیمفونی ۵.۲ قراره از Attributes بجای docblock برای تعریف کردن روت استفاده بشه:
use Symfony\Component\Routing\Annotation\Route; class SomeController { #[Route('/path', name: 'action')] public function someAction() { // ... } }
این قابلیت رو سعی میکنم توی یک پست جدا بصورت مفصل بررسی کنیم.
الان باید آپگرید کنم؟
این نکته مهم رو باید در نظر داشته باشیم که این یک ورژن Major هست و بعد از آپگرید کردن، برای سازگاری باید تغییراتی رو توی کد داشته باشیم. برای مثال کلمه کلیدی match الان یک کلمه کلیدی reserve شده هست و اگه جایی از کد این کلمه رو استفاده کردیم با اون رو تغییر نام بدیم. مشخص کرده که چه تغییراتی رو باید در نظر داشته باشیم.
خب دوستان نظر شما درباره این ورژن چیه؟ چه پیشبینیهایی برای این زبان دارین؟ دیدگاهتون رو توی قسمت نظرات به اشتراک بذارین. روزتون خوش 😉
منابعی که برای این پست استفاده کردم:
