संदर्भ के लिए PHP फ़ॉरच पास: अंतिम तत्व डुप्लिकेटिंग? (बग?)


159

मैं बस एक साधारण php स्क्रिप्ट के साथ कुछ बहुत ही अजीब व्यवहार कर रहा था जो मैं लिख रहा था। मैंने बग को फिर से बनाने के लिए इसे न्यूनतम आवश्यक कर दिया:

<?php

$arr = array("foo",
             "bar",
             "baz");

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

?>

यह आउटपुट:

Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)
Array
(
    [0] => foo
    [1] => bar
    [2] => bar
)

क्या यह एक बग या कुछ वास्तव में अजीब व्यवहार है जो होने वाला है?


इसे फिर से मान से देखें, देखें कि क्या तीसरा समय बदलता है ...?
शकरॉक 22:11

1
@ शकरॉक, यह मानों द्वारा दोहराए गए छोरों के साथ किसी भी अधिक परिवर्तन को प्रकट नहीं करता है।
रीजेंसी

1
दिलचस्प बात यह है कि यदि आप $ आइटम के अलावा किसी अन्य चीज़ का उपयोग करने के लिए दूसरा लूप बदलते हैं तो यह उम्मीद के मुताबिक काम करता है।
स्टीव क्लेरिज

9
हमेशा लूप बॉडी के अंत में आइटम को अनसेट करें: foreach($x AS &$y){ ... unset($y); }- यह वास्तव में php.net पर है (पता नहीं कहां है) क्योंकि यह बहुत गलती की गई है।
रुडी

जवाबों:


170

पहले फॉरेस्ट लूप के बाद, $itemअभी भी कुछ मूल्य का संदर्भ है जिसका उपयोग भी किया जा रहा है $arr[2]। इसलिए प्रत्येक फॉर्च्यूनर दूसरे लूप में कॉल करता है, जो संदर्भ से कॉल नहीं करता है, उस मूल्य को बदलता है, और इस प्रकार $arr[2], नए मूल्य के साथ।

तो लूप 1, मान और $arr[2]बन गया $arr[0], जो 'फू' है।
लूप 2, मान और $arr[2]बनो $arr[1], जो 'बार' है।
लूप 3, मान और $arr[2]बनें $arr[2], जो 'बार' (लूप 2 के कारण) है।

मूल्य 'बाज' वास्तव में दूसरे फॉरेक्स लूप के पहले कॉल पर खो गया है।

आउटपुट को डीबग करना

लूप के प्रत्येक पुनरावृत्ति के लिए, हम $itemसरणी को प्रिंट करने के साथ-साथ मूल्य को भी प्रतिध्वनित करेंगे $arr

जब पहले लूप के माध्यम से चलाया जाता है, तो हम इस आउटपुट को देखते हैं:

foo
Array ( [0] => foo [1] => bar [2] => baz )

bar
Array ( [0] => foo [1] => bar [2] => baz )

baz
Array ( [0] => foo [1] => bar [2] => baz )

लूप के अंत में, $itemअभी भी उसी स्थान की ओर इशारा कर रहा है $arr[2]

जब दूसरा लूप चलाया जाता है, तो हम इस आउटपुट को देखते हैं:

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

आप देखेंगे कि हर बार सरणी किस प्रकार नया मान डालती है $item, यह भी $arr[3]उसी मान से अपडेट होता है, क्योंकि वे दोनों अभी भी उसी स्थान की ओर इशारा कर रहे हैं। जब लूप ऐरे के तीसरे मान पर जाता है, तो इसमें वह मान होगा barक्योंकि यह उस लूप के पिछले पुनरावृत्ति द्वारा सेट किया गया था।

यह एक बग है?

नहीं, यह एक संदर्भित आइटम का व्यवहार है, और बग नहीं है। यह कुछ चलाने के समान होगा:

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

एक फॉरेस्ट लूप प्रकृति में विशेष नहीं है जिसमें वह संदर्भित वस्तुओं को अनदेखा कर सकता है। यह बस उस चर को हर बार नए मूल्य पर सेट कर रहा है जैसे आप एक लूप के बाहर।


4
मुझे थोड़ा पांडित्य सुधार है। $itemएक संदर्भ नहीं है $arr[2], द्वारा समाहित मूल्य द्वारा $arr[2]संदर्भित मूल्य का संदर्भ है $item। अंतर को स्पष्ट करने के लिए, आप भी परेशान हो सकते हैं $arr[2], और $itemअप्रभावित रहेंगे, और लेखन $itemइसे प्रभावित नहीं करेगा।
पॉल बिगगर 5

2
यह व्यवहार समझने के लिए जटिल है और समस्याओं को जन्म दे सकता है। मैं अपने छात्रों को दिखाने के लिए अपने पसंदीदा में से एक के रूप में रखता हूं कि उन्हें "संदर्भ द्वारा" चीजों को क्यों (जब तक वे कर सकते हैं) से बचना चाहिए।
ओलिवियर पोंस

1
$itemजब फ़ॉरेस्ट लूप बाहर निकलता है तो दायरे से बाहर क्यों नहीं जाता है? यह एक बंद समस्या की तरह लगता है?
jocull

6
@jocull: PHP में, foreach, के लिए, जबकि, आदि अपना दायरा नहीं बनाते हैं।
animuson

1
@jocull, PHP में स्थानीय चर नहीं हैं (ब्लॉक)। इसका एक कारण मुझे परेशान करता है।
क्वाटैक्स

29

$itemएक संदर्भ है $arr[2]और इसे दूसरे फॉरेस्ट लूप द्वारा अधिलेखित किया जा रहा है जैसा कि एनिमूसन ने बताया है।

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

unset($item); // This will fix the issue.

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

3

हालांकि यह आधिकारिक तौर पर एक बग नहीं हो सकता है, मेरी राय में यह है। मुझे लगता है कि हमारे यहाँ समस्या यह है $itemकि लूप से बाहर निकलने के दौरान गुंजाइश से बाहर जाने की उम्मीद है क्योंकि यह बहुत सारी अन्य प्रोग्रामिंग भाषाओं में होगा। हालांकि ऐसा नहीं लगता ...

यह कोड ...

$arr = array('one', 'two', 'three');
foreach($arr as $item){
    echo "$item\n";
}    
echo $item;

आउटपुट देता है ...

one
two
three
three

जैसा कि अन्य लोगों ने पहले ही कहा था, आप संदर्भित चर $arr[2]को अपने दूसरे लूप के साथ अधिलेखित कर रहे हैं , लेकिन यह केवल इसलिए हो रहा है क्योंकि $itemकभी भी दायरे से बाहर नहीं गया था। तुम लोग क्या सोचते हो ... बग?


4
1) बग नहीं। यह पहले से ही मैनुअल में कहा जाता है और कई बग रिपोर्ट में खारिज कर दिया गया था। 2) वास्तव में सवाल ... जवाब नहीं है
BoltClock

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

दुर्भाग्य से, PHP {}सामान्य रूप से छोरों या ब्लॉकों के लिए एक नया दायरा नहीं बनाता है । यह भाषा कैसे काम करती है
फैबियन शेंगलर

0

एक आसान व्याख्या, Rasmus Lerdorf, PHP के मूल निर्माता से लगता है: https://bugs.php.net/bug.php?id=71454


इसी तरह की व्याख्या इस साइट में पहले दी गई थी: stackoverflow.com/a/19829715
qdinar

इसी तरह की व्याख्या इससे पहले Bugs.php.net/bug.php?id=29992 पर rasmus@php.net द्वारा दी गई थी
qdinar

0

पीएचपी का सही व्यवहार मेरे उत्पीड़न में एक नोटिस त्रुटि हो सकता है। यदि फ़ॉरच लूप में बनाया गया संदर्भित चर लूप के बाहर उपयोग किया जाता है, तो यह एक नोटिस का कारण होना चाहिए। इस व्यवहार के लिए गिरना बहुत आसान है, जब यह हुआ तब इसे खोलना बहुत मुश्किल था। और कोई भी डेवलपर foreach प्रलेखन पृष्ठ को पढ़ने नहीं जा रहा है, यह मदद नहीं है।

unset()इस तरह के मुद्दे से बचने के लिए आपको अपने लूप के बाद संदर्भ देना चाहिए । एक संदर्भ पर परेशान () मूल डेटा को नुकसान पहुँचाए बिना संदर्भ को हटा देगा।


0

ऐसा इसलिए है क्योंकि आप रेफरी निर्देश (&) द्वारा उपयोग करते हैं। अंतिम मान दूसरे लूप द्वारा प्रतिस्थापित किया जाएगा और यह आपके सरणी को दूषित करेगा। सबसे सरल उपाय दूसरे लूप के लिए अलग नाम का उपयोग करना है:

foreach ($arr as &$item) { ... }

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