शुरुआती के लिए PHP। हैंडलिंग में त्रुटि

छवि

केवल वह जो कुछ नहीं करता है वह गलती नहीं करता है, और हम इसका एक उदाहरण हैं - हम बैठते हैं और अथक परिश्रम करते हैं, पढ़ते हैं हबर :)

इस लेख में, मैं PHP में त्रुटियों के बारे में अपनी कहानी का नेतृत्व करूंगा, और उन्हें कैसे अंकुश लगाऊंगा।

त्रुटियों


त्रुटि परिवार में विविधताएं


त्रुटियों का पता लगाने से पहले, मैं प्रत्येक प्रजाति का अध्ययन करने और सबसे प्रमुख प्रतिनिधियों पर अलग से ध्यान देने की सलाह दूंगा।

किसी एकल त्रुटि को किसी के ध्यान में नहीं जाने से रोकने के लिए, आपको error_reporting () फ़ंक्शन का उपयोग करके सभी त्रुटियों को ट्रैक करने और अपने प्रदर्शन को सक्षम करने के लिए display_errors निर्देश का उपयोग करने में सक्षम करने की आवश्यकता है:

<?php error_reporting(E_ALL); ini_set('display_errors', 1); 

घातक त्रुटियाँ


सबसे दुर्जेय प्रकार की त्रुटियां घातक हैं, वे संकलन के दौरान और पार्सर या पीएचपी-स्क्रिप्ट के काम के दौरान हो सकती हैं, जबकि स्क्रिप्ट बाधित है।

E_PARSE

यह त्रुटि तब दिखाई देती है जब आप एक सकल सिंटैक्स त्रुटि करते हैं और PHP दुभाषिया समझ नहीं पाता है कि आप इससे क्या चाहते हैं, उदाहरण के लिए, यदि आपने घुंघराले या कोष्ठक को बंद नहीं किया है:

 <?php /** * Parse error: syntax error, unexpected end of file */ { 

या उन्होंने एक समझदार भाषा में लिखा:

 <?php /** * Parse error: syntax error, unexpected '...' (T_STRING) */     

अतिरिक्त कोष्ठक भी होते हैं, और इतना महत्वपूर्ण दौर या घुंघराले नहीं:

 <?php /** * Parse error: syntax error, unexpected '}' */ } 

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

 <?php //     error_reporting(E_ALL); ini_set('display_errors', 1); // ..     

E_ERROR

यह त्रुटि तब दिखाई देती है जब PHP समझती है कि आप क्या चाहते हैं, लेकिन यह कई कारणों से काम नहीं करता है। यह त्रुटि स्क्रिप्ट के निष्पादन में बाधा डालती है, और त्रुटि दिखाई देने से पहले कोड काम करेगा:

प्लग-इन नहीं मिला:

 /** * Fatal error: require_once(): Failed opening required 'not-exists.php' * (include_path='.:/usr/share/php:/usr/share/pear') */ require_once 'not-exists.php'; 

एक अपवाद फेंक दिया गया था (किस तरह का जानवर, मैं आपको थोड़ी देर बाद बताऊंगा), लेकिन संसाधित नहीं किया गया था:

 /** * Fatal error: Uncaught exception 'Exception' */ throw new Exception(); 

जब कोई भी अयोग्य क्लास पद्धति को कॉल करने का प्रयास कर रहा है:

 /** * Fatal error: Call to undefined method stdClass::notExists() */ $stdClass = new stdClass(); $stdClass->notExists(); 

मुफ्त मेमोरी का अभाव ( मेमोरी_लिमिट निर्देश में निर्धारित से अधिक) या कुछ और समान:

 /** * Fatal Error: Allowed Memory Size */ $arr = array(); while (true) { $arr[] = str_pad(' ', 1024); } 

बड़ी फ़ाइलों को पढ़ते या डाउनलोड करते समय यह बहुत आम है, इसलिए स्मृति खपत के मुद्दे से सावधान रहें
पुनरावर्ती फ़ंक्शन कॉल। इस उदाहरण में, यह 256 वें पुनरावृत्ति पर समाप्त हुआ, क्योंकि यह xdebug सेटिंग्स में लिखा गया है (हाँ, यह त्रुटि केवल इस रूप में प्रकट हो सकती है जब xdebug एक्सटेंशन सक्षम हो):

 /** * Fatal error: Maximum function nesting level of '256' reached, aborting! */ function deep() { deep(); } deep(); 

घातक नहीं


यह दृश्य स्क्रिप्ट के निष्पादन को बाधित नहीं करता है, लेकिन परीक्षक आमतौर पर उन्हें ढूंढता है। यह ऐसी त्रुटियां हैं जो नौसिखिए डेवलपर्स के लिए सबसे अधिक परेशानी का कारण बनती हैं।

E_WARNING

यह अक्सर तब होता है जब आप किसी फ़ाइल को include करते हुए कनेक्ट करते include , लेकिन यह सर्वर पर दिखाई नहीं देता है या आपने फ़ाइल में पथ को इंगित करने में गलती की है:

 /** * Warning: include_once(): Failed opening 'not-exists.php' for inclusion */ include_once 'not-exists.php'; 

ऐसा तब होता है जब आप फ़ंक्शन को कॉल करते समय गलत प्रकार के तर्कों का उपयोग करते हैं:

 /** * Warning: join(): Invalid arguments passed */ join('string', 'string'); 

उनमें से बहुत सारे हैं, और सब कुछ सूचीबद्ध करने का कोई मतलब नहीं है ...

E_NOTICE

ये सबसे आम त्रुटियां हैं, इसके अलावा, त्रुटि आउटपुट बंद करने और उन्हें पूरे दिन रिवाइव करने के लिए प्रशंसक हैं। कई तुच्छ त्रुटियां हैं।

एक अपरिभाषित चर का उपयोग करते समय:

 /** * Notice: Undefined variable: a */ echo $a; 

जब कोई भी अशुद्ध तत्व तक पहुँच रहा हो:

 /** * Notice: Undefined index: a */ $b = []; $b['a']; 

जब कोई भी अविरल स्थिर पहुंचता है:

 /** * Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT' */ echo UNKNOWN_CONSTANT; 

जब डेटा प्रकार परिवर्तित नहीं होते हैं:

 /** * Notice: Array to string conversion */ echo array(); 

ऐसी त्रुटियों से बचने के लिए - सावधान रहें, और अगर आईडीई आपको कुछ बताता है, तो इसे अनदेखा न करें:

PHPStorm PHP E_NOTICE में

E_STRICT

ये त्रुटियां हैं जो आपको कोड को सही तरीके से लिखना सिखाएंगी ताकि आपको शर्म न आए, खासकर जब से आईडीई आपको तुरंत इन त्रुटियों को दिखाता है। उदाहरण के लिए, यदि आपने एक गैर-स्थैतिक विधि को स्थैतिक कहा है, तो कोड काम करेगा, लेकिन यह किसी तरह गलत है, और यदि भविष्य में कक्षा विधि बदल जाती है और $this प्रतीत होता है तो गंभीर त्रुटियां हो सकती हैं:

 /** * Strict standards: Non-static method Strict::test() should not be called statically */ class Strict { public function test() { echo "Test"; } } Strict::test(); 


इस प्रकार की त्रुटि PHP संस्करण 5.6 के लिए प्रासंगिक है, और उनमें से लगभग सभी को काट दिया गया था
7 मैच। संबंधित RFC में और पढ़ें। अगर किसी को पता है कि ये त्रुटियां और कहां रहीं, तो टिप्पणियों में लिखें


E_DEPRECATED

यदि आप अप्रचलित कार्यों का उपयोग करते हैं तो PHP शपथ ग्रहण करेंगे (यानी जिन्हें चिह्नित के रूप में चिह्नित किया गया है, और वे अगले प्रमुख रिलीज में नहीं होंगे):

 /** * Deprecated: Function split() is deprecated */ //  ,   PHP 7.0 //    PHP 5.3 split(',', 'a,b'); 

मेरे संपादक में, इसी तरह के कार्यों को पार किया जाएगा:

PHPStorm में PHP E_DEPRECated

रिवाज


इस प्रकार, जो कोड डेवलपर खुद "नस्लों" करता है, मैंने उन्हें लंबे समय तक नहीं देखा है, और मैं यह सलाह नहीं देता हूं कि वे दुर्व्यवहार करें:

  • E_USER_ERROR - महत्वपूर्ण त्रुटि
  • E_USER_WARNING - एक महत्वपूर्ण त्रुटि नहीं है
  • E_USER_NOTICE - वे संदेश जो त्रुटियां नहीं हैं

अलग-अलग, यह ध्यान देने योग्य है E_USER_DEPRECATED - इस प्रकार का उपयोग अभी भी प्रोग्रामर को याद दिलाने के लिए बहुत बार किया जाता है कि विधि या फ़ंक्शन पुराना है और इसका उपयोग किए बिना कोड को फिर से लिखना है। ट्रिगर_रोर () फ़ंक्शन का उपयोग इसे और समान त्रुटियों को बनाने के लिए किया जाता है:

 /** * @deprecated Deprecated since version 1.2, to be removed in 2.0 */ function generateToken() { trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED); // ... // code ... // ... } 

अब जब आप अधिकांश प्रकारों और त्रुटियों के प्रकार से परिचित हो गए हैं, तो display_errors निर्देश के संचालन पर एक संक्षिप्त विवरण देने का समय आ गया है:

  • अगर display_errors = on , तो त्रुटि के मामले में ब्राउज़र को html को त्रुटि पाठ और कोड 200 के साथ प्राप्त होगा
  • यदि प्रदर्शन_रियर्स display_errors = off , तो घातक त्रुटियों के लिए प्रतिक्रिया कोड 500 होगा और परिणाम अन्य त्रुटियों के लिए उपयोगकर्ता को वापस नहीं किया जाएगा - कोड सही ढंग से काम नहीं करेगा, लेकिन इसके बारे में किसी को भी नहीं बताएगा


पातलू बनाने का कार्य


PHP में त्रुटियों के साथ काम करने के लिए 3 कार्य हैं:

  • set_error_handler () - त्रुटियों के लिए एक हैंडलर सेट करता है जो स्क्रिप्ट को बाधित नहीं करता है (यानी गैर-घातक त्रुटियों के लिए)
  • error_get_last () - अंतिम त्रुटि के बारे में जानकारी प्राप्त करता है
  • register_shutdown_function () - एक हैंडलर पंजीकृत करता है जो स्क्रिप्ट समाप्त होने पर लॉन्च किया जाएगा। यह फ़ंक्शन त्रुटि संचालकों पर सीधे लागू नहीं होता है, लेकिन इसका उपयोग अक्सर इसके लिए किया जाता है।

अब तर्क के रूप में set_error_handler() का उपयोग करके त्रुटि से निपटने के बारे में कुछ विवरण, यह फ़ंक्शन उस फ़ंक्शन का नाम लेता है जिसे त्रुटि हैंडलिंग मिशन और त्रुटियों के प्रकार जिन्हें मॉनिटर किया जाएगा। एक त्रुटि हैंडलर भी एक वर्ग विधि, या एक अनाम फ़ंक्शन हो सकता है, मुख्य बात यह है कि यह तर्कों की निम्न सूची लेता है:

  • $errno - पहले तर्क में पूर्णांक के रूप में त्रुटि प्रकार है
  • $errstr - दूसरे तर्क में एक त्रुटि संदेश है
  • $errfile - एक वैकल्पिक तीसरे तर्क में फ़ाइल का नाम है जिसमें त्रुटि हुई
  • $errline - एक वैकल्पिक चौथे तर्क में वह पंक्ति संख्या होती है जिस पर त्रुटि हुई
  • $errcontext - वैकल्पिक पांचवें तर्क में उस दायरे में मौजूद सभी चर की एक सरणी होती है जहां त्रुटि हुई

यदि हैंडलर true लौटा, तो त्रुटि को संसाधित माना जाएगा और स्क्रिप्ट निष्पादित करना जारी रखेगा, अन्यथा, एक मानक हैंडलर कहा जाएगा जो त्रुटि को लॉग करता है और, इसके प्रकार के आधार पर, स्क्रिप्ट निष्पादित करना या इसे पूरा करना जारी रखेगा। यहाँ एक उदाहरण हैंडलर है:

 <?php //    ,  E_NOTICE error_reporting(E_ALL & ~E_NOTICE); ini_set('display_errors', 1); //    function myHandler($level, $message, $file, $line, $context) { //         switch ($level) { case E_WARNING: $type = 'Warning'; break; case E_NOTICE: $type = 'Notice'; break; default; //   E_WARNING   E_NOTICE //      //      PHP return false; } //    echo "<h2>$type: $message</h2>"; echo "<p><strong>File</strong>: $file:$line</p>"; echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>"; // ,    ,      return true; } //   ,         set_error_handler('myHandler', E_ALL); 

आप त्रुटियों को संभालने के लिए एक से अधिक फ़ंक्शन असाइन नहीं कर पाएंगे, हालांकि मैं प्रत्येक प्रकार की त्रुटि के लिए अपने स्वयं के हैंडलर को पंजीकृत करना बहुत पसंद करूंगा, लेकिन कोई भी एक हैंडलर नहीं लिखूंगा, और प्रत्येक प्रकार के लिए सीधे पूरे प्रदर्शन तर्क का वर्णन करूंगा
हैंडलर के साथ एक महत्वपूर्ण समस्या है जो ऊपर लिखा गया है - यह घातक त्रुटियों को नहीं पकड़ता है, और इस तरह की त्रुटियों के साथ, साइट के बजाय, उपयोगकर्ता केवल एक रिक्त पृष्ठ देखेंगे, या, इससे भी बदतर, एक त्रुटि संदेश। इस तरह के परिदृश्य को रोकने के लिए, register_shutdown_function () फ़ंक्शन का उपयोग करें और इसे एक फ़ंक्शन को पंजीकृत करने के लिए उपयोग करें जिसे हमेशा स्क्रिप्ट के अंत में निष्पादित किया जाएगा:

 function shutdown() { echo '    '; } register_shutdown_function('shutdown'); 

यह फ़ंक्शन हमेशा काम करेगा!

लेकिन त्रुटियों में वापस, त्रुटि कोड में त्रुटि की उपस्थिति को ट्रैक करने के लिए, हम error_get_last () फ़ंक्शन का उपयोग करते हैं, इसकी सहायता से आप अंतिम ज्ञात त्रुटि के बारे में जानकारी प्राप्त कर सकते हैं, और चूंकि घातक त्रुटियां कोड के निष्पादन में बाधा डालती हैं, वे हमेशा "अंतिम" की भूमिका निभाएंगे:

 function shutdown() { $error = error_get_last(); if ( //       is_array($error) && //       in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR]) ) { //    (       ) while (ob_get_level()) { ob_end_clean(); } //    echo "    ,  "; } } register_shutdown_function('shutdown'); 

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

लोलुपता के बारे में


चलो एक सरल परीक्षण करते हैं और पता करते हैं कि कितने कीमती संसाधन सबसे तुच्छ त्रुटि खाते हैं:

 /** *      */ //     $time= microtime(true); define('AAA', 'AAA'); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[AAA] = $i; } printf('%f seconds <br/>', microtime(true) - $time); 

इस स्क्रिप्ट को चलाने के परिणामस्वरूप, मुझे यह परिणाम मिला:

 0.002867 seconds 

अब लूप में त्रुटि जोड़ें:

 /** *     */ //     $time= microtime(true); $arr = []; for ($i = 0; $i < 10000; $i++) { $arr[BBB] = $i; //   ,      } printf('%f seconds <br/>', microtime(true) - $time); 

परिणाम उम्मीद से बदतर है, और परिमाण का एक क्रम (यहां तक ​​कि परिमाण के दो आदेश!):

 0.263645 seconds 

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

कुत्ते को कहाँ दफनाया गया है


PHP का एक विशेष प्रतीक "@" है - एक त्रुटि दमन ऑपरेटर, इसका उपयोग त्रुटि से निपटने के लिए नहीं लिखने के लिए किया जाता है, लेकिन इस मामले में PHP के सही व्यवहार पर निर्भर करता है:

 <?php echo @UNKNOWN_CONSTANT; 

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

अपवाद


PHP4 के युग में, कोई अपवाद नहीं थे, सब कुछ बहुत अधिक जटिल था, और डेवलपर्स त्रुटियों के साथ संघर्ष करते थे जितना वे कर सकते थे, यह जीवन के लिए नहीं बल्कि मृत्यु के लिए एक लड़ाई थी ... आप लेख के टकराव की इस आकर्षक कहानी में डूब सकते हैं असाधारण कोड। भाग 1 क्या मुझे इसे अभी पढ़ना चाहिए? मैं निश्चित उत्तर नहीं दे सकता, मैं केवल यह नोट करना चाहता हूं कि इससे आपको भाषा के विकास को समझने में मदद मिलेगी, और अपवादों के सभी आकर्षण का पता चलेगा।
PHP में अपवाद असाधारण घटनाएं हैं, त्रुटियों के विपरीत, वे न केवल एक समस्या बताते हैं, बल्कि प्रत्येक विशिष्ट मामले को संभालने के लिए प्रोग्रामर द्वारा अतिरिक्त क्रियाओं की आवश्यकता होती है।

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

अपवाद Exception श्रेणी या उसके कई वंशजों में से एक वस्तु है, इसमें त्रुटि पाठ, स्थिति शामिल है, और इसमें एक अन्य अपवाद का लिंक भी हो सकता है जो इसका मूल कारण बन गया है। PHP में अपवाद मॉडल अन्य प्रोग्रामिंग भाषाओं में उपयोग के समान है। throw ऑपरेटर का उपयोग करके एक अपवाद को फेंक दिया जा सकता है (जैसा कि वे कहते हैं, "फेंक") और आप catch स्टेटमेंट को catch । अपवाद को फेंकने वाला कोड अपवाद को पकड़ने के लिए एक try ब्लॉक से घिरा होना चाहिए। प्रत्येक try ब्लॉक में कम से कम एक मैचिंग catch या finally ब्लॉक होना चाहिए:

 try { //      if (random_int(0, 1)) { throw new Exception("One"); } echo "Zero" } catch (Exception $e) { //      echo $e->getMessage(); } 

किन मामलों में यह अपवादों का उपयोग करने लायक है:

  • अगर एक विधि / कार्य के ढांचे के भीतर कई ऑपरेशन हैं जो विफल हो सकते हैं
  • यदि आपका ढांचा या पुस्तकालय उनके उपयोग की घोषणा करता है

पहले परिदृश्य को स्पष्ट करने के लिए, आइए किसी फ़ाइल में डेटा लिखने के लिए एक फ़ंक्शन का पहले से ही लिया गया उदाहरण लें - बहुत सारे कारक हमें रोक सकते हैं, और ऊपर दिए गए कोड को बताने के लिए कि वास्तव में समस्या क्या थी, आपको एक अपवाद बनाने और फेंकने की आवश्यकता है:

 $directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs'; //     if (!is_dir($directory)) { throw new Exception('Directory `logs` is not exists'); } //         if (!is_writable($directory)) { throw new Exception('Directory `logs` is not writable'); } //  -   ,      if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new Exception('System can\'t create log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); 

तदनुसार, हम इन अपवादों को इस तरह पकड़ेंगे:

 try { //      // ... } catch (Exception $e) { //    echo " : ". $e->getMessage(); } 

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

 //    class FileSystemException extends Exception {} //     class DirectoryException extends FileSystemException { //   const DIRECTORY_NOT_EXISTS = 1; const DIRECTORY_NOT_WRITABLE = 2; } //     class FileException extends FileSystemException {} 

अब, यदि आप इन अपवादों का उपयोग करते हैं, तो आप निम्नलिखित कोड प्राप्त कर सकते हैं:

 try { //      if (!is_dir($directory)) { throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS); } if (!is_writable($directory)) { throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE); } if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Ym-d') . '.log', 'a+')) { throw new FileException('System can\'t open log file'); } fputs($file, date("[H:i:s]") . " done\n"); fclose($file); } catch (DirectoryException $e) { echo "   : ". $e->getMessage(); } catch (FileException $e) { echo "   : ". $e->getMessage(); } catch (FileSystemException $e) { echo "  : ". $e->getMessage(); } catch (Exception $e) { echo " : ". $e->getMessage(); } 

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

तो, अगर आप अपवाद नहीं पकड़ते हैं तो क्या होगा? आपको "घातक त्रुटि: बिना किसी अपवाद के ..." मिलेगा। अप्रिय।

इस स्थिति से बचने के लिए, आपको set_exception_handler () फ़ंक्शन का उपयोग करना चाहिए और हैंडलर को अपवादों के लिए सेट करना चाहिए जिन्हें ट्राइ -कैच ब्लॉक से बाहर फेंक दिया गया है और संसाधित नहीं किया गया है। ऐसे हैंडलर को बुलाने के बाद, स्क्रिप्ट का निष्पादन रोक दिया जाएगा:

 //     //     set_exception_handler(function($exception) { /** @var Exception $exception */ echo $exception->getMessage(), "<br/>\n"; echo $exception->getFile(), ':', $exception->getLine(), "<br/>\n"; echo $exception->getTraceAsString(), "<br/>\n"; }); 

मैं आपको finally ब्लॉक का उपयोग करते हुए निर्माण के बारे में भी बताऊंगा - इस ब्लॉक को निष्पादित किया जाएगा, भले ही कोई अपवाद फेंका गया हो या नहीं:

 try { //      } catch (Exception $e) { //      //     } finally { // ,       } 

यह समझने के लिए कि यह हमें क्या देता है, मैं finally ब्लॉक का उपयोग करने का निम्नलिखित उदाहरण दूंगा:

 try { // -    //     $handler = mysqli_connect('localhost', 'root', '', 'test'); try { //        // ... throw new Exception('DB error'); } catch (Exception $e) { //  ,     //     ,    throw new Exception('Catch exception', 0, $e); } finally { // ,      //      finally mysqli_close($handler); } //     ,       echo "Ok"; } catch (Exception $e) { //  ,    echo $e->getMessage(); echo "<br/>"; //      echo $e->getPrevious()->getMessage(); } 

यानी याद रखें - finally ब्लॉक को निष्पादित किया जाएगा भले ही आप catch ब्लॉक में ऊपर एक अपवाद फेंक दें (वास्तव में, यही वह है जो वह चाहता है)।

केवल सही जानकारी के परिचयात्मक लेख के लिए, जो अधिक जानकारी के लिए तरसते हैं, आप उन्हें लेख में असाधारण कोड पाएंगे;)

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

PHP7 - सब कुछ वैसा नहीं है जैसा पहले था


तो, अब आपने उपरोक्त सभी जानकारी सीख ली है और अब मैं आपको PHP7 में नवाचारों के साथ लोड करूंगा, अर्थात।मैं इस बारे में बात करूंगा कि आधुनिक PHP प्रोजेक्ट पर काम करते समय आपका क्या सामना होगा। इससे पहले, मैंने आपको बताया था और उदाहरणों के साथ दिखाया था कि महत्वपूर्ण त्रुटियों को पकड़ने के लिए आपको किस बैसाखी का निर्माण करने की आवश्यकता है, और इसलिए - PHP7 में उन्होंने इसे ठीक करने का फैसला किया, लेकिन? हमेशा की तरह? कोड के पिछड़े संगतता के लिए बंधा हुआ है, और प्राप्त किया गया है, हालांकि एक सार्वभौमिक समाधान है, लेकिन यह आदर्श से बहुत दूर है। और अब परिवर्तन के बारे में बिंदुओं पर:

  1. जब प्रकार की E_ERRORघातक त्रुटियां या घातक त्रुटियां होती हैं, तो प्रसंस्करण की संभावना के साथ E_RECOVERABLE_ERRORPHP अपवाद को फेंक देती है
  2. इन अपवादों को अपवाद वर्ग विरासत में नहीं मिलता है (याद रखें, मैंने पिछड़ी संगतता के बारे में बात की थी, यह उसके लिए है)
  3. ये अपवाद त्रुटि वर्ग को इनहेरिट करते हैं
  4. एक्सेप्शन और एरर दोनों क्लासेस थ्रॉलेबल इंटरफ़ेस को लागू करते हैं
  5. आप अपने कोड में फेंकने योग्य इंटरफ़ेस को लागू नहीं कर सकते

इंटरफ़ेस Throwableहमें लगभग पूरी तरह से दोहराता है Exception:

 interface Throwable { public function getMessage(): string; public function getCode(): int; public function getFile(): string; public function getLine(): int; public function getTrace(): array; public function getTraceAsString(): string; public function getPrevious(): Throwable; public function __toString(): string; } 

क्या यह मुश्किल है? अब उदाहरण के लिए, उन लोगों को लें जो उच्च और थोड़े आधुनिक थे:

 try { // ,     include 'e_parse_include.php'; } catch (Error $e) { var_dump($e); } 

परिणामस्वरूप, हम त्रुटि पकड़ते हैं और प्रिंट करते हैं:

 object(ParseError)#1 (7) { ["message":protected] => string(48) "syntax error, unexpected '' (T_STRING)" ["string":"Error":private] => string(0) "" ["code":protected] => int(0) ["file":protected] => string(49) "/www/education/error/e_parse_include.php" ["line":protected] => int(4) ["trace":"Error":private] => array(0) { } ["previous":"Error":private] => NULL } 

जैसा कि आप देख सकते हैं, उन्होंने ParseError अपवाद को पकड़ा , जो अपवाद का उत्तराधिकारी है जो जैक द्वारा निर्मित घर में Errorइंटरफ़ेस Throwableको लागू करता है कई अन्य अपवाद हैं, लेकिन मुझे पीड़ा नहीं होगी - स्पष्टता के लिए, मैं अपवादों का एक पदानुक्रम दूंगा:

 interface Throwable |- Exception implements Throwable | |- ErrorException extends Exception | |- ... extends Exception | `- ... extends Exception `- Error implements Throwable |- TypeError extends Error |- ParseError extends Error |- ArithmeticError extends Error | `- DivisionByZeroError extends ArithmeticError `- AssertionError extends Error 

और थोड़ा और विस्तार:

टाइपऑरर - त्रुटियों के लिए जब फ़ंक्शन तर्क का प्रकार पास किए गए प्रकार से मेल नहीं खाता है:

 try { (function(int $one, int $two) { return; })('one', 'two'); } catch (TypeError $e) { echo $e->getMessage(); } 

अंकगणित - गणितीय संक्रियाओं के दौरान हो सकता है, उदाहरण के लिए, जब गणना परिणाम एक पूर्णांक के लिए आवंटित सीमा से अधिक होता है:

 try { 1 << -1; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

DivisionByZeroError - शून्य त्रुटि द्वारा विभाजन:

 try { 1 / 0; } catch (ArithmeticError $e) { echo $e->getMessage(); } 

जोर - एक दुर्लभ जानवर जो प्रकट होता है जब मुखर () में निर्दिष्ट स्थिति संतुष्ट नहीं होती है:

 ini_set('zend.assertions', 1); ini_set('assert.exception', 1); try { assert(1 === 0); } catch (AssertionError $e) { echo $e->getMessage(); } 

सेटिंग्स में इस निर्माण-सर्वर निर्देश zend.assertionsऔर assert.exceptionकाट, और ठीक ही तो
आपको आधिकारिक मैनुअल में पूर्वनिर्धारित अपवादों की पूरी सूची मिल जाएगी , एसपीएल अपवादों के समान पदानुक्रम में

कार्य
PHP7 के लिए एक सार्वभौमिक त्रुटि हैंडलर लिखें जो सभी संभावित अपवादों को पकड़ लेगा।

इस अनुभाग को लिखते समय, PHP 7 में थ्रोएबल अपवाद और त्रुटियों से लेख का उपयोग किया गया था

वर्दी


— , , - ?

, set_error_handler() :

 //     function errorHandler($severity, $message, $file = null, $line = null) { //  ,       @ if (error_reporting() === 0) { return false; } throw new \ErrorException($message, 0, $severity, $file, $line); } //   -  set_error_handler('errorHandler', E_ALL); 

PHP7 , Throwable :

 try { /** ... **/ } catch (\Throwable $e) { //      echo $e->getMessage(); } 


, , , debug_backtrace() debug_print_backtrace() / :

 <?php function example() { echo '<pre>'; debug_print_backtrace(); echo '</pre>'; } class ExampleClass { public static function method () { example(); } } ExampleClass::method(); 

debug_print_backtrace() :

 #0 example() called at [/www/education/error/backtrace.php:10] #1 ExampleClass::method() called at [/www/education/error/backtrace.php:14] 

आप php_check_syntax () फ़ंक्शन या कमांड का उपयोग करके सिंटैक्स त्रुटियों के लिए कोड की जांच कर सकते हैं php -l [ ], लेकिन मैंने उनका उपयोग नहीं देखा है।

ज़ोर


मैं इस तरह के एक विदेशी जानवर के बारे में बात करना चाहूंगा जैसे कि PHP में मुखर ()दरअसल, इस टुकड़े को अनुबंध प्रोग्रामिंग पद्धति के लिए नकल के रूप में माना जा सकता है, और फिर मैं आपको बताऊंगा कि मैंने इसे कैसे इस्तेमाल नहीं किया :)
फ़ंक्शन assert()ने अपने व्यवहार को संस्करण 5.6 से 7.0 के संक्रमण के दौरान बदल दिया, और संस्करण 7.2 में सब कुछ और भी मजबूत हो गया, इसलिए ध्यान से चेंज और PHP पढ़ें;)
पहला मामला यह है कि जब आपको सीधे TODO को कोड में लिखना होगा, ताकि आप दिए गए कार्यक्षमता को लागू करना न भूलें:

 //  asserts  php.ini // zend.assertions=1 assert(false, "Remove it!"); 

इस कोड को निष्पादित करने के परिणामस्वरूप, हमें यह मिलता है E_WARNING:

 Warning: assert(): Remove it! failed 

PHP7 को अपवाद मोड में स्विच किया जा सकता है, और एक त्रुटि के बजाय, एक अपवाद हमेशा दिखाई देगा AssertionError:

 //    «» ini_set('assert.exception', 1); assert(false, "Remove it!"); 

नतीजतन, हम एक अपवाद की उम्मीद करते हैं AssertionError

यदि आवश्यक हो, तो आप एक मनमाना अपवाद फेंक सकते हैं:

 assert(false, new Exception("Remove it!")); 

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

 // callback-      function backlog($script, $line, $code, $message) { echo $message; } //  callback- assert_options(ASSERT_CALLBACK, 'backlog'); //    assert_options(ASSERT_WARNING, false); //      assert(sqr(4) === 16, 'When I send integer, function should return square of it'); // ,   function sqr($a) { return; //    } 

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

 /** *        * * [ * 'host' => 'localhost', * 'port' => 3306, * 'name' => 'dbname', * 'user' => 'root', * 'pass' => '' * ] * * @param $settings */ function setupDb ($settings) { //   assert(isset($settings['host']), 'Db `host` is required'); assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer'); assert(isset($settings['name']), 'Db `name` is required, should be integer'); //    // ... } setupDb(['host' => 'localhost']); 


यदि आप अनुबंधों में रुचि रखते हैं, तो विशेष रूप से आपके लिए मेरे पास PhpDeal ढांचे की एक कड़ी है


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


यदि आपके पास उपयोग का एक जीवंत अनुभव है assert()- मेरे साथ साझा करें, मैं आभारी रहूंगा। और हाँ, इस विषय पर आपके लिए एक और दिलचस्प रीडिंग - PHP के दावे , अंत में एक ही सवाल के साथ :)

निष्कर्ष में


मैं आपके लिए इस लेख से निष्कर्ष लिखूंगा:

  • गलतियाँ करना - उन्हें आपके कोड में नहीं होना चाहिए
  • अपवादों का उपयोग करें - उनके साथ काम करने के लिए ठीक से संगठित होने की आवश्यकता है और खुशी होगी
  • मुखर - उनके बारे में सीखा, और अच्छी तरह से

पुनश्च


यह लेख "PHP फॉर बिगिनर्स" की एक श्रृंखला से एक रिपॉस्ट है:


यदि आपके पास लेख की सामग्री पर या संभवतः रूप में टिप्पणियां हैं, तो टिप्पणियों में सार का वर्णन करें, और हम इस सामग्री को और बेहतर बनाएंगे। लेख लिखने में मदद

के लिए मैक्सिम सलेसरेंको का धन्यवाद

Source: https://habr.com/ru/post/hi440744/


All Articles