PHP 8.3 در تاریخ 23 نوامبر 2023 مصادف با ۲ آذر ۱۴۰۱ منتشر شد؛ این نسخه کلاسهای فقط خواندنی (readonly) را بهینه کرده، تابع جدید json_validate() را افزوده است، و همچنین متدهایی را به کلاس تازه اضافه شده Randomizer افزوده است، قابلیت تشخیص stack overflow و بهبودهای دیگر دارد.
در این پست، تمام ویژگیها، بهبودهای عملکردی، تغییرات و کاهشهایی که در این نسخه انجام شده است، را یکی یکی بررسی خواهیم کرد.
اصلاحات فقط خواندنی RFC
RFC دو تغییر را پیشنهاد کرد، اما تنها یکی از آنها پذیرفته شد: امکان مقداردهی مجدد (reinitialize) ویژگیهای فقط خواندنی در هنگام کلون کردن. ممکن است این تغییر یک تغییر مهمی به نظر برسد، اما این RFC فقط به یک حالت خاص (اما مهم) اشاره دارد: بازنویسی مقادیر ویژگیها، داخل تابع __clone()، به این منظور که ویژگیهای فقط خواندنی را کلون کند.
readonly class Post { public function __construct( public DateTime $createdAt, ) {} public function __clone() { $this->createdAt = new DateTime(); // This is allowed, // even though `createdAt` is a readonly property. }
مشخص کردن نوع ثابتهای کلاس RFC
اکنون میتوانید برای ثابتها کلاس و نوع مقدار را مشخص کنید.
class Foo { const string BAR = 'baz'; }
ویژگی #[Override]
ویژگی جدید #[Override] برای نمایش هدف و قصد برنامه نویس استفاده میشود. در واقع این ویژگی میگوید: ” این متد در حال بازنویسی یک متد والد است. در صورت تغییر نیاز است تا این موضوع اطلاع داده شود”.
در زیر مثالی برایتان آوردهایم:
abstract class Parent { public function methodWithDefaultImplementation(): int { return 1; } } final class Child extends Parent { #[Override] public function methodWithDefaultImplementation(): int { return 2; // The overridden method } }
حال فرض کنید که بخشی از متد والد دچار تغییر نام شده است:
abstract class Parent { public function methodWithNewImplementation(): int { return 1; } }
PHP به لطف ویژگی #[Override]، قادر خواهد بود که تشخیص دهد که متد Child::methodWithDefaultImplementation() دیگر چیزی را بازنویسی نمیکند و یک پیغام خطا رخ خواهد داد.
اندیسهای منفی در آرایهها
پیش از این، اگر یک آرایه خالی داشتید، و یک آیتم با یک اندیس منفی به آن اضافه میکردید، سپس یک آیتم دیگر بدون اندیس اضافه میکردید، آیتم دوم همیشه با اندیس 0 شروع میشد:
$array = []; $array[-5] = 'a'; $array[] = 'b'; var_export($array); //array ( // -5 => 'a', // 0 => 'b', //)
از PHP 8.3 به بعد، آیتم بعدی در این آرایه با اندیس -4 اضافه خواهد شد:
//array ( // -5 => 'a', // -4 => 'b', //)
کلاسهای فقط خواندنی ناشناس
پیش از این، شما قادر به تعریف کلاسهای ناشناس به عنوان فقط خواندنی نبودید. این مشکل در PHP 8.3 رفع شده است:
$class = new readonly class { public function __construct( public string $foo = 'bar', ) {} };
تابع جدید json_validate():
پیش از این، تنها راه برای اعتبارسنجی اینکه یک رشته، JSON معتبر است یا خیر، این بود که آن را دیکد کنید و بررسی کنید که آیا هر گونه خطا ایجاد شده یا خیر. اگر تنها نیاز دارید بدانید که آیا ورودی JSON معتبر است یا خیر، تابع جدید json_validate() برای شما کاربردی خواهد بود، زیرا از حافظه کمتری نسبت به دیکد کردن رشته استفاده میکند.
json_validate(string $json, int $depth = 512, int $flags = 0): bool
add-onهای Randomizer
نسخهی 8.2، کلاس Randomizer را به زبان PHP افزوده است. آپدیت 8.3 تعدادی افزونه کوچک به همراه دارد.
Randomizer::getBytesFromString(string $string, int $length): string
این متد به شما این امکان را میدهد که یک رشته با طول دلخواه ایجاد کنید که از بایتهای انتخاب شده تصادفی یک رشته مشخص تشکیل شدهاند.
Randomizer::getFloat( float $min, float $max, IntervalBoundary $boundary = IntervalBoundary::ClosedOpen ): float
getFloat() یک مقدار اعشاری بین $min و $max برمیگرداند. به لطف یک enum با نامIntervalBoundary، قادر خواهید بود که تعیین کنید که آیا این عدد تصادفی میتواند شامل $min و $max هم باشد یا خیر.
Closed به معنای این است که این مقادیر را شامل شود، در حالی که Open به معنای آن است که این مقادیر را در بر نگیرد. در مثال بالا، ClosedOpen بدین معنا است که شامل $min شود ولی شامل $max نشود.
Randomizer::nextFloat(): float {}
nextFloat() یک اختصار برای getFloat(0, 1, IntervalBoundary::ClosedOpen) است، به عبارت دیگر: این تابع به شما یک مقدار اعشاری تصادفی بین 0 و 1 را بدون در نظر گرفتن 1 ارائه میدهد.
Fetch کردن (واکشی) ثابتِ کلاس به صورت داینامیک
در PHP 8.3، شما میتوانید ثابتها را با سینتکسی داینامیکتر فچ کنید:
class Foo { const BAR = 'bar'; } $name = 'BAR'; // Instead of this: constant(Foo::class . '::' . $name); // You can now do this: Foo::{$name};
همانطور که در مثال بالا مشاهده میکنید، دیگر نیازی به استفاده از تابع constant برای استفاده از ثابت به صورت داینامیک ندارید و PHP در نسخه ۸.۳ این مورد را بهصورت بسیار داینامیکتری فراهم کرده است.
استثناهای تاریخ/زمان مناسبتر RFC
در بسیاری از موارد، هنگامی که مشکلی در رابطه با زمان یا تاریخ اتفاق میافتد، PHP به سادگی یک شی Exception و Error را throw کند، و یا هشدار یا خطایی منتشر میکند. این RFC تمامی حالات ویژه (edge cases) را بررسی میکند و استثنائات مناسب و اختصاصی برای آنها اضافه میکند.
حالا ما استثنائاتی مانند DateMalformedIntervalStringException, DateInvalidOperationException, و DateRangeError داریم.
بطور کلی، موارد اضافه شده جدید هیچ کدی را خراب نمیکنند، زیرا این استثنائات و خطاهای اضافه شده جدید، کلاسهای Exception و Error عمومی را به عنوان زیرکلاس (subclass) قرار میدهند. با این حال، سه تغییر شکننده کوچک (breaking changes) همراه این RFC وجود دارد:
- خطای Epoch doesn’t fit in a PHP integer، حالا یک ارور DateRangeError جدید به جای یک ارور ValueError عمومی برمیگرداند، که آن را به ساب کلاس نمیدهد. این مشکل فقط برای پلتفرمهای 32 بیتی رخ میدهد.
- هشدار The Only non-special relative time specifications are supported for subtraction با استفاده از DateTime::sub() و date_sub() تبدیل به یک DateInvalidOperationException جدید میشود.
- هشدارهای Unknown or bad format (%s) at position %d (%c): %s و String ‘%s’ contains non-relative elements که در هنگام پارس کردن رشتههای نادرست/شکسته DateInterval ایجاد میشوند، به جای نمایش یک هشدار و برگرداندن false، به صورت یک اکسپشن DateMalformedIntervalStringException جدید رخ میدهند، وقتی که از اینترفیس OO استفاده کنیم.
توجه: تغییر شکننده یا breaking changes در صنعت نرم افزار بدین معنی است که پتانسیل شکستن یکپارچگی را دارند. در واقع تغییراتی که روی یک بخش از کد داده میشود و ممکن باعث خطا در عملکرد بخش دیگری شوند.
بهبود Error Handling تابع unserialize()
حالا unserialize() هنگام برخورد با مشکلات E_WARNING را به جای E_NOTICE منتشر میکند.
این RFC همچنین افزودن استثنائات بیشتری هنگام اجرای unserialize() را نیز پیشنهاد داد، اما این بخش پذیرفته نشد.
تغییرات تابع range()
برگرفته از لاگ تغییرات:
- حالا و در PHP 8.3، هنگام ارسال آبجکتها، ریسورسها یا آرایهها به عنوان ورودیهای کرانها (مینیمم و ماکزیمم) یک TypeError رخ میدهد.
- اگر مقدار 0 به عنوان مقدار $step ارسال شود، یک ValueError با توضیحات بیشتر رخ میدهد.
- اگر از یک عدد منفی به عنوان مقدار افزایشی برای $step استفاده شود، یک ValueError رخ میدهد.
- اگر $step یک عدد اعشاری باشد که بتوان آن را به عنوان یک عدد صحیح تفسیر کرد، حالا میتوان این کار امکان پذیر است.
- اگر هر یک از آرگومانها برابر با infinity یا NaN باشد، حالا یک ValueError رخ میدهد.
- اگر $start یا $end رشته خالی باشد، یک E_WARNING ایجاد میشود. مقدار همچنان به مقدار 0 کست میشود.
- اگر $start یا $end بیشتر از یک بایت باشند، تنها در صورتی یک هشدار E_WARNING رخ میدهد که این متغییر رشته غیرعددی باشد.
- اگر $start یا $end به این دلیل که کران دیگر عدد صحیح است به عدد صحیح تبدیل شود، حالا یک E_WARNING رخ میدهد. (مثلا range(5, ‘z’);)
- هنگامی که سعی در تولید یک رنج از کاراکترها داریم و $step یک عدد اعشاری باشد، یک E_WARNING رخ میدهد، جز در حالتی که هر دو کران (مینیمم و ماکزیمم) رشته عددی باشند (مثال: range(‘5’, ‘9’, 0.5); که هشداری رخ نمیدهد).
- تابع range() منجر به تولید لیستی از کاراکترها میشود اگر یکی از کرانها یک رشته عددی باشد به جای اینکه ورودی دیگر به عدد صحیح تبدیل شود (مثال: range(‘5’, ‘z’);).
Propertyهای استاتیک و تریتها
برگرفته از لاگ تغییرات:
استفاده از تریتها با پراپرتیهای استاتیک حالا پراپرتیهای استاتیک ارث برده شده از کلاس والد را به صورت مجدد تعریف میکند. این ویژگی باعث ایجاد یک پراپرتی استاتیک جداگانه برای ذخیره در کلاس فعلی میشود. این مشابه افزودن پراپرتی استاتیک به صورت مستقیم به کلاس بدون استفاده از تریت است.
تشخیص سرریز پشته
در PHP 8.3، دو دستور ini جدید به نامهای zend.max_allowed_stack_size و zend.reserved_stack_size افزوده شدهاند. برنامههایی که در حال نزدیک شدن به پر شدن پشته تماس هستند، حالا ممکن است هنگام استفاده بیشتر از تفاوت بین zend.max_allowed_stack_size و zend.reserved_stack_size، یک خطای Error رخ دهد.
مزیت این ویژگی این است که خطاهای تقسیمبندی ناشی از سرریز پشته دیگر منجر به خطای Segfault نمیشوند و اشکالزدایی را بسیار آسانتر میکنند.
مقدار پیشفرض برای zend.max_allowed_stack_size صفر است، که به این معناست که PHP به طور خودکار یک مقدار را تعیین میکند. همچنین میتوانید -1 را ارائه دهید یا یک مقدار خاص بر حسب بایت تا نشان دهید که هیچ محدودیتی وجود ندارد. دستور zend.reserved_stack_size برای تعیین buffer zone استفاده میشود، به طوری که PHP به جای مصرف شدن حافظه، قادر به اعلان یک خطا خواهد بود. مقدار اینجا باید یک عدد بایتی باشد، اما PHP یک مقدار پیشفرض معقول برای شما تعیین میکند، بنابراین شما لزوماً نیازی به تنظیم آن ندارید، مگر اینکه در موارد خاص برنامهها با مشکل روبهرو شوید.
در پایان، برای فیبرهای PHP، دستور فعلی fiber.stack_size به عنوان max_allowed_stack_size استفاده میشود.
zend.max_allowed_stack_size=128K
تابع جدید ()mb_str_pad
برگرفته از RFC
در PHP، انواع مختلفی از توابع رشته در دو نوع وجود دارند: یکی برای رشتههای بایتی و دیگری برای رشتههای چندبایتی. با این حال، یک خلل قابل توجه در میان توابع رشته چندبایتی، عدم وجود معادل mbstring برای str_pad() است. تابع str_pad() از فقدان کاراکترهای چندبایتی پشتیبانی میکند. که مشکلاتی را در هنگام کار با زبانهایی که از رمزگذاری چندبایتی مانند UTF-8 استفاده میکنند، ایجاد میکند. این RFC پیشنهاد افزودن یک تابع به PHP را ارائه میدهد، که به آن mb_str_pad() میگوییم.
این تابع به این صورت است:
function mb_str_pad( string $string, int $length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT, ?string $encoding = null, ): string {}
کلوژرهای متد جادویی و آرگومانهای نام دار
فرض کنید کلاسی داریم که از متدهای جادویی پشتیبانی میکند:
class Test { public function __call($name, $args) { var_dump($name, $args); } public static function __callStatic($name, $args) { var_dump($name, $args); } }
نسخه 8.3 زبان PHP به شما این امکان را میدهد تا با استفاده از این متدها، کلوژر ایجاد کنید و آرگومانهای نامدار را به کلوژرها پاس دهید. قبلا همچین ویژگی وجود نداشت.
$test = new Test(); $closure = $test->magic(...); $closure(a: 'hello', b: 'world');
محدوده ثابتهای غیرقابل تغییر
پیش از این، محدوده ثابتها هنگام پیاده سازی اینترفیس بررسی نمیگردید. PHP 8.3 این باگ را فیکس کرده، اما این موضوع ممکن است باعث شکستن کد در برخی مکانها شود اگر از وجود این قابلیت آگاه نباشید.
interface I { public const FOO = 'foo'; } class C implements I { private const FOO = 'foo'; }
عملکردهای منسوخ شده کوچک طبق RFC
همانطور که همیشه در هر نسخه، یک RFC وجود دارد که یک مجموعه کوچک از عملکردهای منسوخ شده را افزوده است. به یاد داشته باشید که عملکردهای منسوخ شده خطاها نیستند، و به طور کلی چیزهای مثبتی برای پیشرفت زبان هستند. لیست زیر موارد منسوخ شدهای هستند که تایید شدهاند، میتوانید جزئیات بیشتری را در RFC بخوانید:
- ارسال $widths منفی به mb_strimwidth() منسوخ شده است.
- ثابت NumberFormatter::TYPE_CURRENCY منسوخ و حذف شده است.
- منسوخ شدن و حذف پیاده سازی غیرصحیح Mt19937 (MT_RAND_PHP) پیش از PHP 7.1
- منسوخ و حذف شدن فراخوانی ldap_connect() با 2 پارامتر $host و $port
- عملکردهای منسوخ شده باقی مانده ادعاهای کد ارزیابی شده به صورت رشته
تغییرات کوچک اما قابل توجه
همه تغییرات در PHP از طریق فرآیند RFC عبور نمیکند. در واقع، اکثر تغییرات شامل نگهداری و اصلاح باگها هستند و نیازی به یک RFC ندارند. تمام این تغییرات در سند به روز رسانی لیست شدهاند. ما برخی از مهمترین آنها را لیست کردهایم، اما اگر میخواهید جزئیات بیشتری را بدانید حتماً باید کل لیست را بخوانید.
- هنگام استفاده از FFI، توابع C که یک نوع بازگشتی از void دارند حالا مقدار null را به جای FFI\CData:void میگردانند.
- posix_getrlimit() حالا یک پارامتر اختیاری به نام $res را میپذیرد تا اجازه ی واکشی (fetch) یک ریسورس محدود تکی را بدهد.
- در gc_status() چهار فیلد جدید اضافه شدهاند: running، protected، full، و buffer_size.
- class_alias() حالا از ایجاد نام مستعار برای یک کلاس داخلی پشتیبانی میکند.
- هنگامی که آرگومانهای read و یا error به mysqli_poll() پاس داده نشوند یک ValueError رخ میدهد.
- array_pad() حالا تنها توسط حداکثر تعداد عناصری که یک آرایه میتواند داشته باشد، محدود شده است. قبلاً فقط امکان افزودن حداکثر 1048576 عنصر به صورت همزمان وجود داشت.
- توابع جدید posix: posix_sysconf()، posix_pathconf()، posix_fpathconf() :posix()، و posix_eaccess()
- اجرای چند بارهی proc_get_status() حالا همیشه مقدار صحیح را به سیستمهای posix باز میگرداند.
- یک دستور ini با نام opcache.consistency_checks نیز حذف شده است.
- بهینه سازی در توابع array_sum() و array_product()