2011-12-01 9 views
10

मैं mysqli के चारों ओर एक रैपर फ़ंक्शन बना रहा हूं ताकि मेरा एप्लिकेशन डेटाबेस-हैंडलिंग कोड से अत्यधिक जटिल न हो। इसका हिस्सा mysqli :: bind_param() का उपयोग कर SQL कॉल को पैरामीटर करने के लिए कोड का एक छोटा सा कोड है। bind_param(), जैसा कि आप जानते हैं, संदर्भों की आवश्यकता है। चूंकि यह एक अर्द्ध सामान्य आवरण है, मैं अंत में इस कॉल कर:संदर्भों का मेरा PHP सरणी "जादुई रूप से" मानों की सरणी बन रहा है ... क्यों?

call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs); 

और मुझे एक त्रुटि संदेश मिलता है:

Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given 

उपरोक्त चर्चा जो लोग कहेंगे Forstall है "आप डॉन ' आपके उदाहरण में संदर्भों की आवश्यकता नहीं है "।

मेरे "असली" कोड में थोड़ा और अधिक जटिल की तुलना में किसी को भी पढ़ने के लिए चाहता है, तो मैं निम्नलिखित (उम्मीद) निदर्शी उदाहरण में कोड इस त्रुटि के लिए अग्रणी उबला हुआ है:

class myclass { 
    private $myarray = array(); 

    function setArray($vals) { 
    foreach ($vals as $key => &$value) { 
     $this->myarray[] =& $value; 
    } 
    $this->dumpArray(); 
    } 
    function dumpArray() { 
    var_dump($this->myarray); 
    } 
} 

function myfunc($vals) { 
    $obj = new myclass; 
    $obj->setArray($vals); 
    $obj->dumpArray(); 
} 

myfunc(array('key1' => 'val1', 
      'key2' => 'val2')); 

समस्या ऐसा प्रतीत होता है कि, myfunc() में, setArray() को कॉल करने के बीच और dumpArray() को कॉल करने के बीच, $ obj-> myarray में सभी तत्व संदर्भ होने से रोकते हैं और इसके बजाय मूल्य बन जाते हैं। यह आसानी से उत्पादन को देखकर देखा जा सकता है:

array(2) { 
    [0]=> 
    &string(4) "val1" 
    [1]=> 
    &string(4) "val2" 
} 
array(2) { 
    [0]=> 
    string(4) "val1" 
    [1]=> 
    string(4) "val2" 
} 

ध्यान दें कि सरणी उत्पादन यहां की पहली छमाही में "सही" स्थिति में है। अगर ऐसा करने में यह समझ में आया, तो मैं उस बिंदु पर अपना bind_param() कॉल कर सकता था, और यह काम करेगा। दुर्भाग्यवश, आउटपुट के उत्तरार्ध में कुछ तोड़ता है। सरणी मान प्रकारों पर "" की कमी पर ध्यान दें।

मेरे संदर्भों के साथ क्या हुआ? मेरे द्वारा इसे होने से कैसे रोका जा सकता है? मुझे "PHP बग" कॉल करने से नफरत है जब मैं वास्तव में एक भाषा विशेषज्ञ नहीं हूं, लेकिन क्या यह एक हो सकता है? यह मेरे लिए बहुत अजीब लगता है। मैं फिलहाल अपने परीक्षण के लिए PHP 5.3.8 का उपयोग कर रहा हूं।


संपादित करें:

function setArray(&$vals) { 

मैं करने के लिए इस टिप्पणी जोड़ रहा:

के रूप में एक से अधिक व्यक्ति ने कहा, ठीक setArray() को बदलने के संदर्भ द्वारा अपने तर्क को स्वीकार करना है दस्तावेज क्यों काम करता है।

पीएचपी आम तौर पर, और विशेष रूप से mysqli, "संदर्भ" क्या है, इसकी एक छोटी अवधारणा है। इस उदाहरण का निरीक्षण करें:

$a = "foo"; 
$b = array(&$a); 
$c = array(&$a); 
var_dump($b); 
var_dump($c); 

सबसे पहले, मुझे यकीन है कि आप सोच रहे हैं कि मैं क्यों अदिश चर के बजाय सरणियों का उपयोग कर रहा हूँ - क्योंकि var_dump() क्या एक अदिश के कोई संकेत नहीं दिखाता है यह एक संदर्भ है, लेकिन यह सरणी सदस्यों के लिए करता है।

वैसे भी, इस बिंदु पर, $ बी [0] और $ सी [0] दोनों $ संदर्भ हैं। अब तक सब ठीक है. अब हम कार्यों में हमारी पहली रिंच फेंक:

unset($a); 
var_dump($b); 
var_dump($c); 

$ ख [0] और $ ग [0] दोनों अभी भी एक ही बात के लिए संदर्भ। अगर हम एक बदलते हैं, तो दोनों अभी भी बदल जाएंगे। लेकिन वे क्या संदर्भ हैं? स्मृति में कुछ अज्ञात स्थान। बेशक, कचरा संग्रह बीमा करता है कि हमारा डेटा सुरक्षित है, और तब तक रहेगा जब तक कि हम इसे प्रतिबिंबित नहीं करते।

हमारी अगली चाल के लिए, हम ऐसा करते हैं:

unset($b); 
var_dump($c); 

अब $ ग [0] हमारे आंकड़ों के ही शेष संदर्भ है। और, वाह! जादुई रूप से, यह अब "संदर्भ" नहीं है। Var_dump() के माप से नहीं, और mysqli :: bind_param() के माप से नहीं।

PHP manual कहता है कि डेटा के प्रत्येक टुकड़े पर एक अलग ध्वज 'is_ref' है। '(Refcount> 1)' हालांकि, इस परीक्षण बताते हैं कि 'is_ref' वास्तव में के बराबर है प्रकट होता है

मस्ती के लिए, आप इस प्रकार इस खिलौने उदाहरण में बदलाव कर सकते हैं:

$a = array("foo"); 
$b = array(&$a[0]); 
$c = array(&$a[0]); 

var_dump($a); 
var_dump($b); 
var_dump($c); 

कि नोट तीनों सरणी के सदस्यों पर संदर्भ चिह्न है, जो इस विचार का समर्थन करता है कि 'is_ref' कार्यात्मक रूप से '(refcount> 1)' के बराबर है।

यह मेरे बाहर है क्यों mysqli :: bind_param() पहली जगह में इस भेद की परवाह करेगा (या शायद यह call_user_func_array() ... किसी भी तरह से), लेकिन ऐसा लगता है कि हमें "वास्तव में" क्या सुनिश्चित करने की आवश्यकता है यह है कि संदर्भ गणना कम से कम $ के प्रत्येक सदस्य के लिए है-> bindArgs हमारे call_user_func_array() कॉल में (पोस्ट/प्रश्न की शुरुआत शुरू करें)। और ऐसा करने का सबसे आसान तरीका (इस मामले में) setArray() पास-बाय-रेफरेंस बनाना है।


संपादित करें:

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

तो। मैं निम्नलिखित दावा करता हूं: PHP में, call_user_func_array() (और शायद अधिक) के प्रयोजन के लिए, यह कहकर कि एक डेटा आइटम "संदर्भ" एक ही बात है जैसा कि कह रहा है कि आइटम की संदर्भ संख्या 2 से अधिक या बराबर है (बराबर-मान scalars के लिए PHP के आंतरिक स्मृति अनुकूलन अनदेखी)


administrivia टिप्पणी: मैं के रूप में वह पहली बार सही जवाब का सुझाव देना था जवाब के लिए मारियो साइट श्रेय देना अच्छा लगेगा, लेकिन जब से वह एक टिप्पणी में लिखा था, न कि वास्तविक जवाब है, मैं इसे नहीं कर सका :-(

+1

wouldn ' 'setArray()' विधि को संदर्भ पैरामीटर के रूप में सरणी प्राप्त करने की भी आवश्यकता है? अन्यथा आप केवल एक अस्थायी सरणी के संदर्भ बना रहे हैं। – mario

+0

@ मारियो: आप, महोदय, बिल्कुल सही हैं, इसमें यह मेरी समस्या को ठीक करता है। जो सवाल पूछता है ... _Why_ क्या इससे समस्या ठीक हो जाती है? मैं अभी भी संदर्भ के लिए अस्थायी सरणी के संदर्भ की अपेक्षा करता हूं, केवल उस संदर्भ का संदर्भ जो केवल संदर्भ के कारण स्मृति में हो रहा है। मुझे लगता है कि मुझे PHP मेमोरी प्रबंधन पर पढ़ना चाहिए, अगर मुझे ऐसा दस्तावेज मिल सकता है। –

+0

हां, setArray फ़ंक्शन – malletjo

उत्तर

4

एक संदर्भ के रूप सरणी दर्रा:

function setArray(&$vals) { 
    foreach ($vals as $key => &$value) { 
     $this->myarray[] =& $value; 
    } 
    $this->dumpArray(); 
    } 

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

2

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

function setArray(&$vals) { 
// ... 
} 

CodePad

0

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

अब, किसी और जो कुछ इस तरह जाता है के लिए, मेरा मानना ​​है कि मैं सबसे सरल समाधान है: बस सरणी गुजर call_user_func_array करने के लिए() से पहले ही के लिए एक संदर्भ के लिए प्रत्येक प्रासंगिक सरणी तत्व निर्धारित किया है।

मुझे यकीन नहीं है कि आंतरिक रूप से क्या होता है क्योंकि ऐसा लगता है कि यह काम नहीं करना चाहिए, लेकिन तत्व फिर से एक संदर्भ बन जाता है (जिसे आप var_dump() के साथ देख सकते हैं), और call_user_func_array() तब तर्क को पास करता है अपेक्षित के रूप में संदर्भ। यह फ़िक्स काम करने लगता है भले ही सरणी तत्व पहले से ही एक संदर्भ है, इसलिए आपको पहले जांचना नहीं है।

रिक की कोड में, यह कुछ इस तरह होगा (सब कुछ के बाद bind_param के लिए पहला तर्क संदर्भ के द्वारा होता है, तो मैं पहले एक छोड़ सकते हैं और उसके बाद लोगों के सभी ठीक):

for ($i = 1, $count = count($this->bindArgs); $i < $count; $i++) { 
    $this->bindArgs[$i] = &$this->bindArgs[$i]; 
} 

call_user_func_array(array($stmt, 'bind_param'), $this->bindArgs); 
संबंधित मुद्दे