खोज…


परिचय

पीडीओ (PHP डेटा ऑब्जेक्ट्स) एक्सटेंशन डेवलपर्स को कई अलग-अलग प्रकार के डेटाबेस से कनेक्ट करने और उनके खिलाफ एक समान, ऑब्जेक्ट ओरिएंटेड तरीके से प्रश्नों को निष्पादित करने की अनुमति देता है।

वाक्य - विन्यास

टिप्पणियों

चेतावनी lastInsertId() का उपयोग करते समय अपवादों की जांच करना न भूलें। यह निम्नलिखित त्रुटि फेंक सकता है:

SQLSTATE IM001: ड्राइवर इस फ़ंक्शन का समर्थन नहीं करता है

इस विधि का उपयोग करते हुए आपको अपवादों की ठीक से जाँच कैसे करनी चाहिए:

// Retrieving the last inserted id
$id = null;

try {
    $id = $pdo->lastInsertId(); // return value is an integer    
}
catch( PDOException $e ) {
    echo $e->getMessage();
}

मूल पीडीओ कनेक्शन और पुनर्प्राप्ति

PHP 5.0 के बाद से, PDO डेटाबेस एक्सेस लेयर के रूप में उपलब्ध है। यह डेटाबेस अज्ञेयवादी है, और इसलिए निम्न कनेक्शन उदाहरण कोड को किसी भी समर्थित डेटाबेस के लिए बस DSN को बदलकर काम करना चाहिए।

// First, create the database handle

//Using MySQL (connection via local socket):
$dsn = "mysql:host=localhost;dbname=testdb;charset=utf8";

//Using MySQL (connection via network, optionally you can specify the port too):
//$dsn = "mysql:host=127.0.0.1;port=3306;dbname=testdb;charset=utf8";

//Or Postgres
//$dsn = "pgsql:host=localhost;port=5432;dbname=testdb;";

//Or even SQLite
//$dsn = "sqlite:/path/to/database"

$username = "user";
$password = "pass";
$db = new PDO($dsn, $username, $password);

// setup PDO to throw an exception if an invalid query is provided
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Next, let's prepare a statement for execution, with a single placeholder
$query = "SELECT * FROM users WHERE class = ?";
$statement = $db->prepare($query);

// Create some parameters to fill the placeholders, and execute the statement
$parameters = [ "221B" ];
$statement->execute($parameters);

// Now, loop through each record as an associative array
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
    do_stuff($row);
}

prepare फ़ंक्शन क्वेरी स्ट्रिंग से एक PDOStatement ऑब्जेक्ट बनाता है। इस लौटे ऑब्जेक्ट पर परिणामों की क्वेरी और पुनर्प्राप्ति का निष्पादन किया जाता है। एक विफलता के मामले में, फ़ंक्शन या तो false रिटर्न करता है या एक exception फेंकता exception (यह निर्भर करता है कि पीडीओ कनेक्शन कैसे कॉन्फ़िगर किया गया था)।

Parameterized क्वेरीज़ के साथ SQL इंजेक्शन को रोकना

एसक्यूएल इंजेक्शन एक तरह का हमला है जो दुर्भावनापूर्ण उपयोगकर्ता को एसक्यूएल क्वेरी को संशोधित करने की अनुमति देता है, इसमें अवांछित कमांड जोड़ता है। उदाहरण के लिए, निम्न कोड असुरक्षित है :

// Do not use this vulnerable code!
$sql = 'SELECT name, email, user_level FROM users WHERE userID = ' . $_GET['user'];
$conn->query($sql);

यह इस स्क्रिप्ट के किसी भी उपयोगकर्ता को मूल रूप से हमारे डेटाबेस को संशोधित करने की अनुमति देता है। उदाहरण के लिए निम्नलिखित क्वेरी स्ट्रिंग पर विचार करें:

page.php?user=0;%20TRUNCATE%20TABLE%20users;

यह हमारे उदाहरण को इस तरह दिखता है

SELECT name, email, user_level FROM users WHERE userID = 0; TRUNCATE TABLE users;

हालांकि यह एक चरम उदाहरण है (अधिकांश SQL इंजेक्शन हमले डेटा को हटाने का लक्ष्य नहीं रखते हैं, और न ही अधिकांश PHP क्वेरी निष्पादन फ़ंक्शन बहु-क्वेरी का समर्थन करते हैं), यह एक उदाहरण है कि लापरवाह विधानसभा द्वारा SQL इंजेक्शन हमले को कैसे संभव बनाया जा सकता है पूछताछ। दुर्भाग्य से, इस तरह के हमले बहुत आम हैं, और कोडर्स के कारण अत्यधिक प्रभावी हैं जो अपने डेटा की सुरक्षा के लिए उचित सावधानी बरतने में विफल रहते हैं।

SQL इंजेक्शन को होने से रोकने के लिए, तैयार किए गए कथन अनुशंसित समाधान हैं। सीधे क्वेरी के लिए उपयोगकर्ता डेटा को बदलने के बजाय, एक प्लेसहोल्डर का उपयोग किया जाता है। फिर डेटा को अलग से भेजा जाता है, जिसका अर्थ है कि निर्देशों के सेट के लिए उपयोगकर्ता डेटा को भ्रमित करने वाले SQL इंजन का कोई मौका नहीं है।

जबकि यहाँ विषय पीडीओ है, कृपया ध्यान दें कि PHP MySQLi एक्सटेंशन भी तैयार कथनों का समर्थन करता है

पीडीओ दो प्रकार के प्लेसहोल्डर्स का समर्थन करता है (प्लेसहोल्डर का उपयोग कॉलम या टेबल के नाम के लिए नहीं किया जा सकता है, केवल मान):

  1. नामांकित प्लेसहोल्डर। एक बृहदान्त्र ( : ), एक अलग नाम और उसके बाद (जैसे। :user )

    // using named placeholders
    $sql = 'SELECT name, email, user_level FROM users WHERE userID = :user';
    $prep = $conn->prepare($sql);
    $prep->execute(['user' => $_GET['user']]); // associative array
    $result = $prep->fetchAll();
    
  2. पारंपरिक एसक्यूएल स्थिति प्लेसहोल्डर, के रूप में प्रतिनिधित्व किया ? :

    // using question-mark placeholders
    $sql = 'SELECT name, user_level FROM users WHERE userID = ? AND user_level = ?';
    $prep = $conn->prepare($sql);
    $prep->execute([$_GET['user'], $_GET['user_level']]); // indexed array
    $result = $prep->fetchAll();
    

यदि आपको कभी भी तालिका या स्तंभ नामों को गतिशील रूप से बदलने की आवश्यकता है, तो जान लें कि यह आपके स्वयं के सुरक्षा जोखिमों और बुरे अभ्यास पर है। हालांकि, यह स्ट्रिंग संघनन द्वारा किया जा सकता है। इस तरह के प्रश्नों की सुरक्षा में सुधार करने का एक तरीका अनुमत मूल्यों की एक तालिका निर्धारित करना और उस मूल्य की तुलना करना है जिसे आप इस तालिका में संक्षिप्त करना चाहते हैं।

ध्यान रखें कि केवल DSN के माध्यम से कनेक्शन चारसेट सेट करना महत्वपूर्ण है, अन्यथा यदि कुछ विषम एन्कोडिंग का उपयोग किया जाता है, तो आपका एप्लिकेशन अस्पष्ट अस्पष्टता से ग्रस्त हो सकता है। 5.3.6 से पहले के पीडीओ संस्करणों के लिए डीएसएन के माध्यम से चारसेटिंग सेटिंग उपलब्ध नहीं है और इस प्रकार PDO::ATTR_EMULATE_PREPARES विशेषता को सेट करने के बाद कनेक्शन को false लिए एकमात्र विकल्प उपलब्ध है।

$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

इसके कारण पीडीओ अंतर्निहित डीबीएमएस के मूल तैयार किए गए कथनों का उपयोग करने के बजाय इसका अनुकरण करता है।

हालाँकि, ध्यान रखें कि पीडीओ उन बयानों का अनुकरण करने के लिए चुपचाप वापस आ जाएगा जो MySQL मूल रूप से तैयार नहीं कर सकते हैं: जिन्हें यह मैनुअल ( स्रोत ) में सूचीबद्ध किया जा सकता है।

PDO: MySQL / MariaDB सर्वर से कनेक्ट हो रहा है

MySQL / MariaDB सर्वर से जुड़ने के दो तरीके हैं, जो आपके बुनियादी ढांचे पर निर्भर करता है।

मानक (टीसीपी / आईपी) कनेक्शन

$dsn = 'mysql:dbname=demo;host=server;port=3306;charset=utf8';
$connection = new \PDO($dsn, $username, $password);

// throw exceptions, when SQL error is caused
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// prevent emulation of prepared statements
$connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

चूंकि पीडीओ को पुराने MySQL सर्वर संस्करणों के साथ संगत करने के लिए डिज़ाइन किया गया था (जिसमें तैयार बयानों के लिए समर्थन नहीं था), आपको स्पष्ट रूप से अनुकरण को अक्षम करना होगा। अन्यथा, आप अतिरिक्त इंजेक्शन रोकथाम लाभ खो देंगे, जो आमतौर पर तैयार किए गए बयानों का उपयोग करके दिए जाते हैं।

एक अन्य डिज़ाइन समझौता, जिसे आपको ध्यान में रखना है, वह है डिफॉल्ट एरर हैंडलिंग व्यवहार। यदि अन्यथा कॉन्फ़िगर नहीं किया गया है, तो पीडीओ SQL त्रुटियों का कोई संकेत नहीं दिखाएगा।

दृढ़ता से इसे "अपवाद मोड" पर सेट करने की अनुशंसा की जाती है, क्योंकि यह आपको अतिरिक्त कार्यक्षमता प्राप्त करता है, जब दृढ़ता सार लिखते हैं (उदाहरण के लिए: अपवाद होने पर, UNIQUE बाधा का उल्लंघन करते समय)।

सॉकेट कनेक्शन

$dsn = 'mysql:unix_socket=/tmp/mysql.sock;dbname=demo;charset=utf8';
$connection = new \PDO($dsn, $username, $password);

// throw exceptions, when SQL error is caused
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// prevent emulation of prepared statements
$connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

यूनिक्स जैसी प्रणालियों पर, यदि होस्ट नाम 'localhost' , तो सर्वर से कनेक्शन डोमेन सॉकेट के माध्यम से बनाया जाता है।

डेटाबेस लेनदेन पीडीओ के साथ

डेटाबेस लेनदेन यह सुनिश्चित करता है कि डेटा परिवर्तन का एक सेट केवल स्थायी हो जाएगा यदि प्रत्येक कथन सफल होता है। लेन-देन के दौरान किसी भी क्वेरी या कोड की विफलता को पकड़ा जा सकता है और आपके पास फिर प्रयास किए गए परिवर्तनों को वापस रोल करने का विकल्प होता है।

पीडीओ शुरुआत, कमिटिंग, और वापस लेन-देन के लिए सरल तरीके प्रदान करता है।

$pdo = new PDO(
    $dsn, 
    $username, 
    $password, 
    array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);

try {
    $statement = $pdo->prepare("UPDATE user SET name = :name");

    $pdo->beginTransaction();

    $statement->execute(["name"=>'Bob']);
    $statement->execute(["name"=>'Joe']);

    $pdo->commit();
} 
catch (\Exception $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollback();
        // If we got here our two data updates are not in the database
    }
    throw $e;
}

लेन-देन के दौरान किए गए किसी भी डेटा परिवर्तन केवल सक्रिय कनेक्शन के लिए दिखाई देते हैं। SELECT विवरण बदल गए परिवर्तनों को वापस कर देंगे भले ही वे अभी तक डेटाबेस के लिए प्रतिबद्ध न हों।

नोट : लेन-देन समर्थन के विवरण के लिए डेटाबेस विक्रेता प्रलेखन देखें। कुछ सिस्टम लेनदेन का समर्थन नहीं करते हैं। कुछ नेस्टेड लेनदेन का समर्थन करते हैं जबकि अन्य नहीं करते हैं।

व्यावहारिक उदाहरण पीडीओ के साथ लेनदेन का उपयोग करना

निम्नलिखित अनुभाग में एक व्यावहारिक वास्तविक दुनिया उदाहरण दिखाया गया है जहां लेनदेन का उपयोग डेटाबेस की स्थिरता सुनिश्चित करता है।

निम्नलिखित परिदृश्य की कल्पना करें, मान लें कि आप एक ई-कॉमर्स वेबसाइट के लिए एक शॉपिंग कार्ट का निर्माण कर रहे हैं और आपने दो डेटाबेस टेबलों में ऑर्डर रखने का फैसला किया है। फ़ील्ड्स के साथ एक नाम के orders order_id , name , address , telephone और created_at । और एक दूसरे ने orders_products साथ orders_products नाम दिया है order_id , product_id और quantity । पहली तालिका में ऑर्डर का मेटाडेटा है जबकि दूसरा वास्तविक उत्पाद है जो ऑर्डर किए गए हैं।

डेटाबेस के लिए एक नया आदेश सम्मिलित करना

डेटाबेस में एक नया ऑर्डर डालने के लिए आपको दो काम करने होंगे। सबसे पहले आप की जरूरत है INSERT अंदर एक नया रिकार्ड orders तालिका है कि आदेश (के मेटाडाटा में शामिल होंगे name , address , आदि)। और फिर आप की जरूरत है INSERT में एक रिकॉर्ड orders_products उत्पादों है कि आदेश में शामिल हैं में से हर एक के लिए मेज,।

आप निम्नलिखित के समान कुछ करके ऐसा कर सकते हैं:

// Insert the metadata of the order into the database
$preparedStatement = $db->prepare(
    'INSERT INTO `orders` (`name`, `address`, `telephone`, `created_at`)
     VALUES (:name, :address, :telephone, :created_at)'
);

$preparedStatement->execute([
    'name' => $name,
    'address' => $address,
    'telephone' => $telephone,
    'created_at' => time(),
]);

// Get the generated `order_id`
$orderId = $db->lastInsertId();

// Construct the query for inserting the products of the order
$insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`, `product_id`, `quantity`) VALUES';

$count = 0;
foreach ( $products as $productId => $quantity ) {
    $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ', :quantity' . $count . ')';
    
    $insertProductsParams['order_id' . $count] = $orderId;
    $insertProductsParams['product_id' . $count] = $productId;
    $insertProductsParams['quantity' . $count] = $quantity;
    
    ++$count;
}

// Insert the products included in the order into the database
$preparedStatement = $db->prepare($insertProductsQuery);
$preparedStatement->execute($insertProductsParams);

यह डेटाबेस में एक नया आदेश डालने के लिए बहुत अच्छा काम करेगा, जब तक कि कुछ अप्रत्याशित न हो जाए और किसी कारण से दूसरा INSERT क्वेरी विफल हो जाए। यदि ऐसा होता है, तो आप orders तालिका के अंदर एक नया ऑर्डर समाप्त कर देंगे, जिसमें इससे संबंधित कोई उत्पाद नहीं होगा। सौभाग्य से, फिक्स बहुत सरल है, आपको केवल एक डेटाबेस लेनदेन के रूप में प्रश्नों को करना है।

लेन-देन के साथ डेटाबेस में एक नया आदेश सम्मिलित करना

PDO का उपयोग करते हुए लेन-देन शुरू करने के लिए आपको अपने डेटाबेस में किसी भी प्रश्न को निष्पादित करने से पहले beginTransaction विधि को कॉल करना beginTransaction । फिर आप INSERT और / या UPDATE प्रश्नों को निष्पादित करके अपने डेटा में कोई भी परिवर्तन करना चाहते हैं। और अंत में आप परिवर्तनों को स्थायी बनाने के लिए PDO ऑब्जेक्ट की commit पद्धति को कॉल करते commit । जब तक आप commit विधि को कॉल नहीं करते commit , तब तक आपके डेटा में किए गए हर परिवर्तन को इस बिंदु तक स्थायी नहीं किया जाता है, और आसानी से PDO ऑब्जेक्ट के rollback विधि को कॉल करके आसानी से वापस किया जा सकता है।

निम्नलिखित उदाहरण पर डेटाबेस में एक नया आदेश डालने के लिए लेनदेन के उपयोग का प्रदर्शन किया जाता है, जबकि एक ही समय में डेटा की स्थिरता सुनिश्चित करता है। यदि दो प्रश्नों में से एक विफल हो जाता है तो सभी परिवर्तन वापस हो जाएंगे।

// In this example we are using MySQL but this applies to any database that has support for transactions
$db = new PDO('mysql:host=' . $host . ';dbname=' . $dbname . ';charset=utf8', $username, $password);    

// Make sure that PDO will throw an exception in case of error to make error handling easier
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    // From this point and until the transaction is being committed every change to the database can be reverted
    $db->beginTransaction();    
    
    // Insert the metadata of the order into the database
    $preparedStatement = $db->prepare(
        'INSERT INTO `orders` (`order_id`, `name`, `address`, `created_at`)
         VALUES (:name, :address, :telephone, :created_at)'
    );
    
    $preparedStatement->execute([
        'name' => $name,
        'address' => $address,
        'telephone' => $telephone,
        'created_at' => time(),
    ]);
    
    // Get the generated `order_id`
    $orderId = $db->lastInsertId();

    // Construct the query for inserting the products of the order
    $insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`, `product_id`, `quantity`) VALUES';
    
    $count = 0;
    foreach ( $products as $productId => $quantity ) {
        $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ', :quantity' . $count . ')';
        
        $insertProductsParams['order_id' . $count] = $orderId;
        $insertProductsParams['product_id' . $count] = $productId;
        $insertProductsParams['quantity' . $count] = $quantity;
        
        ++$count;
    }
    
    // Insert the products included in the order into the database
    $preparedStatement = $db->prepare($insertProductsQuery);
    $preparedStatement->execute($insertProductsParams);
    
    // Make the changes to the database permanent
    $db->commit();
}
catch ( PDOException $e ) { 
    // Failed to insert the order into the database so we rollback any changes
    $db->rollback();
    throw $e;
}

पीडीओ: एक प्रश्न द्वारा प्रभावित पंक्तियों की संख्या प्राप्त करें

हम $db से शुरू करते हैं, जो PDO वर्ग का एक उदाहरण है। किसी क्वेरी को निष्पादित करने के बाद हम अक्सर उन पंक्तियों की संख्या निर्धारित करना चाहते हैं जो इससे प्रभावित हुई हैं। rowCount() की विधि PDOStatement अच्छी तरह से काम करेगा:

$query = $db->query("DELETE FROM table WHERE name = 'John'");
$count = $query->rowCount();

echo "Deleted $count rows named John";

नोट: इस विधि का उपयोग केवल INSERT, DELETE और UPDATE कथनों से प्रभावित पंक्तियों की संख्या निर्धारित करने के लिए किया जाना चाहिए। हालाँकि यह विधि SELECT स्टेटमेंट के लिए भी काम कर सकती है, लेकिन यह सभी डेटाबेस के अनुरूप नहीं है।

पीडीओ :: lastInsertId ()

आपको अक्सर एक पंक्ति के लिए ऑटो वेतन वृद्धि आईडी मूल्य प्राप्त करने की आवश्यकता हो सकती है जिसे आपने अपने डेटाबेस तालिका में डाला है। आप इसे LastInsertId () विधि से प्राप्त कर सकते हैं।

// 1. Basic connection opening (for MySQL)
$host = 'localhost';
$database = 'foo';
$user = 'root'
$password = '';
$dsn = "mysql:host=$host;dbname=$database;charset=utf8";
$pdo = new PDO($dsn, $user, $password);

// 2. Inserting an entry in the hypothetical table 'foo_user'
$query = "INSERT INTO foo_user(pseudo, email) VALUES ('anonymous', '[email protected]')";
$query_success = $pdo->query($query);

// 3. Retrieving the last inserted id
$id = $pdo->lastInsertId(); // return value is an integer

Postgresql और oracle में, RETURNING Keyword है, जो वर्तमान में डाले गए या संशोधित पंक्तियों के निर्दिष्ट कॉलमों को लौटाता है। एक प्रविष्टि डालने के लिए यहाँ उदाहरण है:

// 1. Basic connection opening (for PGSQL)
$host = 'localhost';
$database = 'foo';
$user = 'root'
$password = '';
$dsn = "pgsql:host=$host;dbname=$database;charset=utf8";
$pdo = new PDO($dsn, $user, $password);

// 2. Inserting an entry in the hypothetical table 'foo_user'
$query = "INSERT INTO foo_user(pseudo, email) VALUES ('anonymous', '[email protected]') RETURNING id";
$statement = $pdo->query($query);

// 3. Retrieving the last inserted id
$id = $statement->fetchColumn();  // return the value of the id column of the new row in foo_user


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow