Skip to content

Latest commit

 

History

History
339 lines (259 loc) · 29.7 KB

File metadata and controls

339 lines (259 loc) · 29.7 KB

अन्य भाषाएँ:


लाइब्रेरी प्राप्त करना

आप इसे संग्रह के रूप में डाउनलोड कर सकते हैं, इस साइट से क्लोन कर सकते हैं या composer के माध्यम से इंस्टॉल कर सकते हैं (packagist.org का लिंक):

composer require krugozor/database

krugozor/database क्या है?

krugozor/database एक PHP >= 8.0 क्लास लाइब्रेरी है जो PHP एक्सटेंशन mysqli का उपयोग करके MySQL डेटाबेस के साथ सरल, सुविधाजनक, तेज़ और सुरक्षित तरीके से काम करने के लिए है।

जब PHP में पहले से PDO एब्स्ट्रैक्शन और mysqli एक्सटेंशन है तो MySQL के लिए कस्टम क्लास की आवश्यकता क्यों है?

PHP में MySQL डेटाबेस के साथ काम करने वाली सभी लाइब्रेरीज़ की मुख्य कमियाँ हैं:

  • वर्बोसिटी
    • SQL इंजेक्शन को रोकने के लिए, डेवलपर्स के पास दो रास्ते हैं:
      • Prepared statements का उपयोग करना।
      • SQL क्वेरी बॉडी में जाने वाले पैरामीटर्स को मैन्युअल रूप से एस्केप करना। स्ट्रिंग पैरामीटर्स को mysqli_real_escape_string के माध्यम से चलाना और अपेक्षित न्यूमेरिक पैरामीटर्स को संबंधित प्रकारों — int और float में परिवर्तित करना।
    • दोनों दृष्टिकोणों में बड़ी कमियाँ हैं:
      • Prepared statements बेहद वर्बोस हैं। PDO एब्स्ट्रैक्शन या mysqli एक्सटेंशन को "आउट ऑफ द बॉक्स" उपयोग करना, DBMS से डेटा प्राप्त करने के लिए सभी मेथड्स को एग्रीगेट किए बिना बस असंभव है — टेबल से एक वैल्यू प्राप्त करने के लिए कम से कम 5 लाइनें कोड लिखनी होती हैं! और हर क्वेरी के लिए ऐसा!
      • SQL क्वेरी बॉडी में जाने वाले पैरामीटर्स को मैन्युअल रूप से एस्केप करना — इस पर चर्चा भी नहीं की जाती। एक अच्छा प्रोग्रामर एक आलसी प्रोग्रामर होता है। सब कुछ अधिकतम स्वचालित होना चाहिए।
  • डिबगिंग के लिए SQL क्वेरी प्राप्त करने की असंभवता
    • यह समझने के लिए कि प्रोग्राम में SQL क्वेरी क्यों काम नहीं कर रही है, इसे डिबग करना आवश्यक है — लॉजिकल या सिंटैक्स त्रुटि खोजना। त्रुटि खोजने के लिए, SQL क्वेरी को "देखना" आवश्यक है, जिस पर डेटाबेस ने "शिकायत" की, उसके बॉडी में प्रतिस्थापित पैरामीटर्स के साथ। यानी, पूरी तरह से गठित SQL होना। यदि डेवलपर prepared statements के साथ PDO का उपयोग करता है, तो यह... असंभव है! नेटिव लाइब्रेरीज़ में इसके लिए अधिकतम सुविधाजनक तंत्र प्रदान नहीं किए गए हैं। केवल विकृत तरीके अपनाने या डेटाबेस लॉग में देखने के अलावा कुछ नहीं बचता।

समाधान: krugozor/database — MySQL के साथ काम करने के लिए क्लास

  1. वर्बोसिटी को समाप्त करता है — "नेटिव" लाइब्रेरी का उपयोग करते समय एक क्वेरी निष्पादित करने के लिए 3 या अधिक लाइनों के बजाय, आप केवल एक लिखते हैं।
  2. निर्दिष्ट प्लेसहोल्डर टाइप के अनुसार क्वेरी बॉडी में जाने वाले सभी पैरामीटर्स को एस्केप करता है — SQL इंजेक्शन से विश्वसनीय सुरक्षा।
  3. "नेटिव" mysqli एडाप्टर की कार्यक्षमता को प्रतिस्थापित नहीं करता, बल्कि केवल इसे पूरक करता है।
  4. विस्तार योग्य। सार रूप में, लाइब्रेरी केवल पार्सर और SQL इंजेक्शन से गारंटीकृत सुरक्षा के साथ SQL क्वेरी निष्पादन प्रदान करती है। आप लाइब्रेरी की किसी भी क्लास से इनहेरिट कर सकते हैं और लाइब्रेरी के मैकेनिज़्म और mysqli और mysqli_result के मैकेनिज़्म दोनों का उपयोग करके आवश्यक मेथड्स बना सकते हैं।

krugozor/database लाइब्रेरी क्या नहीं है?

विभिन्न डेटाबेस ड्राइवरों के लिए अधिकांश रैपर्स भयानक आर्किटेक्चर के साथ बेकार कोड का ढेर हैं। उनके लेखक, अपने रैपर्स के व्यावहारिक उद्देश्य को स्वयं न समझते हुए, उन्हें क्वेरी बिल्डर्स, ActiveRecord लाइब्रेरीज़ और अन्य ORM समाधानों में बदल देते हैं।

krugozor/database लाइब्रेरी इनमें से कुछ भी नहीं है। यह केवल MySQL DBMS के ढांचे के भीतर सामान्य SQL के साथ काम करने के लिए एक सुविधाजनक उपकरण है — और कुछ नहीं!

placeholders (प्लेसहोल्डर्स) क्या हैं?

Placeholders (प्लेसहोल्डर्स) विशेष टाइप किए गए मार्कर हैं जो SQL क्वेरी स्ट्रिंग में स्पष्ट मानों (क्वेरी पैरामीटर्स) के स्थान पर लिखे जाते हैं। मान स्वयं "बाद में", SQL क्वेरी को निष्पादित करने वाली मुख्य मेथड के बाद के तर्कों के रूप में पास किए जाते हैं:

$result = $db->query(
    "SELECT * FROM `users` WHERE `name` = '?s' AND `age` = ?i",
    "डार्टैगनन", 41
);

placeholders सिस्टम से गुजरे SQL क्वेरी पैरामीटर्स को प्लेसहोल्डर टाइप के आधार पर विशेष एस्केप मैकेनिज़्म द्वारा प्रोसेस किया जाता है। यानी, अब आपको पहले की तरह वैरिएबल्स को mysqli_real_escape_string() जैसे एस्केप फ़ंक्शन्स में रखने या उन्हें न्यूमेरिक टाइप में कन्वर्ट करने की आवश्यकता नहीं है:

<?php
// पहले, DBMS की प्रत्येक क्वेरी से पहले हम
// लगभग यह करते थे (और कई अभी भी "यह" नहीं करते):
$id = (int) $_POST['id'];
$value = mysqli_real_escape_string($mysql, $_POST['value']);
$result = mysqli_query($mysql, "SELECT * FROM `t` WHERE `f1` = '$value' AND `f2` = $id");

अब क्वेरीज़ लिखना आसान, तेज़ हो गया है, और सबसे महत्वपूर्ण बात, krugozor/database लाइब्रेरी सभी संभावित SQL इंजेक्शन को पूरी तरह से रोकती है।

प्लेसहोल्डर सिस्टम का परिचय

प्लेसहोल्डर्स के प्रकार और उनके उद्देश्य नीचे वर्णित हैं। प्लेसहोल्डर प्रकारों से परिचित होने से पहले, यह समझना आवश्यक है कि लाइब्रेरी का मैकेनिज़्म कैसे काम करता है।

PHP की समस्या

PHP एक कमजोर टाइप की भाषा है और इस लाइब्रेरी के विकास के दौरान एक वैचारिक दुविधा उत्पन्न हुई। कल्पना करें कि हमारे पास निम्नलिखित संरचना वाली एक टेबल है:

`name` varchar not null
`flag` tinyint not null

और लाइब्रेरी को (किसी कारण से, संभवतः डेवलपर पर निर्भर नहीं) निम्नलिखित क्वेरी निष्पादित करनी होगी:

$db->query(
    "INSERT INTO `t` SET `name` = '?s', `flag` = ?i",
    null, false
);

इस उदाहरण में, टेक्स्ट not null फ़ील्ड name में null वैल्यू लिखने का प्रयास है, और न्यूमेरिक फ़ील्ड flag में बूलियन टाइप false। इस स्थिति में क्या करें?

  • क्वेरी पैरामीटर्स के सत्यापन की जिम्मेदारी किसकी है - क्लाइंट कोड की या लाइब्रेरी की?
  • क्या इस मामले में प्रोग्राम निष्पादन को बाधित करना आवश्यक है या शायद कुछ मैनिपुलेशन लागू करने चाहिए ताकि डेटा डेटाबेस में लिखा जाए?
  • क्या हम tinyint कॉलम के लिए false वैल्यू को 0 वैल्यू के रूप में व्याख्यायित कर सकते हैं और name कॉलम के लिए null को खाली स्ट्रिंग के रूप में?
  • हम अपने कोड में इस समस्या को कैसे सरल या मानकीकृत कर सकते हैं?

उठाए गए प्रश्नों को ध्यान में रखते हुए, इस लाइब्रेरी में दो ऑपरेशन मोड लागू करने का निर्णय लिया गया।

लाइब्रेरी के ऑपरेशन मोड

  • Mysql::MODE_STRICT — प्लेसहोल्डर टाइप और तर्क टाइप के बीच सख्त मिलान मोडMysql::MODE_STRICT मोड में तर्क टाइप को प्लेसहोल्डर टाइप से मेल खाना चाहिए। उदाहरण के लिए, इंटीजर टाइप प्लेसहोल्डर ?i के लिए तर्क के रूप में 55.5 या '55.5' वैल्यू पास करने का प्रयास एक एक्सेप्शन फेंकेगा:
// सख्त मोड सेट करें
$db->setTypeMode(Mysql::MODE_STRICT);
// यह एक्सप्रेशन निष्पादित नहीं होगा, एक एक्सेप्शन फेंका जाएगा:
// क्वेरी टेम्पलेट "SELECT ?i" में "integer" टाइप के प्लेसहोल्डर के लिए "double" टाइप की वैल्यू निर्दिष्ट करने का प्रयास
$db->query('SELECT ?i', 55.5);
  • Mysql::MODE_TRANSFORM — प्लेसहोल्डर टाइप और तर्क टाइप के बीच बेमेल होने पर तर्क को प्लेसहोल्डर टाइप में परिवर्तित करने का मोड। Mysql::MODE_TRANSFORM मोड डिफ़ॉल्ट रूप से सेट है और एक "सहिष्णु" मोड है — प्लेसहोल्डर टाइप और तर्क टाइप के बीच बेमेल होने पर एक्सेप्शन उत्पन्न नहीं करता, बल्कि PHP भाषा स्वयं के माध्यम से तर्क को आवश्यक प्लेसहोल्डर टाइप में परिवर्तित करने का प्रयास करता है। वैसे, मैं, लाइब्रेरी के लेखक के रूप में, हमेशा इसी मोड का उपयोग करता हूं, सख्त मोड (Mysql::MODE_STRICT) का वास्तविक कार्य में कभी उपयोग नहीं किया, लेकिन शायद आपको इसकी आवश्यकता होगी।

Mysql::MODE_TRANSFORM मोड में निम्नलिखित रूपांतरण की अनुमति है:

  • int टाइप (प्लेसहोल्डर ?i) में परिवर्तित होते हैं
    • फ्लोटिंग पॉइंट नंबर, जो string टाइप और double टाइप दोनों में प्रस्तुत किए जाते हैं
    • bool TRUE को int(1) में, FALSE को int(0) में परिवर्तित किया जाता है
    • null को int(0) में परिवर्तित किया जाता है
  • double टाइप (प्लेसहोल्डर ?d) में परिवर्तित होते हैं
    • पूर्णांक, जो string टाइप और int टाइप दोनों में प्रस्तुत किए जाते हैं
    • bool TRUE को float(1) में, FALSE को float(0) में परिवर्तित किया जाता है
    • null को float(0) में परिवर्तित किया जाता है
  • string टाइप (प्लेसहोल्डर ?s) में परिवर्तित होते हैं
    • bool TRUE को string(1) "1" में, FALSE को string(1) "0" में परिवर्तित किया जाता है। यह व्यवहार PHP में bool से int में टाइप रूपांतरण से भिन्न है, क्योंकि अक्सर, व्यवहार में, बूलियन टाइप को MySQL में संख्या के रूप में लिखा जाता है।
    • numeric टाइप की वैल्यू को PHP रूपांतरण नियमों के अनुसार स्ट्रिंग में परिवर्तित किया जाता है
    • null को string(0) "" में परिवर्तित किया जाता है
  • null टाइप (प्लेसहोल्डर ?n) में परिवर्तित होते हैं
    • कोई भी तर्क।
  • ऐरे, ऑब्जेक्ट और रिसोर्सेज़ के लिए रूपांतरण की अनुमति नहीं है।

लाइब्रेरी में कौन से प्लेसहोल्डर टाइप उपलब्ध हैं?

?i — पूर्णांकों के लिए प्लेसहोल्डर

$db->query(
    'SELECT * FROM `users` WHERE `id` = ?i', 123
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT * FROM `users` WHERE `id` = 123

ध्यान! यदि आप PHP_INT_MAX से अधिक संख्याओं के साथ काम करते हैं, तो:

  • अपने प्रोग्राम में उन्हें केवल स्ट्रिंग्स के रूप में ऑपरेट करें।
  • इस प्लेसहोल्डर का उपयोग न करें, स्ट्रिंग प्लेसहोल्डर ?s का उपयोग करें (नीचे देखें)। बात यह है कि PHP_INT_MAX से अधिक संख्याओं को PHP फ्लोटिंग पॉइंट नंबर के रूप में व्याख्यायित करता है। लाइब्रेरी का पार्सर पैरामीटर को int टाइप में परिवर्तित करने का प्रयास करेगा, परिणामस्वरूप «परिणाम अपरिभाषित होगा, क्योंकि float के पास सही परिणाम वापस करने के लिए पर्याप्त सटीकता नहीं है। इस मामले में न तो चेतावनी और न ही नोटिस जारी किया जाएगा!» — php.net

?d — फ्लोटिंग पॉइंट नंबरों के लिए प्लेसहोल्डर

$db->query(
    'SELECT * FROM `prices` WHERE `cost` IN (?d, ?d)',
    12.56, '12.33'
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT * FROM `prices` WHERE `cost` IN (12.56, 12.33)

ध्यान! यदि आप double डेटा टाइप के साथ काम करने के लिए लाइब्रेरी का उपयोग करते हैं, तो उपयुक्त लोकेल सेट करें ताकि PHP स्तर और DBMS स्तर दोनों पर पूर्णांक और भिन्नात्मक भाग के बीच विभाजक समान हो।

?s — स्ट्रिंग टाइप के लिए प्लेसहोल्डर

तर्क वैल्यूज़ को mysqli::real_escape_string() मेथड से एस्केप किया जाता है:

$db->query(
    'SELECT "?s"',
    "आप सभी मूर्ख हैं, और मैं डार्टैगनन हूं!"
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT "आप सभी मूर्ख हैं, और मैं डार्टैगनन हूं!"

?S — SQL LIKE ऑपरेटर में सम्मिलन के लिए स्ट्रिंग टाइप प्लेसहोल्डर

तर्क वैल्यूज़ को mysqli::real_escape_string() मेथड से एस्केप किया जाता है + LIKE ऑपरेटर में उपयोग किए जाने वाले विशेष वर्णों (% और _) की एस्केपिंग:

$db->query('SELECT "?S"', '% _');

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT "\% \_"

?nNULL टाइप के लिए प्लेसहोल्डर

किसी भी तर्क की वैल्यूज़ को अनदेखा किया जाता है, प्लेसहोल्डर्स को SQL क्वेरी में स्ट्रिंग NULL से प्रतिस्थापित किया जाता है:

$db->query('SELECT ?n', 123);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT NULL

?A* — एसोसिएटिव ऐरे से एसोसिएटिव सेट के लिए प्लेसहोल्डर, की = वैल्यू रूप में जोड़ों का अनुक्रम उत्पन्न करता है

जहां प्रतीक * निम्नलिखित प्लेसहोल्डर्स में से एक है:

  • i (पूर्णांकों के लिए प्लेसहोल्डर)
  • d (फ्लोटिंग पॉइंट नंबरों के लिए प्लेसहोल्डर)
  • s (स्ट्रिंग टाइप के लिए प्लेसहोल्डर)

रूपांतरण और एस्केपिंग के नियम ऊपर वर्णित एकल स्केलर टाइप्स के समान हैं। उदाहरण:

$db->query(
    'INSERT INTO `test` SET ?Ai',
    ['first' => '123', 'second' => 456]
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

INSERT INTO `test` SET `first` = "123", `second` = "456"

?a* — सरल (या एसोसिएटिव) ऐरे से सेट के लिए प्लेसहोल्डर, वैल्यूज़ का अनुक्रम उत्पन्न करता है

जहां * निम्नलिखित टाइप्स में से एक है:

  • i (पूर्णांकों के लिए प्लेसहोल्डर)
  • d (फ्लोटिंग पॉइंट नंबरों के लिए प्लेसहोल्डर)
  • s (स्ट्रिंग टाइप के लिए प्लेसहोल्डर)

रूपांतरण और एस्केपिंग के नियम ऊपर वर्णित एकल स्केलर टाइप्स के समान हैं। उदाहरण:

$db->query(
    'SELECT * FROM `test` WHERE `id` IN (?ai)',
    [123, 456]
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT * FROM `test` WHERE `id` IN ("123", "456")

?A[?n, ?s, ?i, ...] — टाइप और तर्कों की संख्या के स्पष्ट संकेत के साथ एसोसिएटिव सेट के लिए प्लेसहोल्डर, की = वैल्यू जोड़ों का अनुक्रम उत्पन्न करता है

उदाहरण:

$db->query(
    'INSERT INTO `users` SET ?A[?i, "?s"]',
    ['age' => 41, 'name' => "डार्टैगनन"]
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

INSERT INTO `users` SET `age` = 41,`name` = "डार्टैगनन"

?a[?n, ?s, ?i, ...] — टाइप और तर्कों की संख्या के स्पष्ट संकेत के साथ सेट के लिए प्लेसहोल्डर, वैल्यूज़ का अनुक्रम उत्पन्न करता है

उदाहरण:

$db->query(
    'SELECT * FROM `users` WHERE `name` IN (?a["?s", "?s"])',
    ["मार्क्विस डार्किएन", "डार्टैगनन"]
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT * FROM `users` WHERE `name` IN ("मार्क्विस डार्किएन", "डार्टैगनन")

?f — टेबल या फ़ील्ड नाम के लिए प्लेसहोल्डर

यह प्लेसहोल्डर उन मामलों के लिए है जब टेबल या फ़ील्ड का नाम क्वेरी में पैरामीटर के माध्यम से पास किया जाता है। फ़ील्ड और टेबल नामों को "बैकटिक" प्रतीक से घेरा जाता है:

$db->query(
    'SELECT ?f FROM ?f',
    'name',
    'database.table_name'
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT `name` FROM `database`.`table_name`

सीमांकक उद्धरण चिह्न

लाइब्रेरी प्रोग्रामर से SQL सिंटैक्स का पालन करने की अपेक्षा करती है। इसका मतलब है कि निम्नलिखित क्वेरी काम नहीं करेगी:

$db->query(
    'SELECT CONCAT("Hello, ", ?s, "!")',
    'world'
);

— प्लेसहोल्डर ?s को एकल या दोहरे उद्धरण चिह्नों में लिया जाना चाहिए:

$db->query(
    'SELECT concat("Hello, ", "?s", "!")',
    'world'
);

टेम्पलेट रूपांतरण के बाद SQL क्वेरी:

SELECT concat("Hello, ", "world", "!")

जो लोग PDO के साथ काम करने के आदी हैं, उनके लिए यह अजीब लग सकता है, लेकिन एक तंत्र को लागू करना जो यह निर्धारित करे कि किसी मामले में प्लेसहोल्डर वैल्यू को उद्धरण चिह्नों में लेना आवश्यक है या नहीं, एक बहुत ही गैर-तुच्छ कार्य है जिसके लिए एक पूर्ण पार्सर लिखना आवश्यक है।

लाइब्रेरी के साथ काम करने के उदाहरण

फ़ाइल ../console/tests.php में देखें