PHP में, एक क्लोजर क्या है और यह "उपयोग" पहचानकर्ता का उपयोग क्यों करता है?


407

मैं कुछ PHP 5.3.0सुविधाओं की जाँच कर रहा हूँ और साइट पर कुछ कोड भर चला है जो काफी मज़ेदार लगता है:

public function getTotal($tax)
{
    $total = 0.00;

    $callback =
        /* This line here: */
        function ($quantity, $product) use ($tax, &$total)
        {
            $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                strtoupper($product));
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);
        };

    array_walk($this->products, $callback);
    return round($total, 2);
}

अनाम फ़ंक्शंस में एक उदाहरण के रूप में ।

क्या किसी को इस बारे में पता है? कोई दस्तावेज? और यह बुराई लगती है, क्या इसका कभी उपयोग किया जाना चाहिए?

जवाबों:


362

यह कैसे PHP एक बंद को व्यक्त करता है । यह बिल्कुल भी बुराई नहीं है और वास्तव में यह काफी शक्तिशाली और उपयोगी है।

मूल रूप से इसका मतलब यह है कि आप अनाम फ़ंक्शन को स्थानीय चर (इस मामले में, $taxऔर इस संदर्भ में $total) को इस दायरे से बाहर "कैप्चर" करने की अनुमति दे रहे हैं और उनके मान (या स्वयं $totalके संदर्भ में $total) को संरक्षित कर सकते हैं। अनाम फ़ंक्शन ही।


1
तो यह केवल बंद करने के लिए उपयोग किया जाता है? आपके स्पष्टीकरण के लिए धन्यवाद, मुझे अनाम फ़ंक्शन और एक क्लोजर के बीच का अंतर नहीं पता था
सीनडाउन

136
useकीवर्ड के लिए भी प्रयोग किया जाता है अलियासिंग नामस्थान । यह आश्चर्यजनक है कि, PHP 5.3.0 की रिलीज के 3 साल से अधिक समय बाद function ... useभी , वाक्यविन्यास अभी भी आधिकारिक रूप से अनिर्दिष्ट है, जो एक अनअक्लोनेटेड सुविधा को बंद कर देता है। डॉक यहां तक ​​कि अनाम कार्यों और बंद को भ्रमित करता है । मैं केवल (बीटा और अनौपचारिक) दस्तावेज use ()php.net पर पा सकता था, क्लोजर के लिए RFC था ।

2
तो PHP में कार्यान्वित फ़ंक्शन का उपयोग कब किया गया था? मुझे लगता है कि यह PHP 5.3 में था? क्या यह अब किसी भी तरह PHP मैनुअल में प्रलेखित है?
रुब77 7

@Mytskine खैर, डॉक्टर के अनुसार, अनाम कार्य बंद वर्ग का उपयोग करते हैं
मैन्नी फ्लेममंड

1
अब useभी एक traitमें शामिल करने के लिए प्रयोग किया जाता है class!
सीजे डेनिस

477

एक सरल जवाब।

function ($quantity) use ($tax, &$total) { .. };

  1. क्लोजर एक फ़ंक्शन है जिसे एक चर को सौंपा गया है, ताकि आप इसे पास कर सकें
  2. एक क्लोजर एक अलग नामस्थान है, आम तौर पर, आप इस नामस्थान के बाहर परिभाषित चर का उपयोग नहीं कर सकते हैं। उपयोग कीवर्ड आता है :
  3. उपयोग आपको बंद के अंदर सफल चर तक पहुंचने (उपयोग) की अनुमति देता है।
  4. उपयोग जल्दी बाध्यकारी है। इसका मतलब है कि वैरिएबल वैल्यू बंद होने के समय को नियंत्रित किया जाता है। इसलिए$taxक्लोजर के अंदरसंशोधनकरने का कोई बाहरी प्रभाव नहीं है, जब तक कि यह एक पॉइंटर नहीं है, जैसे कोई ऑब्जेक्ट है।
  5. आप वेरिएबल्स में पॉइंटर्स के रूप में पास कर सकते हैं जैसे की स्थिति में &$total। इस तरह, $totalDOES का मान बाहरी प्रभाव को संशोधित करता है, मूल चर का मान बदल जाता है।
  6. क्लोजर के अंदर परिभाषित चर, क्लोजर के बाहर से पहुंच योग्य नहीं हैं।
  7. क्लोजर और फ़ंक्शंस में समान गति होती है। हां, आप इनका उपयोग अपनी सभी लिपियों में कर सकते हैं।

जैसा कि @Mytskine ने बताया कि शायद सबसे अच्छा इन-डेप्थ स्पष्टीकरण क्लोजर के लिए RFC है । (इसके लिए उसे अपवोट करें।)


4
$closure = function ($value) use ($localVar as $alias) { //stuff};Parse: syntax error, unexpected 'as' (T_AS), expecting ',' or ')'
यूज़

1
@ KalZekdor, php5.3 के साथ की पुष्टि की, पदावनत लगता है। मैंने उत्तर को अपडेट किया, आपके प्रयास के लिए धन्यवाद।
जुपा ३०'१४

4
मैं # 5 बिंदु को जोड़ना चाहूंगा कि इस तरह, एक पॉइंटर को मान को संशोधित करने का &$totalआंतरिक प्रभाव पड़ता है। दूसरे शब्दों में, यदि आप इसे परिभाषित करने के बाद क्लोजर के $total बाहर के मूल्य को बदलते हैं , तो नया मान केवल एक पॉइंटर होने पर पास हो जाता है।
बिलानोआह

2
@ AndyD273 उद्देश्य वास्तव में बहुत समान है, सिवाय इसके कि globalकेवल वैश्विक नेमस्पेस useतक पहुंच की अनुमति देता है जबकि माता-पिता के नाम स्थान में चर तक पहुंचने की अनुमति देता है। वैश्विक चर को आमतौर पर बुराई माना जाता है। मूल गुंजाइश तक पहुँचने अक्सर एक बंद बनाने का बहुत उद्देश्य है। यह "बुराई" नहीं है क्योंकि यह गुंजाइश बहुत सीमित है। जेएस जैसी अन्य भाषाएं मूल रूप से पैरेंट स्कोप के वेरिएबल का उपयोग करती हैं (पॉइंटर के रूप में, वैल्यू कॉपी के रूप में नहीं)।
zupa

1
इस लाइन ने मेरी दो घंटे की व्यर्थ खोज बंद कर दीYou can pass in variables as pointers like in case of &$total. This way, modifying the value of $total DOES HAVE an external effect, the original variable's value changes.
ब्लैकपूल

69

function () use () {}PHP के लिए बंद करने की तरह है।

बिना use, फ़ंक्शन पैरेंट स्कोप वैरिएबल तक नहीं पहुँच सकता

$s = "hello";
$f = function () {
    echo $s;
};

$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$f(); // hello

useवेरिएबल का मान जब समारोह परिभाषित किया गया है, जब नहीं कहा से है

$s = "hello";
$f = function () use ($s) {
    echo $s;
};

$s = "how are you?";
$f(); // hello

use चर के साथ संदर्भ &

$s = "hello";
$f = function () use (&$s) {
    echo $s;
};

$s = "how are you?";
$f(); // how are you?

4
इसे पढ़ने के बाद मुझे थोड़ा और स्क्रॉल करने का पछतावा नहीं है लेकिन लगता है कि तीसरे ब्लॉक में टाइपो के लिए एक मामूली संपादन की आवश्यकता है। $ Obj के बजाय $ s होना चाहिए।
उपयोगकर्ता

53

बंद सुंदर हैं! वे बहुत सी समस्याओं को हल करते हैं जो गुमनाम कार्यों के साथ आती हैं, और वास्तव में सुरुचिपूर्ण कोड संभव बनाती हैं (कम से कम जब तक हम php के बारे में बात करते हैं)।

जावास्क्रिप्ट प्रोग्रामर हर समय बंद का उपयोग करते हैं, कभी-कभी इसे जाने बिना भी, क्योंकि बाध्य चर स्पष्ट रूप से परिभाषित नहीं होते हैं - यही "उपयोग" php में है।

उपरोक्त से बेहतर वास्तविक दुनिया के उदाहरण हैं। मान लें कि आपको एक उप-मान द्वारा बहुआयामी सरणी को सॉर्ट करना है, लेकिन प्रमुख परिवर्तन।

<?php
    function generateComparisonFunctionForKey($key) {
        return function ($left, $right) use ($key) {
            if ($left[$key] == $right[$key])
                return 0;
            else
                return ($left[$key] < $right[$key]) ? -1 : 1;
        };
    }

    $myArray = array(
        array('name' => 'Alex', 'age' => 70),
        array('name' => 'Enrico', 'age' => 25)
    );

    $sortByName = generateComparisonFunctionForKey('name');
    $sortByAge  = generateComparisonFunctionForKey('age');

    usort($myArray, $sortByName);

    usort($myArray, $sortByAge);
?>

चेतावनी: बिना कोड वाला कोड (मेरे पास php5.3 atm नहीं है), लेकिन इसे कुछ इस तरह दिखना चाहिए।

वहाँ एक नकारात्मक पहलू है: अगर आप उन्हें बंद करने के साथ सामना करते हैं तो बहुत सारे php डेवलपर्स थोड़े असहाय हो सकते हैं।

क्लोजर के अच्छे-से समझने के लिए, मैं आपको एक और उदाहरण दूंगा - इस बार जावास्क्रिप्ट में। समस्याओं में से एक है स्कूपिंग और ब्राउज़र अंतर्निहित अतुल्यकालिकता। विशेष रूप से, अगर यह window.setTimeout();(या अंतःवृत्त) की बात आती है । इसलिए, आप सेटटाइमआउट के लिए एक फ़ंक्शन पास करते हैं, लेकिन आप वास्तव में कोई पैरामीटर नहीं दे सकते हैं, क्योंकि प्रदान करने वाले पैरामीटर कोड को निष्पादित करते हैं!

function getFunctionTextInASecond(value) {
    return function () {
        document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
    }
}

var textToDisplay = prompt('text to show in a second', 'foo bar');

// this returns a function that sets the bodys innerHTML to the prompted value
var myFunction = getFunctionTextInASecond(textToDisplay);

window.setTimeout(myFunction, 1000);

myFunction एक प्रकार का पूर्वनिर्धारित पैरामीटर के साथ एक फ़ंक्शन देता है!

ईमानदार होने के लिए, मुझे 5.3 और गुमनाम कार्यों / बंदों के बाद से php बहुत पसंद है। नामस्थान अधिक महत्वपूर्ण हो सकते हैं, लेकिन वे बहुत कम सेक्सी हैं


4
ohhhhhhhh, तो उपयोग में पारित करने के लिए प्रयोग किया जाता है अतिरिक्त चर, मैंने सोचा कि यह कुछ अजीब काम था। धन्यवाद!
सीनडाउन

38
सावधान रहे। जब फ़ंक्शन CALLED होता है, तो मानों को पास करने के लिए मापदंडों का उपयोग किया जाता है। क्लोजर का उपयोग फ़ंक्शन को DEFINED करने पर मानों को "पास" करने के लिए किया जाता है।
स्टेप्स जूल

जावास्क्रिप्ट में, कोई कार्य के लिए प्रारंभिक तर्क निर्दिष्ट करने के लिए बाइंड () का उपयोग कर सकता है - आंशिक रूप से लागू फ़ंक्शन देखें
S

17

Zupa ने 'उपयोग' के साथ नज़दीकियों को समझाने और अर्लीबाइंडिंग और रेफरेंसिंग के बीच अंतर को समझाते हुए एक बड़ा काम किया, जो 'उपयोग किए गए' हैं।

इसलिए मैंने एक चर (= प्रतिलिपि) के शुरुआती बंधन के साथ एक कोड उदाहरण बनाया:

<?php

$a = 1;
$b = 2;

$closureExampleEarlyBinding = function() use ($a, $b){
    $a++;
    $b++;
    echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
    echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";    
};

echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";  

$closureExampleEarlyBinding();

echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";

/* this will output:
Before executing $closureExampleEarlyBinding() $a = 1
Before executing $closureExampleEarlyBinding() $b = 2
Inside $closureExampleEarlyBinding() $a = 2
Inside $closureExampleEarlyBinding() $b = 3
After executing $closureExampleEarlyBinding() $a = 1
After executing $closureExampleEarlyBinding() $b = 2
*/

?>

एक चर को संदर्भित करने के साथ उदाहरण (चर से पहले 'और' वर्ण पर ध्यान दें);

<?php

$a = 1;
$b = 2;

$closureExampleReferencing = function() use (&$a, &$b){
    $a++;
    $b++;
    echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />";
    echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />"; 
};

echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";   

$closureExampleReferencing();

echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";    

/* this will output:
Before executing $closureExampleReferencing() $a = 1
Before executing $closureExampleReferencing() $b = 2
Inside $closureExampleReferencing() $a = 2
Inside $closureExampleReferencing() $b = 3
After executing $closureExampleReferencing() $a = 2
After executing $closureExampleReferencing() $b = 3
*/

?>

2

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

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

यह PHP में तथाकथित सरल तरीका है।

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