PDA

View Full Version : حرفه ای: هشدار جدی در مورد توابع دیتابیس php و اموزش PDO



cooper47
دوشنبه 31 تیر 1392, 04:59 صبح
سلام و خسته نباشید،


در وب سایت PHP: Hypertext Preprocessor (http://php.net/) هشدار جدیدی در مورد توابع mysql_* ( مانند mysql_connect , mysql_close , mysql_query و ... ) داده شده که واقعا نیازه اونو جدی بگیرید


تمام این توابع کنار گذاشته شده اند و در ورژن های بعدی حذف خواهند شد

و به زودی تمام اسکریپت هایی که از این توابع استفاده میکنند از کار خواهند افتاد !
پس این تایپک رو جدی بگیرید


متن هشدار خود سایت


This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi (http://www.php.net/manual/en/book.mysqli.php) or PDO_MySQL (http://www.php.net/manual/en/ref.pdo-mysql.php) extension should be used. See also MySQL: choosing an API (http://www.php.net/manual/en/mysqlinfo.api.choosing.php) guide and related FAQ (http://www.php.net/manual/en/faq.databases.php#faq.databases.mysql.deprecated) for more information.همانطور که ذکر شده 2 راه جایگزین پیشنهاد شده

1-استفاده از mysqli

2-استفاده از pdo


خب در سایت های مختلف مقایسه هایی انجام شده که بنده از سایت Web development tutorials, from beginner to advanced | Nettuts+ (http://net.tutsplus.com/) یک مقایسه رو برای شما بیان میکنم


PDO
MySQLi

پشتیبانی از دیتابیس
12 نوع مختلف
تنها mysql

API
شی گرای
شی گرایی + توابع

مکان های اسمی
بله
خیر


در مورد تفاوت های دیگر با توابع قدیمی mysql در همین تایپک به بحث میپردازم


همچنین ابتدا اموزش PDO رو بیان خواهم کرد و سپس در صورت نیاز MySQLi

با این وجود اگر به هیچ کدام اشنایی نداریم پیشنهاد میکنم حتما از PDO استفاده کنید



تمام مطالب توسط خود بنده نوشته شد است با استفاده از 3 منبع که در پایان ذکر میکنم

کپی برداری از مطالب با ذکر لینک تایپک بلامانع است

cooper47
دوشنبه 31 تیر 1392, 05:01 صبح
اتصال به دیتابیس

PDO از 12 نوع مختلف دیتابیس پشتیبانی میکند به شرطی که درایور ان نصب باشد،

با این وجود چون قصد جایگزینی توابع mysql_* رو داریم، دیتابیس mysql رو بررسی خواهیم کرد




$db = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);


در هنگام ساخت شی PDO یک ارگومان متنی به آن میدهیم که در مثال بالا "mysql:host=$host;dbname=$dbname", $user, $pass می باشد


که host هست ما و متغیر های user pass یوزر و پسورد دیتابیس هستند


دقت کنید تمام توابع با متغیر $db در اموزش بیان میشود و اگر با یک نام دیگر این شی رو ایجاد کردید باید با اون نام توابع رو استفاده کنید


قطع اتصال دیتابیس


برای قطع اتصال تنها کافی هست شی را از بین ببرید و یا مقدار جدید به ان بدهید





unset($db);

$db = null


با یکی از دستورات بالا دیتابیس بسته خواهد شد.

cooper47
دوشنبه 31 تیر 1392, 05:03 صبح
تنظیمات خصوصیات دیتابیس

با استفاده از متد setAttribute میتوانید خصوصیات مورد نظر خود را تنظیم کنید،

این که روش خوبی هست که قبل از کار با دیتابیس خصوصیات رو تنظیم کنید

بعضی از این خصوصیات در لینک PHP: PDO::setAttribute - Manual (http://www.php.net/manual/en/pdo.setattribute.php) قابل مشاهده هست


در اینجا به چند خصوصیت مهم می پردازم


PDO::ATTR_ERRMODE

با این خصوصیت تعیین میکنید در هنگام مواجه با ارور در اجرای دستورات ، PDO چگونه برخورد کند

مقادیر این خصوصیت

PDO::ERRMODE_SILENT ( پیش فرض )

با این مقدار تنها اررور در متغیر pdo ذخیره میشود و هیچ عمل دیگری صورت نمیگیرد ( نحوه کار با اررور ذخیره شده رو در اینده توضیح خواهم داد)


PDO::ERRMODE_WARNING

با این مقدار pdo یک هشدار ایجاد میکند که بر حسب کانفیگ شما ، به کاربر نمایش داده میشود یا در فایل ذخیره میشود یا اصلا ذخیره نمیشود.


PDO::ERRMODE_EXCEPTION (پیشنهاد بنده )

با استفاده از این مقدار در صورت برخورد با اررور throw رخ میدهد

دقت کنید برای استفاده از این خصوصیت باید از try catch استفاده کنید



یک مثال برای ست کردن و PDO::ERRMODE_EXCEPTION


try {
$db = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); //اتصال
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); //ست کردن نحوه نمایش ارور

$db->prepare('DELECT test FROM db'); // یک دستور که باعث ایجاد خطا میشود
}
catch(PDOException $e) {
echo "یک خطا رخ داده است : ".$e->getMessage()ک
}




PDO::ATTR_DEFAULT_FETCH_MODE

با استفاده از این خصوصیت نحوه fetch (خروجی یک ردیف ) را معین میکنید

مقدایر به صورت زیر است

PDO::FETCH_ASSOC

خروجی یک ارایه با اندیس های نام فیلد ها می باشد ( اگر دارای فیلد name باشد مقدار به صورت ['name'] قابل دسترسی هست )

PDO::FETCH_BOUND

یک نوع خاصی از خروجی هست که نیازمند درس های اینده هست، و تا اون موقع بدون توضیح رها میشود

PDO::FETCH_CLASSیک نوع خاصی از خروجی هست که نیازمند درس های اینده هست، و تا اون موقع بدون توضیح رها میشود

PDO::FETCH_NUM

خروجی یک ارایه با اندیس های شماره می باشد ( اگر اولین فیلد name باشد مقدار به صورت [0] قابل دسترسی هست )

PDO::FETCH_BOTH (پیشفرض)

تلفیقی از FETCH_ASSOC و FETCH_NUM

PDO::FETCH_OBJ (پیشنهاد بنده)

خروجی به صورت شی خواهد بود ( اگر در خروجی فیلد name باشد و متغیر $row باشد، مقدار فیلد با $row->name قابل دسترس خواهد بود )

PDO::ATTR_EMULATE_PREPARES

در مورد این در آینده صحبت میکتیم، با این وجود فعلا این مقدار را false قرار بدید(مهم)

نمونه کد برای تغییر خصوصیات




$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_BOTH);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

cooper47
دوشنبه 31 تیر 1392, 05:05 صبح
متد quote جایگزین تابع mysql_real_escape_string


با استفاده از این متد میتوانید در برابر حملات sql injection برنامه خود را محافظت کنید

دقت داشته باشید قبل از اجرای این کد باید charset را ست کنید

برای utf-8 کد زیر را به کار ببرید


$db->exec("SET NAMES utf8");



خوب 2 ارگومان تابع دارد

ارگومان اول متغیر ما هست

ارگومان دوم که اختیاری هست نوع متغیر رو بیان میکند برای مثال PDO::PARAM_STR یا PDO::PARAM_INT یا PDO::PARAM_NULL



به توصیه PHP هم دقت کنید


you are strongly recommended to use PDO::prepare() (http://www.php.net/manual/en/pdo.prepare.php) to prepare SQL statementهمانطور که ذکر کرده استفاده ازین تابع توصیه نمیشود و سرعت کمتری دارند، به جای اون استفاده از prepare پیشنهاد شده که در اینده اموزش داده خواهد شد


هشدار: این تابع برای تمام نوع متغیر ها quotes ( علامت ' ) را در 2 طرف متغیر قرار میدهد و نیاز به قرار دادن ان در کوئری نیست !


مثال :


$unsafe = "I'm Ali!"; //متغیر مورد نظر
$safe = $db->quote($unsafe); //مقدار خروجی : 'I''m Ali!'





اجرای کوئری Query با PDO

خوب قسمت اصلی دیتابیس مسلما اجرای دستورات هست
3 متد برای اینکار در PDO موجود هست که به توضیح 2 متد در اینجا اکتفا میکنم


1-متد exec

این متد دستورات شما رو اجرا میکند و تعداد ردیف تحت تاثیر را برمیگرداند ( برای UPDATE , DELETE و CREATE )

هشدار: این متد برای دستورات SELECT به هیچ وجه کاربرد ندارد

هشدار2: این تابع در صورت مواجه با خطا مقدار FALSE را برمیگرداند، برای مقایسه برای جلوگیری از اشتباه شدن مقدار FALSE با 0 باید از ==! استفاده کنید به مثال توجه کنید




if($db->exec($query) === false ) // درست
echo 'خطا رخ داد';
if($db->exec($query) == false ) // نادرست
echo 'خطا رخ داد';
if(! $db->exec($query) ) //نادرست
echo 'خطا رخ داد';


2-متد query

در صورتی که مایل به اجرای دستورات SELECT هستید ( برای بقیه دستورات هم میتوانید از این متد استفاده کنید ) این متد کاربرد دارد

دقت کنید خروجی این متد یک شئ دیگر هست به نام PDOStatement (http://www.php.net/manual/en/class.pdostatement.php)

و برای پردازش اطلاعات باید اموزش این شی که در اینده مطرح خواهد شد را بخوانید

یک دستور ساده




$query = "SELECT * FROM `users`";

$result = $db->query($query);
//$result یک شئ از نوع دیگر است
print_r($result->fetchAll());



در مورد متد fetchAll بعدا صحبت خواهد شد فعلا بدانید این متد تمام ردیف ها را برمیگرداند و متد fetch یک ردیف را


3-متد prepare

کار با این متد همانند متد قبلی هست ولی تفاوت هایی دارند که بعدا ذکر خواهد شد

خروجی این متد یک شئ دیگر هست به نام PDOStatement (http://www.php.net/manual/en/class.pdostatement.php)

فعلا بدانید مثال بالا را با prepare میتوان به این شکل اجرا کرد






$query = "SELECT * FROM `users`";

$result = $db->prepare($query);
$result->execute(); // این خط اضافه شده است

print_r($result->fetchAll());

cooper47
دوشنبه 31 تیر 1392, 05:06 صبح
PDOStatement چیست ؟


بعد از اجرای متد های prepare و query خروجی انها به صورت PDOStatement هست،

PDOStatement در واقع شی ای برای پردازش اطلاعات می باشد و دارای متد های مختلفی هست

در اینجا متد های مهم و پر کاربرد ذکر خواهد شد


متغیر $result در اموزش ازین پس یک PDOStatement می باشد


متد setFetchMode

این متد همانند خصوصیت PDO::ATTR_DEFAULT_FETCH_MODE که در پست http://www.webhostingtalk.ir/f148/86678/#post819518 اموزش داده شد نحوه خروجی ردیف ها را تعیین می کند

دقت کنید تفاوت ان این است که تنها برای این شی کاربرد دارد

برای مثال اگر شما 2 PDOStatement دارید و در یکی این خصوصیت را تغییر دهید در دیگری تغییر نمیکند

مقدار پیشفرض ان مقداری است که به PDO::ATTR_DEFAULT_FETCH_MODE دادید


متد fetch

این متد یک ردیف را برمیگرداند ( خروجی بسته به fetchmode میتواند ارایه یا شی باشد )

دقت کنید در ورودی این متد هم میتوانید نحوه خروجی را جدا تنظیم کنید

برای مثال


$result->fetch(PDO::FETCH_OBJ)


متد fetchAll

این متد ارایه ای از تمام ردیف ها را بر میگرداند ( مقدار هر فیلد ارایه بسته به fetchmode میتواند ارایه یا شی باشد )

دقت کنید در ورودی این متد هم میتوانید نحوه خروجی را جدا تنظیم کنید


متد rowCount

این متد جایگزین متد mysql_num_rows و mysql_affected_rows می باشد

در صورتی که دستور INSERT , UPDATE , DELETE اجرا شده باشد تعداد ردیف های تحت تاثیر قرار گرفته را بر میگرداند

در غیر این صورت در بعضی از دیتابیس ها ( از جمله MySQL ) اگر دستور SELECT اجرا شده باشد تعداد ردیف های انتخابی را برمیگرداند

cooper47
دوشنبه 31 تیر 1392, 05:07 صبح
Prepared Statements( متد prepare )

تا اینجا اکثر توضیحات جایگزینی برای توابع mysql بود و با توضیحات بالا تقریبا میتوانید توابع را جایگزین کنید

اما دست نگه دارید !

امکان جدیدی اضافه شده است !

Prepared Statements !

با استفاده از این امکان میتوانید سرعت را افزایش دهید ، امنیت کد های خود را بالا ببرید

یکی از کابرد های Prepared Statements این هست که شما میتوانید با استفاده از اون چندین دستور مشابه با ارسال تنها متغیر اجرا کنید،


فرض کنید میخواهید در دیتابیس 2 ردیف ذخیره کنید ، اگر به روش قبلی بخواهید اینکارو انجام بدید باید 2 بار کوئری مشابه بنویسید که تنها مقادیر فیلد ها عوض خواهد شد

اما با Prepared Statements میتوانید یک کوئری بنویسید و مقداری فیلد ها را جداگانه به دیتابیس ارسال کنید


در Prepared Statements شما به جای مقادیر یک مکان قرار میدهید

جایگزین میتواند با نام باشد یا بدون نام


مکان بدون نام

در این روش شما به جای مقدار تنها علامت سوال ( ؟ ) قرار میدهید

برای مثال


$query = "SELECT * FROM `users` WHERE username = ?";
$result = $db->prepare($query);



مکان با نام

در این روش شما به جای مقدار تنها نامی برای ان مکان انتخاب میکنید ( دقت کنید نام با : شروع می شود )

برای مثال



$query = "SELECT * FROM `users` WHERE username = :username"; // :username
$result = $db->prepare($query);





مقدار دهی به مکان با متد bindParam از PDOStatement


در هردو روش مکان دهی بعد از اجرای متد prepare خروجی همانطور که ذکر شد شی PDOStatement هست

این شئ متدی به نام bindParam دارد که با کمک ان میتوان مقدار مکان ها را مشخص کرد

برای مثال

در روش بدون نام

ورودی اول شماره مکان است




$result->bindParam(1, $_POST['username']);


در روش با نام

ورودی اول نام مکان است




$result->bindParam(':username', $_POST['username']);



دقت کنید برای تاکید ورودی را از متغیر POST دادم ( که کاربر وارد میکنه ) که بیان کنم این روش کاملا در برابر تزریق کد امن هست





اجرای Prepared Statement


برای اجرای کوئری بعد از مقدار دهی به مکان ها با استفاده از متد execute در PDOStatement دستور را اجرا میکنیم

مثال


$result->execute();

print_r($result->fetchAll);




روش کوتاه برای مقدار دهی مکان ها با تابع execute

مثلا استفاده از تابع bindParam برای تک تک مکان ها کاری سخت هست

روش سریع تری برای اینکار وجود دارد

برای این روش مقداری را به صورت ارایه به متد execute ارسال کنید

در روش بدون نام ارایه بدون اندیس یا با اندیس شماره ها می باشد

در روش با نام اندیس باید نام مکان باشد ( بدون : )

مثال بدون نام




$query = "SELECT * FROM `users` WHERE username = ? AND password = ?";
$result = $db->prepare($query);
$result->execute(array($username,$password));


مثال با نام



$query = "SELECT * FROM `users` WHERE username = :username AND password = :password";
$result = $db->prepare($query);
$result->execute(array('username'=>$username,'password'=>$password));




اجرای چند کوئری مشابه با یک کوئری

با استفاده از این امکان میتوانید در سرعت و حجم کد ها تغییر ایجاد کنید

برای اینکار کافی هست ابتدا مکان ها را ست کنید و بعد از اجرای execute مکان های جدید قرار دهید و دوباره دستور execute را اجرا کنید

مثال




$query = "INSERT INTO `users` (`username`,`password`) VALUES (:username,:password)";
$result = $db->prepare($query);
$result->execute(array('username'=>'test','password'=>'test')); // ساخت یوزر جدید

$result->execute(array('username'=>'admin','password'=>'admin')); // ساخت یوزر دیگر با همان دستور

cooper47
دوشنبه 31 تیر 1392, 09:04 صبح
مطالب تکمیلی

PDO::FETCH_BOUND و متد bindColumn از PDOStatement

با استفاده این 2 میتوانید در هنگام fetch به متغیر های از پیش تعریف شده مقدار دهید

مثال




$db->bindColumn(1,$username); //با شماره
$db->bindColumn('password',$password); //با نام فیلد

while($row = $db->fetch(PDO::FETCH_BOUND))
{
echo $username.'-'.$password;
}



PDO::FETCH_CLASS


با استفاده از این امکان میتوانید خروجی را در یک شی قرار دهید

مثال




class User
{
public $username;
public $password;
}

$STH->setFetchMode(PDO::FETCH_CLASS, 'User');

while($user = $db->fetch())
{
echo $user->username.'-'.$user->password;
}

PDO::ATTR_EMULATE_PREPARES

قبلا ذکر شد مقدار این خصوصیت را false قرار دهید، اما این خصوصیت چیست

اکثر دیتابیس های جدید از prepare پشتیبانی میکنند ولی ورژن های قدیمی پشتیبانی نمیکنند، هنگامی که این مقدار true باشد pdo عملیات prepare را شبیه سازی میکند،

با این وجود توصیه میشود همیشه این مقدار را خاموش کنید ، چرا که pdo در ورژن جدید php قرار داده شده هست و مسلما هنگامی که ورژن php این هست ورژن دیتابیس هم پشتیبانی خواهد کرد



گرفتن اخرین ای دی ذخیره شده در دیتابیس




echo $db->lastInsertId();
اخرین ای دی را به شما بر میگرداند





تعداد ردیف های تحت تاثیر قرار گرفته

1-اگر از exec استفاده کردید

متد exec تعداد را بر میگرداند
2-اگر از prepare و query استفاده کردید

متد rowCount از PDOStatement تعداد را بر میگرداند

eshpilen
دوشنبه 31 تیر 1392, 09:07 صبح
و به زودی تمام اسکریپت هایی که از این توابع استفاده میکنند از کار خواهند افتاد !
پس این تایپک رو جدی بگیرید

فکر نمیکنم خیلی زود این اتفاق بیفته. چون ضرورتی نداره و موارد مشابه در تاریخ اغلب در مدتی نسبتا طولانی ساپورت شدن تا اینکه در نهایت حذف شده. در خیلی موارد اصولا اون ویژگی حداقل برای مدتی هم که شده صرفا از حالت پیشفرض و فعال درمیاد و افراد و برنامه هایی که بخوان میتونن با دستورات یا روشهایی اون رو فعال کنن.
بعنوان یک مثال میتونیم ویژگی رجیسترگلوبالز رو ذکر کنیم که سالهای سال همین روال رو طی کرده.

ایجاد این همه دردسر و لزوم تغییر این همه برنامه، باید توجیه کافی داشته باشه تا در کوتاه مدت انجام بشه.

البته در اینکه اکیدا توصیه میشه برنامه های جدید با استانداردهای جدید نوشته بشن شکی نیست.

cooper47
دوشنبه 31 تیر 1392, 09:17 صبح
فکر نمیکنم خیلی زود این اتفاق بیفته. چون ضرورتی نداره و موارد مشابه در تاریخ اغلب در مدتی نسبتا طولانی ساپورت شدن تا اینکه در نهایت حذف شده. در خیلی موارد اصولا اون ویژگی حداقل برای مدتی هم که شده صرفا از حالت پیشفرض و فعال درمیاد و افراد و برنامه هایی که بخوان میتونن با دستورات یا روشهایی اون رو فعال کنن.
بعنوان یک مثال میتونیم ویژگی رجیسترگلوبالز رو ذکر کنیم که سالهای سال همین روال رو طی کرده.

ایجاد این همه دردسر و لزوم تغییر این همه برنامه، باید توجیه کافی داشته باشه تا در کوتاه مدت انجام بشه.

البته در اینکه اکیدا توصیه میشه برنامه های جدید با استانداردهای جدید نوشته بشن شکی نیست.

از نظر بنده بهتر هست تغییرات ایجاد بشه در اسکریپت،

مهم قضیه اتفاق افتادنش هست نه زمانش ، مهم اینه که اسکریپت های نوشته شده با این زبان نیازمند تغییر هستند و کسانی که برنامه نویسند اسکریپت های خودشونو بهتر هست بروز کنند


هشدار php کاملا واضح هست،
will be removed in the future
حال اگر این توابع کاملا پاک شدند جای اعتراضی نمی ماند ،
نمیتوان بر احتمال اینکه کاملا پاک نمیشه تکیه کرد


موفق باشید،

Dead Space
دوشنبه 31 تیر 1392, 10:41 صبح
همین الانش کسانی که روی PHP 5.5 هستن این توابع براشون کار نمی کنه.

amin7x
پنج شنبه 03 مرداد 1392, 03:01 صبح
این آموزش آسان PDO برای mysql است و به این صورت است که اول روش قبلی دستورات رو میگه و بعدش روش جدید دستورات رو با PDO میگه.

PDO Tutorial for MySQL Developers (http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers)

olampiad
چهارشنبه 15 آبان 1392, 22:23 عصر
یه سوال داشتم
pdo یه کلاس آمادس
یا باید از اول خوذمون بنویسیمش
ممنون

saeidpsl
چهارشنبه 15 آبان 1392, 23:56 عصر
اینم (http://delgosha1.ir/ups/uploads/PDO.rar) چندین کد نمونه (سلکت دلیت آپدیت اد و ....)

MMSHFE
پنج شنبه 16 آبان 1392, 11:07 صبح
یه سوال داشتم
pdo یه کلاس آمادس
یا باید از اول خوذمون بنویسیمش
ممنون
PDO یک Extension و مجموعه ای از چندین کلاس آماده در PHP هست و نیازی به نوشتنش ندارین. این تاپیک هم داره نحوه کار با اون رو آموزش میده.

olampiad
یک شنبه 24 آذر 1392, 22:53 عصر
سلام
فرق بین این دوتا چیه؟



while($tt=$r->fetch(pdo::FETCH_ASSOC)){

echo $tt['id'].'<br>';
}

while($tt=$r->fetch()){

echo $tt['id'].'<br>';
}


ممنون