برگزیده های پرشین تولز

واقعن فکر میکنید این عبارت درست کار میکنه؟ select * from table_name where title like “%?%”

mhm007_007

Registered User
تاریخ عضویت
29 دسامبر 2005
نوشته‌ها
1,248
لایک‌ها
184
سن
33
محل سکونت
tehran
امروز میخوام درباره خطای General error: 25 bind or column index out of range صحبت کنم.
اگه کدها درست نشون داده نمیشن میتونید پست اصلی رو ببینید

همونطور که از متن خطا معلوم هست مشکل در هماهنگ کردن پارامترهای متصل شده (بایند شده) به کوئری اس کیو ال هست.

کد زیر رو در نظر بگیرید

$pdo = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
$stmt=$pdo->prepare('select * from table_name where id < ? and count < ? ');
$result= $stmt->execute([1000]);

اگه این رو اجرا کنید با همین خطا مواجه میشید چون دو تا placeholder (تو اینجا منظور ? هست) مشخص شده ولی تو تابع execute فقط یک مقدار مشخص شده که دلیل بروز این خطا هست.

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

$stmt=$pdo->prepare('select * from table_name where title like "%?%"');
$result= $stmt->execute(['test']);

بعد از خوندن مستندات PHP و PDO متوجه شدم که خود placeholder نباید تو کوتیشن یا هرچیز دیگه ای قرار بگیره فقط خود placeholder باید باشه دلیلش هم این هست که وقتی کوئری prepare شده میخواد شبیه سازی (emulate) بشه (چه توسط PDO چه توسط MySQL) سیستمی که قرار هست اون رو چک کنه (PDO یا MySQL منظورم هست) میاد placeholder ها رو جدا میکنه و کل کوئری رو صحت سنجی میکنه حالا اینجا اگه کوتیشن یا هرچیز دیگه ای ببینه خطا میده چون دیگه قرار نیست هیچ مقداری دیگه تو کوئری باشه. به خاطر همین صجت سنجی کوئری، دیگه نام جدول و نام ستون نمیتونه به صورت نشانگر placeholder باشه چون بدون مقادیر مشخص شده اونها، امکان صحت سنجی وجود نداره.

برابر توضیحات، کد بالا باید به این صورت باز نویسی بشه

$stmt=$pdo->prepare('select * from table_name where title like ?');
$table_name='test';
$result= $stmt->execute(['%' . $table_name . '%']);

همچنین میتونیم با استفاده از تابع bindParam این کار رو انجام بدیم.

$stmt=$pdo->prepare('select * from table_name where title like ?');
$table_name='test';
$stmt->bindParam(1, '%' . $table_name . '%');
$result= $stmt->execute();

یا

$stmt=$pdo->prepare('select * from table_name where title like :table_name');
$table_name='test';
$stmt->bindParam(':table_name','%' . $table_name . '%');
$result= $stmt->execute();

دلیل اصلی شبیه سازی هم جلوگیری از حملات SQL Injection هست; حالا شبیه سازی به دو صورت انجام میشه یا توسط PDO یا MySQL. حالا فرقشون چیه؟

اگه شبیه سازی رو MySQL انجام بده ابتدا خود کوئری ارسال میشه بعد از اینکه MySQL اون رو تائید کرد(البته کارهای دیگه ای مثل آنالیز کردن و بهینه سازی کوئری هم انجام میشه) تو یه ارتباط دیگه خود مقادیر اصلی ارسال میشه; اگه شبیه سازی توسط PDO انجام بشه اول PDO مقادیر رو فیلتر میکنه تا از نظر امنیتی مشکل ساز نشن و سپس داخل کوئری جایگزاری میکنه و اونرو به MySQL میفرسته همونجور که مشخص هست تو این روش یک درخواست کمتر به سرور MySQL ارسال میشه.

من خودم ترجیح میدم MySQL عملیات جایگزاری رو انجام بده فکر میکنم مطمئن تر هست همچنین فکر میکنم درخواست اضافی که تو این روش هست خیلی رو سرور فشار نمیاره یه چیز دیگه هم بگم که ممکن هست سیستم دیتابیستون از شبیه سازی پشتیبانی نکنه که اینجا میتونید کاری کنید که خود PDO این کار رو براتون انجام بده.

مقدار متغیر ATTR_EMULATE_PREPARES مشخص میکنه که عملیات شبیه سازی عبارات prepare شده توسط PDO انجام بشه یا توسط سرور دیتابیس.

$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES , true);// emulate by PDO library
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES , false);//emulate by Database Server (MySQL)

سایر پستهای بلاگ من رو هم ببینید
 
بالا