گزینه های اتصال به پایگاه داده - connection options

04 بهمن 1397
درسنامه درس 3 از سری آموزش PDO
PDO-connection-options

با سلام، در قسمت قبل در رابطه با مباحث اولیه ی اتصال با PDO صحبت کردیم. قرار بر این شد که بر اساس یک مثال جلو رفته و توضیحات را ارائه کنیم بنابراین در این جلسه به ادامه ی بحث های قسمت اول می پردازیم.

ساخت DSN برای اتصال

مثالی که در جلسه ی قبل به شما ارائه دادیم، کد زیر بود:

$host = '127.0.0.1';
$db   = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
try {
     $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
     throw new \PDOException($e->getMessage(), (int)$e->getCode());
}

در جلسه ی قبل در مورد credentials توضیحاتی ارائه دادیم و تک تک موارد را کامل کردیم. در این قسمت به سراغ connection options می رویم.

connection options در PDO

در قسمت بعد از credentials باید آرایه ای داشته باشیم که ما نام آن را options گذاشته ایم. این آرایه شامل آپشن ها (موارد دلخواه) ای است که یا بسیار مهم هستند و یا تجربه ی کاری شما با PDO را بسیار بهتر می کنند. مواردی که در مثال بالا آورده ایم به ترتیب شامل آپشن های زیر هستند:

  • PDO::ATTR_ERRMODE : این یک آپشن بسیار ضروری است که همیشه باید روی PDO::ERRMODE_EXCEPTION تنظیم شده باشد. این گزینه به PDO می گوید هر زمانی که query ما به خطایی برخورد کرد، یک exception را throw کند. از شما انتظار می رود با مباحث throw/exception آشنا باشید. اگر نمی دانید این ها چه هستند در اینترنت سرچی بزنید. به طور خلاصه thow (به معنی پرتاب) و exception (به معنی استثنا) هستند و هنگام کار با خطا ها با آن ها کار می کنیم. اگر این آپشن به همین صورتی که گفته شد تنظیم شده باشد دیگر نیازی نیست که ما مانند mysql_query به صورت دستی کدی بنویسیم تا خطا را به ما نشان دهد. این موضوع به ما کمک بسیاری در حل مشکلات سیستم می کند.
  • PDO::ATTR_EMULATE_PREPARES : این گزینه به PDO می گوید که emulation mode را روشن کند یا خیر. این موضوع را در آینده مورد بحث قرار خواهیم داد اما باید بدانید که اکثر برنامه نویسان و محققان اعتقاد دارند به غیر از مواردی خاص، اگر از نسخه های PHP و MySQL به روز استفاده می کنید بهتر است این حالت را خاموش کنید، بعدا می توانید با استفاده از ()PDO::setAttribute در هنگام اجرا آن را به صورت دستی روشن کنید بنابراین جای نگرانی نیست. ما در این مثال این حالت را خاموش کرده (مقدار false) و بعدا توضیحات بیشتری ارائه خواهیم کرد.
  • PDO::ATTR_DEFAULT_FETCH_MODE : این گزینه تنها برای راحتی توسعه دهنده استفاده می شود. با اینکه همیشه می توانید هنگام صدا زدن تابع fetch آن را تعیین کنید (مثال: ;$row = $stmt->fetch(PDO::FETCH_ASSOC)) راحت تر است که از ابتدا آن را برای تمام حالت ها تنظیم کرده و سپس در هنگام کد نویسی، آن را برای موارد خاص تغییر دهید. همچنین هنگام استفاده از foreach جایی برای تعیین fetch mode نداریم، بنابراین بهتر است از همان ابتدا آن را تنظیم کنیم. مشهور ترین گزینه ها برای این آپشن، PDO::FETCH_ASSOC و PDO::FETCH_OBJ هستند که به ترتیب ردیف (row) های به دست آمده را در آرایه های متناظر (associative array) یا اشیاء(objects) تحویل می دهند.

مدیریت خطاها (Handling errors)

تا اینجا قسمت 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

تا به اینجای کار یک DSN ساختیم. پس از اینکه تمام این موارد را انجام دادیم، برای اتصال به پایگاه داده، باید از کلاس PDO یک شیء یا نمونه ایجاد می کردیم. برای ساخت این شیء به چهار پارامتر نیاز داشتیم که DSN پارامتر اول آن بود. پارامتر های بعدی user$ و pass$ و options$ بودند که در مثال بالا تعریف شدند. ما برای ساخت این شیء از روش همیشگی و ساده ی ساخت اشیا با کلید new استفاده نکردیم و دلیل آن را در قسمت مدیریت خطاها (Handling errors) توضیح دادیم. بنابراین برای ساخت این شیء، آن را درون یک ساختار try..catch قرار دادیم:

try {
     $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
     throw new \PDOException($e->getMessage(), (int)$e->getCode());
}

نباید ها

ما کارهایی که باید انجام دهیم را ذکر کردیم، اما کار هایی نیز وجود دارند که هیچ گاه نباید انجام شوند:

  • PDO::ATTR_PERSISTENT : متاسفانه این آپشن به صورت کیلویی در آموزش های آنلاین استفاده می شود! اما این گزینه نه تنها مزیتی برای وب سایت های کوچک و عادی ندارد، بلکه معایب بسیاری را شامل می شود (توضیح این معایب از موضوع این جلسه خارج است). تنها زمانی از این گزینه استفاده کنید که تحقیقات بسیاری روی آن انجام داده اید و آن را برای کار خود مناسب دیده اید.
  • استفاده از ()die و echo، یا هر نوع از اپراتور های خروجی، برای گرفتن exception ها: هیچ گاه پیام های خطا را بدون شرط به نمایش در نیاورید. همیشه از handler عمومی سایت استفاده کنید.

خلاصه ی این جلسه

با جمع زدن مطالب این جلسه و جلسه ی قبل، می توانیم یک اتصال خوب و فنی به پایگاه داده داشته باشیم. موارد کیفی و امنیتی در این اتصال نیز ذکر شد و امیدواریم از آن استفاده ی کافی برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش PDO توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (3 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Alireza
16 اسفند 1398
سلام و ممنون بابت آموزشتون، سوال من اینه که آیا placeholder ها ثابت باشن و توسط define تعریف بشن مشکلی داره؟ چون من توابع مربوط به دیتابیس رو در فایلی جدا با پسوند .php ذخیره کردم به این صورت: define("DRIVER", "mysql"); define("DBHOST", "localhost"); و ... به بتونم از فایل های دیگه به این مقادیر دسترسی داشته باشم. ممنون میشم راهنمایی کنید. تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
26 اسفند 1398
سلام دوست عزیز من از متغیر ها استفاده کردم اما برای ثابت ها تا حالا بررسی نکردم. با این حال فکر نمی کنم هیچ مشکلی باشه. شما با دستور require_once اون فایل (فایل حاوی ثابت ها) رو وارد فایل اصلی کنید و بعدش از این مقادیر استفاده کنید. اگر مشکلی باشه PHP به شما خطا میده و اگر نه، باید کوئری بدون خطا اجرا بشه.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

زهرا
12 بهمن 1398
سلام ممنون از مطالب بسیار روان و قابل وفهمتون. من وقتی میخوام به دیتا بیس وصل بشم اینپیام برام ظاهر میشه:connection failedcould not find driver مشکل از کجاست؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
13 بهمن 1398
سلام دوست عزیز، این خطا میگه driver مورد نظر برای شما فعال نیست. مثلا اگر از PDO استفاده می کنید باید مطمئن بشید که ماژول pdo_mysql روی سرور شما فعال هست (از فایل php.ini می تونین کمک بگیرین). همچنین سعی کنید PHP خودتون رو آپدیت کنید. همچنین به فایل php.ini برید و قسمت زیر رو از حالت کامنت خارج کنید (اگر کامنت شده): extension=php_pdo_mysql.dll در صورتی که مشکل حل نشد با hosting خودتون تماس بگیرید.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

میلاد
13 فروردین 1398
خیلی ممنون بابت توضیحات روان و فوق العادتون استفاده از ()die و echo، یا هر نوع از اپراتور های خروجی، برای گرفتن exception ها: هیچ گاه پیام های خطا را بدون شرط به نمایش در نیاورید. همیشه از handler عمومی سایت استفاده کنید. این بخش رو درست متوجه نشدم.چرا نباید از die استفاده کرد؟؟؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
15 فروردین 1398
سلام دوست عزیز، از طرف شما ممنونم. در وب سایت رسمی PHP و خیلی از آموزش های PDO کدی شبیه به کد زیر رو میبینین: try { $stmt = $pdo->query($sql); } catch (PDOException $e) { die($e->getMessage()); } (اینجا راست چین هست، شما میتونین چپ چین شده و مرتب شده اش رو اینجا ببینید: https://jsbin.com/wizesow/1/edit?html,output ) این کد تا وقتی که برای development (مرحله ی توسعه و ساختن وب سایت، نه استفاده ی واقعی از سایت) باشه مشکلی نداره اما زمانی که می خوایم کد ها رو روی سرور بزاریم و همه ی مردم ازش استفاده کنن مشکل دار میشه! مشکل اینجاست که اگر از این نوع دستور های گرفتن خروجی استفاده کنید، خطای شما مستقیما روی صفحه و برای همه چاپ میشه! این یک مشکل بزرگ امنیتی هست که ممکنه اطلاعات حساس مربوط به سرور شما رو لو بده. راه های خیلی بهتری برای کار با exception ها وجود داره (مثل استفاده از Wrapper هایی که در گیت هاب و ... هست). اگر زمانی خواستید از این دستورات استفاده کنید (گرچه که پیشنهاد نمیشه، چون دردسر داره) باید مکانیسمی بچینید که دستورتون فقط برای شما به نمایش در بیاد و به هیچ عنوان روی صفحه نمایش داده نشه.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

yasin
18 فروردین 1399
سلام آموزش بسیار خوبی بود و یک نکته اگر این گونه از Exception ها استفاده شود مشکلی داره؟؟ catch (PDOException $e){ if ($e->getCode()==1045){ echo 'Error Number 1 '; } if ($e->getCode()==1049){ echo 'Error Number 2 '; } if ($e->getCode()==2002){ echo 'Error Number 3 '; } }

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
27 فروردین 1399
سلام دوست عزیز، این روش از لو رفتن اطلاعات جلوگیری می کنه و مشکلی نداره اما خودتون ممکنه درست حسابی متوجه خطا ها نشید. بهتره یه سیستم داشته باشید که خطا ها روی سرور خودتون log بشه (درون یک فایل متنی نوشته بشه). کارش یه تابع ساده هست که خطا ها رو توی یک فایل بنویسه.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.