بررسی تغییرات PHP 8

HP 8ورژن 8 در تاریخ 26 Nov 2020 منتشر شد که در این مقاله به بررسی تغییرات این نسخه خواهیم پرداخت.لیست تغییرات این نسخه:New FeaturesJITNamed argumentsAttributes (Annotations)Constructor property promotionUnion typesMatch expressionNullsafe operatorSaner string to number comparisonsConsistent type errors for internal functionsNew Classes, Interfaces, and FunctionsWeak Map classStringable interfacestr_contains(), str_starts_with(), str_ends_with()Type system and error handling improvementsAbstract trait method validationFatal error for incompatible method signaturesThe @ operator no longer silences fatal errors.Inheritance with private methodsMixed typeStatic return typeOpaque objects instead of resources for Curl, Gd, Sockets, OpenSSL, XMLWriter, and XML extensionsOther syntax tweaks and improvementsTrailing Comma in Parameter ListThrow is now an expressionAllow ::class on objectsNew FeaturesJIT(Just in Time Compiler)bayad kamel tar konm bad biaram injaNamed argumentsاین امکان فراهم شده است که بتوان پارامتر های ورودی را نامگذاری کرد و هنگام فراخوانی تابع از نام آن ها برای مقدار دهی استفاده کرد نه فقط ترتیبی که پارامتر ها تعریف شده اند.str_contains(needle: 'Bar', haystack: 'Foobar');قبل از ورودی باید اسم پارامتر را به همراه دو نقطه ":" ذکر کرد.Attributes (Annotations)قبلا در فریم ورک سیمفونی برای تعریف کردن هر Route برای هر متد Controller از Annotation استفاده می شد برای مثال:class PostsController { /** * @Route(&quot/api/posts/{id}&quot, methods={&quotGET&quot}) */ public function get($id) { /* ... */ }}و یک کدی در فریم ورک سیمفونی نوشته شده بود که این Annotation رو می خوند و یک Route برای این کنترلر ایجاد می کرد.حالا در این نسخه این Annotation نویسی به خود زبان php اضافه شده و بیرون از فریم ورک Symfony هم شما میتوانید Annotation های خودتون رو بنویسید و با استفاده از PHP Reflection آن ها را بخوانید و در کد های خود از آن استفاده کنید.شکل نوشتاری Annotation ها به صورت زیر است:class PostsController { #[Route(&quot/api/posts/{id}&quot, methods: [&quotGET&quot])] public function get($id) { /* ... */ }}Constructor property promotionدر این نسخه با نوشتن کد کمتری می توان property های یک کلاس را تعریف کرد و با استفاده از constructor مقدار دهی کرددر php 7:class Point { public float $x; public float $y; public float $z public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0) { $this->x = $x; $this->y = $y; $this->z = $z; } }در PHP 8:class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0){ } }Union typesگاهی نیاز است نوع یک متغیر یا خروجی تابع را همزمان از دو نوع مشخص کنیم این امکان در این نسخه فراهم شده است. با استفاده از پایپ "|" این کار رو انجام میدیم:class Number { public function __construct( private int|float $number) {}}new Number('NaN'); // TypeErrorMatch expressionساختار جدیدی به نام match در این نسخه PHP اضافه شده است که بسیار شبیه به switch case است و میتونه در آینده بخاطر کمک کردن به ساده تر و خوانا تر شده کد استفاده های زیادی داشته باشه.این ساختار جدید به عنوان ورودی یک مقدار را میگیرد و با مقادیری که دارد مقایسه میکند و در نهایت مقداری که با آن تطابق پیدا کرده است را باز میگرداند. از مزیت های match به switch می توان به مقایسه دقیق تر آن اشاره کرد که match نوع داده ای هر دو طرف را درنظر میگیرد.echo match (8.0) { '8.0' => &quotOh no!&quot, 8.0 => &quotThis is what I expected&quot, };Nullsafe operatorخیلی از مواقع نیاز است که بررسی کنیم متغیری یا مشخصه کلاسی مقداری دهی شده است یا خیر تا از آن استفاده کنیم، در این نسخه operator جدیدی معرفی شده است که در صورت استفاده از آن روی متغیری که null است به ارور null pointer exception برخورد نمی کنیم به همین دلیل نام آن NullSafe است.برای مثال کد زیر که در هر مرحله دستیابی به دیتا null بود آن بررسی شده است:if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } }اما کد بالا را میتوان در این نسخه به صورت ساده تر نوشت:$country = $session?->user?->getAddress()?->country;در این کد در هرکجا متغیری null باشد کد ادامه نمی دهد که به ارور null pointer بخورد مقدار null را باز میگرداند.Saner string to number comparisonsدر نسخه‌های قبلی PHP، هنگام مقایسه بین رشته‌ها و اعداد، در برخی از موارد رفتار غیر منطقی رو از PHP شاهد بودیم در این نسخه این رفتار بهبود یافته و هنگام مقایسه اعداد و رشته ها PHP رفتار دقیق تر و منطقی تری را از خودش نشون میده.برای مثال در نسخه قبلی به این صورت عمل می کرد0 == &quotfoo&quot // true 0 == &quot&quot // true 42 == &quot42foo&quot // trueاما حالا در نسخه جدید به صورت زیر عمل می کند:0 == &quotfoo&quot // false 0 == &quot&quot // false 42 == &quot42foo&quot // falseConsistent type errors for internal functionsاگر شما در حال حاضر یک تابع تعریف کنید و آرگومانی با و نوع int داشته باشد و هنگام استفاده از اون تابع ورودی با نوع دیگری مثلا string به آن بدهید هنگام اجرا با خطا مواجه می شوید که امر طبیعی و درستی است حالا جالبه بدونید که اگر این اتفاق برای توابعی که برای خود زبان PHP هستند (توابع built-in) بیفته رفتار های متفاوتی رو خواهید دید. این رفتار در این نسخه بهبود یافته.مثال زیر را در PHP 7.4 ببینید:var_dump(strlen(new stdClass));این منجر به هشدار زیر می شود:Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4 NULLدر PHP 8، کد بالا با خطای زیر مواجه می شود:Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4 Stack trace: #0 {main} thrown in /path/to/your/test.php on line 4New Classes, Interfaces, and FunctionsWeak Map classاین کلاس Reference به یک Object را به عنوان کلید و یک مقدار را به عنوان Value قبول میکند، به شما اجازه مید یک map از Object ها و مقادیر دلخواه خود درست کنید.قبلا هم در نسخه های پیش از این می توانستیم توسط SplObjectStorage این کار رو انجام بدیم ولی این کلاس اجازه پاک کردن آن Object ها رو به Garbage Collector نمیداد اگر در این کلاس وجود داشت اما Weak Map این اجازه را می دهد.$map = new WeakMap; $obj = new stdClass; $map[$obj] = 42; var_dump($map);خروجی:object(WeakMap)#1 (1) { [0]=> array(2) { [&quotkey&quot]=> object(stdClass)#2 (0) { }[&quotvalue&quot]=> int(42)} }Stringable interfaceیک interface در این نسخه اضافه شده است به نام Stringable که کلاس هایی که آن رو Implement می کنند باید تابع ()to_string__ را پیاده سازی کنند. با وجود این interface میتوان توابع ای با ورودی یا خروجی از نوع String یا Stringable تعریف کرد:function showStuff(string|Stringable $value) { // A Stringable will get converted to a string here by calling // __toString. print $value; }str_contains(), str_starts_with(), str_ends_with()در این نسخه توابع built-in به زبان PHP اضافه شده است که کار با رشته ها را برای ما آسان تر می کند، بریم با هم بررسیشون کنیم:str_contains()این تابع یک رشته را داخل رشته ی دیگری جستجو میکند و اگر پیدا شد مقدار True را باز میگرداند.پیش از این برای این کار می توانستیم از توابع strpos نیز استفاده کنیم که اگر رشته مورد نظر را پیدا نمی کرد مقدار false را برمی گرداند و اگر پیدا میکرد عدد جایی که پیدا شده بود را باز میگرداند.str_starts_with(),str_ends_with()همانطور که از اسم این دو تابع مشخص است مقدار true را برای زمانی که رشته ما، با رشته مورد نظر شروع یا تمام بشه باز میگرداند.str_starts_with (string $haystack , string $needle) : boolstr_ends_with (string $haystack , string $needle) : boolType system and error handling improvementsAbstract trait method validationدر نسخه های قبلی وقتی یک متد در Trait به صورت abstract تعریف می شد و کلاسی که آن را پیاده سازی می کرد اگر signature متد دقیقا مثل متد abstract پیاده نمی کرد php اروری نمی داد ولی در این نسخه در رابطه با این موضوع php سختگیرانه تر عمل میکنه و ارور میده:trait T { abstract public function test(int $x); }class C { use T; // Allowed, but shouldn't be due to invalid type. public function test(string $x) {} }اگر در نسخه های قبلی این کد اجرا بشه اروری داده نمیشه ولی اگر در این نسخه اجرا بشه ارور زیر برگردانده میشه:Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path//your/test.php on line 10Fatal error for incompatible method signaturesدر ارث بری اگر signature متد به درستی پیاده سازی نشه در ورژن های قبلی یک warning چاپ میکنه ولی در این ورژن با ارور مواجه میشه، برای مثال این کد در نسخه های قبل هشدار می دهد ولی در این نسخه ارور می دهد:class C1 { public function method(array $a) {} } class C2 extends C1 { public function method(int $a) {} }The @ operator no longer silences fatal errorsاپراتور @ در PHP اگر پشت دستوری قرار می گرفت و اون دستور با ارور مواجه می شد آن ارور را نادیده می گرفت و کد ادامه پیدا می کرد اما از این نسخه دیگر اینطور نیست و در صورتی که آن دستور با ارور مواجه شود کد دیگر ادامه پیدا نمی کند و ارور می دهد. برای مثال کد زیر در نسخه ها قبل ارور نمی دهد ولی در این نسخه با ارور مواجه می شود:@$a = 1 / 0;PHP 7:اروری نمی دهد و به اجرای ادامه کد می پردازد:PHP 8:Fatal error: Uncaught DivisionByZeroError: Division by zero in /home/user/scripts/code.php:7 Stack trace: #0 {main} thrown in /home/user/scripts/code.php on line 7Inheritance with private methodsخب همانطور که میدانید متد private را کلاس فرزند به ارث نمی برد، در ورژن ها قبلی اگر در کلاس فرزند یک متد مانند متد private کلاس والد تعریف کنیم با کمال تعجب با ارور مواجه می شویم اما در این ورژن فقط با یک warning مواجه می شویم.class Foo { final private function testFoo(): void {} } class ChildFoo extends Foo{ private function testFoo(): void {} }php7:Warning: Private methods cannot be final as they are never overridden by other classes in ... on line …php 8:Fatal error: Cannot override final method Foo::testFoo() in ... on line …Mixed typeقبلا در داکیومنت های PHP این نوع داده را زیاد دیده ایم و زمانی استفاده می شود که بخواهیم نوعی تعریف کنیم که تمام نوع ها را پوشش دهد.برای مثال:function dd(mixed $var): void { var_dump($var); }در کد بالا نوع ورودی تابع dd می تواند از هر نوعی باشد.Static return typeدر این نسخه امکانی فراهم شده است تا توابع را محدود کنیم که فقط مقادیر static برگردانند:class Foo { public static function getInstance(): static { return new static(); } }Opaque objects instead of resources for Curl, Gd, Sockets, OpenSSL, XMLWriter, and XML extensionsتوابعی مثل curl_init مقدار هایی با نوع resource باز می گرداندن که خب مشکل های خاص خودشو داشت و Garbage Collector عملکرد خوبی با resource ها نداشت. حالا در این نسخه این توابع بجای resource ها object بر میگردانند:PHP 7:var_dump(curl_init()); //resource(5) of type (curl)PHP8:var_dump(curl_init()); //object(CurlHandle)#1 (0) {}Other syntax tweaks and improvementsTrailing Comma in Parameter Listدر ورژن های قبلی کد زیر با ارور مواجه می شود اما د این ورژن میتوان در closure function ها و ورودی هایی که use به تابع پاس داده می شوند بعد از آخرین ورودی کاما آورده شود:در ورژن های قبلی با ارور زیر مواجه می شود:function() use ($foo,$bar,) {} Parse error: syntax error, unexpected ')', expecting '&' or variable (T_VARIABLE) in ... on line …Throw is now an expressionنوشتن throw در دستور های زیر باعث ارور در نسخه های قبلی میشد اما در این ورژن نوشتن آن مجاز است:$value = isset($data) ? $data : throw new \Exception('value not set'); $value ??= throw new \Exception('value not set');Allow ::class on objectsبرای چاپ کردن نام کامل کلاس ما از این User::class عبارت استفاده می کنیم این عبارت روی نام کلاس ها قابل استفاده است، در این نسخه امکان استفاده آن روی یک object نیز فراهم شده است برای مثال:User::class; $user::class;این دو عبارت در این نسخه خروجی یکسانی دارند. https://virgool.io/@RasoulEsmaeili/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D8%A7%D8%AA-php-81-wzvibvec8mn4 ممنون که تا انتهای این مقاله همراه من بودید، شادُ خندونُ سلامت باشید. (: