میخوای وارد بازار کار بشی و محصولات خودت رو بفرشی همین الان ثبت نام کن
0

اجرای کوئری ها و SQL Injection قسمت ۵

۷۴ / ۱۰۰

سلام به شما همراهان و دوستان محترم سایت آموزشی و فروشگاهی مدرسه کدنویسی به دوره ی آموزش PDO خوش آمدید.

در قسمت قبلی با بدیهیات و مقدمات اجرای کوئری ها و مبحث SQL Injection آشنا شدیم. در این قسمت (اجرای کوئری ها و SQL Injection قسمت ۵) به ذکر مثال ها و توضیحات نکات ریزتر این مبحث خواهیم پرداخت.

 


Prepared statement ها و اجرای کوئری

حتما از جلسه ی قبل یادتان است که این کوئری را:

$sql = "SELECT * FROM users WHERE email = '$email' AND status='$status'";

تبدیل به یکی از کوئری های زیر کردیم:

$sql = 'SELECT * FROM users WHERE email = ? AND status=?';

$sql = 'SELECT * FROM users WHERE email = :email AND status=:status';

حالا ادامه ی مطلب:

برای اجرای یک کوئری که در آن از placeholder ها استفاده شده است باید از متد ()PDO::prepare استفاده شود ( به این کار prepare کردن یا آماده سازی می گوییم). این تابع، همان شیء PDOStatement را که در جلسات قبل به آن اشاره کردیم را بر میگرداند اما هیچ داده ای را به آن الصاق نمی کند.

در آخر نیز برای اجرای کوئری باید از متد ()execute استفاده کرده و مقادیر را به صورت یک آرایه به آن پاس بدهید. سپس می توانید داده های خود را از statement بگیرید (البته در صورتی که کوئری برای گرفتن داده باشد):

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email, $status]);
$user = $stmt->fetch();
// یا
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status=:status');
$stmt->execute(['email' => $email, 'status' => $status]);
$user = $stmt->fetch();

همانطور که می بینید برای استفاده از placeholder های موقعیتی (ر.ک جلسه ی قبل) از یک آرایه ی عادی استفاده می کنیم اما زمانی که با placeholder های اسمی کار می کنیم باید از آرایه های متناظر (associative array) استفاده کنیم، بدین صورت که key ها مساوی با نام placeholder در کوئری باشند.

نکته ی اول: شما نمی توانید از placeholder های موقعیتی و اسمی به طور همزمان در یک کوئری استفاده کنید.

نکته ی دوم: توجه داشته باشید که placeholder های موقعیتی به شما کمک می کنند کد های کوتاه تری بنویسید اما نسبت به ترتیب آرگومان های پاس داده شده به ()execute حساس هستند؛ یعنی دقیقا به ترتیب placeholder ها در کوئری اصلی هستند

(به طور مثال علامت سوال اول، اولین آرگومان را می گیرد، دومی نیز آرگومان دومی را می گیرد و الی آخر). از طرف دیگر placeholder های اسمی کد را طولانی تر می کنند اما نسبت به موقعیت حساس نیستند (چرا که در آرایه ی متناظر با جفت های key و value تنظیم می شوند – ر.ک مثال بالا).

نکته ی سوم: در placeholder های اسمی، زمانی که بخواهید آرایه را به ()execute تحویل دهید نیازی به گذاشتن علامت دو نقطه نیست (ر.ک مثال بالا).


متد های مربوط به binding

ارسال داده ها به ()execute، مانند مثال بالا، حالت پیش فرض و راحت ما است. زمانی که از این متد استفاده شود، تمام داده ها به صورت رشته ای در می آیند. با این حال، این روش در اکثر مواقع سالم بوده و هیچ مشکلی ایجاد نمی کند اما در برخی از موارد بهتر است نوع داده ها را شخصا تعیین کنیم. مواردی که زیر مجموعه ی چنین حالتی هستند عبارتند از:

  • دستورات LIMIT (یا هر دستور SQL ای که operand1 (عملوند) های رشته ای را قبول نمی کند) در صورتی که emulation mode فعال باشد.
  • کوئری های پیچیده ای که ممکن است با نوع operand (عملوند) اشتباهی، از کار بیوفتند.
  • انواع ستون های غیر عادی مانند BIGINT یا BOOLEAN که تنها operand هایی با نوع خاص را قبول می کنند.

در چنین حالت هایی باید از explicit binding2 استفاده شود که در این جهت می توانید از توابع ()bindValue و ()bindParam استفاده کنید.

۱- operand (در فارسی عَمَلوَند): در برنامه نویسی هر شیء ای که قابلیت دستکاری شدن را داشته باشد، عملوَند نام دارد. آن را با عملگر یا اپراتور (operator) اشتباه نگیرید. به طور مثال در عبارت “۱ + ۲” اعداد ۱ و ۲ عملوَند و علامت “+” عملگر (operator) است.

۲- explicit binding: فعل bind در فارسی معنیِ “جفت کردن” یا “به هم چسباندن” می دهد. منظور از binding در برنامه نویسی، چسباندن یک مقدار به متغیر آن است.

به طور مثال، چند خط بالاتر گفتیم که هنگام اجرای کوئری با ()execute یک سری آرگومان را به ان پاس می دادیم. این آرگومان ها همان مقادیری بودند که در کوئری باید به جای placeholder قرار می گرفتند یا به عبارت دیگر مقادیری بودند که باید به placeholder خودشان bind میشدند یا میچسبیدند. آن حالت، یک binding عادی یا غیر صریح بود. چرا؟

چون ما مستقیما و صریحا تعیین نمیکردیم که کدام مقدار جای کدام placeholder را بگیرد؛ مثلا اولین مقدار جای اولین علامت سوال را می گرفت و الی آخر. همچنین در حالت placeholder های اسمی نیز با یک آرایه ی متناظر خود به خود جای placeholder را می گرفتند (یا bind می شدند).

توجه کنید که در هر حال ما به عنوان برنامه نویس تعیین می کنیم که کدام مقدار جای کدام placeholder را بگیرد اما در این حالت ما چنین چیزی را به صورت صریح و دستی تعیین نمیکردیم.

 

حالا explicit binding یا “binding صریح” به این معنی است که ما به طور دستی و صریح چنین کاری را انجام دهیم.

 

مثلا بالاتر گفتیم که می توان از تابع ()bindValue استفاده کرد. مثال زیر را ببینید تا متوجه شوید:

binding غیر صریح (در حالت پیش فرض):

$stmt->execute(['email' => $email, 'status' => $status]);

binding صریح (با استفاده از توابعی مثل ()bindValue):

<?php
/* Execute a prepared statement by binding PHP variables */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindValue(':calories', $calories, PDO::PARAM_INT);
$sth->bindValue(':colour', $colour, PDO::PARAM_STR);
$sth->execute();
?>

در این مثال با استفاده از تابع bindValue، هر کدام از مقادیر را به صورت دستی و تک تک به placeholder خود bind کردیم. به این قسمت از کد دقت کنید:

$sth->bindValue(':calories', $calories, PDO::PARAM_INT);

$sth->bindValue(':colour', $colour, PDO::PARAM_STR);

در این کد ها ما برای مقادیر calories (به معنی کالری) و colour (به معنی رنگ) به صورت جداگانه و دستی تعریف کرده ایم که به کدام placeholder متصل شوند.

حال سوالی که پیش می آید این است که چرا باید از این روش استفاده کرد؟ علاوه بر مواردی که در بالا ذکر شد، در برخی از دستورات MySQL (مانند UPDATE) به این مشکل برخورد میکنیم. در جلسات بعدی در مورد آن ها صحبت خواهیم کرد.

سوال: چه پارامتر هایی را می توان bind کرد؟

پاسخ: تنها رشته ها و اعداد (فقط به صورت literal) می توانند bind شوند. مقادیر literal مقادیری هستند که مستقیما تعیین می شوند نه از طریق محاسبات و توابع دیگر.

به طور مثال، عدد ۱ در عبارت x = 1 از نوع literal است (مستقیما وارد سورس کد شده است) اما با اینکه عبارت (x = cos(0 (یعنی کوسینوسِ صفر) برابر با ۱ است اما دیگر این ۱ از نوع literal نیست، چرا که مستقیما در سورس کد تعریف نشده بلکه طی یک عملیات محاسباتی یا توسط توابع دیگر به عنوان خروجی برگردانده شده است (به عبارت دیگر، در سورس کد وارد نشده، بلکه به وجود آمده).

در موارد دیگر اصلا نمی توانید از prepared statement ها در PDO استفاده کنید. راه هایی برای حل این مشکل نیز وجود دارد که در جلسات بعدی به آن ها اشاره خواهد شد.


خلاصه ی مقاله

در این قسمت از سری آموزشی کار با رابط شیء گرای PDO به تکمیل مبحث prepared statement ها و اجرای کوئری ها پرداختیم. همچنین مثال هایی در این رابطه ذکر کرده و تفاوت هایی جزئی و فنی اما مهم و کاربردی را توضیح دادیم. امیدوارم از این قسمت استفاده کرده باشید.

ادامه سری آموزش اتصال شیء گرا به پایگاه داده ( PDO ) را دنبال کنید . برای مطالعه پست‌ها بیشتر ، ما را در مدرسه کدنویسی تلگرام  و یا در تی جوان اینستاگرام دنبال کنید.

نظراتتون رو زیر همین پست با ما به اشتراک بگذارید.

 

 

ارسال دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

×

سلام کاربر عزیز

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

روی لوگومون کلیک کنید

× چطور میتونم کمکتون کنم؟