با سلام، در این جلسه در رابطه با مبحث Error handling یا مدیریت خطا صحبت خواهیم کرد. به طور خلاصه می توان گفت Error handling سعی دارد تا با پیش بینی و مدیریت خطاهای ممکن در سیستم ما از متوقف شدن یا به خطر افتادن برنامه ی ما جلوگیری کند.
با اینکه حالت های مختلفی برای مدیریت خطا در PDO موجود است اما مورد صحیح و استاندارد آن PDO::ERRMODE_EXCEPTION
است و شما باید همیشه از آن استفاده کنید.
بنابراین دو راه دارید: یا همیشه هنگام ساخت کانکشن خود، این دستور را هم به عنوان connection option وارد کنید و یا اینکه بعد از ساخت PDO از این خط استفاده نمایید:
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
تمام کاری که باید برای دریافت error reporting (گزارش خطاها) انجام دهید، همین است.
بر خلاف اشتباهی که در اکثر سایت های اینترنتی می بینید شما نباید برای گزارش (report) خطاها (error) آن ها را catch (منظور بنده اپراتور try..catch
است) کنید.
یک ماژول (مانند لایه ی پایگاه داده) نباید خطاهای خود را گزارش کند بلکه چنین کاری وظیفه ی site-wide handler است. در ضمن شما نیازی به نوشتن این handler ندارید و خود PHP دارای یک handler خوب است.
تنها کاری که ما باید انجام دهیم این است که خطاها را به صورت exception گزارش کنیم. تنها استثناء این قانون زمانی است که میخواهید یک نمونه از PDO بسازید.
اگر یادتان باشد در جلسات قبل گفتیم که این حالت ممکن است اطلاعات حساس شما را نمایش دهد. بنابراین راه حل زیر را ارائه دادیم (قسمت زیر، یادآوری از قسمت های قبلی است):
یادآوری: ... تا اینجا قسمت credentials (اعتبارات) و connection options (گزینه های اتصال) را توضیح دادیم. تنها قسمت باقی مانده در مثال ما، قسمت مدیریت خطا است که در کد زیر می بینید:
try { $pdo = new PDO($dsn, $user, $pass, $options); } catch (\PDOException $e) { throw new \PDOException($e->getMessage(), (int)$e->getCode()); }
چرا از این شیوه استفاده کرده ایم؟
اگر exception ای داشته باشیم که دریافت نشود (uncaught)، تبدیل به Fatal error در PHP خواهد شد. تا اینجای کار مشکلی نیست و ما به گزارش خطاها نیاز داریم تا بتوانیم مشکل برنامه را رفع کنیم اما این خطا شامل stack trace ای است که به خطا الصاق می شود.
این خطا در مورد PDO به این نحو عمل می کند که پارامتر های constructor را نیز به ما بر میگرداند؛ همانطور که می دانید این پارامتر ها همان اعتبارات ما (نام کاربری، رمز عبور و …) است.
حتما می گویید که ما گزارش خطا (error reporting) را در وب سایت های واقعی غیر فعال می کنیم، بنابراین اعتبارات ما به کسی نشان داده نمی شود.
حرف شما کاملا درست است و ما فقط برای محکم کاری، exception مورد نظر را میگیریم (catch) و دوباره پرتاب (throw) می کنیم تا حتی کوچکترین احتمالی هم از نمایان شدن اعتبارات ما برای کسی وجود نداشته باشد. [پایان یادآوری]
قسمت بالا، یادآوری از قسمت های قبلی بود و باید توجه داشته باشید که لازم نیست تمام عملیات های PDO خود را داخل try/catch
قرار دهید، بلکه catch کردن یک exception باید در مواقع خاصی انجام بگیرد نه بدون حساب و کتاب.
به عبارت دیگر exception ها در PDO هیچ چیز خاصی ندارند و تنها خطا هستند بنابراین شما نیز باید با آن ها مانند بقیه ی خطاها برخورد کنید.
به طور مثال اگر از قبل یک handler برای خطاها داشته اید نباید یک handler دیگر به طور اختصاصی برای PDO بسازید. از طرفی اگر handler نداشته اید و اهمیتی نداده اید، مشکلی نیست چرا که خودِ PHP یک handler خوب در اختیار ما قرار میدهد که خطاها را به خوبی گزارش می کند.
یکی از مشکلات دوره های آموزشی PDO در اینترنت (حتی در منابع خارجی) مبحث error handling است.
زمانی که توسعه دهندگان برای بار اول با exception ها در PDO آشنا می شوند، تصور می کنند که exception ها تنها مخصوص همین زمینه هستند و به همین خاطر فقط در همین زمینه exception ها را مدیریت می کنند. اما همانطور که گفتیم، exception ها مسئله ی خاصی نیستند و فقط یک نوع خطا هستند مانند بقیه ی خطاها، بنابراین اگر از قبل با exception ها کار نمی کردید چرا باید الان این کار را به این شدت انجام دهید؟
توجه داشته باشید که ما نمی گوییم handle کردن خطاها و exception ها بد است، بلکه می گوییم هر چیزی جایی دارد و باید آگاهانه انجام شود.
در وب سایت رسمی PHP آمده است که:
If your application does not catch the exception thrown from the PDO constructor, the default action taken by the zend engine is to terminate the script and display a back trace. This back trace will likely reveal the full database connection details, including the username and password.
ترجمه: اگر برنامه ی شما exception ای را که توسط PDO constructor به آن پاس داده می شود، دریافت نکند موتور zend اسکریپت شما را متوقف کرده و back trace را نمایش خواهد داد. این back trace به احتمال زیاد اطلاعات اتصال به پایگاه داده ی شما (شامل نام کاربری و رمز عبور) را نمایان خواهد کرد.
این جمله به شدت گنگ نوشته شده و باعث سردر گمی بسیاری از برنامه نویسان شده است. در واقع اصلا چیزی به نام نمایش دادن back trace نداریم! کاری که موتور Zend انجام می دهد این است که exception ای را که catch نشده باشد تبدیل به یک fatal error (باید از دوره های مقدماتی PHP با این مبحث آشنا باشید) می کند.
سپس این fatal error مانند هر خطای دیگری رفتار می کند و فقط هنگامی که دستورات مربوطه در فایل php.ini
نوشته شده باشد نمایش داده می شود. بنابراین شما چه exception را دریافت کنید و چه نکنید، این مبحث ربطی به نمایان شدن اطلاعات حساس ندارد چرا که تنظیمات configuration کاملا متفاوتی دارد. بنابراین به جای catch کردن exception ها در PDO، سرور خود را به طور صحیح تنظیم کنید.
در حالت توسعه (زمانی که در حال کدنویسی برای سایت هستید) نمایش خطاها را در فایل php.ini روشن کنید:
ini_set('display_errors', 1);
در حالت تولید (زمانی که وب سایت برای بازدید عموم باز است و دارد کار می کند) می توانید نمایش خطاها را خاموش کرده اما خطاهای لاگین را روشن بگذارید:
ini_set('display_errors', 0); ini_set('log_errors', 1);
دلیل این کار این است که برخی از خطاها باید به کاربر نمایش داده شوند. البته در دنیای وب فارسی که بسیاری از کاربران انگلیسی بلد نیستند و یا حداقل با اصطلاحات وب سایت ها آشنایی ندارند بهتر است برای خطاهای لاگین یک handler جداگانه به زبان فارسی بنویسید تا کاربر سر در گم نشود.
سوال بسیار مهم این است که این توصیفات چه مواقعی برای catch کردن exception ها مناسب است؟
شما تنها در دو موقعیت می توانید exception های PDO را catch کنید:
try { $pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data); } catch (PDOException $e) { $existingkey = "Integrity constraint violation: 1062 Duplicate entry"; if (strpos($e->getMessage(), $existingkey) !== FALSE) { // Take some action if there is a key constraint violation, i.e. duplicate name } else { throw $e; } }
اما در حالت کلی هیچ نیازی به نوشتن کد خاص یا برخورد خاص با exception ها در PDO نیست. به طور خلاصه برای گزارش خطاها به صورت صحیح در PDO:
PDO::ERRMODE_EXCEPTION
است).try..catch
برای گزارش خطاها استفاده نکنید.display_errors=off
و log_errors=on
(البته با توجه به مسئله ای که برای کاربران فارسی زبان گوشزد شد).display_errors=on
E_ALL
استفاده شود.در این قسمت در رابطه با مدیریت خطاها و exception ها و همچنین ساختار های try..catch
صحبت کرده و ابهامات و اشکالات پیرامون این مسائل را باز کردیم. امیدوارم از این قسمت لذت برده باشید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.