लिमट क्लॉज में बाइंडवैल्यू पद्धति कैसे लागू करें?


117

यहाँ मेरे कोड का एक स्नैपशॉट है:

$fetchPictures = $PDO->prepare("SELECT * 
    FROM pictures 
    WHERE album = :albumId 
    ORDER BY id ASC 
    LIMIT :skip, :max");

$fetchPictures->bindValue(':albumId', $_GET['albumid'], PDO::PARAM_INT);

if(isset($_GET['skip'])) {
    $fetchPictures->bindValue(':skip', trim($_GET['skip']), PDO::PARAM_INT);    
} else {
    $fetchPictures->bindValue(':skip', 0, PDO::PARAM_INT);  
}

$fetchPictures->bindValue(':max', $max, PDO::PARAM_INT);
$fetchPictures->execute() or die(print_r($fetchPictures->errorInfo()));
$pictures = $fetchPictures->fetchAll(PDO::FETCH_ASSOC);

मुझे मिला

आपके SQL सिंटैक्स में कोई त्रुटि है; सही लाइन पर '' 15 ', 15' के पास उपयोग करने के लिए अपने MySQL सर्वर संस्करण से मेल खाने वाले मैनुअल की जाँच करें 1

ऐसा लगता है कि पीडीओ SQL कोड के लिमिट भाग में मेरे चरों में एकल उद्धरण जोड़ रहा है। मैंने इसे देखा मैंने पाया कि यह बग जो मुझे लगता है कि संबंधित है: http://bugs.php.net/bug.php?id/44339

कि मैं क्या देख रहा हूँ? यह बग अप्रैल 2008 से खोला गया है! इस बीच हम क्या करने वाले हैं?

मुझे कुछ पेजिनेशन बनाने की जरूरत है, और यह सुनिश्चित करने की आवश्यकता है कि एसक्यूएल स्टेटमेंट भेजने से पहले डेटा साफ, sql injection-safe हो।



जवाबों:


165

मुझे याद है कि यह समस्या पहले थी। बाइंड फ़ंक्शन को पास करने से पहले एक पूर्णांक को मान दें। मुझे लगता है कि यह इसे हल करता है।

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);

37
धन्यवाद! लेकिन PHP 5.3 में, उपरोक्त कोड ने एक त्रुटि कहा "घातक त्रुटि: संदर्भ द्वारा पैरामीटर 2 को पारित नहीं कर सकता"। यह वहाँ एक int कास्टिंग पसंद नहीं है। इसके बजाय (int) trim($_GET['skip']), प्रयास करें intval(trim($_GET['skip']))
विल मार्टिन

5
अच्छा होगा यदि किसी ने स्पष्टीकरण दिया कि ऐसा क्यों है ... एक डिजाइन / सुरक्षा (या अन्य) के दृष्टिकोण से।
रॉस

6
यह केवल तभी काम करेगा जब तैयार किए गए स्टेटमेंट्स सक्षम हों । यह अक्षम हो जाएगा अगर यह अक्षम है (और इसे अक्षम किया जाना चाहिए!)
मदार का भूत

4
@ मैं इसका विशेष रूप से उत्तर नहीं दे सकता- लेकिन मैं यह बता सकता हूं कि लिमिट और ओएफएसईटी ऐसे फीचर्स हैं, जो इस सभी PHP / MYSQL / PDO पागलपन से प्रभावित थे, जिन्होंने सर्किट को हिट किया ... वास्तव में, मेरा मानना ​​है कि यह Lerdorf था जो ओवरसॉ था कुछ साल पहले कार्यान्वयन लागू करें। नहीं, यह सवाल का जवाब नहीं देता है, लेकिन यह इंगित करता है कि यह एक आफ्टरमार्केट ऐड-ऑन है, और आप जानते हैं कि वे कितनी अच्छी तरह से कभी-कभी बाहर काम कर सकते हैं ....
फ्रेड विवेग्यू

2
@RR PDO मानों की बजाय बंधन की अनुमति नहीं देता - बल्कि चर। यदि आप bindParam (':' कुछ ', 2) को आज़माते हैं, तो आपको एक त्रुटि होगी क्योंकि PDO चर के लिए एक पॉइंटर का उपयोग करता है जो एक संख्या में नहीं हो सकता है (यदि $ i 2 है तो आपके पास $ i की ओर एक सूचक हो सकता है लेकिन मेरी ओर नहीं संख्या 2)।
कृत्जन

44

सबसे सरल उपाय यह होगा कि एमुलेशन मोड को बंद कर दिया जाए। आप इसे केवल निम्न पंक्ति जोड़कर कर सकते हैं

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

साथ ही, पीडीओ कनेक्शन बनाते समय इस मोड को कंस्ट्रक्टर पैरामीटर के रूप में सेट किया जा सकता है । यह एक बेहतर समाधान हो सकता है क्योंकि कुछ रिपोर्ट उनके ड्राइवर setAttribute()फ़ंक्शन का समर्थन नहीं करते हैं।

यह न केवल बाध्यकारी के साथ आपकी समस्या को हल करेगा, बल्कि आपको सीधे मूल्यों को भेजने execute()देगा, जिससे आपका कोड नाटकीय रूप से कम हो जाएगा। यह मानते हुए कि इम्यूलेशन मोड पहले से ही सेट किया गया है, पूरा मामला कोड की आधा दर्जन पंक्तियों को ले जाएगा

$skip = isset($_GET['skip']) ? (int)trim($_GET['skip']) : 0;
$sql  = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$stmt  = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);

SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes... यह मेरे लिए इतना सरल क्यों नहीं है :) जबकि मुझे यकीन है कि यह वहाँ ज्यादातर लोगों को मिलेगा, मेरे मामले में मैंने स्वीकार किए गए उत्तर के समान कुछ का उपयोग करने के लिए समाप्त कर दिया। बस भविष्य के पाठकों के लिए एक सिर!
मैथ्यू जॉनसन

@MatthewJohnson यह कौन सा ड्राइवर है?
आपका कॉमन सेंस

मुझे यकीन नहीं है, लेकिन मैनुअल में यह कहा गया है PDO::ATTR_EMULATE_PREPARES Enables or disables emulation of prepared statements. Some drivers do not support native prepared statements or have limited support for them। यह मेरे लिए एक नया है, लेकिन फिर मैं बस पीडीओ के साथ शुरू कर रहा हूं। आमतौर पर mysqli का उपयोग करते हैं, लेकिन मुझे लगा कि मैं अपने क्षितिज को व्यापक बनाने की कोशिश करूंगा।
मैथ्यू जॉनसन

@MatthewJohnson यदि आप mysql के लिए PDO का उपयोग कर रहे हैं, तो ड्राइवर इस फ़ंक्शन का समर्थन करता है। इसलिए, आपको यह संदेश कुछ गलती के कारण मिल रहा है
आपका कॉमन सेंस

1
यदि आपको ड्राइवर समर्थन समस्या संदेश मिला है, तो फिर से जाँच करें कि क्या आप setAttributeकथन के लिए कॉल करते हैं ($ stm, $ stmt) न कि pdo ऑब्जेक्ट के लिए।
जेहंग अहान

17

बग रिपोर्ट को देखते हुए, निम्नलिखित कार्य कर सकते हैं:

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);

$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);  

लेकिन क्या आपको यकीन है कि आपका आने वाला डेटा सही है? क्योंकि त्रुटि संदेश में, संख्या के बाद केवल एक उद्धरण प्रतीत होता है (जैसा कि पूरी संख्या के विपरीत उद्धरणों में संलग्न है)। यह आपके आने वाले डेटा के साथ एक त्रुटि भी हो सकती है। क्या आप print_r($_GET);पता लगाने के लिए एक कर सकते हैं ?


1
'' 15 ', 15'। पहला नंबर पूरी तरह से उद्धरणों में संलग्न है। दूसरे नंबर पर कोई उद्धरण नहीं है। तो हाँ, डेटा अच्छा है।
नाथन एच।

8

यह सिर्फ सारांश के रूप में।
LIMIT / OFFSET मानों को पैरामीटर करने के लिए चार विकल्प हैं:

  1. ऊपरPDO::ATTR_EMULATE_PREPARES बताए अनुसार अक्षम करें ।

    जो प्रति मूल्यों ->execute([...])को हमेशा तार के रूप में दिखाने से रोकता है ।

  2. मैनुअल ->bindValue(..., ..., PDO::PARAM_INT)पैरामीटर जनसंख्या पर स्विच करें ।

    जो हालांकि -> निष्पादित सूची [] से कम सुविधाजनक है।

  3. बस यहाँ एक अपवाद करें और SQL क्वेरी तैयार करते समय सादे पूर्णांक को प्रक्षेपित करें।

     $limit = intval($limit);
     $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");

    कास्टिंग महत्वपूर्ण है। अधिक सामान्यतः आप ->prepare(sprintf("SELECT ... LIMIT %d", $num))ऐसे उद्देश्यों के लिए उपयोग होते देखते हैं।

  4. यदि आप MySQL का उपयोग नहीं कर रहे हैं, लेकिन उदाहरण के लिए SQLite, या Postgres; आप सीधे SQL में बाध्य पैरामीटर भी डाल सकते हैं।

     SELECT * FROM tbl LIMIT (1 * :limit)

    फिर से, MySQL / MariaDB लिमाइट क्लॉज में अभिव्यक्तियों का समर्थन नहीं करता है। अभी नहीं।


1
मैंने 3 के लिए% d के साथ स्प्रिंटफ () का उपयोग किया होगा, मैं कहूंगा कि यह चर के साथ थोड़ा और स्थिर है।
हकर्रे

हाँ, varfunc कास्ट + इंटरपोलेशन सबसे व्यावहारिक उदाहरण नहीं है। मैं अक्सर {$_GET->int["limit"]}ऐसे मामलों के लिए अपने आलसी का उपयोग करता हूं ।
मारियो

7

के लिये LIMIT :init, :end

आपको उस तरह से बांधने की जरूरत है। अगर आपके पास ऐसा कुछ था तो $req->execute(Array());यह काम नहीं करेगा क्योंकि यह PDO::PARAM_STRसरणी में सभी संस्करणों के लिए डाली जाएगी और LIMITआपके लिए पूरी तरह से एक इंटेगर की आवश्यकता होगी। bindValue या BindParam जैसा आप चाहते हैं।

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);

2

चूंकि किसी ने नहीं बताया कि ऐसा क्यों हो रहा है, मैं एक जवाब जोड़ रहा हूं। यह व्यवहार कर रहा है इसका कारण यह है क्योंकि आप उपयोग कर रहे हैं trim()। यदि आप के लिए PHP मैनुअल को देखो trim, वापसी प्रकार है string। फिर आप इसे पास करने का प्रयास कर रहे हैं PDO::PARAM_INT। इसके आस-पास आने के कुछ तरीके हैं:

  1. उपयोग filter_var($integer, FILTER_VALIDATE_NUMBER_INT)यह सुनिश्चित करने के लिए कि आप पूर्णांक पास कर रहे हैं।
  2. जैसा कि दूसरों ने कहा, उपयोग करना intval()
  3. के साथ कास्टिंग (int)
  4. जाँच करना कि क्या यह एक पूर्णांक है is_int()

बहुत अधिक तरीके हैं, लेकिन यह मूल रूप से मूल कारण है।


3
यह तब भी होता है जब चर हमेशा पूर्णांक होता है।
felwithe

1

bindValue पीडीओ :: PARAM_INT का उपयोग करके ऑफ़सेट और सीमा और यह काम करेगा


-1

// पहले (वर्तमान त्रुटि) $ क्वेरी = ".... सीमा: पी 1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior);

// AFTER (त्रुटि सुधारा) $ क्वेरी = ".... सीमा: पी 1, 30;"; ... $ limiteInferior = (int) $ limiteInferior; $ stmt-> bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);


-1

PDO::ATTR_EMULATE_PREPARES मुझे दे दिया

ड्राइवर इस फ़ंक्शन का समर्थन नहीं करता है: यह ड्राइवर सेटिंग की त्रुटि का समर्थन नहीं करता है।

मेरा वर्कअराउंड $limitएक स्ट्रिंग के रूप में एक वैरिएबल सेट करना था , फिर इसे तैयार स्टेटमेंट में निम्न उदाहरण में संयोजित करें:

$limit = ' LIMIT ' . $from . ', ' . $max_results;
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE company_id = :cid ORDER BY name ASC' . $limit . ';' );
try {
    $stmt->execute( array( ':cid' => $company_id ) );
    ...
}
catch ( Exception $e ) {
    ...
}

-1

PHP के विभिन्न संस्करणों और PDO की विषमताओं के बीच बहुत कुछ चल रहा है। मैंने यहां 3 या 4 तरीकों की कोशिश की, लेकिन काम करने में लिमिट नहीं मिला।
मेरा सुझाव है कि इंट्रीग्रेशन () फिल्टर के साथ स्ट्रिंग फॉर्मेटिंग / कंसट्रक्शन का उपयोग करें :

$sql = 'SELECT * FROM `table` LIMIT ' . intval($limitstart) . ' , ' . intval($num).';';

SQL इंजेक्शन को रोकने के लिए intval () का उपयोग करना बहुत महत्वपूर्ण है, खासकर यदि आप $ _GET या इस तरह से अपनी सीमा प्राप्त कर रहे हैं। यदि आप ऐसा करते हैं कि यह काम करने का सबसे आसान तरीका है।

'पीडीओ में लिमिट के साथ समस्या' के बारे में चर्चा बहुत है, लेकिन मेरा विचार यहाँ है कि पीडीओ परम को कभी भी लिमिट के लिए इस्तेमाल करने के लिए मानसिक रूप से तैयार नहीं किया गया था क्योंकि वे एक त्वरित फ़िल्टर काम करेंगे। फिर भी, यह थोड़ा भ्रामक है क्योंकि दर्शन हमेशा किसी भी एसक्यूएल इंजेक्शन को खुद से फ़िल्टर नहीं करने के लिए किया गया है, बल्कि 'है पीडीओ हैंडल'।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.