طی هفته گذشته با انتشار نسخه ۷.۴ زبان محبوب PHP، شاهد نوشته شدن مقالات بسیار خوبی از توسعه دهندگان این زبان بودیم; که شامل مثال های بسیار خوبی هم هستند و تمام فیچر های جدید رو به خوبی شرح دادند.
در این نوشته به طور کامل به شرح عملکردهای منسوخ شده (Deprecations) در این نسخه میپردازیم که شامل موارد زیر میشوند:
کلمه Deprecated در لغت به معنای منسوخ میباشد. در برنامه نویسی Deprecations یا منسوخ شده ها، عوامل بازدارنده ما برای استفاده از بخشی از کد هستند; چونکه دیگه موثر نیستند و یا مشکلات امنیتی بوجود میارند. این بخش میتونه شامل کلیدواژه ها، توابع و یا روش هایی که ما در کدهامون استفاده میکنیم باشه.
یک ویژگی منسوخ شده به احتمال زیاد در آینده نزدیک حذف خواهد شد.
اگر دقت کرده باشید یکی از انواع خطاهایی که در PHP داریم، Deprecated error یا خطای منسوخ شدگی هست، که این قابلیت از نسخه ۵ به زبان PHP اضافه شد و در واقع یک راهنما هست که به شما اطلاع میده که دستور، تابع و یا قطعه کدی که در PHP استفاده کرده اید در نسخههای بالاتر PHP منسوخ و حذف شده و شما می بایست تا قبل از به روز کردن به نسخه بالاتر به فکر جایگزین برای آن باشید... که معمولا با یک جستجوی ساده در سایت PHP میتوانید معادل جدید آن را پیدا کنید.
پس بسیار مهمه که این کلیدواژه ها و یا توابع رو در کدهامون علامت گذاری کنیم و اون ها رو ریفکتور کنیم.
فرض کنید یک وب سایتی فروشگاهی هنوز داره از الگوریتمهای هَش کردن چندین سال پیش استفاده میکنه. من به شخصه دوست ندارم اطلاعات مربوط به کارت اعتباریم یا هر گونه اطلاعات حساس دیگری رو در چنین سایتی وارد کنم و مطمئن هستم شما هم این کار رو نمیکنید.
الحاق (Concatenation) در زبان های برنامه نویسی به عملی گفته میشود که طی آن رشته هایی از کاراکترها و متغیرها به یکدیگر متصل میشوند. این عملگر یک عملگر دوتایی یا باینری به شمار میرود. در زبان PHP ما این عملگر رو با نماد نقطه (.) میشناسیم. ( نماد + در جاوا، جاوااسکریپت، سی پلاس پلاس)
برای توضیح تغییر اولویت در مورد این عملگر، لازمه با اصول اولیه شروع کنیم (دلیل طولانی شدن این قسمت هم همینه); فرض کنید قراره عبارت ریاضی زیر رو که جز ریاضیات پایه هست، حل کنیم.
1 + 3 x 5 = ?
مسلما براحتی به عدد ۱۶ میرسیم که مراحلش به شکل زیره:
در ریاضیات ما اصول ساده ای داریم که مشخص میکنه ابتدا چه عملی را باید انجام دهیم. اولویت انجام اعمال ریاضی به شرح زیر است:
به این سری از قواعد، قانون تقدم (Precedence) میگند که مشخص میکنه: هنگام ارزیابی یک عبارت ریاضی کدام عملگر باید زودتر انجام شود. (کدام عملیات ابتدا باید صورت بگیره -> در مثال ما اول ضرب و سپس جمع) .... همین قواعد در برنامه نویسی هم وجود دارند اما کمی پیچیده تر از چند ضرب و تقسیم ساده هستند. در واقع اعمال محاسباتی تنها یکی از چندین نوع عملگرها، در اکثر زبان ها هستند.
در کنار قانون تقدم، مفهوم دیگری هم داریم به نام شرکت پذیری (Associativity) که نحوه شرکت پذیری عملگرها رو در عبارات مشخص میکنه. شرکت پذیری در عملگرها به ۳ دسته زیر تقسیم میشود:
به طور مثال عملیات زیر رو در نظر بگیرید:
5 - 3 - 1 = x
عملگر تفریق از نوع left-associative (شرکت پذیر از چپ به راست) هست.. به همین خاطر ابتدا ۵ رو منهای ۳ میکنیم و سپس نتیجه رو منهای ۱ میکنیم. پس x برابر ۱ میشه.
از سوی دیگر عملگرهای انتسابی (مثلا =) از نوع right-associative (شرکت پذیر از راست به چپ) هستند. و عکس قضیه بالا اتفاق میفته. مثال زیر:
عملگر هایی مثل < ، > ، new و غیره جز عملگر های none-associative یا غیر شرکت پذیر هستند. عملگرهایی که غیر شرکت پذیر هستند و دارای تقدم یکسانی هستند، نمیتوانند در کنار هم استفاده شوند. به طور مثال عبارت زیر صحیح نیست:
راهنمایی رسمی PHP برای حل این مشکل استفاده از پرانتز است، که حتی اگر بهش نیازی هم نباشه قطعا به خوانایی بیشتر کدهای شما کمک خواهد کرد. مشکلی که وجود داره و تیم PHP تا حدودی اون رو حل کرده اینه که: عملگر الحاق تقدم یکسانی با عملگر جمع و تفریق داره. یعنی اگر سعی کنید در نسخه ۷.۳ PHP کد زیر رو اجرا کنید:
با چنین اخطاری روبرو میشید:
“Warning: A non-numeric value encountered”
ممکنه این خطا کمی عجیب به نظر برسه اما در واقع دلیل جالبی داره... علت چنین رفتاری اینه که عملگرهای ( + )، ( - ) و ( . ) هر سه عملگرهای شرکت پذیر از چپ به راست هستند; که تقدم یکسانی هم دارند.. پس PHP اون ها رو به شکل زیر تفسیر میکنه:
اول رشته رو به ۳ الحاق میکنه و بعد وقتی میخواد ترکیب به دست اومده رو با ۵ جمع کنه ... خوب نمیتونه; چون ترکیب به دست اومده دیگه یه عدد نیست که بخواهد با ۵ جمعش کنه. و در متن خطا هم میگه که "با یک مقدار غیر عددی برخورد کردم"
در نسخه های بعدی این مشکل حل خواهد شد.. اما تدبیر PHP در زمان حال و در نسخه ۷.۴ برای این مشکل اینه که بجای خطا، یه هشدار منسوخ شدگی به شما نمایش میده که بهتون میگه در صورت نیاز از پرانتز استفاده کنید. در نسخه ۸ زبان PHP تقدم این عملگر ها تغییر خواهد کرد و عملگر الحاق در سطح پایین تری نسبت به عملگرهای محاسباتی قرار خواهد گرفت.
در زیر لیستی از عملگرها رو به ترتیب تقدم اونها (از بالا به پایین) مشاهده میکنید. عملگرهایی که در یک سطر قرار دارند، دارای تقدم یکسان هستند:
اگر نمیدونید این عملگر چه کار میکنه، به طور خلاصه بگم که این عملگر مدل کوتاه شده یک if و else هست. شرط رو درش قرار میدید و بعد از ( ? ) حالت true تعریف میشه و بعد از ( : ) هم حالت false
Condition ? true : false ;
جالبه بدونید یه باگ در این عملگر وجود داره. اگر در قسمت false این عملگر، یک عملگر سهگانه دیگه بگذاریم و به صورت زنجیره ای این عملگرها رو با هم استفاده کنیم.. این باگ خودشو نشون میده. به مثال زیر دقت کنید:
در مثال بالا متغیر a موجود و برابر با ۱ هست، اما number همیشه حالت false این ارزیابی رو برمیگردونه (Five). در واقع پارسر PHP به این شکل عمل میکنه.
در زبان PHP برخلاف سایر زبان های برنامه نویسی ، همانطور که می توانید در تصویر موجود در بخش قبل هم مشاهده کنید، شرکت پذیزی عملگر ( ? ) از نوع چپ به راست هست و این نوع رفتار زیاد متداول نیست.
برای حل این مشکل و ناامید نکردن توسعه دهنده هایی که از زبان های دیگه میاند، با پیشنهاد نیکیتا پوپوف برای به روز کردن این ویژگی با یک فرایند دو مرحله ای موافقت شد. (این طرح با ۳۵ رأی موافق در برابر ۱۰ رأی مخالف به تصویب رسید)
در نسخه ۷.۴ استفاده از این نوع عملگر سه تایی بدون پرانتز های آشکار، هشدار منسوخ شدگی رو در بر خواهد داشت
و در نسخه ۸ قراره، خطای زمان اجرای مناسبی بهش اختصاص بدند.
اگر می خواهید از عملگرهای زنجیره ای سه گانه بدون مواجهه شدن با خطا استفاده کنید ، کاری که باید انجام دهید اینست که هر عملیات را در داخل پرانتز قرار دهید. به شکل زیر:
سال های زیادی هست که در مورد این ویژگی بحث میشه... در حقیقت قبل از نسخه ۵.۱ PHP این بحث وجود داشته.
یوهانس شلوتر عضو فعلی تیم مهندسی MySQL ، که قبل ها مدیر انتشار نسخه ۵.۳ PHP بود; در مورد عدم امکان استفاده از اکسپشن در مجیک متد toString__ چنین میگفت:
"... هیچ راهی برای اطمینان از این امر وجود ندارد که اکسپشنی که در طول کار به یک رشته منتقل می شود توسط Zend Engine به درستی هندل شود و این تغییر نخواهد کرد مگر اینکه قسمتهای بزرگی از این موتور بازنویسی شوند ..."
و در اون زمان این قضیه مشکل بزرگی بود. بحث رو کمی باز میکنیم: toString__ یک مجیک متده که PHP در اختیار ما میگذاره و به یک کلاس امکان تصمیمگیری درباره چگونگی رفتار در حین تبدیل به یک رشته رو میده. این متد زمانی فراخوانی میشه که آبجکت ایجاد شده از یک کلاس رو echo کنیم.
استفاده از اکسپشن داخل این متد در نسخه های زیر ۷.۴ ما رو با یه خطای جدی یا fatal error روبرو میکنه. دلیلش اینه که تبدیل رشته ها در نقاط زیادی داره انجام میشه (داخن خود اِنجین و یا کتابخونه های استاندارد) و تمام این نقاط در واقع آمادگی هندل کردن اکسپشن ها رو ندارند. راه حل این مشکل هم استفاده از یک کنترل کننده خطا (error handler) هست. دقیقا همون کاری که فریمورک Symfony انجام میده:
اما این تکنیک هم وابسته به پارامتر errcontext$ هست ، که از PHP ۸ به بعد دیگر در دسترس نخواهد بود. نیکیتا پوپوف پیشنهادش رو به این شکل مطرح کرده: اجازه بدیم متد toString__ از اکسپشن ها استفاده کنه و خطاهایی مثل “could not be converted to string” و “toString() must return a string value__” رو با اکسپشن های مناسب عوض میکنیم.
قبل از نسخه ۷.۴ PHP ، دستیابی به مقدار آرایه ها به ۲ روش انجام میشد.
درسته که هر دو روش کار میکردند، اما روش دوم کمتر متداول بود و به گفته آندری گروموف میتونه باعث سردرگمی در خواندن کد بشه. نکته خوبی که وی در پیشنهاد خود ارائه داد این بود که سینتکس آکولاد، تقریبا هیچ مستنداتی نداره و نسبت به سینتکس براکت از عملکردهای کمتری هم برخورداره. به طور مثال با سینتکس آکولاد نه میشه آرایه ایجاد کرد و نه میشه به عناصر آرایه اضافه کرد.
پیشنهاد گروموف شامل منسوخ شدن کلی سینتکس آکولاد میباشد و از این به بعد نمیتوان از آکولاد برای دسترسی به المان های آرایه و یا offset های در رشته ها استفاده کرد.
این اولین باری نیست که توسعه دهندگان PHP در این باره صحبت می کنند ، در واقع این ویژگی در PHP ۵.۱ منسوخ میشه اما قبل از انتشار رسمی مجدداً به هسته اضافه میشه. اما این دفعه قضیه متفاوته و این طرح با ۳۷ رأی موافق در برابر ۶ رأی مخالف به تصویب رسید.
در ۲ دهه گذشته این ویژگی چند بار حذف و دوباره اضافه شده. این ویژگی در طول انتشار نسخه ۷ زبان PHP مورد بحث و بررسی قرار گرفته. بعد از نسخه ۷ هم تگ های کوتاه هنوز هم موجودند و هنگامی که بخواهیم Good Practice ها رو رعایت کنیم، باعث مشکل میشند.برای استفاده نکردن از تگهای کوتاه دلایل مختلفی هست:
این مورد هم مانند سایر منسوخ شده ها، شامل یک فرایند دو مرحله ای میشه. در نسخه ۷.۴ استفاده از ویژگی، هشدارهای منسوخ شدگی رو در بر خواهد داشت... و در نسخه ۸ قراره، این ویژگی کاملا حذف بشه.
خبر خوب اینکه برای تغییر این تگ ها به تگ های کامل در کدهاتون، میتونید براحتی از PHP-CS-Fixer استفاده کنید
یکی از اهداف بزرگ PHP ۷ سادهسازی کدها هست که راه رو برای PHP ۸ هموار میکنه. یکی از راه های انجام این کار، حذف کردن تمام ویژگی هایی است که معمولاً مورد استفاده قرار نمی گیرند. یکی از این ویژگی ها نوع داده float هست.. نوع داده float در PHP دارای ۲ مستعار هم است (double & real).
نوع double هنوز به ندرت استفاده میشه اما نوع real دیگه عملا استفاده نمیشه. پس پیشنهاد منسوخ شدن نوع داده real و تابع ()is_real با ۳۵ رأی موافق در برابر ۷ رأی مخالف به تصویب رسید.
برای به روز رسانی کدها تنها کافیه: نوع داده real رو با float عوض کنید و تمام رفرنس ها به تابع ()is_real رو با تابع ()is_float عوض کنید
فکر میکنم دسته زیادی از خوانندگان این نوشته، چیز زیادی درباره مجیک کوتیشن ها نشنیده باشند. امروزه اکثر توسعه دهنده های زبان PHP برای کار کردن با اطلاعات موجود در بانک داده، عملا خودشون کوئری ها رو نمینویسند.. از ORM (Object Relational Mapper) هایی مثل Eloquent و یا Doctrine استفاده میکنند.
اما قبل از اینکه این ابزار ها بوجود بیاند، اگر شما در زبان PHP یک مبتدی بودید و میخواستید کوئری SQL بنویسید، برای فیلتر داده های ورودی کاربر میتونستید از مجیک کوتیشن ها استفاده کنید. هدف از چنین ویژگی غلبه بر SQL Injection بود. اما مشکل اینجاست که این توابع حملات مخرب به پایگاه داده ها رو متوقف نکردند و به همین خاطر هم این ویژگی مدتهاست که مورد انتقاد قرار میگیره. در نهایت این طرح با ۴۶ رأی موافق در برابر ۰ رأی مخالف به تصویب رسید.
ما در PHP تابعی به نام ()filter_var داریم که یه متغیر رو با یک پرچم (Flag) مشخص فیلتر می کنه. یکی از پرچم های موجود در این تابع "FILTER_SANITIZE_MAGIC_QUOTES" بود; که در واقع در پشت صحنه متد دیگری به نام ()addslashes رو فراخوانی میکرد.
همانطور که دیدیم مجیک کوتیشن ها در PHP ۷.۴ منسوخ شده اند. از اونجا که ما دیگه مجیک کوتیشن ها رو نخواهیم داشت ، منطقی خواهد بود که به طور قاطع حرکت کنیم و پرچم مورد نظر رو با پرچم دیگری به نام "FILTER_SANITIZE_ADD_SLASHES" جایگزین کنیم.
پیشنهاد مربوط به این منسوخ شدگی، توصیه میکنه اگر توسعه دهنده از این پرچم استفاده کرد; با یک هشدار منسوخ شدگی بهش بگیم: از پرچم جدید "FILTER_SANITIZE_ADD_SLASHES" استفاده کنه.
این تابع وجود یک کلید خاص رو در یک آرایه بررسی میکنه. اما تا پیش از این اگر یک آبجکت هم بهش پاس میدادید باز هم یک مقدار bool بر میگردوند. پس نتیجه هرگز قابل اعتماد نبود و باید مطمئن باشید آنچه به این تابع پاس میدهید واقعا یک آرایه ست...
پیشنهاد مربوط به این ویژگی توصیه میکنه: اگر کاربر آبجکتی رو به این تابع پاس داد، با یک هشدار منسوخ شدگی بهش بگیم: برای آبجکت ها باید از تابع ()property_exists استفاده کنه. در نهایت این طرح با وجود رأی های مخالف تصویب شد.
رفلکشن یه ابزار فوق العاده در PHP ست. رفلکشن کلاسی از پیش ساخته شده در PHP هست که به ما کمک میکنه اطلاعات خاصی (پراپرتیها، متدها، اکسپشنها) در مورد یک کلاس رو بازیابی کنیم.
یکی از اینترفیس های Reflection API اینترفیس Reflector هست. این اینترفیس توسط تمام کلاس های رفلکشن که قابل اِکسپورت هستند، پیاده سازی (implement) میشه و ۲ تا متد داره:
متد export کمی عجیب عمل میکنه و با وجود اینکه هیچ پارامتری رو دریافت نمیکنه، در تمام ساب کلاس ها پیاده سازی میشه. بسته به پارامتر دریافتی میتونه به عنوان یه Constroctor و یا یه متد ()toString__ عمل کنه.
همان طور که در قطعه کد بالا میبینید، عملکرد این متد میتونه به راحتی شبیهسازی بشه; در واقع استفاده از روش جایگزین، به جای متد ()export منطقی تر هم هست.
پیشنهاد مربوط به منسوخ شدگی این ویژگی، شامل حذف این متد از اینترقیس Reflector هست که با ۳۷ رأی موافق در برابر ۴ رأی مخالف به تصویب رسید.
این مورد یکی از منسوخ شدگی هاست که قراره هسته PHP رو تمیز تر کنه و امید هست که در بهبود سرعت هم اثرگذار باشه. توابع ()strrpos و ()mb_strrpos توابع کار با رشته ها هستند و کمک میکنند تکه خاصی از یک داده نوع رشته ای رو در درون یک رشته پیدا کنیم.
ساختار این تابع در نسخه های قبلی به شکل زیر بود:
mb_strrpos ( string $haystack , string $needle [, int $offset = 0 [, string $encoding = mb_internal_encoding() ]] ) : int
مشکلی که این پیشنهاد در تلاش است آن را حل کند اینه که در PHP ۵.۲ ، پارامتر انکودینگ به عنوان پارامتر چهارم به این تابع پاس داده میشد (پیش از آن پارامتر سوم بود که جای خودش رو به offset داد).
در نسخه ۷.۴ PHP پاس دادن پارامتر چهارم به این تابع هشدار منسوخ شدگی رو در بر خواهد داشت و در PHP ۸ این تابع تنها ۳ پارامتر رو دریافت خواهد کرد.
توابع ()implode و ()explode یک زوج بسیار جالب در PHP هستند. این ۲ تابع جز اولین مواردی بودند که من در PHP باهاشون آشنا شدم.
تابع ()implode دو پارامتر دریافت میکنه. اولی یه توکن هست (بهش glue هم میگن و معمولا کاراکتر جداکننده تک کاراکتری هست) و دومین پارامتر یه آرایه هست. این تابع داده های داخل آرایه رو با استفاده از توکن به همدیگه میچسبونه.
نکته جالب این بود که میتونستید جای پارامترهای اول و دوم این تابع رو عوض کنید و همه چیز هم کار میکرد. اما در نسخه ۷.۴ اگر توکن رو به عنوان پارامتر دوم ارسال کنیم، هشدار منسوخ شدگی رو در بر خواهد داشت. این پیشنهاد به این دلیل ارائه شد تا ثبات بیشتری بین این تابع و ()explode وجود داشته باشه (ترتیب پارامترها در هر دو تابع کاملا یکسان است).
کلوژر ها مفهوم جدیدی نیستند اما مسلما کمی غیرمتداول هستند..
برخلاف سایر زبانها، به عنوان مثال جاوااسکریپت که کلوژر ها میتونند به عنوان اسکوپ (Scope یا دامنه) تعریف بشند; در PHP یک کلاس کلوژر، یک کلاس ناشناس فراخواندنی ست که میتونیم پارامتر هامون رو بهش بایند کنیم. کلاسهای کلوژر برای استفاده از توابع ناشناس مورد استفاده قرار میگیرند. یک کلاس کلوژر چندین متد داره که بهش اجازه میده، توابع ناشناس رو بعد از ساخته شدن کنترل کنه.
در نسخه های قبل از ۷.۴، به راحتی میشد this$ رو از کلوژر آنبایند کرد.
$closure->bindTo(null);
در نسخه ۷.۴ PHP انجام این کار، هشدار منسوخ شدگی رو در بر خواهد داشت و در نسخه ۸ this$ همیشه در متدهای غیراستاتیک موجود خواهد بود.
این تابع برای تبدیل متن منطقی عبری به متن تصویری عبری استفاده میشد. جایگزین:
nl2br(hebrev($hebrewText));
این تابع برای تغییر مجموعه کاراکتر در رشتهها استفاده میشد. جایگزین:
این پیشنهاد با ۲۵ رأی موافق در برابر ۷ رأی مخالف به تصویب رسید.
این تابع رشته فرمت شدهای از ارز (currency) رو با توجه زبان مورد نظر بر میگردوند. جایگزین: Intl
یکی دیگر از توابع نسبتا قدیمی PHP که از نسخه ۴.۰.۲ موجود بوده. این تابع یک مقدار هَش شده رو که برای "EZMLM" نیاز بود، محاسبه میکرد. EZMLM دیگه پشتیبانی نشد و آخرین زمان انتشارش به بیش از ۱ دهه قبل برمیگرده.
این تابع برای بازیابی گزینه "include_path" به مقدار اصلیش، در فایل PHP.ini استفاده میشد. جایگزین:
این تابع یک دایرکتیو ini هست که اجازه میده در توابعی مثل ()include و ()require بتونیم URL وارد کنیم، که این کار مشکلات امنیتی خاص خودش رو به وجود میاره. اگر در حین استارتآپ مقدار این گزینه برابر با ۱ باشه، هشدار منسوخ شدگی رو خواهیم داشت.
این نوشته بسیار طولانی شد اما کاملا لازم بود، برای اینکه درک کنیم PHP داره به چه سمتی حرکت میکنه و اینکه این آپدیت ها چگونه باعث تغییرات در PHP ۸ خواهند شد; که نهایتا در روتین کاری توسعه دهندگان این زبان هم اثر میگذاره.
دیدگاه من اینه که PHP با حذف توابعی که استفاده نمیشند اما هنوز در هسته انجین وجود دارند، داره سبک میشه و به سینتکس روان تری میرسه.. تمام این موارد و بهبودهایی که در Performance این زبان بعد از نسخه ۷ شاهد بودیم، همه نوید از یک نسخه ۸ منسجم و قدرتمند رو میده که قراره در کامپایلرش از JIT (Just In Time Complilation) استفاده بشه و در انتهای ۲۰۲۱ منتشر میشه.