قطعا زمانی که تیتر مقاله را خواندید، فهمیدید که درباره چه چیزی قرار است صحبت کنیم، قابلیت ها و امکانات بسیار خوبی بعد از آپدیت php به نسخه PHP 8 آن اضافه شده است که باعث می شود ضعف های قبلی پی اچ پی را از یاد ببریم. بهترین و البته مهم ترین ویژگیهای خوب و حیرت انگیز، میتوانیم از JIT، Match و … نام ببریم که قصد داریم تمامی این ها را در این مقاله بگوییم و بررسی هایی داشته باشیم. در مقاله ویژگیهای جدید php 8 با ما همراه باشید.
زمان انتشار PHP 8
با توجه به چیز هایی که گفته شد و ما هم فهمیدیم، زمان عرضه و انتشار PHP 8، نوامبر سال ۲۰۲۰ میلادی یا آذر ماه سال ۱۳۹۹ خواهد بود. نسخهی PHP 8 نسخهی ای بسیار ویژه و کلیدی برای PHP می باشد که به همراه خود تغییرات و ویژگی های بسیار مهم و زیادی آورده است.
همانطور که گفته شد تغییرات ویژه و زیادی انجام شده است و احتمالا شما هم باید بعد از آپدیت این نسخه، تغییراتی را در کد های خود اعمال کنید و این تغییرات برای افرادی که قبلا با PHP 8 کار کردند، بسیار بی دردسر خواهد بود.
Union types
Union types یکی از مهمترین و کلیدی ترین ویژگیهای جدید php 8 است. شما قادر هستید به آسانی برای پارامتر ورودی تابع تان تعداد زیادی داده (type) تعریف کنید که در آخر یکی از آنها اعمال شود.
public function foo(Foo|Bar $input): int|float;
همانطور که در کد بالا مشخص است برای پارامتر ورودی دو نوع یا type تعریف کردهایم FOO و Bar که با علامت | (به معنی یا میباشد) از هم فاصله گرفته اند. در نوع خروجی تابع هم به همین صورت، نوع int و یا float قرار است خروجی این تابع تعریف شود.
به این مسئله دقت کنید، همانطور که میدانیم نوع void قادر به داشتن خروجی نیست پس به هیچ عنوان جایی در Union typeها نخواهد داشت. یک نکتهی مهمتر این است که اگر بخواهیم نوع پارامتر یا خروجی یک تابع را از نوع nullable تعریف کنیم، قادر هستیم از علامت سوال یا null| بهره بگیریم. (علامت | به معنی یا میباشد). در مورد زیر از دو نوع nullable هم استفاده شده است.
public function foo(Foo|null $foo): void;public function bar(?Bar $bar): void;
JIT
ویژگیهای جدید php 8 : کامپایلر JIT (مخفف کلمات Just In Time) برای بیشتر شدن و البته بهتر شدن کارایی کاربرد دارد. البته این راه هم بگوییم که این کامپایلر به طور مستقیم تاثیری بر افزایش سرعت پروژههای PHP ندارد به این معنی که در درخواستهایی که در پروژه می گیرید یا می دهید تاثیری نخواهد داشت و مطمئنا تاثیر زیادی بر روی پروژههای تحت وب PHP نمیگذارد.
اپراتور nullsafe
احتمالا در گذشته اگر با اپراتور null coalescing که با علامت ?? شناخته میشود کار کرده باشید، میدانید که شما نمیتوانید این اپراتور را موقعی به کار بگیرید که میخواهید یک متد را صدا کنید و باز بینی کنید که اگر مقداری داشت از آن مقدار استفاده کنید، در غیر این صورت مقدار null را به جای آن بگذارید.
در مثال زیر پروژههای یک کاربر با متد projects گرفته شده است و داخل متغیر projects قرار گرفته است. در شرط پایینی گفته شده است که اگر projects وجود داشت plans آن پروژه را بگیرد و داخل متغیر sendPlans قرار دهد در غیر این صورت null را داخل متغیر sendPlans بگذارد.
$project = $user->project(); $sentPlans = $project ? $project->plans() : null;
با اپراتور جدید nullsafe قادر هستیم که این کد را از دو خط به یک خط تبدیل کنیم و شرطمان را مشاهده کنیم، به مثال پایین توجه کنید.
$sendPlans = $user->project()?->plans();
همینطور که می توانید ببینید گفته ایم که در صورت داشتن user->projects$، با علامت <-? به شرطمان ادامه بده و تعیین کرده ایم که plans را برگردان.
آرگومانهای نام گذاری شده یا Named arguments
آرگومانهای نام گذاری یا Name argumentها به ما توانایی می دهد که با گذاشتن نام پارامترها، پارامترهای یک تابع را بدون ترتیب هم مقدار دهی کنیم و اجباری به مقداردهی همه پارامترها به ترتیب تعریف شده نشده باشد، به مثالی که در پایین آورده ایم توجه کنید.
function foo(string $a, string $b, ?string $c = null, ?string $d = null) { /* … */ } foo( b: 'value b', a: 'value a', d: 'value d',);
در کد بالا ما پارامترهای تابع foo را بدون ترتیب و به شکلی که خودمان خواستیم مقداردهی کردیم.
ویژگی یا attributeها
به طور کلی Attributeها در زبانهای برنامهنویسی به منظور یادداشت هاو نوعی ترفند برای اضافه کردن دادههای meta به کلاسها کاربرد دارد، بی آنکه اجباری به تجزیه کردن docblocksها باشد که قطعا شما آن ها را به صورت زیر مشاهده کرده اید
/** * @param string $message */function foo(string $message) {}
اما در زیر مثالی از RFC که کوچک شده ( مخفف شده) کلمههای Request For Comments می باشد، وجود دارد و نشان دهنده آن است که Attributeها به چه صورتی می باشند.
use App\Attributes\ExampleAttribute; #[ExampleAttribute]class Foo{ #[ExampleAttribute] public const FOO = 'foo'; #[ExampleAttribute] public $x; #[ExampleAttribute] public function foo(#[ExampleAttribute] $bar) { }}#[Attribute]class ExampleAttribute{ public $value; public function __construct($value) { $this->value = $value; }}
دقت کنید که Attribute پایهای به شکل PhpAttribute در RFC اصلی صدا زده میشود ولی با یک RFC متفاوت دچار تغییر شده است. اگر علاقه دارید که بفهمید به چه صورت می توانید Attributeهای خود را درست کنید، باید از PHP-8-attribute بهره بگیرید.
Match Expression
قادر هستیم Match Expression را برادر بزرگتر switch در PHP بنامیم به این دلیل که می تواند به شکل ویژه ای ساختار بهبود یافتهی switch نیز باشد. وقتی که شما از Match بهره می گیرید بر عکس switch هیچ نیازی به استفاده از break نخواهید داشت زیرا شما از ساختار آرایه بهره گرفته اید، به مثال پایین خوب توجه کنید.
قصد داریم کد ساده ای را بنویسیم، اول کد را با ساختار switch مینویسیم تا بفهمیم با Match به چه اندازه کد ما بهینهتر می شود.
switch ($statusCode) { case 200: case 300: $message = null; break; case 400: $message = 'not found'; break; case 500: $message = 'server error'; break; default: $message = 'unknown status code'; break;}
قطعا شما ساختار switch که در بالا آورده شده است کاملا می شناسید، می بینید که ورودی یک status code میباشد و طبق عدد داده شده به ورودی پیام تعیین شده در switch چاپ میشود. اکنون همین کد را در ساختار match می بینیم.
$message = match ($statusCode) { ۲۰۰, ۳۰۰ => null, ۴۰۰ => 'not found', ۵۰۰ => 'server error', default => 'unknown status code',};
قطعا برای شما تعجب آور است که فقط با حذف کدهای زائد و بهره گرفتن از ساختار آرایه موفق شدیم همان کاربرد switch را با این تفاوت که بسیار بهینه تر شده، در اختیار داشته باشیم.
بهبود ویژگی تابع constructor در شی گرایی
به این دلیل که مشکل به وجود آمده را به شکل کامل بفهمید، به مثالی که در پایین آورده ایم خوب دقت کنید.
class Money { public Currency $currency; public int $amount; public function __construct( Currency $currency, int $amount, ) { $this->currency = $currency; $this->amount = $amount; }}
در بالا می بینیم که ما دو property یا ویژگی با نامهای currency و amount تعریف کردهایم، اکنون قصد داریم این دو ویژگی را با استفاده از تابع سازنده یا constructor مقداردهی کنیم. می بینیم که اسم دو ویژگی را داخل پرانتزهای تابع constructor گذاشته ایم و داخل براکت {} آنها را مقدار دهی کردهایم.
class Money { public function __construct( public Currency $currency, public int $amount, ) {}}
با آپدیت های این نسخه ما به آسانی قادر هستیم همه کارهایی که در بالا به شکل وقت گیر انجام دادهایم را تنها با جای گذاری ویژگیها (property) داخل پرانتز () تابع constructor آنها را تعریف و مقداردهی کنیم.
return کردن نوع جدید static
اگر با مباحث شئ گرایی درون PHP آشناییتی داشته باشید مستحضر هستید که در گذشته قادر بودیم نوع self را return یا به شکل خروجی برگردانیم، ولی برای نوع static این ویژگی رخ نمی داد. اکنون که PHP 8 تغییراتی را برای خودش ایجاد کرده است، این کار را هم می توان انجام داد و برای نوع static این ویژگی رخ می دهد.
class Foo{ public function test(): static { return new static(); }}
با علم بر اینکه ماهیت داینامیک (dynamic) بودن PHP، این ویژگی بدون شک امکانی به شدت مفید و قابل استفاده برای توسعه دهندهگان PHP می باشد که قادر هستند خروجی یا return با نوع static داشته باشند.
نوع داده یا type جدید Mixed
این ویژگی شاید امکانی به ضرر PHP باشد ولی باید بگوییم که در بعضی از مواقع باید باشد و بسیار واجب است. نوع Mixed این توانایی را دارد که typeها یا نوع دادههای پایین را در خود قرار دهد.
string|int|float|bool|null|array|object|callable|resource
نوع mixed قادر است به شکل نوع دادهی یک تابع یا نوع خروجی آن باشد و در بعضی از موارد میتواند به عنوان نوع پارامترهای یک کلاس نیز به کار گرفته شود. اکنون با نوع داده mixed شما این توانایی را دارید که یک متغیر را تعریف کنید که امکان دریافت هر نوع داده ای را دارد و در خودش جای می دهد. حتی شما قادر هستید به راحتی نوع خروجی یک تابع را mixed بگذارید تا تعیین شود خروجی این تابع میتواند هر چیزی باشد.
class Example { public mixed $exampleProperty; public function foo(mixed $foo): mixed {}}
این مسئله را همواره به یاد داشته باشید، به این دلیل که mixed نوع دادهی null را نیز داخل خود دارد پس نباید متغیری را به عنوان nullable تعریف کنید و در صورتی که این کار را انجام دهید، خطای زیر به شما نمایش داده می شود.
کد:
function bar(): ?mixed {}
خطا:
// Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
throw expression
قبل از PHP 8 این قابلیت وجود نداشت که شما یک exception را در کد تک خطیتان که امکان داشت یک exception شکل بگیرد را throw کنید. اما بعد از آمدن نسخهی جدید PHP 8 شما قادر هستید با همه عبارات یا expressionها یک exception پرتاب یا Throw کنید. به مثال پایین توجه کنید.
$fn = fn() => throw new \Exception('oops');
در مثالی که بالا آورده شد، با یک <= یا به عبارتی دیگر arrow function یک exception پرتاب کنید. در زیر هم ternary expression به کار گرفته شده است.
$value = isset($_GET['value']) ? $_GET['value'] : throw new \InvalidArgumentException('value not set'); $value ??= throw new \InvalidArgumentException('value not set'); $foo = $bar ?: throw new \InvalidArgumentException('$bar is falsy'); $foo = $bar ?? throw new \InvalidArgumentException('$bar is not set');
ارث بری با توابع private
قبل از PHP 8 به شکلی بود که همان بررسیهایی که بر روی متدهای public و protected صورت می گرفت، بر روی private هم اعمال می شد، به شکلی ساده تر، متدهای private باید همان چهارچوب هایی را رعایت کنند که متدهای protected و public رعایت میکردند و از آن جایی که متدهای private داخل کلاسهای فرزند قرار ندارند و این مسئله به طور کلی کار را دچار سردرگمی می کند.
RFC این شکل را عوض کرده است و بقیه بررسیهای مرتبط با ارثبری برای متدهای private انجام نخواهد شد و نتیجه می گیریم که استفاده از final private function نیز تاثیری ندارد و اگر از آن استفاده کنید با Warning پایین مواجه می شوید.
Warning: Private methods cannot be final as they are never overridden by other classes
Weak maps
Weak mapها یک مجموعهای دادهها (object) هستند که کلیدها به طرز ضعیفی reference شدهاند، به این صورت که از زباله بودن (از بین رفتن) پیشگیری نمی کند.
در PHP 8 کلاس weak map معرفی شدهاند تا شیهایی (object) درست کنیم تا به شکل کلیدهای weak mapها به کار گرفته شوند و زمانی که به هیچ شیای reference نداشتند، قابلیت این را داشت که آنها را به راحتی از weak map حذف کرد و یا به طور کلی ناپدید کرد. در پرازشهایی که بسیار طول می کشد این ویژگی قادر است به کمبود حافظه آن کمک کند و سبب شود نتیجه و کارایی بسیار مفید تر و بهتری را دارا باشد.
$map = new WeakMap;$obj = new stdClass;$map[$obj] = 42;var_dump($map);
اگر به توضیحاتی که دادیم دقت کرده باشید، نتیجه کد را به شکل پایین مشاهده خواهید کرد.
object(WeakMap)#1 (1) { [۰]=> array(2) { ["key"]=> object(stdClass)#2 (0) { } ["value"]=> int(42) }}
اگر شما شی را unset کنید آن کلید به شکلی اتوماتیک از weak map برداشته خواهد شد.
unset($obj);var_dump($map);
و درانتها نتیجه به صورت پایین می باشد
object(WeakMap)#1 (0) {}
قابلیت استفاده از class:: بر روی آبجکت ها
کلمهای خیلی کاربردی و تاثیر گذار برای شناختن این که آبجکت (object) از کدام کلاس ساخته شده است، در گذشته ما از تابع get_class بهره می بردیم تا بفهمیم یک آبجکت از کدام کلاس ساخته شده است ولی اکنون با به کار گیری از class:: بر روی خود آبجکتی که خودتان تعیین کردید، قادر هستید که بفهمید آبجکت شما از چه کلاسی استفاده می کند.
$foo = new Foo(); var_dump($foo::class);
Non-capturing catches
قبل از نسخه PHP 8 هر وقتی که شما قصد داشتید از catch به منظور خطایابی بهره بگیرید، مجبور بودید تا Exception موجود در catch را داخل متغیری سیو (ذخیره) کنید و احتمالا هم به هیچ عنوان از آن متغیر در خطایابی خود بهره نمی بردید و نیازی به استفاده از آن نبوده است. این مورد داخل PHP 8 رفع شده است و اکنون شما توانایی این را دارید که بدون آوردن متغیر Exception خود را استفاده کنید.
به جای استفاده از کد پایین:
try { // Something goes wrong} catch (MySpecialException $exception) { Log::error("Something went wrong");}
این کد را استفاده میکنید:
try { // Something goes wrong} catch (MySpecialException) { Log::error("Something went wrong");}
دقت کنید که شما همواره مجبور هستید نوع catch خود را تعیین کنید، صحیح است که در PHP 8 شما قادر هستید متغیر را نیاورید ولی اگر به نوع آن دقت نکنید با خطا مواجه خواهید شد. اگر قصد دارید که همه انواع catchهای متفاوت را داشته باشید پیشنهاد می شود از نوع Throwable در catch خود استفاده کنید.
try { // Something goes wrong} catch (Throwable) { Log::error("Something went wrong");}
آخرین کاما درون لیست پارامترهای تابع
امکانات جدید php 8 : قبل از نسخه PHP 8 شما این امکان را نداشتید تا موقع جای گذاری پارامترهای تابع یک, (comma) اضافه نیز در انتهای آن بگذارید مانند لیستهای آرایهای. ولی اکنون این ویژگی به توابع هم افزوده شده است و شما قادر هستید مانند لیست با آن ها کار کنید.
public function( string $parameterA, int $parameterB, Foo $objectfoo,) { // …}
توجه داشته باشید که ویژگی افزودن کاما ( , ) به use در closureها هم افزوده شده است که این مورد در RFC هم آورده شده است اما تا به این لحظه قابلیت استفاده از آن را ندارند.
$longArgs_longVars = function ( $longArgument, $longerArgument, $muchLongerArgument, // Trailing commas were allowed in parameter lists in PHP 8.0) use ( $longVar1, $longerVar2, $muchLongerVar3) { // body};$longArgs_longVars( $longArgumentValue, $obj->longMethodCall(), $obj->longPropertyName ?? $longDefault,);
آبجکتهای DateTime را از Interface ایجاد کن
از گذشته شما قادر بودید که یک شئ یا object از DateTime را از یک DateTimeImmutable با بهره گیری از کد زیر درست کنید.
DateTime::createFromImmutable($immutableDateTime)
ولی روش دیگری که وجود داشت در نوع خودش یک ترفند یا یک دستی می باشد که با افزودن
DateTime::createFromInterface()
و
DatetimeImmutable::createFromInterface()
ولی اکنون یک روش بهتر و کاربردی تری وجود دارد تا شما DateTime و DateTimeImmutable را به یکدیگر تبدیل کنید.
DateTime::createFromInterface(DateTimeInterface $other); DateTimeImmutable::createFromInterface(DateTimeInterface $other);
Stringable جدید برای interface
Stringable interface به طور کلی این ویژگی را میدهد تا قرار دادن tostring__ در interfaceها تبدیل یا type hint را انجام دهید. هر زمانی که شما tostring__ را قرار می دهید ( پیاده سازی می کنید) به شکل اتوماتیک در پشت صحنه این مورد را پیاده سازی میکند و دیگر نیاز به پیاده سازی دوبارهی آن نیست.
class Foo{ public function __toString(): string { return 'foo'; }} function bar(string|Stringable $stringable) { /* … */ } bar(new Foo());bar('abc');
تابع جدید str_contains
این تابع سبب شده است تا ما به جای استفاده از strpos برای بررسی این که آیا رشتهای داخل رشتهی دیگر است یا خیر از تابع str_contains بهره بگیریم، به جای استفاده از کد پایین:
if (strpos('string with lots of words', 'words') !== false) { /* … */ }
از این کد میتوانیم بهره بگیریم:
if (str_contains('string with lots of words', 'words')) { /* … */ }
توابع جدید str_starts_with و str_ends_with
امکانات جدید php 8 : کاربرد این دو تابع از اسم آنها مشخص است، تابع str_starts_with بررسی میکند آیا رشتهای که به عنوان پارامتر اول دادهاید با رشتهای که پارامتر دوم دادهایم آغاز خواهد شد یا نه. تابع str_ends_with هم برعکس این کار را انجام میدهد و آخر و تمام شدن یک رشته را مورد بازبینی قرار می دهد.
str_starts_with('haystack', 'hay'); // truestr_ends_with('haystack', 'stack'); // true
خلاصه و جمع بندی :
در نوشته ویژگیهای جدید php 8 همانطور که اشاره شد ، شاهد تغییراتی بسیار مهم و کاربردی در نسخهی جدید یعنی PHP 8 بودید که اکثر آنها را با یکدیگر مرور کردیم، PHP اهداف بسیار بزرگی دارد که برای مثال یکی از آنها تبدیل شدن به سریعترین زبان برنامه نویسی مفسری جهان است که با هر بار به روز رسانی PHP شاهد این هستیم که چقدر سرعت و بهینگی این زبان افزایش یافته است و همچنان رو به افزایش و گسترش خواهد بود.
اگر ویژگی جدیدی هست که ما در این مقاله اشاره نکرده ایم لطفا در قسمت نظرات اشاره فرمایید تا همگی از آن استفاده کنیم.