PHP के जादू के तरीकों के संदर्भ में `ट्रिगर_रोर` बनाम` एक्सेप्शन` को फेंक दें


13

मैं जादू के तरीकोंtrigger_error के संदर्भ में सही उपयोग (यदि कोई हो) पर एक सहकर्मी के साथ बहस कर रहा हूं । सबसे पहले, मुझे लगता है कि इस एक मामले को छोड़कर बचना चाहिए ।trigger_error

कहते हैं कि हमारे पास एक विधि है foo()

class A {
    public function foo() {
        echo 'bar';
    }
}

अब कहते हैं कि हम सटीक समान इंटरफ़ेस प्रदान करना चाहते हैं, लेकिन सभी विधि कॉलों को पकड़ने के लिए एक जादू पद्धति का उपयोग करें

class B {
    public function __call($method, $args) {
        switch (strtolower($method)) {
        case 'foo':
            echo 'bar';
            break;
        }
    }
}

$a = new A;
$b = new B;

$a->foo(); //bar
$b->foo(); //bar

दोनों कक्षाएं उसी तरह से हैं जैसे वे प्रतिक्रिया करते हैं, foo()लेकिन एक अमान्य विधि को कॉल करते समय भिन्न होते हैं।

$a->doesntexist(); //Error
$b->doesntexist(); //Does nothing

मेरा तर्क यह है कि trigger_errorकिसी अज्ञात तरीके के पकड़े जाने पर जादू के तरीकों को कॉल करना चाहिए

class B {
    public function __call($method, $args) {
        switch (strtolower($method)) {
        case 'foo':
            echo 'bar';
            break;
        default:
            $class = get_class($this);
            $trace = debug_backtrace();
            $file = $trace[0]['file'];
            $line = $trace[0]['line'];
            trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
            break;
        }
    }
}

ताकि दोनों वर्ग पहचान (लगभग) व्यवहार करें

$a->badMethod(); //Call to undefined method A::badMethod() in [..] on line 28
$b->badMethod(); //Call to undefined method B::badMethod() in [..] on line 32

मेरा उपयोग-केस एक ActiveRecord कार्यान्वयन है। मैं उन __callतरीकों को पकड़ने और संभालने के लिए उपयोग करता हूं जो अनिवार्य रूप से एक ही काम करते हैं, लेकिन जैसे कि , Distinctया जैसे संशोधक हैंIgnore

selectDistinct()
selectDistinctColumn($column, ..)
selectAll()
selectOne()
select()

या

insert()
replace()
insertIgnore()
replaceIgnore()

तरह के तरीके where(), from(), groupBy(), आदि हार्ड-कोडेड हैं।

जब आप गलती से कॉल करते हैं तो मेरा तर्क उजागर होता है insret()। यदि मेरे सक्रिय रिकॉर्ड कार्यान्वयन ने सभी तरीकों को हार्डकोड किया है तो यह एक त्रुटि होगी।

किसी भी अच्छे अमूर्त के साथ, उपयोगकर्ता को कार्यान्वयन के विवरण से अनजान होना चाहिए और केवल इंटरफ़ेस पर निर्भर होना चाहिए। जादू के तरीकों का उपयोग करने वाले कार्यान्वयन को किसी भी अलग तरह से व्यवहार क्यों करना चाहिए? दोनों में त्रुटि होनी चाहिए।

जवाबों:


7

एक ही ActiveRecord इंटरफेस के दो कार्यान्वयन ले लो ( select(), where()आदि)

class ActiveRecord1 {
    //Hardcodes all methods
}

class ActiveRecord2 {
    //Uses __call to handle some methods, hardcodes the rest
}

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

PHP पर वापस - यदि दोनों वर्ग एक ही इंटरफ़ेस लागू करते हैं, तो उन्हें समान व्यवहार क्यों नहीं दिखाना चाहिए?

तो __callया __callStaticगलत विधि का पता लगाने, वे भाषा की नकल डिफ़ॉल्ट व्यवहार करने के लिए एक त्रुटि उत्प्रेरित करने चाहिए

$class = get_class($this);
$trace = debug_backtrace();
$file = $trace[0]['file'];
$line = $trace[0]['line'];
trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);

मैं यह तर्क नहीं दे रहा हूं कि क्या अपवादों पर त्रुटियों का उपयोग किया जाना चाहिए (वे 100% नहीं होने चाहिए), लेकिन मेरा मानना ​​है कि PHP के जादू के तरीके अपवाद हैं - इरादा इरादा :) - भाषा के संदर्भ में इस नियम के लिए


1
क्षमा करें, लेकिन मैं इसे नहीं खरीदता। हमें भेड़ क्यों होना चाहिए क्योंकि एक दशक पहले पीएचपी में अपवाद नहीं थे जब कक्षाओं को पहली बार पीएचपी में लागू किया गया था 4.something
मैथ्यू शेर्ले

@ मैथ्यू संगति के लिए। मैं अपवादों बनाम त्रुटियों पर बहस नहीं कर रहा हूं (कोई बहस नहीं है) या क्या PHP में डिज़ाइन विफल है (यह करता है), मैं तर्क दे रहा हूं कि इस बहुत ही अनोखी परिस्थिति में, स्थिरता के लिए , भाषा के व्यवहार की नकल करना सबसे अच्छा है
क्रिस 13

संगति हमेशा एक अच्छी बात नहीं है। एक सुसंगत लेकिन खराब डिज़ाइन अभी भी एक खराब डिज़ाइन है जो दिन के अंत में उपयोग करने के लिए एक दर्द है।
मैथ्यू शेर्ले

@ मैथ्यू सच है, लेकिन एक अवैध तरीके से कॉल पर IMO एक त्रुटि को ट्रिगर करता है खराब डिज़ाइन 1 नहीं है) कई (अधिकांश!) भाषाओं में यह अंतर्निहित है, और 2) मैं एक भी मामले के बारे में नहीं सोच सकता जहां आप कभी भी चाहते हैं अमान्य विधि कॉल को पकड़ने और उसे संभालने के लिए?
क्रिस

@chriso: ब्रह्मांड पर्याप्त रूप से बड़ा है कि ऐसे मामलों का उपयोग किया जाता है जो न तो आप या मैं कभी ऐसा होने का सपना देखेंगे। आप __call()डायनामिक रूटिंग करने के लिए उपयोग कर रहे हैं , क्या यह वास्तव में अनुचित है कि कहीं न कहीं किसी ट्रैक के मामले में जहां कोई भी मामला विफल हो सकता है उसे संभालना चाहता है? किसी भी दर पर, यह मंडलियों में जा रहा है, इसलिए यह मेरी अंतिम टिप्पणी होगी। उस दिन के अंत में आप क्या करेंगे, यह एक निर्णय कॉल के लिए नीचे आता है: बेहतर समर्थन बनाम संगति। दोनों तरीकों से कोई विशेष हैंडलिंग के मामले में आवेदन पर समान प्रभाव पड़ेगा।
मैथ्यू शार्ले

3

मैं अपनी राय रखने के लिए वहाँ जा रहा हूँ, लेकिन अगर आप trigger_errorकहीं भी उपयोग करते हैं, तो आप कुछ गलत कर रहे हैं। अपवाद जाने का रास्ता है।

अपवादों के लाभ:

  • उन्हें पकड़ा जा सकता है। यह एक बहुत बड़ा लाभ है, और केवल वही होना चाहिए जिसकी आपको आवश्यकता है। लोग वास्तव में कुछ अलग करने की कोशिश कर सकते हैं अगर उन्हें उम्मीद है कि कुछ गलत हो सकता है। त्रुटि आपको यह अवसर नहीं देती है। यहां तक ​​कि एक कस्टम त्रुटि हैंडलर स्थापित करना एक अपवाद को पकड़ने के लिए एक मोमबत्ती पकड़ नहीं है। आपके प्रश्न में आपकी टिप्पणियों के बारे में, एक 'उचित' आवेदन पूरी तरह से आवेदन के संदर्भ पर निर्भर करता है। लोग अपवाद नहीं पकड़ सकते अगर उन्हें लगता है कि यह उनके मामले में कभी नहीं होगा। लोगों को एक विकल्प नहीं देना एक बुरी बात है ™।
  • ढेर के निशान। अगर कुछ गलत होता है, तो आप जानते हैं कि समस्या कहां और किस संदर्भ में हुई है। क्या आपने कभी यह पता लगाने की कोशिश की है कि कुछ मुख्य विधियों में से एक त्रुटि कहाँ आ रही है? यदि आप बहुत कम मापदंडों के साथ किसी फ़ंक्शन को कॉल करते हैं, तो आपको एक बेकार त्रुटि मिलती है जो आपके द्वारा कॉल किए जा रहे तरीके की शुरुआत को उजागर करती है, और जहां यह कॉल कर रही है, वहां से पूरी तरह से निकल जाती है।
  • स्पष्टता। उपरोक्त दोनों को मिलाएं और आपको स्पष्ट कोड मिल जाएगा। यदि आप त्रुटियों को संभालने के लिए एक कस्टम त्रुटि हैंडलर का उपयोग करने का प्रयास करते हैं (जैसे त्रुटियों के लिए स्टैक के निशान उत्पन्न करने के लिए), तो आपकी सभी त्रुटि हैंडलिंग एक फ़ंक्शन में है, न कि जहां त्रुटियों को वास्तव में उत्पन्न किया जा रहा है।

अपनी चिंताओं को संबोधित करते हुए, एक विधि को कॉल करना जो मौजूद नहीं है, एक वैध संभावना हो सकती है । यह पूरी तरह से उस कोड के संदर्भ पर निर्भर करता है जिसे आप लिख रहे हैं, लेकिन कुछ मामले हैं जहां ऐसा हो सकता है। आपके सटीक उपयोग के मामले को संबोधित करते हुए, कुछ डेटाबेस सर्वर कुछ कार्यक्षमता की अनुमति दे सकते हैं जो अन्य नहीं करते हैं। क्षमताओं की जांच करने के लिए एक फ़ंक्शन बनाम बनाम try/ catchअपवाद का उपयोग करना __call()पूरी तरह से एक अलग तर्क है।

एकमात्र उपयोग-मामला जिसके बारे में मैं सोच सकता हूं कि वह उपयोग trigger_errorके लिए है E_USER_WARNINGया कम है। एक ट्रिगर E_USER_ERRORहालांकि हमेशा मेरी राय में कोई त्रुटि है।


प्रतिक्रिया के लिए धन्यवाद :) - 1) मैं मानता हूं कि अपवादों को लगभग हमेशा त्रुटियों पर इस्तेमाल किया जाना चाहिए - आपके सभी तर्क मान्य बिंदु हैं। हालाँकि .. मुझे लगता है कि इस संदर्भ में आपका तर्क विफल हो जाता है ..
क्रिस

2
" एक विधि को कॉल करना जो मौजूद नहीं है एक वैध संभावना हो सकती है " - मैं पूरी तरह से असहमत हूं। प्रत्येक भाषा में (जिसका मैंने कभी उपयोग किया है), एक फ़ंक्शन / विधि को कॉल करना जो मौजूद नहीं है, जिसके परिणामस्वरूप त्रुटि होगी। यह ऐसी स्थिति नहीं है जिसे पकड़ा और संभाला जाए। स्टेटिक भाषाएं आपको अमान्य पद्धति कॉल के साथ संकलन नहीं करने देंगी और कॉल तक पहुंचते ही डायनेमिक भाषाएं विफल हो जाएंगी।
क्रिस

यदि आप मेरा दूसरा संपादन पढ़ते हैं तो आप मेरा उपयोग-केस देखेंगे। मान लें कि आपके पास एक ही वर्ग के दो कार्यान्वयन हैं, एक __call का उपयोग कर रहा है और एक हार्डकोड तरीकों से। कार्यान्वयन विवरण को अनदेखा करते हुए, दोनों वर्गों को एक ही इंटरफ़ेस लागू करने पर अलग-अलग व्यवहार क्यों करना चाहिए? यदि आप उस वर्ग के साथ एक अमान्य विधि कहते हैं जिसमें हार्डकोड किए गए तरीके हैं, तो PHP एक त्रुटि को ट्रिगर करेगा । का उपयोग करते हुए trigger_error__call या __callStatic की नकल करता है भाषा के डिफ़ॉल्ट व्यवहार के संदर्भ में
chriso

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

@chriso मुझे एकमात्र कारण यह मानना ​​पसंद है कि कोर अभी भी त्रुटियों का उपयोग करता है पीछे की संगतता के लिए है। उम्मीद है कि PHP6 एक और छलांग होगा जैसे PHP5 था और पूरी तरह से त्रुटियों को छोड़ दें। दिन के अंत में, दोनों त्रुटियां और अपवाद समान परिणाम उत्पन्न करते हैं: कोड निष्पादित करने की तत्काल समाप्ति। एक अपवाद के साथ आप निदान कर सकते हैं कि क्यों और कहाँ हालांकि इतना आसान है।
मैथ्यू शार्ले

3

मानक PHP त्रुटियों को अप्रचलित माना जाना चाहिए। PHP पूर्ण रूप से स्टैक ट्रेस के साथ अपवादों में त्रुटियों, चेतावनियों और सूचनाओं को परिवर्तित करने के लिए एक अंतर्निहित वर्ग ErrorException प्रदान करता है। आप इसे इस तरह उपयोग करते हैं:

function errorToExceptionHandler($errNo, $errStr, $errFile, $errLine, $errContext)
{
if (error_reporting() == 0) return;
throw new ErrorException($errStr, 0, $errNo, $errFile, $errLine);
}
set_error_handler('errorToExceptionHandler');

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


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

1
नहीं, E_NOTICES को अपवादों में बदलना अच्छा है! सभी नोटिस त्रुटियों को माना जाना चाहिए; यह अच्छा अभ्यास है कि आप उन्हें अपवादों में परिवर्तित कर रहे हैं या नहीं।
वेन

2
आम तौर पर मैं आपसे सहमत होता हूं, लेकिन दूसरा आप किसी तीसरे पक्ष के कोड का उपयोग करना शुरू करते हैं, यह जल्दी से इसके चेहरे पर गिर जाता है।
मैथ्यू शार्ले

लगभग सभी 3 पार्टी पुस्तकालय E_STRICT हैं E_ALL सुरक्षित है। अगर मैं उस कोड का उपयोग कर रहा हूं जो नहीं है, तो मैं इसमें जाऊंगा और इसे ठीक करूंगा। मैंने बिना किसी समस्या के वर्षों तक इस तरह काम किया है ।
वेन

आपने स्पष्ट रूप से पहले दोहरे का उपयोग नहीं किया है। कोर वास्तव में इस तरह अच्छा है, लेकिन कई, कई तीसरे पक्ष के मॉड्यूल नहीं हैं
मैथ्यू शार्ले

0

IMO, यह इसके लिए एक बिल्कुल वैध उपयोग मामला है trigger_error:

function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
    if (error_reporting() & $errno) {
        // only process when included in error_reporting
        return handleException(new \Exception($errstring, $errno));
    }
    return true;
}

function handleException($exception){
    // Here, you do whatever you want with the generated
    // exceptions. You can store them in a file or database,
    // output them in a debug section of your page or do
    // pretty much anything else with it, as if it's a
    // normal variable

    switch ($code) {
        case E_ERROR:
        case E_CORE_ERROR:
        case E_USER_ERROR:
            // Make sure script exits here
            exit(1);
        default:
            // Let script continue
            return true;
    }
}

// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');


// ---------------------------------- //

// Generate warning
trigger_error('This went wrong, but we can continue', E_USER_WARNING);

// Generate fatal error :
trigger_error('This went horrible wrong', E_USER_ERROR);

इस रणनीति का उपयोग करते हुए, $errcontextयदि आप $exception->getTrace()फ़ंक्शन के भीतर करते हैं, तो आपको पैरामीटर मिलता है handleException। यह कुछ डिबगिंग उद्देश्यों के लिए बहुत उपयोगी है।

दुर्भाग्य से, यह केवल तभी काम करता है जब आप trigger_errorसीधे अपने संदर्भ से उपयोग करते हैं, जिसका अर्थ है कि आप फ़ंक्शन को उपनाम देने के लिए एक आवरण फ़ंक्शन / विधि का उपयोग नहीं कर सकते हैं trigger_error(इसलिए function debug($code, $message) { return trigger_error($message, $code); }यदि आप संदर्भ में डेटा चाहते हैं तो आप ऐसा कुछ नहीं कर सकते )।

मैं एक बेहतर विकल्प की तलाश में था, लेकिन अभी तक मुझे कोई नहीं मिला है।

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