تابع prepare() در PHP
تابع prepare() یکی از مهمترین ابزارها در PHP برای اجرای ایمن پرسوجوهای SQL است. این تابع در دو کتابخانه اصلی PDO و MySQLi وجود دارد و امکان استفاده از Prepared Statements را فراهم میکند. در این مقاله به صورت کامل توضیح میدهیم که prepare() چه کاری انجام میدهد، چرا مهم است و چگونه آن را در عمل به کار ببرید.
چرا از prepare() استفاده کنیم؟
- جلوگیری از SQL Injection: پارامترها جدا از ساختار پرسوجو ارسال میشوند و همین امر از تزریق کدهای مخرب جلوگیری میکند.
- بهبود عملکرد: هنگام اجرای مکرر یک پرسوجو میتوانید پرسوجو را یکبار آماده کنید و چندینبار اجرا کنید.
- مدیریت دادههای باینری: ارسال فایلها یا دادههای باینری امنتر و سادهتر میشود.
تفاوت بین PDO::prepare و mysqli::prepare
هر دو متد مفهوم یکسانی دارند اما پیادهسازی و امکانات جانبیشان متفاوت است. PDO از prepared statements با نامهای پارامتر (named) و مکاننماهای موقعیتی (positional) پشتیبانی میکند؛ در حالی که MySQLi عمدتاً از مکاننماهای علامت سوال (?) استفاده میکند.
| ویژگی | PDO::prepare() | mysqli_prepare() |
|---|---|---|
| پارامترها | نامی (:name) و موقعیتی (?) | فقط موقعیتی (?) |
| پشتیبانی از چندپایگاه | بله | خیر (فقط MySQL) |
| نمایش خطا | قابل تنظیم (Exceptions) | نیاز به بررسی دستی یا mysqli_report |
نمونه ساده با PDO
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$sql = "SELECT id, username FROM users WHERE email = :email";
$stmt = $pdo->prepare($sql);
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
?>توضیح: در این مثال ابتدا یک شی PDO ساخته میشود. سپس یک پرسوجوی SQL با پارامتر نامی (:email) آماده میشود. با فراخوانی execute و ارسال آرایهای از مقادیر، مقدار پارامتر جایگزین میشود و نتیجه با fetch برگردانده میشود. تنظیم ATTR_EMULATE_PREPARES روی false باعث استفاده از prepared statements واقعی میشود.
نمونه با mysqli و bind_param
prepare("INSERT INTO posts (title, body, created_at) VALUES (?, ?, NOW())");
$stmt->bind_param('ss', $title, $body);
$stmt->execute();
$stmt->close();
?>توضیح: در MySQLi از علامتهای سوال به عنوان placeholder استفاده میشود. bind_param نوع دادهها را به صورت رشتهای مشخص میکند (‘s’ برای string) و متغیرها به صورت مرجع (reference) به تابع متصل میشوند. پس از execute باید stmt را بست.
نکات و پیچیدگیهای حرفهای
- bindParam vs bindValue: در PDO، bindParam به صورت مرجع عمل میکند و مقدار در زمان execute گرفته میشود؛ bindValue مقدار فعلی را بایند میکند. وقتی مقدار قبل از execute ثابت است از bindValue استفاده کنید.
- LIMIT و شناسهها: آگاه باشید که نام ستونها یا مقادیر کلید (identifiers) را نمیتوان با placeholders جایگزین کرد. برای مقادیری مانند LIMIT یا نام فیلد باید آنها را به صورت امن با cast یا سفیدسازی (whitelist) مدیریت کنید.
- ATTR_EMULATE_PREPARES: برخی درایورها به خاطر محدودیتها آمادهسازی را شبیهسازی میکنند. این ممکن است اثرات امنیتی و کارایی داشته باشد؛ در PDO بهتر است آن را false قرار دهید تا prepares واقعی استفاده شوند.
- خطاها و exceptions: حالت ERRMODE در PDO را روی EXCEPTION قرار دهید تا خطاها قابل مدیریت با try-catch باشند. در MySQLi از mysqli_report یا بررسی بازگشتیها استفاده کنید.
بهترین شیوهها (Best Practices)
- همیشه از prepared statements برای ورودیهای کاربر استفاده کنید.
- نام ستون و نام جدول را هرگز از ورودی مستقیم کاربر نگیرید. اگر نیاز است از white-list استفاده و مقادیر را validate کنید.
- برای دادههای باینری از bindParam یا bindValue و ارسال با type مناسب بهره ببرید.
- در صورت اجرای چندباره، prepare را یکبار انجام دهید و فقط execute را تکرار کنید تا سربار پایگاه داده کاهش یابد.
- پس از پایان کار، statement را ببندید و منابع را آزاد کنید.
مثال پیشرفته: اجرای چندباره با PDO
beginTransaction();
$stmt = $pdo->prepare("INSERT INTO orders (user_id, amount, status) VALUES (?, ?, ?)");
foreach ($orders as $o) {
$stmt->execute([$o['user_id'], $o['amount'], $o['status']]);
}
$pdo->commit();
?>توضیح: در این مثال از تراکنش برای تضمین اتمیک بودن عملیات استفاده شده و یک پرسوجو تنها یکبار آماده میشود و برای هر سفارش مقداردهی و اجرا میگردد که کارایی و انسجام داده را افزایش میدهد.
اشکالات رایج و رفع آنها
- فراموش کردن بستن statement یا قطع اتصال باعث نشت منابع میشود — همیشه close یا null را فراخوانی کنید.
- متفاوت بودن انواع (type) هنگام bind_param — از حروف type صحیح (‘i’, ‘d’, ‘s’, ‘b’) استفاده کنید.
- اعتماد کامل به prepares کافی نیست — همیشه ورودیها را validate و sanitize کنید و اجازه ندهید نام ستونها از ورودی مستقیم حاصل شوند.
جمعبندی
تابع prepare() در PHP یک ابزار قوی برای ایمنسازی و بهینهسازی پرسوجوهای SQL است. با درک تفاوتهای PDO و MySQLi، استفاده صحیح از bindParam/bindValue، مدیریت استثناها و رعایت بهترین شیوهها میتوانید هم از حملات SQL Injection جلوگیری کنید و هم عملکرد برنامه را بهبود دهید.
اگر میخواهید مثالی خاص یا راهنمایی درباره یک سناریوی واقعی (مثلاً ارسال فایل، پیمایش دادههای حجیم، یا استفاده در چارچوبهای خاص) داشته باشید، میتوانم کد و توضیح دقیقتری ارائه دهم.
آیا این مطلب برای شما مفید بود ؟



