رفع عامل سکته Parse error: syntax error, unexpected T_OBJECT_OPERATOR
خطای Parse error: syntax error, unexpected T_OBJECT_OPERATOR یعنی مفسر PHP به عملگر -> رسیده، در جایی که دستور زبانِ نسخهی فعالِ PHP اجازهی آن را نمیدهد. در بیشتر موارد ریشهی ماجرا ناسازگاری نسخه است (کد برای PHP جدیدتری نوشته شده و سرور نسخهی قدیمیتری اجرا میکند) و در بقیهی موارد یک اشتباه تایپی ساده. راهحل هم به همین ترتیب است: یا نسخهی PHP همان دامنه را بالا ببرید، یا آن خط کد را بازنویسی کنید.
اول خطا را درست بخوانید
هر خطای Parse دقیقاً سه چیز به شما میدهد: نام توکنی که مفسر انتظارش را نداشت، مسیر فایل و شمارهی خط. نمونهی کامل معمولاً چنین شکلی دارد:
Parse error: syntax error, unexpected T_OBJECT_OPERATOR in /home/user/public_html/inc/config.php on line 42
T_OBJECT_OPERATOR نام داخلیِ همان عملگر -> است؛ یعنی چیزی که مفسر نتوانست هضم کند، خودِ فلشِ دسترسی به عضو شیء بوده. نکتهی مهم این است که خطای Parse در مرحلهی کامپایل رخ میدهد نه اجرا، بنابراین حتی یک خط از آن فایل هم اجرا نمیشود و بازدیدکننده فقط یک صفحهی سفید میبیند. همان اتفاقی که سالها پیش هنگام نصب اسکریپتهایی مثل IPS میافتاد و صفحهی installation بدون هیچ پیام قابلفهمی سفید میماند.
اگر display_errors روی سرور خاموش باشد حتی همین متن خطا را هم نمیبینید و فقط با یک صفحهی سفید طرف هستید. در آن حالت اول از بیرون مطمئن شوید که ماجرا واقعاً از کد است و پای DNS یا گواهی SSL در میان نیست؛ چکاپ رایگان سلامت سایت همین بررسی بیرونی را بدون نیاز به دسترسی سرور انجام میدهد. بعد سراغ فایل error_log بروید؛ متن کامل خطای Parse همیشه آنجا ثبت میشود.
خودِ متن پیام هم نسخه را لو میدهد. PHP 5.3 و پایینتر فقط نام خام توکن را مینوشتند: unexpected T_OBJECT_OPERATOR. از PHP 5.4 پیامهای Parse مفصلتر شدند و تا PHP 7.4 به شکل unexpected '->' (T_OBJECT_OPERATOR) در میآیند. PHP 8 هم نام توکن را کنار گذاشت و مینویسد unexpected token "->". پس اگر پیامی که میبینید فقط نام خام توکن است، تقریباً مطمئن باشید روی PHP 5.3 یا قدیمیتر هستید.
علت اول و شایعتر: ناسازگاری نسخهی PHP
تا پیش از PHP 5.4 نمیشد عملگر -> را مستقیماً روی شیئی گذاشت که همان لحظه با new ساخته میشود. این دو الگو روی PHP 5.3 و پایینتر دقیقاً همین خطا را تولید میکنند:
(new Mailer)->send();— ساخت شیء و صدا زدن متدش در یک خط(new Mailer)->name;— خواندن پراپرتی روی شیء تازهساخته
یک تصور غلط رایج را هم همینجا کنار بگذاریم: زنجیرهکردن روی خروجی یک تابع، یعنی getConfig()->get('db_host');، هیچوقت خطای نحوی نداده و روی PHP 5 هم معتبر است. آنچه PHP 5.4 اضافه کرد دسترسی به عضو روی new بود، بهعلاوهی آرایهایکردن خروجی تابع مثل foo()[0].
الگوی سوم مربوط به نسخههای خیلی جدیدتر است: عملگر nullsafe یعنی ?-> فقط از PHP 8.0 به بعد شناخته میشود و روی PHP 7.4 در همان نقطه خطای نحوی میدهد.
به همین دلیل بود که در آن دوره، بردن نسخه به PHP 5.4 یا بالاتر مشکل را حل میکرد. امروز هم منطق همان است و فقط اعداد جابهجا شدهاند: قالب یا افزونهای که برای PHP 8 نوشته شده، روی سروری که هنوز PHP 7 اجرا میکند به همین شکل میشکند. در سیپنل نسخهی PHP برای هر دامنه جداگانه تنظیم میشود، پس کاملاً ممکن است روی یک اکانت هاست اشتراکی لینوکس با کنترلپنل سیپنل یک دامنه سالم باشد و دامنهی کناریاش نه.
علت دوم: اشتباه تایپی واقعی
اگر نسخهی PHP مشکلی ندارد، دنبال یکی از اینها بگردید:
- جاافتادن علامت دلار: نوشتن
this->save();بهجای$this->save();. این مورد تا PHP 7.4 خطای Parse میدهد؛ روی PHP 8 از نظر نحوی معتبر شمرده میشود و به خطای زمان اجرا تبدیل میگردد. - تکرار عملگر بعد از یک ویرایش نیمهکاره:
$user->->name - قاطیشدن با عملگر استاتیک:
User::->find(1); - جاافتادن پرانتز دور
new: نوشتنnew Mailer->send();که در هیچ نسخهای معتبر نیست و باید(new Mailer)->send();باشد. از PHP 8.4 شکلnew Mailer()->send();هم پذیرفته میشود، اما پرانتزِ سازنده همچنان اجباری است. - عملگر رهاشده بدون نام عضو:
$user->;— ریشهاش همین ماجراست، هرچند پیام خطا در این حالت بهunexpected ';'تغییر میکند.
قاعدهی طلایی خواندن خطاهای Parse این است: مفسر خطی را گزارش میکند که در آن متوجه اشتباه شده، نه لزوماً خطی که اشتباه در آن است. اگر خط گزارششده کاملاً سالم به نظر میرسد، آخرین خط ناتهیِ قبل از آن را نگاه کنید؛ سمیکالن جاافتاده، پرانتز بستهنشده یا کوتیشن باز مانده معمولاً همانجاست.
تشخیص گامبهگام
- محدودهی خطا را ببینید:
sed -n '35,50p' /home/user/public_html/inc/config.php - فایل را فقط از نظر نحوی بررسی کنید (اجرا نمیشود):
php -l /home/user/public_html/inc/config.php - نسخهی خط فرمان را بگیرید:
php -v - نسخهای که وبسرور واقعاً اجرا میکند را جدا چک کنید؛ یک فایل موقت با محتوای
<?php phpinfo();بسازید و بلافاصله بعد از دیدن نسخه حذفش کنید؛ افشای پیکربندی سرور خطرناک است.
همین تفاوت میان PHP خط فرمان و PHP وبسرور یکی از رایجترین دامهاست: php -l ممکن است بدون خطا برگردد چون CLI روی نسخهی جدید است، در حالی که همان فایل از مرورگر میشکند. روی سیپنل هر نسخه باینری مستقل خودش را دارد؛ مستقیم صدایش بزنید تا دقیقاً نسخهی مقصد تست شود: /opt/cpanel/ea-php74/root/usr/bin/php -l /home/user/public_html/inc/config.php
راهحل ۱: تغییر نسخهی PHP همان دامنه
در سیپنل از بخش Software گزینهی Select PHP Version (در نسخههای جدیدتر MultiPHP Manager) نسخه را برای همان دامنه عوض کنید. در دایرکتادمین و پلسک هم معادل همین گزینه وجود دارد، اما نام منو و فهرست نسخههای در دسترس بسته به پیکربندی سرور و ارائهدهنده فرق میکند.
دو هشدار پیش از تغییر: اول اینکه بالا بردن نسخه میتواند بخشهای قدیمیتر همان سایت را بشکند، پس اول بکاپ بگیرید. دوم اینکه فهرست اکستنشنهای PHP برای هر نسخه جداگانه نگهداری میشود؛ بعد از سوییچ، ماژولهایی مثل mysqli، gd یا mbstring را دوباره فعال کنید وگرنه خطای Parse جای خودش را به خطای «کلاس یا تابع پیدا نشد» میدهد. اگر سرور مجازی یا اختصاصی خودتان را مدیریت میکنید، اجرای سیپنل روی آن نیازمند لایسنس اورجینال سیپنل است.
راهحل ۲: بازنویسی همان خط کد
اگر به هر دلیلی نمیتوانید نسخه را عوض کنید، خط را به دو خط بشکنید. بهجای (new Mailer)->send(); بنویسید $mailer = new Mailer; و در خط بعد $mailer->send();. برای عملگر nullsafe هم بهجای $user?->email یک شرط ساده با isset() بگذارید. نتیجهی هر دو یکسان است و روی نسخههای قدیمی هم اجرا میشود. پیش از هر ویرایشی یک کپی نگه دارید: cp config.php config.php.bak
اگر خطا بعد از سوییچ نسخه و اصلاح کد هم پابرجاست، معمولاً پای چیز دیگری در میان است: کشِ opcache که هنوز نسخهی قدیمی فایل را نگه داشته، یک .htaccess که نسخهی PHP را برای همان پوشه بازنویسی میکند، یا فایلی که با include از مسیر دیگری بارگذاری میشود. در این حالت پشتیبانی تخصصی سرور میتواند لاگ وبسرور و پیکربندی PHP را مستقیم بررسی کند.
جدول سریع توکنهای خطاهای Parse
| توکن در پیام خطا | معادل در کد | معمولاً یعنی |
|---|---|---|
T_OBJECT_OPERATOR |
-> |
دسترسی به عضو شیء در جای غیرمجاز یا نسخهی قدیمی PHP |
T_PAAMAYIM_NEKUDOTAYIM |
:: |
اشتباه در فراخوانی استاتیک یا ثابت کلاس |
T_VARIABLE |
$name |
سمیکالن جاافتاده در خط قبل |
T_DOUBLE_ARROW |
=> |
خطا در تعریف آرایهی انجمنی |
T_STRING |
نام تابع یا کلاس | کلمهی کلیدی غلط یا کوتیشن بستهنشده |
end of file |
پایان فایل | آکولاد یا پرانتز بستهنشده |
چطور دیگر تکرار نشود
- پیش از انتشار، فایلهای تغییرکرده را lint کنید. برای کل پروژه:
find . -name '*.php' -exec php -l {} \; - نسخهی PHP محیط توسعه را با سرور یکی نگه دارید؛ اختلاف نسخه بین لپتاپ و هاست منبع اصلی این خطاست.
- حداقل نسخهی موردنیاز اسکریپت را از مستندات یا کلید
requireدرcomposer.jsonبخوانید و با نسخهی سرور مقایسه کنید. - در محیط عملیاتی
display_errorsرا خاموش وlog_errorsرا روشن نگه دارید و خطاها را ازerror_logبخوانید؛ نمایش مسیر کامل فایلها به بازدیدکننده نشتی اطلاعاتی است. - فایلها را مستقیم روی سایت زنده ویرایش نکنید؛ یک اشتباه تایپی کافی است تا کل سایت سفید شود.
جمعبندی: نام توکن میگوید مفسر سرِ چه چیزی گیر کرده، شمارهی خط میگوید کجا را نگاه کنید و نسخهی PHP تعیین میکند آن نوشتار مجاز هست یا نه. با همین سه نشانه، هر خطای Parse در چند دقیقه رفع میشود.
