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

What's New in PHP 8? - Part 3

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

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

اعتبارسنجی برای trait های abstract

همانطور که می دانید زبان PHP می تواند به صورت شیء گرا نوشته شود اما وراثت آن از نوع single inheritance است که یعنی هر کلاس فقط می تواند از یک کلاس دیگر ارث بری داشته باشد. برای حل این موضوع trait ها معرفی شدند که در واقع راهی برای اجرای متدها در چندین کلاس هستند. هر trait می تواند متدهای abstract داشته باشد. به گفته ی documentation زبان PHP ما می توانیم از متدهای abstract در trait ها برای اجباری کردن آن در یک کلاس خاص استفاده کنیم، البته در این حالت باید signature یا امضای دو متد برابر باشد؛ یعنی تعداد و نوع آرگومان ها حتما یکی باشد. مثال:

<?php
trait Hello {
    public function sayHelloWorld() {
        echo 'Hello'.$this->getWorld();
    }
    abstract public function getWorld();
}

class MyHelloWorld {
    private $world;
    use Hello;
    public function getWorld() {
        return $this->world;
    }
    public function setWorld($val) {
        $this->world = $val;
    }
}
?>

با استفاده از متد getWorld و تعریف آن به صورت abstract، کلاس MyHelloWorld را مجبور به تعریف متدی به نام getWorld کرده ایم (نحوه ی تعریف آن به سلیقه ی خود این کلاس است - نکته ی الزامی، وجود این متد است). حالا بر اساس گفته ی Nikita Popov که پیشنهاد دهنده ی RFC بوده است، اعتبارسنجی signature ها پراکنده و به شکل زیر بوده است:

  • در حالتی که پیاده سازی متد در کلاس استفاده کننده از trait انجام می شود، مجبور به اعتبارسنجی نیستیم و می توانیم آن را رد کنیم.
  • زمانی که پیاده سازی متد از کلاس پدر می آید مجبور به اعتبارسنجی هستیم.
  • زمانی که پیاده سازی متد از کلاس فرزند می آید، مجبور به اعتبارسنجی هستیم.

Nikita برای مورد اول مثال زیر را می زند:

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) {}
}

همانطور که می بینید آرگومان متد Test در trait از نوع عددی اما در کلاس C از نوع رشته ای است. این کد در PHP صحیح است اما از نظر Nikita نباید اینچنین باشد. این RFC جدید پیشنهاد می دهد که در صورت عدم همخوانی دو متد با یکدیگر، PHP باید یک Fatal Error را برگرداند (فارغ از اینکه متد اصلی از فرزند یا پدر یا غیره است):

Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10

این RFC با رای کامل تایید شده است.

method signature های ناسازگار

در زبان PHP اگر signature دو method با هم سازگار نباشد، یا با خطای fatal یا با warning روبرو می شویم (بسته به اینکه مشکل اصلی کجاست). مثلا اگر کلاسی در PHP از یک interface استفاده کند و متدهای این دو از نظر signature با هم متفاوت باشند، یک خطای Fatal دریافت می کنیم. یک مثال ساده از بروز خطا از طریق وراثت interface:

interface I {
	public function method(array $a);
}
class C implements I {
	public function method(int $a) {}
}

اگر از PHP نسخه ی 7.4 به بعد استفاده می کنید، کد بالا یک خطای fatal خواهد داد که به شکل زیر است:

Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7

اما اگر این ناسازگاری در signature ها بین متدهای کلاس پدر و فرزند باشد فقط یک خطای warning می گیریم. مثال:

class C1 {
	public function method(array $a) {}
}
class C2 extends C1 {
	public function method(int $a) {}
}

از PHP نسخه ی 7.4 به بعد، کد بالا یک warning به شکل زیر را نمایش می دهد:

Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

تا به اینجا طبق انتظارمان پاسخ گرفته ایم اما یک RFC جدید ثبت شده است که بر اساس آن، نتیجه ی تغییر signature ها بین دو متد در هر حالتی که باشند حتما fatal error خواهد بود. بنابراین مثال قبلی که رابطه ی پدر و فرزندی را به ما نشان می داد به جای Warning یک Fatal error به شکل زیر را نمایش می دهد:

Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

آرایه هایی با ایندکس منفی

در زبان PHP اگر آرایه ای با ایندکس منفی شروع شود (start_index < 0) ایندکس های بعدی از صفر شروع می شوند (بدون توجه به اینکه مقدار قبلی چقدر بوده است). به طور مثال متد Array_fill را در PHP در نظر بگیرید:

<?php
$a = array_fill(5, 6, 'banana');
$b = array_fill(-2, 4, 'pear');
print_r($a);
print_r($b);
?>

آرگومان اول start_index یا ایندکس اولیه برای شروع است، آرگومان دوم تعداد عناصری است که باید وارد آرایه شود و آرگومان سوم مقدار این عناصر است. خروجی دستور بالا به شکل زیر است:

Array
(
    [5]  => banana
    [6]  => banana
    [7]  => banana
    [8]  => banana
    [9]  => banana
    [10] => banana
)
Array
(
    [-2] => pear
    [0] => pear
    [1] => pear
    [2] => pear
)

همانطور که می بینید ما برای آرایه ی دوم، ایندکس اولیه را 2- دادیم اما نکته ی مهم اینجاست که عضو بعدی آرایه 1- نیست بلکه مستقیما از صفر شروع می شود. برای اینکه مسئله روشن تر شود به جای 2- از 5- استفاده می کنم:

$a = array_fill(-5, 4, true);
var_dump($a);

در نسخه های 7.4 به بعد PHP نتیجه ی زیر را می گیریم:

array(4) {
	[-5]=>
	bool(true)
	[0]=>
	bool(true)
	[1]=>
	bool(true)
	[2]=>
	bool(true)
}

بنابراین نتیجه همان است. حالا این RFC جدید پیشنهاد داده است که می گوید فرمول کلی این فرآیند باید به شکل start_index + 1 باشد و یک واحد یک واحد افزوده شود. با این حساب نتیجه ی کد بالا در PHP نسخه ی 8 به شکل زیر خواهد بود:

array(4) {
	[-5]=>
	bool(true)
	[-4]=>
	bool(true)
	[-3]=>
	bool(true)
	[-2]=>
	bool(true)
}

بنابراین اگر در کدهایتان از این موارد استفاده می کنید، حتما با بروزرسانی نسخه ی PHP، کدهای خود را نیز به روز رسانی کنید. ما موارد بعدی را در جلسه ی بعد بررسی خواهیم کرد.



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

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

نویسنده شوید

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

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