ویژگی های جدید php 8

دسته بندی: برنامه نویسی
سطح مقاله: پیشرفته
زمان مطالعه: 41 دقیقه
۲۱ مهر ۱۳۹۹

همان‌طور که از تیتر مقاله مشخص است، ویژگی‌ها و امکانات مهمی به نسخه‌‌ی جدید PHP 8 اضافه شده است که باعث می‌شود نقص‌هایی که PHP در نسخه‌های قبلی خود داشت، برطرف شوند. از ویژگی‌های جالب و جذاب می‌توانیم به JIT، Match و ... اشاره کنیم که قرار است همه‌ی آن‌ها را در این مقاله بررسی کنیم. در مقاله ویژگی‌های جدید php 8 با ما همراه باشید.

فهرست محتوای این مقاله

زمان انتشار PHP 8 

طبق اخبار منتشر شده، زمان انتشار PHP 8، نوامبر سال 2020 یا آذر سال 1399 می‌باشد. نسخه‌‌ی PHP 8 یک نسخه‌ی بسیار مهم برای PHP خواهد بود که با خود تغییرات بسیار مهمی را آورده است.

همان‌طور که گفته شد تغییرات مهمی صورت گرفته است و شاید شما مجبور به یک سری تغییرات در کدهایتان باشید که البته اگر تا الان با آخرین ورژن‌های PHP کار کرده باشید، این تغییرات برای شما سخت نخواهد بود.

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 نمی‌گذارد. ما در سون لرن مقاله‌ای در خصوص کامپایلر JIT در PHP نسخه 8 نوشته‌ایم که می‌تواند آن را مطالعه کرده و متوجه کارایی آن شوید.

اپراتور 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) {
    200, 300 => null,
    400 => 'not found',
    500 => '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 تغییراتی در خود داده است این غیر ممکن را نیز ممکن کرده است.

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) {
	[0]=>
	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'); // true
str_ends_with('haystack', 'stack'); // true

تابع جدید fdiv

تابع جدید fdiv شبیه به تابع‌های قبلی fmod و intdiv نیست، در حقیقت این تابع امکان تقسیم بر صفر را می‌دهد و به جای اینکه خطا دریافت کنیم سه مقدار INF برای مثبت بی نهایت، INF- برای منفی بی نهایت و NAN که به معنی Not a Number (عدد نیست) است.

تابع جدید get_debug_type

تابع get_debug_type نوع یک متغیر را به عنوان خروجی برمی‌گرداند، شاید با خود گفته باشید که این تابع خیلی شبیه به تابع gettype باشد اما این تابع جدید جزئیات بیشتری برای آرایه‌ها، رشته‌ها، کلاس‌ها و شئ‌های (object) دارای نام مستعار (anonymous) به ما می‌دهد.

برای مثال اگر ما تابع gettype را بر روی یک کلاس صدا بزنیم خروجی این تابع شئ (Object) آن خواهد بود. اما اگر همین کلاس را با استفاده از get_debug_type صدا بزنیم نام کلاس را برای ما به عنوان خروجی برمی‌گرداند.

تابع جدید get_resource_id

Resourceها متغیرهای خاص درون PHP هستند که به Resourceهای بیرونی اشاره می‌کنند. Curl Handler، اتصال به پایگاه داده و باز کردن یک فایل درون کد می‌توانند مثال‌هایی از Resourceها باشد که می‌توانند به int تبدیل یا cast شوند که تا قبل از PHP 8 این کار با قرار دادن (int) پشت متغیر تبدیل می‌شد. اما الان در PHP 8 تابع get_resource_id اضافه شده است که این کار را بسیار ساده‌تر کند.

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

قبل از PHP 8:

$resourceId = (int) $resource;

بعد از PHP 8:

$resourceId = get_resource_id($resource);

بهبود متد abstract در traitها

ویژگی‌های جدید php 8 : درون Traitها شما می‌توانید متدهایی (Method) از نوع abstract ایجاد کنید که هر زمان یک کلاس این traitها را use کرد مجبور به پیاده سازی متد abstract درون trait شود. قبل از PHP 8 امضای (منظور از امضا یا signature همان نوع ورودی و خروجی یک تابع است) پیاده سازی متدهای abstract معتبر نبود. قبلا از PHP 8 به صورت زیر معتبر بود.

trait Test {
    abstract public function test(int $input): int;
}

class UsesTrait
{
    use Test;

    public function test($input)
    {
        return $input;
    }
}

در PHP 8 زمانی که شما می‌خوا‌هید یک متد abstract برای trait را پیاده‌سازی کنید، اعتبارسنجی را به شکلی بهتر و به درستی انجام می‌دهد. پس از PHP 8 به بعد شما می‌توانید این گونه این متد را پیاده سازی کنید.

class UsesTrait
{
    use Test;

    public function test(int $input): int
    {
        return $input;
    }
}

پیاده سازی شئ token_get_all

قسمت‌‌های مختلفی از PHP به عنوان T_SR شناخته یا نشان داده‌ می‌شوند، خروجی identifiersها در PHP خطاهای parse error هستند که پیغام زیر را می‌دهند.

Parse error: unexpected T_FUNCTION, expecting ',' or ';' in script.php on line 10.

در PHP به این‌ها php token می‌گویند،‌ لیست کامل php tokenها در مستندات اصلی وب‌سایت PHP وجود دارد و شما ‌می‌توانید آن‌ها را بررسی کنید.

اگر می‌خواهید تمامی tokenهای اسکریپت داده شده به شما را بررسی کنید می‌توانید مثل کد زیر رفتار کنید.

tokens = token_get_all('<!--?php echo; ?-->');
 
foreach ($tokens as $token) {
    if (is_array($token)) {
        echo "Line {$token[2]}: ", token_name($token[0]), " ('{$token[1]}')", PHP_EOL;
    }
}
 
Line 1: T_OPEN_TAG ('<!--?php ')
Line 1: T_ECHO ('echo')
Line 1: T_WHITESPACE (' ')
Line 1: T_CLOSE_TAG ('?-->')

در حال حاضر این تابع یک رشته تک کاراکتری یا یک آرایه با آیدی token، متن و شماره خط را  برمی‌گر‌داند. اما  در PHP 8 شما می‌توانید کلاس جدید PhpToken را هم استفاده کنید. برای اینکه بتوانید از این کلاس استفاده کنید مثل کدهای زیر عمل کنید.

class MyPhpToken extends PhpToken {
    public function getLowerText() {
        return strtolower($this->text);
    }
}
 
// getAll works the same as token_get_all(), but returns array of PhpToken
$tokens = MyPhpToken::getAll($code);
var_dump($tokens[0] instanceof MyPhpToken); // true
$tokens[0]->getLowerText();

تابع PhpToken::getAll جایگزین تابع token_get_all شده است که به جای برگرداندن آرایه و رشته، آرایه‌ای از شی PhpToken برمی‌گرداند.

Variable syntax tweaks

RFC می‌خواهد که شماری از ناسازگاری‌های قواعد متغیرها را در PHP 8 درست کند، PHP چهار نوع قواعد فراخوانی را در خود دارد.

  • آرایه‌ها:
    $test['test'], $test{'test'}
  • شئ‌ها:
  • $test->sth, $test->sth()
  • استاتیک‌ها:
  • Test::$sth, Test::sth(), Test::STH
  • فراخوانی کردن:
  • test()

در حال حاضر رشته‌هایی در هم (که درون یکدیگر جای داده‌ شده‌اند) "foo" کاملا قابل استناد هستند، اما این به چه معناست؟ بگذارید به این مثال نگاهی بیندازیم. ساختارهایی مانند کد زیر کاملا مجاز هستند، اگر چه رشته "foo$bar" غیر قابل استناد هستند.

"foo"[0] یا "foo"->bar()

اما RFC قصد دارد که رفتارهای این دو نوع رشته را با هم‌دیگر سازگار کند، مانند مثال زیرین.

"foo$bar"[0] 
"foo$bar"->baz()

ext-json همیشه در دسترس است

قبل از PHP 8 این امکان وجود داشت که PHP بدون فعال بودن اکستنشن JSON کامپایل شود اما از این به بعد این امکان وجود ندارد. از زمانی که JSON بسیار مورد استقبال قرار گرفته است و در حال استفاده است توسعه دهنگان می‌توانند همیشه از آن استفاده کنند.

نوع خطاهای سازگار

پاس دادن پارامتر اشتباه به توابع باعث می‌شود که خروجی کار ما خطا از نوع TypeErrorباشد، برای توابع داخلی نوع خطا بستگی به فاکتورهای متفاوتی دارد اما نتیجه warning یا null خواهد بود. RFC پیشنهاد می‌کند تا Exceptionهای مختلف از TypeError به صورت سازگار ایجاد شود.

function foo(int $bar) {}
foo("not an int");
// TypeError: Argument 1 passed to foo() must be of the type int, string given

طبقه بندی مجدد Warningهای engine

بسیاری از خطاهایی‌ که به صورت warning و یا notice نمایش داده می‌شدند به خطاهای مناسب‌تری تبدیل شده‌اند، خطاهای زیر تغییر کرده‌اند.

  • خطای Undefined variable: به جای noticeها Error exception
  • خطای Undefined array index: هشدار یا warning به جای notice
  • خطای Division by zero: به جای warningها DivisionByZeroError exception
  • و...

لیست کامل تغییرات رامی‌توانید از engine_warnings بخوانید.

اپراتور @ دیگر جلوی خطاهای fatal را نمی‌گیرد

اگر اپراتور @ استفاده کردید در پروژه خود و می‌خواهید به PHP 8 ارتقا پیدا کنید باید حتما display_errors=Off را برای پروژه خودتان که در فاز production است قرار بدهید تا خطاها را نمایش ندهد.

پیش‌فرض سطح گزارش خطا

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

Error Mode پیش‌فرض PDO

قبل از نسخه‌‌ی PHP 8 اگر خطایی در اتصال PDO با پایگاه‌داده می‌افتاد خطاهایی رخ نمی‌داد مگر اینکه خود برنامه‌نویس یک Exception برای خطاهای احتمالی تعریف می‌کرد. اما از PHP 8 به بعد Error mode به PDO::ERRMODE_EXCEPTION تغییر خواهد کرد.

الویت الصاق کردن

یک سری تغییراتی در اولویت بندی الصاق کردن به وجود آمده است که باهم بررسی می‌کنیم. فرض کنید شما کدی را به شکل زیر بنویسید:

echo "sum: " . $a + $b;

PHP کد بالا را به شکل زیر تفسیر می‌کند:

echo ("sum: " . $a) + $b;

اما در PHP 8 تغییراتی ایجاد شده است که از این به بعد PHP به صورت زیر این کد را تفسیر می‌کند.

echo "sum: " . ($a + $b);

علمگرهای بیتی و حسابی سخت‌ گیرانه‌تر می‌شوند

قبل از PHP 8 این امکان وجود داشت که شما عملگرها یا operatorهای بیتی را بر روی آرایه‌ها، آبجکت‌ها و resourceها که در بالا اشاره شد، پیاده سازی کنید اما از این نسخه‌‌ی به بعد دیگر این امکان وجود ندارد و یک TypeError پرت یا throw می‌کند.

[] % [42];
$object + 4;

با namespaceها به عنوان single token رفتار کن

PHP هر بخش در namespace که با \ از هم جدا می‌شوند را تفسیر می‌کند، حال RFC کاری کرده است که شما حتی می‌توانید اسم‌های رزور شده در PHP را نیز در namespace خود استفاده کنید.

namespace iter\fn;

Saner numeric strings

رشته‌ها در PHP ‌می‌توانند به شکل‌های مختلفی تفسیر بشوند، حالا شاید از خود بپرسید این به چه معناست؟

یک رشته میتواند در چهار دسته مختلف قرار بگیرد.

  • رشته‌های عددی:‌ به طور کلی رشته‌های عددی شامل یک عدد درون خود می‌باشند که گاها نیز با خود whitespace یا فضای خالی درون رشته را نیز دارند. برای مثال "123" این عدد که درون رشته است یا " 1.23e2" این رشته که اعداد همراه با یک فاصله نیز هستند که فقط درون خود عدد یا عدد با فاصله (whitespace) دارند.
  •  رشته‌های عددی که فقط با عدد شروع می‌شوند: این نوع رشته‌های عددی در اصل در شروع رشته فقط حاوی عدد هستند اما در ادامه رشته حاوی حروف نیز خواهند شد که می‌توانند فاصله یا whitespace نیز داشته باشند. برای مثال "123abc" که با عدد شروع شده است و در ادامه حاوی رشته نیز است و " 123" که با عدد شروع شده است و در ادامه حاوی فاصله نیز هست
  • رشته‌ی غیر عددی:‌ رشته‌هایی که حاوی دو دسته‌ای که در بالا تعریف کرده‌ایم نیستند
  • integer string: یا همان رشته‌های عددی (از نوع دیگر) با همان قوانین اما به شکل سختگیرانه‌تر

اما دیگر وجود فاصله‌ها در ابتدای رشته و یا صفرهای بی‌مورد در ابتدای رشته که حاوی عدد است قابل پذیرش نیست،‌ در پایین مشکلاتی که بیان شده است که منشع همین موارد بالا هستند. لازم به ذکر است که این مشکلات را RFC بیان کرده است.

  • رشته‌هایی که با عدد شروع می‌شوند این اتفاق می‌افتد که به عنوان عدد تفسیر بشوند و همین باعث رو به رو شدن با خطا می‌شویم
  • تابع is_numeric خروجی گمراه کننده‌ای می‌دهد به گونه‌ای که زمانی که وقتی ما رشته که حاوی عدد است به عنوان ورودی به این تابع می‌دهیم خروجی true می‌شود
  • رشته‌هایی که با فاصله و بعد با عدد شروع می‌شوند بیشتر عدد شناخته می‌شوند تا رشته‌هایی که با فاصله یا whitespace تمام می‌شوند

هدف این است که این‌ها این است که تمامی این موارد به یک مفهوم واحد دربیایند،‌ با این تغییرات در PHP 8 هر نوع رشته‌‌ی متفرقه‌‌ای به عنوان non-numeric شناخه می‌شود و خطای TypeError را نمایش خواهد داد. تغییرات به شکل زیر خواهد بود.

function getSomething(int $i) { var_dump($i); }
 
getSomething("123 "); // int(123)
 
getSomething("123abc"); // TypeError
 
var_dump(is_numeric("123 ")); // bool(true)
 
$string = 'The world';
 
var_dump($string['4str']);   // string(1) "w" with E_WARNING "Illegal string offset '4str'"
 
var_dump($string['4.5']);    // string(1) "w" with E_WARNING "String offset cast occurred" if the secondary vote is accepted otherwise TypeError
 
var_dump($string['string']);
 
var_dump(123 + "123 "); // int(246)
 
var_dump(123 + "123abc"); // int(246) with E_WARNING "A non-numeric value encountered"
 
var_dump(123 + "string"); // TypeError
 
var_dump(123 & "123 ");  // int(123)
 
var_dump(123 & "123abc"); // int(123) with E_WARNING "A non-numeric value encountered"
 
var_dump(123 & "abc");    // TypeError

جمع بندی:

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

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

چه امتیازی به این مقاله می دید؟
نویسنده امیر صالحی
عاشق دنیای برنامه نویسی و چالش های بزرگش

نظرات کاربران

امیرحسین شکری

درود امیر جان، عالی بود.

ارسال دیدگاه
خوشحال میشیم دیدگاه و یا تجربیات خودتون رو با ما در میون بذارید :