آشنایی با تازه های PHP 8 – از صفر تا صد


PHP 8 در تاریخ 6 آذر 1399 منتشر خواهد شد. این یک نسخه major تازه از زبان PHP است، یعنی برخی تغییرهای ناسازگار با نسخههای قبلی در آن وجود دارد و همچنین قابلیتهای زیاد جدید دیگری به همراه بهبودهای عملکردی به آن اضافه شده است. در حال حاضر PHP 8 در حالت قفل قابلیت است، یعنی هیچ قابلیت جدید دیگری نمیتواند به آن اضافه شود. این مقاله ما را در مسیر آشنایی با تازه های PHP 8 کمک میکند.
با توجه به این تغییرهای ناسازگار که اشاره کردیم، احتمال بالایی وجود دارد که نیاز باشد برخی تغییرها در کدهای موجود خود ایجاد کنید تا بتوانید آن را روی PHP 8 اجرا نمایید. با این حال اگر کد خود را به تدریج و با معرفی قابلیتهای جدید این زبان بهروزرسانی کرده باشید، این ارتقا کار دشواری نخواهد بود، چون اغلب تغییرهای ناسازگار در نسخههای پیش از *.7 منسوخ اعلام شدهاند. ما همه این موارد منسوخشده را در این مقاله فهرست خواهیم کرد.
PHP 8 علاوه بر تغییرهای ناسازگار مجموعه خوبی از قابلیتهای جدید از قبیل کامپایلر JIT، انواع Union، خصوصیتها (attributes) و موارد دیگر را عرضه کرده است.
قابلیتهای جدید PHP 8
در این بخش برخی قابلیتهای جدید را که در نسخه 8 زبان PHP اضافه شدهاند، مورد بررسی قرار میدهیم.
انواع Union
با توجه به ماهیت دینامیک انواع در زبان PHP، موارد زیادی وجود دارند که انواع Union میتوانند مفید واقع شوند. انواع Union به مجموعههایی از دو یا چند نوع گفته میشود که مشخص شده فقط یکی از این دو نوع میتواند مورد استفاده قرار گیرد.
توجه کنید که void هرگز نمیتواند بخشی از نوع Union باشد، چون به کلی مقداری بدون نوع بازگشتی است. به علاوه یونیونهای «تهیپذیر» (nullable) را میتوان با استفاده از null یا با استفاده از نمادگذاری موجود ? نوشت:
JIT
کامپایلر JIT یعنی «کامپایلر درجا» (just in time) بهبود عملکرد چشمگیری را نشان میدهد که البته همیشه در چارچوب درخواستهای وب قرار ندارد. زمانی که این نوع از کامپایلر را روی وباپلیکیشنهای واقعی تست میکنیم به نظر میرسد که کامپایلر JIT تفاوت چندانی در این نوع پروژههای PHP ایجاد نمیکند و در صورت وجود بهبود نیز چندان چشمگیر نخواهد بود.
اگر میخواهید در مورد JIT و کارهایی که در PHP انجام میدهد بیشتر بدانید، میتوانید از این مقاله (+) استفاده کنید.
عملگر nullsafe
اگر با عملگر تجمیع تهی آشنا باشید، حتماً با مشکلاتی که دارد نیز آشنا هستید. این عملگر روی فراخوانیهای متد کار نمیکند. به جای آن نیاز به بررسیهای آنی دارید یا باید روی تابعهای کمکی optional که برخی فریمورکها ارائه میکنند تکیه داشته باشید.
Optional
با اضافه شدن عملگر nullsafe به نسخه جدید PHP اینک میتوانیم رفتار شبیه تجمیع تهی را روی متدها نیز داشته باشیم. به مثال زیر توجه کنید:
آرگومانهای نامدار
«آرگومانهای نامدار» (Named arguments) امکان ارسال مقادیر به یک تابع را با تعیین نام مقدار فراهم ساختهاند، به طوری که دیگر نیازی به توجه به ترتیب آرگومانها وجود ندارد و میتوانید پارامترهای اختیاری را نیز رد کنید.
برای کسب اطلاعات بیشتر در این مورد، میتوانید مقاله زیر را مطالعه کنید:
خصوصیتها
«خصوصیتها» (Attributes) که در زبانهای دیگر برنامهنویسی عموماً «حاشیهنویسی» (annotation) نامیده میشوند، یک روش برای افزودن فرادادهها را به کلاسها فراهم میسازند و به این ترتیب دیگر نیازی به تحلیل بلوکهای کد هم نداریم. به عنوان یک مثال مختصر در ادامه مثالی از نمای ظاهری خصوصیتها را میبینید:
عبارت Match
این عبارت را میتوان برادر بزرگتر عبارت switch در نظر گرفت. Match میتواند مقادیر را بازگشت دهد و نیازی به گزارههای break هم ندارد. با بهرهگیری از این عبارت میتوانید شرطها را ترکیب کنید، از ترکیب نوع صریح استفاده کنید و هیچ نوع کاهش نوع نداشته باشید. به مثال زیر توجه کنید:
ارتقای مشخصه سازنده
در نسخه جدید زبان PHP به جای اینکه مشخصههای کلاس را تعیین کرده و یک سازنده برای آنها معرفی کنیم، میتوانیم این دو را در یک بخش منفرد ترکیب کنیم. بنابراین به جای این که به صورت زیر عمل کنیم:
میتوانیم به صورت زیر عمل کنیم:
البته در مورد ارتقای مشخصه حرفهای زیادی برای گفتن وجود دارد، اما توضیح تفصیلی در این خصوص خارج از حیطه این راهنمای مختصر است.
انواع بازگشتی جدید static
با این که قبلاً هم امکان بازگشت self وجود داشت، اما static قبل از PHP 8 یک نوع بازگشتی معتبر در این زبان نبود. با توجه به ماهیت دینامیک نوعها در PHP این قابلیتی است که برای بسیاری از توسعهدهندگان مفید خواهد بود.
نوع جدید mixed
برخی افراد این نوع جدید را در زبان PHP یک اشتباه محض میدانند. نوع mixed موجب بروز سردرگمی در افراد زیادی شده است. با این حال تعبیه این نوع در زبان PHP استدلالهای قدرتمندی دارد. این نوع جدید مفاهیم زیادی را وارد زبان PHP میکند:
- یک تابع میتواند هیچ چیز یا Null بازگشت دهد.
- میتوانیم یکی از چند نوع مختلف را انتظار داشته باشیم.
- ما میتوانیم یک نوع را انتظار داشته باشیم که دارای سرنخ نوع در PHP نباشد.
با توجه به دلایل فوق، این که نوع mixed اضافه شده است کاری خوبی محسوب میشود. mixed خودش به معنی یکی از انواع زیر است:
- array
- bool
- callable
- int
- float
- null
- object
- resource
- string
توجه کنید که mixed میتواند به صورت یک پارامتر از نوع مشخصه و نه به صورت یک نوع بازگشتی باشد.
همچنین توجه کنید که mixed خود از قبل شامل null است و امکان تهیپذیر ساختن آن وجود ندارد. بنابراین کد زیر موجب بروز خطا میشود:
صدور استثنا
در نسخه جدید PHP دیگر throw یک گزاره نیست و یک عبارت است و به این ترتیب امکان ایجاد throw exception در جاهای مختلف وجود دارد:
وراثت با متدهای خصوصی
قبلاً از PHP برای بهکارگیری بررسیهای یکسان وراثت روی متدهای عمومی، حفاظتشده و خصوصی استفاده میکردیم. به بیان دیگر متدهای خصوصی باید از همان قواعد امضای متد به عنوان متدهای حفاظتشده و عمومی تبعیت کنند. این موضوع معنای چنانی ندارد، چون متدهای خصوصی نباید از سوی کلاسهای فرزند خود مورد دسترسی قرار گیرند.
این وضعیت تغییر یافته است به طوری که این بررسیهای وراثت دیگر روی متدهای خصوصی اجرا نمیشوند. به علاوه استفاده از final private function نیز معنای چندانی ندارد و از این رو انجام این کار موجب بروز هشدار زیر میشود:
نگاشتهای ضعیف
یک پیادهسازی WeakMap بر اساس weakrefs که در نسخه 7.4 به PHP اضافه شده بود در نسخه جدید به این زبان افزوده شده است. WeakMap ارجاعهایی به اشیا نگهداری میکند که این اشیا را از این که garbage collect شوند، باز میدارد.
با در نظر گرفتن ORM-ها، آنها غالباً کشهایی را پیادهسازی میکنند، ارجاعهایی به کلاسهای entity دارند که عملکرد رابطههای بین entity -ها را بهبود میبخشند. این اشیای entity نمیتوانند تا زمانی که ارجاع به آنها کش شده است garbage collect شوند، هر چند اگر کش تنها چیزی باشد که به آن ارجاع دارند.
اگر این لایه کشینگ از ارجاعهای ضعیف و نگاشتها استفاده کرده باشد، PHP این اشیا را زمانی که هیچ ارجاع دیگری به آنها وجود نداشته باشد garbage collect میکند. به طور خاص در مورد ORM-ها که میتوانند چند صد یا چند هزار entity را درون یک درخواست مدیریت کنند، نگاشتهای ضعیف میتوانند روش بهتری ارائه کنند که از نظر مصرف منابع برای کار با این اشیا بهینه باشند. ظاهر نگاشت ضعیف به صورت مثال زیر است:
استفاده از ::class روی اشیا
یکی از قابلیتهای جدید و مفید دیگر PHP امکان استفاده از ::class روی اشیا است. به این ترتیب به جای این که از ()get_class روی یک شیء استفاده میکنیم، میتوانیم از این روش جدید بهره بگیریم. طرز کار آن مانند ()get_class است.
Non-capturing catches
تا قبل از PHP 8 هر زمان که میخواستیم یک استثنا را catch کنیم، باید آن را در یک متغیر ذخیره میکردیم و مهم نبود که از این متغیر استفاده میکنیم یا نه. با معرفی قابلیت Non-capturing catches در نسخه جدید این زبان میتوانیم این متغیر را نادیده بگیریم. بنابراین به جای این که به صورت زیر عمل کنیم:
اکنون از روش زیر استفاده میکنیم:
توجه کنید که همواره باید نوع را قید کنیم و مجاز به داشتن یک catch خالی نیستیم. اگر بخواهید همه استثناها و خطاها را به دام بیندازید، میتوانید از Throwable به عنوان نوع catch استفاده کنید.
کامای پایانی در لیستهای پارامتر
با این که قبلاً در زمان فراخوانی یک تابع امکان استفاده از کامای پایانی وجود داشت، اما در لیستهای پارامتر چنین امکانی تعبیه نشده بود. اکنون این مسئله در نسخه 9 زبان PHP مجاز است، یعنی میتوانیم به صورت زیر عمل کنیم:
توجه کنید که کاماهای پایانی در لیست use از کلوژرها نیز پشتیبانی میشوند.
ایجاد اشیای DateTime از اینترفیس
ما قبلاً میتوانستیم شیء DateTime را از یک شیء DateTimeImmutable با استفاده از روش زیر بسازیم:
اما روش دیگر پیچیده بود. با اضافه شدن ()DateTime::createFromInterface و ()DatetimeImmutable::createFromInterface اکنون یک روش عمومی برای تبدیل اشیای DateTime و DateTimeImmutable به همدیگر وجود دارد:
اینترفیس جدید Stringable
اینترفیس Stringable میتواند به عنوان سرنخ نوع برای هر چیزی استفاده شود که ()toString را پیادهسازی میکند. هر زمان که یک کلاس ()toString__ را پیادهسازی میکند، به صورت خودکار اینترفیس را در پشت صحنه پیادهسازی میکند و هیچ نیازی به پیادهسازی دستی آن وجود ندارد.
تابع جدید ()str_contains
اکنون دیگر نیازی نیست که از ()strpos برای دانستن این که رشتهای حاوی رشته دیگر است یا نه استفاده کنیم. به جای آن که به صورت زیر عمل کنیم:
اکنون به صورت زیر عمل میکنیم:
تابعهای جدید ()str_starts_with و ()str_ends_with
این دو تابع نیز مدتها بود که انتظار داشتیم به PHP اضافه شود و اکنون در هسته مرکزی این زبان افزوده شدهاند:
تابع جدید ()fdiv
تابع جدید ()fdiv کاری مشابه تابعهای ()fmod و ()intdiv انجام میدهد که امکان تقسیم بر 0 است. به این ترتیب به جای دریافت خطا، بسته به نوع عملیات، مقادیر INF، -INF یا NAN به دست میآیند.
تابع جدید ()get_debug_type
تابع ()get_debug_type یک نوع متغیر بازگشت میدهد. ()get_debug_type خروجی بسیار مفیدتری برای آرایهها، رشتهها و کلاسها و اشیای بینام ارائه میکند.
برای نمونه فراخوانی ()gettype روی یک کلاس \Foo\Bar یک object بازگشت میدهد. اما استفاده از ()get_debug_type نام کلاس را بازگشت میدهد.
تابع جدید ()get_resource_id
Resource-ها نوع خاصی از متغیر در PHP هستند که به منابع بیرونی اشاره میکنند. یک نمونه از آن یک اتصال MySQL است و به عنوان نمونه دیگر میتوان به یک دستگیره فایل اشاره کرد.
هر یک از این منابع یک ID دارند. تا پیش از این تنها روش برای دانستن این ID این بود که منبع را به نوع int تبدیل کنیم:
PHP 8 تابع ()get_resource_id را معرفی کرده است که این عملیات را به روشی روشنتر و از نظر نوع امن انجام میدهد:
متدهای مجرد در بهبودهای خصیصه
«خصیصهها» (Traits) میتوانند متدهای مجردی تعیین کنند که باید از سوی کلاسی که از آنها استفاده میکند، پیادهسازی شوند. با این حال یک مشکل وجود دارد. تا پیش از PHP 8 امضای این پیادهسازیهای متد اعتبارسنجی نمیشد. برای نمونه امضای زیر معتبر محسوب میشد:
PHP 8 اعتبارسنجی امضای متد را در زمان استفاده از یک خصیصه و پیادهسازی متدهای مجرد آن انجام میدهد. این بدان معنی است که این بار باید از کد زیر استفاده کنید:
پیادهسازی شیء ()token_get_all
تابع ()token_get_all یک آرایه از مقادیر بازگشت میدهد. یک کلاس PhpToken با یک متد ()PhpToken::getAll اضافه شده است. این پیادهسازی به جای مقادیر ساده با اشیا کار میکند. به این ترتیب حافظه کمتری مصرف میشود و خواندن آن نیز آسانتر است.
دستکاری ساختار متغیر
ساختار متغیر یکنواخت در نسخه جدید PHP چند مورد از ناسازگارهای را در این خصوص رفع کرده است. اینها مواردی بودند که در طی نسخههای قبل نادیده گرفته شده بودند.
حاشیهنویسی نوع برای تابعهای داخلی
بسیاری از افراد تقاضا داشتند که حاشیهنویسیهای نوع مناسبی را به همه تابعهای داخلی PHP اضافه کنند. این یک مشکل قدیمی بود و در نهایت با توجه به همه تغییرهایی که در نسخههای PHP صورت گرفته است، اکنون رفع شده است. این بدان معنی است که در نسخه جدید این زبان، تابعها و متدهای داخلی اطلاعات کاملی از نوع دادهها ارائه میکنند.
ext-json همیشه در دسترس است
تا پیش از این، امکان کامپایل PHP بدون فعالسازی پسوند JSON وجود داشت، اما این موضوع دیگر ممکن نیست. از آنجا که JSON کاربرد بسیار گستردهای دارد، بهتر است که توسعهدهندگان بتوانند به صورت پیشفرض روی آن تکیه کنند، تا این که مجبور باشند ابتدا آن را فعال کنند.
تغییرهای ناسازگار
همچنان که در ابتدای این مقاله بیان کردیم، نسخه 8 PHP یک نسخه major محسوب میشود و از این رو تغییرهای ناسازگاری با نسخههای پیشین معرفی کرده است. بهترین کاری که میتوانید در این خصوص انجام دهید، این است که نگاهی به فهرست این موارد در این سند (+) بیندازید. بسیاری از این تغییرهای ناسازگار در نسخههای قبلی 7.* منسوخ اعلام شدهاند. بنابراین اگر در طی سالهای اخیر کدتان را بهروز نگه داشتهاید، ارتقا به PHP 8 نباید کار دشواری باشد.
خطاهای یکنواخت نوع
تابعهای تعریف شده کاربر در PHP همواره TypeError ایجاد میکنند، اما تابعهای داخلی چنین نیستند، بلکه هشدارهایی ارائه کرده و null بازگشت میدهند. در نسخه PHP 8 این رفتار تابعهای داخلی طوری تغییر یافته که سازگاری بیشتری داشته باشد.
هشدارهای موتور مجدداً طبقهبندی شده است
در نسخه جدید PHP بسیاری از خطاهایی که قبلاً تنها هشدار یا اخطار ایجاد میکرد، به انواع مناسبی از خطا تبدیل شده است. بنابراین هشدارهای جدید تغییر یافتهاند:
- متغیر تعریف نشده (Undefined variable): به جای اخطار یک استثنای Error تولید میکند.
- اندیس تعریف نشده آرایه: به جای اخطار، هشدار ارائه میکند.
- تقسیم بر صفر: به جای هشدار یک استثنای DivisionByZeroError تولید میکند.
- تلاش برای افزایش/کاهش مشخصه '%s' غیر شیء: به جای هشدار یک استثنای Error تولید میکند.
- تلاش برای ویرایش مشخصه '%s' غیر شیء: به جای هشدار یک استثنای Error تولید میکند.
- تلاش برای انتساب کاهش مشخصه '%s' غیر شیء: به جای هشدار یک استثنای Error تولید میکند.
- ایجاد شیء پیشفرض از مقدار خالی: به جای هشدار یک استثنای Error تولید میکند.
- تلاش برای دریافت مشخصه '%s' غیر شیء: به جای اخطار یک هشدار تولید میکند.
- مشخصه تعریف نشده: %s::$%s: به جای اخطار، هشدار تولید میکند.
- امکان اضافه شدن عنصر به آرایه وجود ندارد، زیرا اندیس بعدی پر است: به جای هشدار یک استثنای Error تولید میشود.
- امکان لغو تعیین آفست در متغیر غیر آرایهای وجود ندارد: به جای هشدار یک استثنای Error تولید میشود.
- امکان استفاده از مقدار اسکالر به عنوان آرایه وجود ندارد: به جای هشدار یک استثنای Error تولید میشود.
- تنها آرایهها و Traversables میتوانند غیر فشرده شوند: به جای هشدار یک استثنای TypeError تولید میکند.
- آرگومان نامعتبری برای ()foreach عرضه شده است: به جای هشدار یک استثنای TypeError تولید میکند.
- نوع آفست غیرمجاز است: به جای هشدار یک استثنای TypeError تولید میکند.
- نوع آفست در isset غیر مجاز یا خالی است: به جای هشدار یک استثنای TypeError تولید میکند.
- نوع آفست غیر مجاز، غیر تعیین شده است: به جای هشدار یک استثنای TypeError تولید میکند.
- تبدیل آرایه به رشته: به جای اخطار یک هشدار ارائه میشود.
- منبع به شناسه #%d به عنوان آفست استفاده شده است و به عدد صحیح (%d) تبدیل شده است: به جای اخطار، هشدار ارائه میشود.
- تبدیل آفست رشته رخ داده است: به جای اخطار، هشدار تولید میشود.
- آفست رشته مقداردهی اولیه نشده است: به جای اخطار، هشدار تولید میشود.
- امکان انتساب رشته خالی به یک آفست رشته وجود ندارد: به جای هشدار یک استثنای Error تولید میشود.
- منبع ارائه شده یک منبع استریم معتبر نیست: به جای هشدار یک استثنای TypeError تولید میشود.
عملگر @ دیگر خطاهای fatal را خاموش نمیکند
این امکان وجود دارد که این تغییر موجب افشای خطاهایی شود که تا پیش از PHP 8 پنهان بودهاند. مطمئن شوید که TypeError را روی سرورهای پروداکشن خود تعیین کردهاید.
سطح گزارشدهی پیشفرض خطا
در نسخه جدید به جای هر چیزی به جز E_NOTICE و E_DEPRECATED از E_ALL استفاده میشود. این بدان معنی است که خطاهای زیادی ممکن است ظاهر شوند که قبلاً در حالت خاموش نادیده گرفته میشدند.
حالت خطای پیشفرض PDO
حالت خطای پیشفرض کنونی برای PDO حالت «خاموش» (silent) است. این به آن معنی است که اگر یک خطای SQL رخ بدهد، هیچ خطا یا هشداری صادر نمیشود و هیچ استثنایی ظاهر نمیشود، مگر این که توسعهدهنده مدیریت خطای صریح خود را پیادهسازی کند. در نسخه جدید سطح پیشفرض خطا برای PDO به PDO::ERRMODE_EXCEPTION تغییر یافته است.
تقدم الحاق
با این که این موضوع در PHP 7.4 منسوخ شده است، اما در نسخه 8 این زبان عملاً اعمال شده است. به این ترتیب اگر کدی مانند زیر بنویسید:
PHP آن را قبلاً به این صورت تفسیر میکرد:
اما در PHP 8 این کد به صورت زیر تفسیر خواهد شد:
بررسیهای صریحتر نوع برای عملگرهای حسابی و بیتی
تا پیش از PHP 8 امکان استفاده از عملگرهای حسابی و بیتی روی آرایهها، منابع و اشیا وجود داشت. این موضوع اینک منتفی شده است و خطای TypeError ایجاد میکند:
نامهای دارای «فضای نام» به یک توکن منفرد تبدیل میشوند
PHP قبلاً هر بخش از یک فضای نام را که با بکاسلش جدا میشود به صورت یک دنباله از توکنها تفسیر میکرد. اما این رفتار اکنون تغییر یافته و از این رو نامهای رزرو شده میتوانند در فضاهای نام استفاده شوند.
رشتههای عددی معقولتر
سیستم نوعبندی PHP تلاش میکند تا کارهای هوشمندانه زیادی در زمان مواجهه با اعداد در رشتهها انجام دهد. در نسخه جدید PHP تلاش شده تا این رفتار منسجمتر و روشنتر شود.
تبدیل رشته به عدد به روش معقولتر
در این تغییر تلاش شده تا حالت عجیب قبلی در PHP که 0 == "foo" منجر به نتیجه True میشود را اصلاح کند. برخی حالتهای خاص دیگر نیز وجود داشتند که در این نسخه اصلاح شدهاند.
تغییر امضای متد بازتابی
سه امضا متد کلاسهای بازتابی تغییر یافتهاند:
و به صورت زیر درآمدهاند:
اگر این کلاسها را بسط دهید و همچنان بخواهید از PHP 7 و PHP 8 پشتیبانی کنید، باید از امضای متدهای زیر استفاده کنید:
مرتبسازی پایدار
تا پیش از PHP 8 الگوریتمهای مرتبسازی غیر پایدار بودند. این بدان معنی است که ترتیب عناصر برابر تضمین نشده بود. PHP 8 این رفتار را در مورد همه تابعهای مرتبسازی تغییر داده است تا یک مرتبسازی با ثبات به دست آید.
خطای مهم برای امضاهای متد ناسازگار
خطاهای وراثت ناشی از امضاهای متد ناسازگار در نسخههای قبلی PHP بسته به علت خطا و سلسله مراتب وراثت یک خطای fatal یا یک هشدار ارائه میکنند.
موارد منسوخشده و تغییر یافته دیگر
در طی توسعه *.PHP 7 چند مورد منسوخ اضافه شده است که اکنون در PHP 8 نهایی شدهاند. این موارد شامل فهرست زیر میشوند:
- موارد منسوخ شده در PHP 7.2
- موارد منسوخ شده در PHP 7.3
- موارد منسوخ شده در PHP 7.4
- تبدیل نوع float به رشته وابسته به Locale
به این ترتیب به پایان این مقاله با موضوع آشنایی با تازههای PHP 8 میرسیم. امیدواریم موارد مطرح شده در این نوشته به ارتقای دانش شما مخاطبین در زمینه جدیدترین نسخه از زبان برنامهنویسی PHP کمک کرده باشد.