تغییرات جدید نسخه 8 زبان PHP چیست؟ (قسمت چهارم)

What's New in PHP 8? - Part 4

09 مرداد 1399
تغییرات جدید نسخه ی 8 زبان PHP چیست؟ (قسمت چهارم)

در جلسات قبل با JIT و مفاهیم پیرامون آن آشنا شدیم اما نسخه ی هشتم PHP قابلیت های جدیدی دیگری را نیز به همراه دارد و این قابلیت ها منحصر به JIT نیستند. من چند عدد از این خصوصیت ها را برای شما انتخاب کرده ام تا با هم بررسی کنیم.

نسخه دوم Union types

به زبان ساده union type ها مقادیری را قبول می کنند که ممکن است از تایپ های مختلفی باشند. در حال حاضر PHP از union types پشتیبانی نمی کند (به استثنای دستور Type? و تایپ خاص iterable) بنابراین قبل از نسخه ی 8 زبان PHP، استفاده از union type ها به phpdoc annotation محدود بود. به طور مثال:

class Number {
	/**
	 * @var int|float $number
	 */
	private $number;

	/**
	 * @param int|float $number
	 */
	public function setNumber($number) {
		$this->number = $number;
	}

	/**
	 * @return int|float
	 */
	public function getNumber() {
		return $this->number;
	}
}

اگر با phpdoc block آشنا نیستید (خطوط کامنت شده در بالا) می توانید به لینک زیر مراجعه کنید:

https://docs.phpdoc.org/latest/getting-started/your-first-set-of-documentation.html

البته به طور خلاصه می توان گفت که این doc block ها برای مشخص کردن هدف هر قسمت از کدهای شما هستند. حالا با RFC جدید برای union type ها می توانیم از signature متدها نیز از آن ها استفاده کنیم تا دیگر مجبور به استفاده از inline documentation (همان doc block های بالا) نباشیم بلکه از این به بعد از ساختار T1|T2 برای تعریف آن ها استفاده کنیم. مثال:

class Number {
	private int|float $number;

	public function setNumber(int|float $number): void {
		$this->number = $number;
	}

	public function getNumber(): int|float {
		return $this->number;
	}
}

آقای Nikita Popov در این RFC می گوید:

“Supporting union types in the language allows us to move more type information from phpdoc into function signatures, with the usual advantages this brings:

  • Types are actually enforced, so mistakes can be caught early.
  • Because they are enforced, type information is less likely to become outdated or miss edge-cases.
  • Types are checked during inheritance, enforcing the Liskov Substitution Principle.
  • Types are available through Reflection.
  • The syntax is a lot less boilerplate-y than phpdoc.”

ترجمه:

پشتیبانی از union types به ما کمک می کند تا اطلاعات بیشتری از type یک داده را از phpdoc به function signature منتقل کنیم تا از مزیت های آن بهره مند شویم:

  • type ها اجباری می شوند بنابراین اشتباهات در همان ابتدا (در مراحل توسعه) مشخص می شوند.
  • از آنجایی که type ها اجباری می شوند، اطلاعات type ها قدیمی نشده و در حالت های خاص نیز استفاده خواهد شد.
  • type ها در هنگام ارث بری بررسی می شوند بنابراین از قانون Liskov Substitution پیروی خواهیم کرد.
  • type ها از طریق reflection هم قابل دسترسی خواهند بود.
  • نحو و قواعد نوشتن آن نیز نسبت به phpdoc کمتر به کدهای boilerplate (کدهای آماده و تکراری - در این مورد بیشتر مطالعه کنید) شباهت دارد.

با این حساب Union type ها از تمام type های داده پشتیبانی خواهند کرد، البته محدودیت هایی نیز وجود دارد:

  • تایپ void نمی تواند قسمتی از union type ها باشد چرا که void به معنی برنگشتن هیچ مقداری از یک تابع است.
  • تایپ null فقط در union type ها قابل استفاده است و نمی توان از آن به صورت مستقل استفاده کرد.
  • استفاده از تایپ ?T (با نام nullable type شناخته می شود) به معنای T|null مجاز است اما اجازه نداریم که از خود ?T در union type استفاده کنیم. مثلا ?T1|T2 مجاز نیست و به جای آن باید از T1|T2|null استفاده کنیم.
  • بسیاری از توابع در PHP می توانند مقدار False را برگردانند (مثل strops و strstr و substr و غیره) بنابراین استفاده از false نیز مجاز است.

اگر می خواهید در مورد union type ها بیشتر بدانید به لینک زیر مراجعه کنید:

https://wiki.php.net/rfc/union_types_v2

خطاهای یکسان بین توابع پیش فرض و توابع تعریف شده

در حال حاضر اگر پارامتری را که Type غیرمجاز دارد به یک تابع پاس بدهیم، بر اساس اینکه آن تابع internal باشد یا user-defined باشد، رفتار متفاوتی را خواهیم دید. منظور از توابع internal همان توابع built-in است؛ یعنی توابعی که به صورت پیش فرض در زبان PHP وجود دارد اما توابع User-defined توابعی هستند که توسط خود توسعه دهنده تعریف می شوند. مثال:

<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
    echo "Example function.\n";
    return $retval;
}
?>

ما در زبان PHP هیچ تابعی به نام foo نداریم بنابراین باید خودمان آن را به شکل بالا تعریف کنیم. مسئله اینجاست که اگر تابعی از ما پارامتر خاصی بخواهد (مثلا رشته ای) و ما چیزی غیر از آن را پاس بدهیم (مثلا یک عدد) شاهد رفتار زیر خواهیم بود:

  • در توابعی که توسط توسعه دهنده تعریف می شوند، یک خطای TypeError می گیریم.
  • در توابع پیش فرض بر اساس اینکه با چه تابعی سر و کار داریم، نتایج مختلفی می گیریم اما معمولا اینطور است که warning داده شده و null برگردانده می شود.

به طور مثال بیایید این کار را با یک تابع پیش فرض انجام بدهیم:

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

همچنین اگر strict_types را فعال کرده باشیم یا اگر اطلاعات آرگومان type را مشخص کرده باشد، شاهد رفتار دیگری خواهیم بود. در چنین سناریوهایی PHP متوجه خطای تایپ شده و یک TypeError می گیریم. این مسئله باعث مشکلات مختلفی می شود که در این قسمت از RFC توضیح داده شده اند. برای رفع این مشکل RFC جدید پیشنهاد می دهد که در صورتی که با چنین وضعیتی روبرو بودیم همیشه خطای ThrowError را دریافت کنیم. بنابراین در نسخه ی 8 زبان PHP کد بالا باعث خطای زیر خواهد شد:

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 4

در قسمت بعد با ویژگی های بیشتری آشنا خواهیم شد.


سایر قسمت‌های این مبحث را در لینک‌های زیر مطالعه کنید:

تغییرات جدید نسخه 8 زبان PHP (قسمت اول)
تغییرات جدید نسخه 8 زبان PHP (قسمت دوم)
تغییرات جدید نسخه 8 زبان PHP (قسمت سوم)
تغییرات جدید نسخه 8 زبان PHP (قسمت پنجم)
تغییرات جدید نسخه 8 زبان PHP (قسمت ششم)

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.