تغییرات PHP 8.0

926
تغییرات PHP 8.0

PHP 8.0 یک نسخه با تغییرات عمده و یک نقطه عطف قابل‌توجه در PHP است، زیرا چندین ویژگی جدید برای تایپ سیستم، سینتکس، مدیریت خطا، رشته‌ها، برنامه‌نویسی شیء‌گرا و… ارائه کرده است.

PHP سعی می‌کند در مورد تغییرات backward incompatible که می‌تواند بخش‌های زیادی از برنامه‌ها را با مشکل مواجه کند، محافظه‌کار باشد و با این وجود، چندین ویژگی اساسی جدید را در PHP 8.0 ایجاد کرده است.

ویژگی‌هایی مانند Named Parameters ،JIT ،Attributes و Constructor Properties اصلاحات و تغییرات نحوی (syntax) اساسی را به ارمغان آورده‌اند، در حالی که اصلاحاتی جزئی مانند مهاجرت resource به شیء، بهبود‌های مدیریت خطا و تغییرات و بهبودهای عملگر‌ها و عملگرهای مقایسه‌ای احتمال باگ‌های نادیده‌گرفته‌شده را کاهش می‌دهند.


PHP 8.0 در چه زمانی منتشر شد؟

پی‌اچ‌پی ۸ در تاریخ ۲۶ نوامبر ۲۰۲۰ به عنوان نسخه‌ی جدیدی از این زبان به شکل رسمی در دسترس عموم قرار گرفت. این به‌روزرسانی، بسیاری از بهینه‌سازی‌ها و ویژگی‌های قدرتمند را به زبان اضافه کرده است. در این مقاله از کوئرا بلاگ به مهم‌ترین ویژگی‌های این نسخه می‌پردازیم و مزیت‌های آن نسبت به نسخه‌های قبل را بررسی می‌کنیم.

شاید به این مقاله هم علاقه‌مند باشید: PHP چیست؟

ویژگی‌های جدید اصلی

Named Parameters

PHP 8.0 علاوه‌بر پارامترهای موقعیتی که از قبل وجود داشتند، پارامترهای نام‌گذاری‌شده را نیز در فراخوانی تابع امکان‌پذیر می‌کند.

str_contains(needle: 'Bar', haystack: 'Foobar');

این تغییر باعث می‌شود تا نام‌های پارامتر تابع، بخشی از API عمومی باشند.

Attributes

attribute‌ها به شما این امکان را می‌دهند که متادیتاها را برای توابع، کلاس‌ها، پراپرتی‌ها و پارامترها تعریف کنید. attribute‌ها به نام کلاس‌های PHP (که خود با یک attribute معرفی می‌شوند) مرتبط می‌شوند و با استفاده از Reflection API می‌توان آن‌ها را در زمان اجرا دریافت کرد.

#[CustomAttribute]
class Foo {
    #[AnotherAttribute(42)]
    public function bar(): void {}
}

attributeها امکان تعریف annotationهایی که قبلاً برای ذخیره‌سازی آن‌ها به DocBlock نیاز بود را بسیار آسان می‌کنند.

Constructor Properties

یک سینتکس جدید برای تعریف مستقیم پراپرتی‌های کلاس از کانستراکتور کلاس (متد جادویی construct__ ) اضافه شده است.

class User {
    public function __construct(private string $name) {}
}

PHP 8.0 در constructor از تعریف سطح دسترسی (public ،private یا protected) و type hint پشتیبانی می‌کند. این پراپرتی‌ها به‌عنوان پراپرتی‌های کلاس با همان سطح دسترسی و نوعی که در کانستراکتور معرفی می‌شوند، اضافه می‌شوند.

این قابلیت backward incompatible می‌تواند هنگام تعریف کلاس‌های value-object، به کاهش boilerplate code کمک کند.

کامپایلر Just-In-Time

PHP Opcache از JIT پشتیبانی می‌کند. JIT به‌طور پیش‌فرض غیر‌فعال است. در‌صورت فعال‌سازی آن، دستورالعمل‌های native را کامپایل و ذخیره می‌کند. JIT تفاوت محسوسی در برنامه‌های وب متصل به IO ایجاد نمی‌کند، اما باعث افزایش عملکرد برنامه‌هایی که از CPU زیاد استفاده می‌کنند، می‌شود.

# Enabling JIT in php.ini
opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing

توجه داشته باشید که JIT هنوز جدید است و یک روز قبل از انتشار PHP 8.0 رفع‌اشکال شده است. لایه‌های اضافه‌شده توسط JIT دیباگ کردن و profiling را دشوارتر می‌کنند.

Null-safe Operator

یکی دیگر از تغییرات PHP 8.0، عملگر Null-safe است که ایمنی در زنجیره‌سازی متد یا پراپرتی را هنگامی که امکان null بودن مقدار برگشتی یا پراپرتی وجود دارد، فراهم می‌کند.

return $user->getAddress()?->getCountry()?->isoCode;

اگر عملگر ?-> null-safe با مقدار null مواجه شود، بقیه عبارت را نادیده می‌گیرد و بلافاصله و بدون ایجاد خطا، null را برمی‌گرداند.

match expressions

match expressionها مشابه بلوک‌های switch هستند، با این تفاوت که بلوک‌های match مقایسه‌های type safe را فراهم می‌کنند، از یک مقدار بازگشتی پشتیبانی می‌کنند، برای خارج شدن به دستور break نیاز ندارند و از چندین مقدار برای مطابقت پشتیبانی می‌کنند. همچنین با در نظر گرفتن همه موارد موجود برای مقایسه، تضمین می‌کنند که حداقل یکی از مطابقت‌ها صورت می‌گیرد.

$response = match('test') {
    'test' => $this->sendTestAlert(),
    'send' => $this->sendNuclearAlert(),
};

ممکن است همه بلوک‌های switch به‌خوبی به بلوک‌های match تبدیل نشوند، مخصوصاً در کدی که به backward compatibility یا بلوک‌های switch با چندین دستور (بر‌خلاف عبارات تک‌خطی) احتیاج دارد.

WeakMap‌ها

یک WeakMap اجازه ذخیره‌سازی و مرتبط کردن مقادیر دلخواه را به key‌های شیء می‌دهد. اما بدون جلوگیری از garbage collect شدن، شیء با حذف شدن در هر جای دیگری از map خارج می‌شود.

یک WeakMap مشابه SplObjectStorage است. هر دو از objects به‌عنوان کلید استفاده می‌کنند و اجازه ذخیره مقادیر دلخواه را می‌دهند. با این حال، یک WeakMap مانع از بازیافت حافظه شیء نمی‌شود.


توابع و کلاس‌های جدید

PHP 8.0 چند تابع جدید را برای سهولت بررسی رشته (بررسی وجود یک زیررشته در رشته، بررسی شروع رشته با یک زیررشته و بررسی خاتمه رشته با یک زیررشته) ارائه کرده است تا جایگزین فراخوانی‌های strpos() !== false ـــــــ که خوانایی کمتری دارند و به‌دلیل عملگرهای مقایسه‌ای weak type مستعد خطا هستند ـــــــــ شوند.

تابع تعریف مثال
str_containsبررسی می‌کند که آیا needle درون haystack می‌باشد یا خیرstr_contains('Foobar', 'Foo')
str_starts_withبررسی می‌کند که آیا needle با رشته haystack آغاز می‌شود یا خیرstr_starts_with('PHP 8.0', 'PHP')
str_ends_withبررسی می‌کند که آیا needle با رشته haystack پایان می‌یابد یا خیرstr_ends_with('PHP 8.0', '8.0')

PHP 8.0 همچنین توابعی مانند fdiv ،get_resource_id ،get_debug_type و preg_last_error_msg را ارائه کرده است.

تابع تعریف
fdivبخش float از استاندارد IEEE-754 در Floating-Point Arithmetic
get_resource_idID داخلی را برای PHP resource مورد‌نظر برمی‌گرداند.
get_debug_typetype داخلی یک متغیر را برمی‌گرداند.
preg_last_error_msgپیغام خطای آخرین عملیات preg را برمی‌گرداند.

رابط جدید stringable

از دیگر تغییرات PHP 8.0 می‌توان به رابط جدید Stringable اشاره کرد؛ که به‌طور خودکار به تمام کلاس‌هایی که روش tostring__ را پیاده‌سازی می‌کنند، اضافه می‌شود و آن‌ها صریحاً اعلان implements Stringable را نمایش می‌دهند.

با استفاده از رابط Stringable، معرفی type‌های string|Stringable به توابعی که می‌توانند رشته‌ها یا اشیاء را با روش ()tostring__ بپذیرند یا رد کنند، بسیار آسان شده است.

کلاس جدید PhpToken Tokenizer

کلاس جدید PhpToken یک رابط شیءگرا را به‌عنوان جایگزینی برای تابع مبتنی بر آرایه‌ی token_get_all فراهم می‌کند.

بیشتر بخوانید: آرایه های PHP به زبان ساده


بهبودهای Type System

PHP 8.0 با افزودن union type‌ها و تایپ mixed، تایپ سیستم را بهبود بخشیده است.

Union Type‌ها

Union Typeها اجازه معرفی بیش از یک تایپ (return types ،parameters و class properties) را می‌دهند.

function parse_value(string|int|float): string|null {}

همچنین از false به‌عنوان type خاصی (برای Boolean false) پشتیبانی می‌کنند؛ خصوصیتی رایج در کدهای قدیمی که از Exceptionها استفاده نمی‌کردند.

شبه‌تایپ mixed جدید

PHP 8.0 تایپ mixed را که قبلاً به‌طور گسترده در کامنت‌های DocBlock مورد استفاده قرار می‌گرفت، به همراه دارد.

function dd(mixed $var): void {
    var_dump($var);
}

از تایپ mixed می‌توان برای نشان دادن اینکه هر تایپی می‌تواند پذیرفته یا برگردانده شود، استفاده کرد. در context کلاس/رابط، تایپ mixed از قوانین اصل جایگزینی Liskov تبعیت می‌کند.

static return type

این ویژگی که قبلاً در DocBlock پشتیبانی می‌شد، اکنون در PHP 8.0 پشتیبانی می‌شود. static return type اعلام می‌کند که یک شیء از کلاس فراخوانی‌شده برگردانده خواهد شد.

class Foo {
    public static function getInstance(): static {
        return new static();
    }
}

بهبودهای مدیریت خطا

یک تغییر عمده و backward incompatible در PHP این است که اکنون توابع داخلی با type error‌ها یا value errorها مخالفت می‌کنند.

این تغییر، رفتار قدیمی PHP در بروز یک هشدار و برگرداندن null در صورت مواجه شدن با مقداری که نمی‌تواند استفاده کند را اصلاح می‌کند. این رفتار اغلب نامطلوب است، زیرا هشدارهای PHP اجرای بلوک باقی‌مانده را متوقف نمی‌کنند.

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

تقریباً تمام توابع داخلی در PHP اکنون type checking را اعمال می‌کنند و به‌جای هشدارها، PHP اکنون TypeError‌ها یا ValueErrorها را نادیده می‌گیرد. بر‌اساس کدهای قدیمی، این تغییر می‌تواند مشکلاتی ایجاد کند. اکنون PHP با این خطاها جسورانه‌تر و غیر‌قابل‌بخشش برخورد می‌کند.

throw در expression‌ها

قبل از PHP 8.0، امکان پرتاب یک استثنا از یک expression (به‌عنوان مثال یک ternary statement) وجود نداشت. امکان‌پذیر شدن این مورد نیز یکی دیگر از تغییرات PHP 8.0 است.

$value = isset($_GET['value'])
    ? $_GET['value']
    : throw new \InvalidArgumentException('value not set');

Catch exceptions فقط از طریق type

در PHP 8.0، می‌توانیم exception‌ها از طریق تایپ آن‌ها و بدون گرفتن شیء exception دریافت کنیم.

try {
} catch (TypeError) {
  // Did not catch the $exception object
}

error reporting پیش‌فرض روی E_ALL تنظیم شده است

کانفیگ پیش‌فرض PHP 8.0 روی نمایش همه پیغام‌های خطا است.با این کار تمام deprecationها و strict warningها در ورژن‌های قبل پنهان می‌شوند.

startup error‌ها به‌صورت پیش‌فرض نمایش داده می‌شوند

این نسخه از PHP به‌صورت پیش‌فرض، startup error‌ها (failure to load dynamic extensions، invalid INI configurations و…) را نمایش می‌دهد.

assertion‌ها به‌صورت پیش‌فرض exception صادر می‌کنند

assertionها (()assert) استثناهایی هنگام assertion failureها پرتاب می‌کنند. قبل از PHP 8.0، به یک پیکربندی صریح که به‌طور پیش‌فرض غیرفعال بود، احتیاج بود.

عملگر Error Suppression @ خطاهای fatal را غیر‌فعال نمی‌کند

PHP 8.0 رفتار عملگر Error Suppression @ را که fatal error‌هایی که منجر به خرابی اسکریپت می‌شوند را غیر‌فعال می‌کرد، اصلاح کرده است، زیرا عملگر @ از fatal error‌ها جلوگیری نمی‌کند، بلکه پیغام‌های خطا را پنهان می‌کند.


مهاجرت منبع (resource) به شیء (object)

یکی از تلاش‌های طولانی‌مدت در توسعه PHP کنار گذاشتن resource تایپ‌ها بود، زیرا کار کردن با آن‌ها دشوار بود. حتی در PHP 8.0 نیز resourceها از typing پشتیبانی نمی‌کنند.

resource objectها با زباله‌روب (garbage collector) نیز به‌خوبی کار نمی‌کردند. این امر منجر به نشت حافظه (memory leak) در resource objectهایی (مانند xml) می‌شد.

در PHP 8.0، برخی از پرکاربردترین افزونه‌ها از resource objectهای متداول به کلاس‌های استاندارد PHP منتقل شده‌اند.

در resource object ،PHP 8.0ها به‌عنوان value-objectها به‌جای کلاس‌های کاملاً مشخص با متدهای موجود در آن‌ها کار می‌کنند. اکثر این کلاس‌ها امکان ایجاد نمونه‌سازی با ساختار جدید ()Foo را نیز ندارند و باید با توابع موجود که resource objectها را در نسخه‌های قبلی بازگردانده‌اند، نمونه‌سازی شوند.

مهاجرت resource به شیء در PHP 8.0 کاملاً یکپارچه است، زیرا در همه توابع اشیاء جدید را برگردانده و می‌پذیرد و با همان معنای resource objectهای قبلی رفتار می‌کند.

object (PHP >= 8.0)resource (PHP < 8.0)Extension
CurlHandleCurlCurl
CurlMultiHandlecurl_multiCurl
CurlShareHandlecurl_shareCurl
GdImagegdGD
SocketSocketsSockets
AddressInfoAddressInfoSockets
OpenSSLAsymmetricKeyOpenSSL keyOpenSSL
OpenSSLCertificateOpenSSL X.509OpenSSL
OpenSSLCertificateSigningRequestOpenSSL X.509 CSROpenSSL
XMLWriterxmlwriterXMLWriter
XMLParserxmlXML

تغییرات برنامه‌نویسی شیء‌گرا (object-oriented) در PHP

PHP 8.0 اولین نسخه با تغییرات عمده است که در‌مورد نقض اصل جایگزینی Liskov سخت‌گیری می‌کند. نسخه‌های قبل از PHP 8.0 در نحوه مدیریت امضای ناسازگار متدها، مشکلاتی داشتند.

در PHP 8.0، همه عدم‌تطابق امضا‌ها، از جمله traitهای انتزاعی، منجر به fatal error می‌شوند. علاوه بر این، امضاها برای متد‌های جادویی PHP نیز اجرا می‌شوند.

Fatal error‌ها در امضاهای متد ناسازگار

در این نسخه، وقتی اصل جایگزینی Liskov هنگام ارث‌بری کلاس‌ها یا پیاده‌سازی رابط‌ها رعایت نشود، fatal errorهایی ایجاد می‌شوند. قبل از PHP 8.0، فقط امضاهای ناسازگار هشدار ارسال می‌کردند.

class Foo {
    public function process(stdClass $item): array {}
}

class SuperFoo extends Foo{
    public function process(array $items): array {}
    //                      ^^^^^ mismatch
}
Fatal error: Declaration of SuperFoo::process(array $items): array must be compatible with Foo::process(stdClass $item): array in ... on line ...

امضاهای کلاس متد جادویی با سخت‌گیری اجرا می‌شوند

از نسخه PHP 8.0 به بعد، اگر متد‌های جادویی (به‌عنوان مثال ()tostring__ و ()get__ و…) type‌ها را اعلام ‌کنند، باید امضای مورد‌انتظار PHP را پیاده‌سازی کنند. این تغییر به‌منظور جلوگیری از اعلام یک متد جادویی توسط کاربر که از معنای اصلی آن پیروی نمی‌کند، ایجاد شده است.

class Foo {
    public function __toString(): object
   {
    }
}

در نسخه‌های قبلی PHP، تعاریفی مانند Foo::__toString(): object مجاز بودند، اما در PHP 8.0 اگر امضا مطابق با الزامات نباشد، یک استثنا ارسال می‌شود.

فراخوانی استاتیک متدهای غیر‌استاتیک کلاس منجر به fatal error می‌شود

PHP 8.0 دیگر اجازه فراخوانی استاتیک متدهای غیراستاتیک کلاس را نمی‌دهد.

class Foo {
    public function bar() {}
}
Foo::bar();

نسخه‌های قبلی اخطار deprecation منتشر می‌کردند، اما از PHP 8.0 به بعد یک fatal error ایجاد می‌شود.

قوانین وراثت (inheritance) در روش‌های کلاس خصوصی اعمال نمی‌شوند

PHP 8.0 سخت‌گیری در اجرای abstract و static را برای متدهای private کلاس کم می‌کند. این تغییر به‌دلیل اینکه متدهای private فقط private هستند، ایجاد شده است.

در PHP 8.0، کلاس‌های فرزند (child class) مجاز به تعریف متدهای abstract و تغییر flag‌های static برای متدهای خصوصی هستند.

ثابت جادویی class:: روی اشیاء پشتیبانی می‌شود

ثابت جادویی class:: نام کلاس را به‌صورت کامل برمی‌گرداند. در نسخه‌های قبل، این ثابت فقط در کلاس‌ها (مانند Foo\Bar::class) مجاز بود، اما در PHP 8.0، ثابت جادویی class:: روی اشیاء نمونه‌سازی‌شده نیز کار می‌کند.


تغییرات مرتبط با رشته

در بین تغییرات PHP 8.0، چندین تغییر ظریف وجود دارد که ممکن است در ابتدا مشخص نباشند، اما می‌توانند نتایج کاملاً غیر‌منتظره‌ای را به همراه داشته باشند.

یک تفاوت عمده در PHP 8.0 این است که PHP اکنون یک رشته خالی بین هر کاراکتر از یک رشته مشخص در‌نظر می‌گیرد.

قبل از PHP 8.0، بررسی وجود یک رشته خالی ("") مجاز نبود، اما PHP 8.0 آن را می‌پذیرد و اینکه یک رشته خالی بین هر کاراکتر وجود دارد را برمی‌گرداند.

مدیریت چند‌بایتی یا توابعی مانند strlen هنوز همان مقادیر نسخه‌های قدیمی را برمی‌گردانند، اما همه توابعی که یک زیر‌رشته را در یک رشته جستجو می‌کنند، تغییر کرده‌اند.

علاوه بر این، PHP 8.0 نحوه اولویت‌بندی و پشتیبانی از اصلاح‌کننده‌های جدید در توابع sprintf (مانند اصلاح‌کنندگان %h و %H و * width and precision) توسط عملگر الحاق رشته را تغییر داده است.

substr ،iconv_substr و grapheme_substr‌ رشته خالی را در offset‌های خارج از محدوده بر‌می‌گردانند

این توابع پارامترهای needle و offset را به طول رشته مقید می‌کنند و به‌جای برگرداندن false، یک رشته خالی را برمی‌گردانند.

substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
mb_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
iconv_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0
grapheme_substr('FooBar', 42, 3); // "" in PHP >=8.0, false in PHP < 8.0

عملگرهای -/+ هنگام استفاده با عملگر (.)concat تقدم بیشتری دارند

هنگامی که عملگرهای ریاضی + و - در یک عبارت با عملگر الحاق (.) استفاده شوند، تقدم بیشتری پیدا می‌کنند. این کار منجر به اخطار deprecation در نسخه‌های قبل از PHP 8.0 می‌شد، اما اکنون بی‌صدا و طبق warning اتفاق می‌افتد.

echo 35 + 7 . '.' . 0 + 5;
// 42.5 in PHP >= 8.0
// 47 in PHP <= 8.0

تبدیل مستقل محلی float به string

هنگامی که یک مقدار float ‌به string تبدیل می‌شود، PHP دیگر از زبان سیستم استفاده نمی‌کند. نسخه‌های قبل از PHP 8.0 هنگام انجام این کار، مکان فعلی (که thread-safe نیست) یا سیستم را در‌نظر می‌گرفتند که منجر به خروجی‌های رشته ناسازگار می‌شد، زیرا برخی مناطق، به ویژه کشورهای اروپایی، جداکننده هزار و علامت اعشاری متفاوتی نسبت به ایالات متحده دارند.

برای تبدیل float به string، می‌توان از اصلاح کننده f% در توابعی نظیر printf استفاده کرد.

علاوه بر این، اصلاح‌کننده‌های جدید %h و %H برای توابعی نظیر printf تعریف شده‌اند که نسخه‌هایی فارغ از زبان از اصلاح‌کننده‌های %g و %G هستند.

بیشتر بخوانید: ویژگی های جدید PHP 8.3 که باید بشناسید – نگاهی کامل به تغییرات PHP 8.3

جمع‌بندی

در این مقاله تلاش کردیم تا به مهم‌ترین تغییرات PHP 8.0 مانند Union Type، کامپایلر JIT، عملگر Null-safe و بسیاری دیگر بپردازیم؛ تا دید بهتری به ویژگی‌های جدید این نسخه پیدا کنید. امیدواریم از خواندن این مقاله لذت برده باشید. خوشحال می‌شویم تا نظرات خود را در قالب کامنت با ما درمیان بگذارید.

آموزش برنامه نویسی با کوئرا کالج
کوئرا بلاگ

اشتراک در
اطلاع از
guest

2 دیدگاه‌
قدیمی‌ترین
تازه‌ترین بیشترین واکنش
بازخورد (Feedback) های اینلاین
View all comments
حمیدرضا احمدی
حمیدرضا احمدی
2 سال قبل

عالیه4

نسرین نادری
نسرین نادری
2 سال قبل

سلام دوست کوئرایی عزیز
خوشحالیم که این مطلب برای شما مفید بوده