PHP: टाइप हिंटिंग - `क्लोजर` और` कॉलेबल` के बीच अंतर


128

मैंने देखा कि अगर हम कुछ कॉलबैक फ़ंक्शन को चलाने की अपेक्षा करते हैं तो मैं Closureया तो या इसके Callableप्रकार का उपयोग कर सकता हूं । उदाहरण के लिए:

function callFunc1(Closure $closure) {
    $closure();
}

function callFunc2(Callable $callback) {
    $callback();
}

$function = function() {
    echo 'Hello, World!';
};

callFunc1($function); // Hello, World!
callFunc2($function); // Hello, World!

सवाल:

यहाँ क्या अंतर है? दूसरे शब्दों में, कब उपयोग करना है Closureऔर कब उपयोग करना है Callableया वे एक ही उद्देश्य से काम करते हैं?

जवाबों:


173

अंतर यह है कि, Closureएक अनाम फ़ंक्शन होना चाहिए, जहां callableएक सामान्य फ़ंक्शन भी हो सकता है।

आप इसे नीचे दिए गए उदाहरण से देख / परख सकते हैं और आप देखेंगे कि आपको पहले वाले के लिए एक त्रुटि मिलेगी:

function callFunc1(Closure $closure) {
    $closure();
}

function callFunc2(Callable $callback) {
    $callback();
}

function xy() {
    echo 'Hello, World!';
}

callFunc1("xy"); // Catchable fatal error: Argument 1 passed to callFunc1() must be an instance of Closure, string given
callFunc2("xy"); // Hello, World!

इसलिए यदि आप केवल हिंट बेनामी फ़ंक्शन उपयोग टाइप करना चाहते हैं: Closureऔर यदि आप भी सामान्य कार्यों callableको टाइप हिंट के रूप में उपयोग करने की अनुमति देना चाहते हैं ।


5
आप सरणी के लिए कॉल करने के साथ वर्ग विधियों का भी उपयोग कर सकते हैं, उदाहरण के ["Foo", "bar"]लिए Foo::barया इसके [$foo, "bar"]लिए $foo->bar
एंड्रिया

17
ऑफटॉपिक, लेकिन संबंधित: PHP 7.1 के बाद से, अब आप आसानी से एक क्लोजर में बदल सकते हैं callFunc1(Closure::fromCallable("xy")):। wiki.php.net/rfc/closurefromcallable
nevvermind

मैं अभी भी यह नहीं देखता कि मैं केवल अनाम फ़ंक्शन को क्यों कॉल करना चाहूंगा। यदि मैं कोड साझा करता हूं, तो मुझे परवाह नहीं करनी चाहिए कि फ़ंक्शन कहां से आता है। मेरा मानना ​​है कि PHP के क्वार्क्स में से एक, उन्हें भ्रम से बचने के लिए एक या दूसरे तरीके को निकालना चाहिए। लेकिन मुझे ईमानदारी से Closure+ Closure::fromCallableदृष्टिकोण पसंद है , क्योंकि स्ट्रिंग या सरणी callableहमेशा अजीब रही है।
रोबो रोबोक

2
@RoboRobok Closureको विरोध के रूप में केवल (अनाम फ़ंक्शन) की आवश्यकता के लिए एक कारण, फ़ंक्शन callableके दायरे से परे पहुंच को रोकने के लिए होगा। उदाहरण के लिए जब आपके पास कोई private methodहै तो आप किसी को गाली देना नहीं चाहते हैं callable। देखें: 3v4l.org/7TSf2
fyrye

58

उनके बीच मुख्य अंतर यह है कि closureएक वर्ग और callableएक प्रकार है

callableप्रकार कुछ भी है कि किया जा सकता है स्वीकार करता है कहा जाता है :

var_dump(
  is_callable('functionName'),
  is_callable([$myClass, 'methodName']),
  is_callable(function(){})
);

जहां केवल एक अनाम फ़ंक्शन स्वीकार closureकरेगा । ध्यान दें कि PHP संस्करण 7.1 में आप फ़ंक्शन को बंद करने के लिए परिवर्तित कर सकते हैं जैसे :।Closure::fromCallable('functionName')


उदाहरण:

namespace foo{
  class bar{
    private $val = 10;

    function myCallable(callable $cb){$cb()}
    function myClosure(\Closure $cb){$cb()} // type hint must refer to global namespace
  }

  function func(){}
  $cb = function(){};
  $fb = new bar;

  $fb->myCallable(function(){});
  $fb->myCallable($cb);
  $fb->myCallable('func');

  $fb->myClosure(function(){});
  $fb->myClosure($cb);
  $fb->myClosure(\Closure::fromCallable('func'));
  $fb->myClosure('func'); # TypeError
}

तो एक closureओवर का उपयोग क्यों करें callable?

सख़्ती क्योंकि एक closureएक वस्तु के लिए कुछ अतिरिक्त तरीकों है कि: call(), bind()और bindto()। वे आपको एक वर्ग के बाहर घोषित फ़ंक्शन का उपयोग करने और इसे निष्पादित करने की अनुमति देते हैं जैसे कि यह एक कक्षा के अंदर था।

$inject = function($i){return $this->val * $i;};
$cb1 = Closure::bind($inject, $fb);
$cb2 = $inject->bindTo($fb);

echo $cb1->call($fb, 2); // 20
echo $cb2(3);            // 30

आप एक सामान्य फ़ंक्शन पर विधियों को कॉल करना पसंद नहीं करेंगे क्योंकि यह घातक त्रुटियों को बढ़ाएगा। तो इस तरह से घेरने के लिए आपको कुछ लिखना होगा:

if($cb instanceof \Closure){}

हर बार यह जाँच करना व्यर्थ है। इसलिए यदि आप उन विधियों का उपयोग करना चाहते हैं जो बताती हैं कि तर्क एक है closure। अन्यथा बस एक सामान्य का उपयोग करें callback। इस तरफ; आपके कोड के बजाय फ़ंक्शन कॉल पर एक त्रुटि उत्पन्न होती है, जिससे इसे निदान करना बहुत आसान हो जाता है।

एक तरफ ध्यान दें पर:closure वर्ग अपनी के रूप में नहीं बढ़ाई जा सकती अंतिम


1
आप अन्य स्कोप में भी कॉल करने योग्य उपयोग कर सकते हैं।
बिमल पौडेल

इसका मतलब है कि आपको callableकिसी भी नाम स्थान पर अर्हता प्राप्त करने की आवश्यकता नहीं है ।
जेफ पकेट

0

यह उल्लेखनीय है कि यह PHP के संस्करणों 5.3.21 से 5.3.29 के लिए काम नहीं करेगा।

उन संस्करणों में से किसी में भी आपको आउटपुट मिलेगा:

नमस्ते दुनिया! कैटलेबल घातक त्रुटि: कॉल 1 करने के लिए दिया गया तर्क 1 () कॉल का उदाहरण होना चाहिए> कॉलेबल का उदाहरण दिया गया है, लाइन 16 में / in / kqeYD में कहा जाता है और लाइन 7 में / kqeDD में परिभाषित किया गया है।

कोड 255 के साथ प्रक्रिया समाप्त हो गई।

कोई व्यक्ति https://3v4l.org/kqeYD#v5321 का उपयोग करके प्रयास कर सकता है

सादर,


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

5
ऐसा इसलिए है क्योंकि callablePHP 5.4 में पेश किया गया था। कि PHP से पहले नाम के एक वर्ग का एक उदाहरण की उम्मीद है callable, बस के रूप में यदि आप के लिए एक संकेत निर्दिष्ट था PDO, DateTime, या \My\Random\ClassName
डेवी शफीक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.