Kodokon kodokon.com

PDO: प्रीपेयर्ड स्टेटमेंट और ट्रांज़ैक्शन

PDO को उसी तरह कॉन्फ़िगर करें जैसे उत्पादन में होता है, SQL इंजेक्शन को संरचनात्मक रूप से समाप्त करें और ट्रांज़ैक्शन से अपने लेखन को भरोसेमंद बनाएँ।

10 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

PDO एकीकृत डेटाबेस एक्सेस इंटरफ़ेस है: MySQL, PostgreSQL या SQLite - सभी एक ही API से चलाए जाते हैं। तीन सेटिंग्स एक भोली कनेक्शन को उत्पादन वाली कनेक्शन से अलग करती हैं: ERRMODE_EXCEPTION ताकि हर SQL त्रुटि चुपचाप विफल होने के बजाय एक अपवाद उत्पन्न करे, साफ़ परिणामों के लिए डिफ़ॉल्ट रूप से FETCH_ASSOC, और EMULATE_PREPARES को false पर सेट करना ताकि प्रीपेयर्ड स्टेटमेंट वास्तव में सर्वर द्वारा निष्पादित हों। charset=utf8mb4 को सीधे DSN में जोड़ें - इसे घोषित करने की यही एकमात्र भरोसेमंद जगह है।

PHP
<?php

declare(strict_types=1);

$dsn = 'mysql:host=localhost;dbname=shop'
    . ';charset=utf8mb4';

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];

$pdo = new PDO($dsn, 'app_user', 'secret', $options);
उत्पादन कनेक्शन: अपवाद, एसोसिएटिव फ़ेच, नेटिव प्रिपरेशन।

प्रीपेयर्ड स्टेटमेंट SQL इंजेक्शन के विरुद्ध आपकी एकमात्र संरचनात्मक रक्षा है। सिद्धांत यह है: क्वेरी की संरचना, अपने :email या ? प्लेसहोल्डरों सहित, पहले सर्वर को भेजी जाती है; डेटा अलग से पीछे आता है और उसकी कभी SQL के रूप में व्याख्या नहीं होती। किसी वेरिएबल को क्वेरी में जोड़ना, चाहे वह "एस्केप" किया हुआ ही क्यों न हो, एक पेशेवर गलती बनी रहती है। नेम्ड प्लेसहोल्डर कोड को पठनीय बनाते हैं; छोटी क्वेरियों के लिए पोज़िशनल प्लेसहोल्डर काफ़ी हैं।

PHP
<?php

declare(strict_types=1);

$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(
    PDO::ATTR_ERRMODE,
    PDO::ERRMODE_EXCEPTION,
);

$pdo->exec(
    'CREATE TABLE users (
        id INTEGER PRIMARY KEY,
        email TEXT NOT NULL
    )'
);

$insert = $pdo->prepare(
    'INSERT INTO users (email) VALUES (:email)'
);
$insert->execute(['email' => 'lea@example.com']);

$query = $pdo->prepare(
    'SELECT id, email FROM users
     WHERE email = :email'
);
$query->execute(['email' => 'lea@example.com']);

var_dump($query->fetch(PDO::FETCH_ASSOC));
स्वयं-निहित SQLite उदाहरण: प्रिपरेशन, बाइंडिंग, निष्पादन।

एक ट्रांज़ैक्शन परमाणुता (atomicity) की गारंटी देता है: या तो सारे लेखन सफल होते हैं, या एक भी नहीं। आदर्श पैटर्न: beginTransaction(), काम किसी try के भीतर, ब्लॉक के अंत में commit(), और catch में rollBack() - फिर आप अपवाद को दोबारा फेंकते हैं, क्योंकि विफलता को छिपाना खुद विफलता से भी बुरा होगा। ट्रांज़ैक्शन को छोटा रखें: किसी नेटवर्क कॉल या लंबी प्रक्रिया के दौरान रोका गया हर लॉक पूरे एप्लिकेशन में समवर्तिता (concurrency) को गिरा देता है।

PHP
<?php

declare(strict_types=1);

$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(
    PDO::ATTR_ERRMODE,
    PDO::ERRMODE_EXCEPTION,
);

$pdo->exec(
    'CREATE TABLE accounts (
        id INTEGER PRIMARY KEY,
        balance INTEGER NOT NULL
    )'
);
$pdo->exec(
    'INSERT INTO accounts (balance)
     VALUES (100), (20)'
);

$pdo->beginTransaction();

try {
    $debit = $pdo->prepare(
        'UPDATE accounts
         SET balance = balance - :n
         WHERE id = :id'
    );
    $debit->execute(['n' => 50, 'id' => 1]);

    $credit = $pdo->prepare(
        'UPDATE accounts
         SET balance = balance + :n
         WHERE id = :id'
    );
    $credit->execute(['n' => 50, 'id' => 2]);

    $pdo->commit();
} catch (Throwable $e) {
    $pdo->rollBack();
    throw $e;
}

echo 'Transfer done';
परमाणु स्थानांतरण: या तो सब कुछ सफल होता है, या सब कुछ वापस लौटा दिया जाता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. कोई प्रीपेयर्ड स्टेटमेंट SQL इंजेक्शन को क्यों रोकता है?
    • यह स्ट्रिंग में अपोस्ट्रोफ़ी को स्वतः एस्केप कर देता है
    • SQL संरचना और डेटा अलग-अलग यात्रा करते हैं: डेटा की कभी SQL कोड के रूप में व्याख्या नहीं होती
    • यह PHP और सर्वर के बीच क्वेरी को एन्क्रिप्ट करता है
  2. किसी प्रीपेयर्ड स्टेटमेंट में :param प्लेसहोल्डर के ज़रिए आप क्या पास कर सकते हैं?
    • एक वैल्यू: स्ट्रिंग, संख्या या null
    • किसी टेबल या कॉलम का नाम
    • एक पूरा ORDER BY क्लॉज़
  3. आदर्श ट्रांज़ैक्शन पैटर्न में, आप catch ब्लॉक में क्या करते हैं?
    • जो सफल हुआ उसे सहेजने के लिए commit()
    • rollBack(), फिर अपवाद को दोबारा फेंकना
    • कुछ नहीं: ट्रांज़ैक्शन खुद ही समाप्त हो जाता है