2010-07-11 14 views
18

अस्वीकरण; मैं नुकसान और eval के "बुराइयों" से पूरी तरह वाकिफ हूँ, सहित, लेकिन तक सीमित नहीं: प्रदर्शन के मुद्दों, सुरक्षा, पोर्टेबिलिटी आदिPHP eval और कैप्चरिंग त्रुटियों (जितना संभव हो सके)

समस्या

eval पर पीएचपी मैनुअल पढ़ना .. ।

eval() NULL भेजता है जब तक वापसी का मूल्यांकन किया कोड में कहा जाता है, जो मामले में वापस जाने के लिए भेजे गए मान वापस लौटा है। यदि में एक पार्स त्रुटि है, तो मूल्यांकन कोड, eval() लौटाता है और निम्नलिखित कोड का निष्पादन सामान्य रूप से जारी रहता है। eval() में set_error_handler() का उपयोग करके एक पार्स त्रुटि को पकड़ने के लिए संभव नहीं है।

संक्षेप में, झूठी वापसी को छोड़कर कोई त्रुटि कैप्चर नहीं है जो बहुत उपयोगी है, लेकिन मैं सुर ईआई बेहतर तरीके से कर सकता हूं!

कारण

साइट की कार्यक्षमता मैं पर काम कर रहा हूँ का एक हिस्सा भाव को क्रियान्वित करने पर निर्भर करता है। मैं सैंडबॉक्स या निष्पादन मॉड्यूल के पथ से गुजरना नहीं चाहता, इसलिए मैंने eval का उपयोग समाप्त कर दिया है। इससे पहले कि आप चिल्लाओ "क्या होगा यदि ग्राहक खराब हो गया ?!" पता है कि ग्राहक बहुत भरोसेमंद है; वह अपनी साइट को तोड़ना नहीं चाहेगा, और किसी को भी इस कार्यक्षमता तक पहुंच प्राप्त करने के लिए सर्वर की मालिकाना बहुत अधिक है, बेकार होने के बावजूद।

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

define('CR',chr(13)); 
define('LF',chr(10)); 

function test($cond=''){ 
    $cond=trim($cond); 
    if($cond=='')return 'Success (condition was empty).'; $result=false; 
    $cond='$result = '.str_replace(array(CR,LF),' ',$cond).';'; 
    try { 
     $success=eval($cond); 
     if($success===false)return 'Error: could not run expression.'; 
     return 'Success (condition return '.($result?'true':'false').').'; 
    }catch(Exception $e){ 
     return 'Error: exception '.get_class($e).', '.$e->getMessage().'.'; 
    } 
} 

नोट्स

  • समारोह किसी भी घटना
  • में एक संदेश स्ट्रिंग रिटर्न कोड अभिव्यक्ति एक एकल लाइन टुकड़ा होना चाहिए:

    यह वही है मैं अब तक किया है PHP के बिना, PHP टैग के बिना और बिना किसी अर्धविराम के

  • नई लाइनें रिक्त स्थान में परिवर्तित हो जाती हैं
  • एक चर परिणाम को रोकने के लिए जोड़ा जाता है (अभिव्यक्ति, एक अस्थायी चर प्रयोग किया जाता है सही या गलत, और आदेश eval की वापसी के साथ संघर्ष के लिए नहीं में लौटना चाहिए।)

तो, क्या आप आगे बढ़ेगी उपयोगकर्ता सहयोगी? क्या कोई और पार्सिंग फ़ंक्शंस है जो संभावित त्रुटियों/मुद्दों को बेहतर तरीके से इंगित कर सकता है?

क्रिस।

+0

अगर आप क्या "भाव" आप का उपयोग करेगा के बारे में अधिक प्रतिक्रिया प्रदान कर सकता है के साथ, हो सकता है हम मदद कर सकते हैं चाहिए अधिक। मैं उपयोगकर्ता इनपुट को प्रमाणित करने के लिए कुछ अच्छी टोकन_get_all सामग्री के बारे में सोच सकता हूं;) – NikiC

+0

सामान्य PHP कोड? मैं परिभाषित कार्यों और कक्षाओं के संभावित अपवाद के साथ, PHP की पूर्ण पहुंच की अनुमति देने की योजना बना रहा हूं, जिसकी आवश्यकता नहीं है। – Christian

उत्तर

11

मुझे अपने प्रश्न का एक अच्छा विकल्प/उत्तर मिला है।

सबसे पहले, मुझे यह कहकर शुरू करना है कि जब मैं error_reporting (E_ALL) सेट करता हूं तो निकिक का सुझाव काम करता है; नोटिस PHP आउटपुट में दिखाए जाते हैं, और ओबी के लिए धन्यवाद, वे कब्जा कर लिया जा सकता है।

इसके बाद, मैं इस बहुत ही उपयोगी कोड मिल गया है:

/** 
* Check the syntax of some PHP code. 
* @param string $code PHP code to check. 
* @return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned. 
*/ 
function php_syntax_error($code){ 
    if(!defined("CR")) 
     define("CR","\r"); 
    if(!defined("LF")) 
     define("LF","\n") ; 
    if(!defined("CRLF")) 
     define("CRLF","\r\n") ; 
    $braces=0; 
    $inString=0; 
    foreach (token_get_all('<?php ' . $code) as $token) { 
     if (is_array($token)) { 
      switch ($token[0]) { 
       case T_CURLY_OPEN: 
       case T_DOLLAR_OPEN_CURLY_BRACES: 
       case T_START_HEREDOC: ++$inString; break; 
       case T_END_HEREDOC: --$inString; break; 
      } 
     } else if ($inString & 1) { 
      switch ($token) { 
       case '`': case '\'': 
       case '"': --$inString; break; 
      } 
     } else { 
      switch ($token) { 
       case '`': case '\'': 
       case '"': ++$inString; break; 
       case '{': ++$braces; break; 
       case '}': 
        if ($inString) { 
         --$inString; 
        } else { 
         --$braces; 
         if ($braces < 0) break 2; 
        } 
        break; 
      } 
     } 
    } 
    $inString = @ini_set('log_errors', false); 
    $token = @ini_set('display_errors', true); 
    ob_start(); 
    $code = substr($code, strlen('<?php ')); 
    $braces || $code = "if(0){{$code}\n}"; 
    if (eval($code) === false) { 
     if ($braces) { 
      $braces = PHP_INT_MAX; 
     } else { 
      false !== strpos($code,CR) && $code = strtr(str_replace(CRLF,LF,$code),CR,LF); 
      $braces = substr_count($code,LF); 
     } 
     $code = ob_get_clean(); 
     $code = strip_tags($code); 
     if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) { 
      $code[2] = (int) $code[2]; 
      $code = $code[2] <= $braces 
       ? array($code[1], $code[2]) 
       : array('unexpected $end' . substr($code[1], 14), $braces); 
     } else $code = array('syntax error', 0); 
    } else { 
     ob_end_clean(); 
     $code = false; 
    } 
    @ini_set('display_errors', $token); 
    @ini_set('log_errors', $inString); 
    return $code; 
} 

यह आसानी से लगता है कि मैं क्या जरूरत है (Yay) वास्तव में करता है!

$filePath = '/tmp/tmp_eval'.mt_rand(); 
file_put_contents($filePath, $evalCode); 
register_shutdown_function('unlink', $filePath); 
require($filePath); 

तो $ evalCode में किसी भी त्रुटि त्रुटियों हैंडलर द्वारा नियंत्रित किया जाएगा:

+0

वाह, यह अच्छा लग रहा है। और फिर मैंने कुछ नया सीखा है: 'ब्रेक' में तर्क हो सकता है! बाद में मैं वास्तव में यह समझने के लिए इस कोड पर एक नज़र डालूंगा कि यह क्या करता है। केवल एक नोट: यदि कोड में भारी पार्स त्रुटियां हैं, तो 'token_get_all' स्वयं त्रुटियों को आउटपुट कर सकता है, उदा। यदि आप स्रोत में '\\' का उपयोग कर रहे हैं (PHP <5.3 में)। – NikiC

+1

यह बहुत अच्छा है। अफसोस की बात यह है कि यह अभी भी नहीं कर सकता - गलत फ़ंक्शन कॉल के विरुद्ध सुरक्षा प्रदान नहीं कर सकता, उदा। फ़ंक्शन नामों में सरल वर्तनी की गलतियों। PHP वास्तव में eval() में घातक त्रुटियों को पकड़ने के बारे में सोचना चाहिए। –

12

मुझे नहीं लगता कि eval आपको अपवाद फेंक देगा, यह सामान्य त्रुटियों को फेंक देगा।eval और पहले और eval के बाद इन जगह एक

ob_start(); 

को पकड़ने के लिए:

if ('' !== $error = ob_get_clean()) { 
    // output the error somehow to the client 
} 

मैं वहाँ बेहतर त्रुटि हैंडलिंग के साथ eval की तरह एक और समारोह है नहीं लगता। php_check_syntax है लेकिन यह केवल फ़ाइल को मान्य करता है।

+0

हां, मैंने उन्हें परिभाषित किया है, उल्लेख करने के लिए खेद है। कोड के ऊपर फिक्स्ड। अपवादों के रूप में, मुझे पूरा यकीन है कि यह न तो नहीं है, हालांकि, यह कोड भविष्य में अपवाद जोड़ने के बाद भविष्य में असफल हो सकता है, इसलिए इससे उन्हें किसी भी तरह से संभालने में कोई दिक्कत नहीं होती है। – Christian

+0

मैं 'check_syntax' नहीं ढूंढ पाया। – hakre

+0

@hakre thx निश्चित – NikiC

2

तुम भी कुछ इस तरह की कोशिश कर सकते हैं।

4

कैसे eval अंदर पार्स त्रुटि() के लिए परीक्षण करने के लिए:

$result = @eval($evalcode . "; return true;"); 

$result == false हैं, तो $evalcode एक पार्स त्रुटि है और 'वापसी सच' भाग पर अमल नहीं करता है। जाहिर है $evalcode नहीं वापसी ही कुछ है, लेकिन इस चाल आप प्रभावी रूप से भाव में पार्स त्रुटि के लिए परीक्षण कर सकते हैं ...

+0

बहुत बुद्धिमान विचार! मैंने अभी तक इसका परीक्षण नहीं किया है लेकिन मैं करूँगा। – itoctopus

+0

बहुत अच्छा समाधान – Hien

+1

आप 'eval''d कोड में पार्स त्रुटियों को नहीं पकड़ सकते हैं!यह सिर्फ आपकी लिपि के निष्पादन को रोक देगा। – DUzun