هنگامی که قصد داریم در PHP به یک پایگاه داده دسترسی داشته باشیم، برای ما دو راه وجود دارد:MySQLi
و PDO
. بنابراین چه چیزهایی را قبل از انتخاب یکی از اینها باید دانست؟ تفاوت ها، پشتیبانی از پایگاه داده، پایداری و نگرانی هایی که در این پست ذکر می شود.
خلاصه
نوع | POD | MySQLi |
---|---|---|
پشتیبانی از پایگاه داده | 12 درایور مختلف | فقط MySQL |
API | شی گرا | شی گرا + رویه ای |
ارتباط | ساده | ساده |
نام پارامترها | بله | خیر |
نقشه برداری شی | بله | بله |
اظهارات آماده | بله | خیر |
کارایی | بللل | سریع |
روش های ذخیره کردن | بله | بله |
ارتباط
برای ارتباط با دیتابیس، باید روش های زیر بصورت سهولت انتخاب شود:
// PDO
$pdo=newPDO("mysql:host=localhost;dbname=database",'username','password');
// mysqli, procedural way
$mysqli=mysqli_connect('localhost','username','password','database');
// mysqli, object oriented way
$mysqli=newmysqli('localhost','username','password','database');
توجه داشته باشید که از این ارتباط ها (connection) و منابع (resources) در ادامه ی آموزش استفاده می شود.
پشتیبانی از API
هر دوی PDO
و MySQLi
در واقع API
های شی گرا ارائه می دهند، اما MySQLi همچنین API های رویه ای نیز ارائه می دهد. – که باعث می شود برای تازه واردان درکش آسان باشد. اگر شما با درایور های PHP MySQL آشنا باشید، خواهید دید که مهاجرت به MySQLi رویه ای چقدر آسان است. از سوی دیگر، هنگامی که شما در PDO استاد شدید، می توانید به راحتی از آن با هر دیتابیسی که میلتان است استفاده کنید!
پشتیبانی از پایگاه داده
مزیت اصلی PDO
نسبت به MySQLi
در پشتیبانی از درایور دیتابیس است. در این لحظه که در حال نوشتن این پست هستم، PDO از 12 درایور مختلف پشتیبانی می کند، MySQLi
هم تنها از MySQL
پشتیبانی می کند.
برای چاپ یک لیست از تمام درایورهایی که PDO در حال حاظر از آنها پشتیبانی می کند، از کد زیر استفاده کنید:
var_dump(PDO::getAvailableDrivers());
این به چه معنی است؟ خوب، در شرایط خاصی که می خواهید پروژه خود را به یک دیتابیس دیگر سوئیچ کنید، PDO باعث می شود که فرایند روشن و واضح باشد. بنابراین تمام کاری که لازم دارید این است که رشته ی اتصال و چند
query
را تغییر دهید – در صورتی که آنها از متدی استفاده می کنند که دیتابیس جدید شما از آن پشتیبانی نمی کند، با MySQLi نیاز دارید که هر تکه از کدها را بازنویسی کنید.
نام پارامترها
این یکی دیگر از ویژگی ها مهمی است که PDO دارد. پارامترهای اتصال بطور قابل توجهی ساده تر از استفاده از اتصال عددی:
$params=array(':username'=>'test',':email'=>$mail,':last_login'=>time()-3600);
$pdo->prepare('
SELECT * FROM users
WHERE username = :username
AND email = :email
AND last_login > :last_login');
$pdo->execute($params);
… نسبت به روش MySQLi است:
$query=$mysqli->prepare('
SELECT * FROM users
WHERE username = ?
AND email = ?
AND last_login > ?');
$query->bind_param('sss','test',$mail,time()-3600);
$query->execute();
شاید با دیدن پارامتر علامت سوال فکر کنید این راه کوتاه تری است، اما نسبت به نام پارامترها انعطاف پذیری ندارد. با توجه به این واقعیت که توسعه دهنده ها همیشه باید ترتیب پارامترها را نگه دارند، در برخی از شرایط قابل هک می باشد.
متاسفانه، MySQLi از نام پارمترها پشتیبانی نمی کند.
نقشه برداری شی
هر دوی PDO
و MySQLi
می توانند نتایج را به شی تبدیل کنند. این وقتی می آید که شما نمی خواهید از یک لایه ی انتزاعی پایگاه داده سفارشی استفاده کنید، در حالی که می خواهید رفتارش شبیه به ORM
باشد. فرض کنید که ما یک کلاس User
داریم که تعدای خواص (properties
) دارد، که نام فیلد ها از یک دیتابیس انتخاب شده است:
classUser{
public $id;
public $first_name;
public $last_name;
publicfunctioninfo()
{
return '#'.$this->id.': '.$this->first_name.' '.$this->last_name;
}
}
بدون نقشه برداری شی، ما نیاز داریم که مقدار هر فیلد را قبل از اینکه بتوانیم از متد ()info
به درستی استفاده کنیم پر کنیم (یا به صورت دستی و یا از طریق سازنده).
این به ما اجاره می دهد که این خواص را قبل از اینکه شی از روی آنها ساخته شود از قبل تعریف کنیم، به عنوان مثال:
$query = "SELECT id, first_name, last_name FROM users";
// PDO
$result =$pdo->query($query);
$result->setFetchMode(PDO::FETCH_CLASS,'User');
while($user=$result->fetch()){
echo$user->info()."\n";
}
// MySQLI, procedural way
if ($result=mysqli_query($mysqli,$query)){
while ($user=mysqli_fetch_object($result,'User')){
echo $user->info()."\n";
}
}
// MySQLi, object oriented way
if($result=$mysqli->query($query)){
while($user=$result->fetch_object('User')){
echo$user->info()."\n";
}
}
امنیت
بیایید فرض کنیم که یک هکر می خواهد تعدادی SQL
مخرب از طریق پارامتر username
وارد تزریق کند:
$_GET['username']="'; DELETE FROM users; /*"
اگر ما در این بخش شکست بخورم، هکر می تواند query هایی مخرب مثل پاک کردن تمام رکورد های جدول users
وارد کند (هر دوی PDO و MySQLi از query های چندگانه پشتیبانی می کنند).
// PDO, "manual" escaping
$username=PDO::quote($_GET['username']);
$pdo->query("SELECT * FROM users WHERE username = $username");
// mysqli, "manual" escaping
$username=mysqli_real_escape_string($_GET['username']);
$mysqli->query("SELECT * FROM users WHERE username = '$username'");
همانطور که می بینید، ()PDO::quote
نه تنها از رشته ها فرار (escape) می کند، بلکه آنها را نقل قول هم می کند. از طرف دیگر، ()mysqli_real_escape_string
تنها از رشته ها فرار می کند; شما نیاز دارید که بطور دستی روش نقل قول را اعمال کنید.
// PDO, prepared statement
$pdo->prepare('SELECT * FROM users WHERE username = :username');
$pdo->execute(array(':username'=>$_GET['username']));
// mysqli, prepared statements
$query=$mysqli->prepare('SELECT * FROM users WHERE username = ?');
$query->bind_param('s',$_GET['username']);
$query->execute();
من پیشنهاد می کنم که همیشه از دستورات آماده با پرس و جوی محدود بجای ()PDO::quote
و()mysqli_real_escape_string
استفاده کنید.
کارایی
در حالی که هر دوی PDO و MySQLi سریع هستند، MySQLi به طور قابل توجهی در معیار سریع تر است – %2.5~
برای دستورات غیر آماده، و %6.5~
برای آنهایی که آماده است. با این حال، MySQLخالص حتی از این دو هم سریع تر است. بنابراین اگر شما واقعا زیر فشار هر زمان از عملکر هستید، پس این چیزی است که باید در نظر داشته باشید.
خلاصه
در نهایت، PDO برنده ی این مسابقه می شود. با پشتیبانی از 12 درایور مختلف دیتابیس، (18 درایور مختلف دیتابیس!)، نام پارامترها، می توانیم عملکرد های کوچک را نادیده بگیریم و API ها. از نقطه نظر امنیتی، هر دوی آنها امن هستند تا زمانی که یک توسعه دهنده قرار است از آنها در راهی استفاده کند. بنابراین اگر هنوز از MySQLi استفاده می کنید، شاید زمان تغییر است.