2010-07-11 19 views
18

Tuyên bố từ chối; Tôi hoàn toàn nhận thức được những cạm bẫy và "tệ nạn" của eval, bao gồm nhưng không giới hạn: vấn đề hiệu suất, an ninh, tính di động, vvLỗi PHP và lỗi chụp (nhiều nhất có thể)

Vấn đề

Đọc hướng dẫn PHP trên eval .. .

eval() trả về NULL trừ khi lợi nhuận là gọi trong mã đánh giá, trong đó trường hợp giá trị thông qua để trở lại là trả lại. Nếu có lỗi phân tích cú pháp trong mã được đánh giá, eval() trả lại SAI và thực thi mã sau đây tiếp tục bình thường. Nó không phải là có thể bắt lỗi phân tích cú pháp trong eval() bằng cách sử dụng set_error_handler().

Tóm lại, không có lỗi chụp ngoại trừ trả về false rất hữu ích, nhưng tôi ei có thể làm tốt hơn!

Lý do

Một phần của chức năng của trang web tôi đang làm việc trên dựa vào thực hiện biểu thức. Tôi không muốn đi qua con đường của sandbox hoặc mô-đun thực hiện, vì vậy tôi đã kết thúc bằng cách sử dụng eval. Trước khi bạn hét lên "nếu khách hàng trở nên tồi tệ thì sao ?!" biết rằng khách hàng được tin tưởng khá nhiều; anh ta sẽ không muốn phá vỡ trang web của riêng mình, và bất cứ ai truy cập vào chức năng này sở hữu khá nhiều máy chủ, bất kể eval.

Khách hàng biết về các biểu thức như trong Excel, và nó không phải là một vấn đề giải thích sự khác biệt nhỏ, tuy nhiên, có một số hình thức cảnh báo là khá nhiều chức năng tiêu chuẩn.

Đây là những gì tôi có cho đến nay:

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().'.'; 
    } 
} 

Ghi chú

  • Hàm trả về một chuỗi tin nhắn trong bất kỳ sự kiện
  • Khái niệm mã nên được một mảnh single-line của PHP, không có thẻ PHP và không có dấu chấm phẩy kết thúc
  • Dòng mới được chuyển đổi thành dấu cách
  • Một biến được thêm vào để chứa kết quả (biểu hiện phải trả lại đúng hoặc sai, và để không mâu thuẫn với lợi nhuận eval, một biến temp được sử dụng.)

Vì vậy, những gì bạn sẽ thêm để tiếp tục trợ giúp người dùng? Có thêm chức năng phân tích cú pháp nào có thể xác định tốt hơn các lỗi/vấn đề có thể xảy ra không?

Chris.

+0

nếu bạn có thể cung cấp thông tin phản hồi hơn về những gì "biểu thức", bạn sẽ sử dụng, có lẽ chúng ta có thể giúp hơn. Tôi có thể nghĩ ra một số thứ token_get_all tốt đẹp để xác thực người dùng nhập vào;) – NikiC

+0

Mã PHP bình thường? Tôi dự định cho phép truy cập đầy đủ vào PHP, với ngoại lệ có thể là việc định nghĩa các hàm và các lớp, điều này không cần thiết. – Christian

Trả lời

11

Tôi đã tìm thấy giải pháp thay thế/câu trả lời phù hợp cho câu hỏi của mình.

Trước tiên, hãy để tôi bắt đầu bằng cách nói rằng đề xuất của nikic hoạt động khi tôi đặt error_reporting (E_ALL); thông báo được hiển thị trong đầu ra PHP, và nhờ OB, chúng có thể được ghi lại.

Tiếp theo, tôi đã tìm thấy mã này rất hữu ích:

/** 
* 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; 
} 

Có vẻ nó dễ dàng thực hiện chính xác những gì tôi cần (yay)!

+0

Wow, điều này có vẻ tốt đẹp. Và một lần nữa tôi đã học được một cái gì đó mới: 'break' có thể có một đối số! Sau này tôi sẽ xem xét mã này để thực sự hiểu được nó làm gì. Chỉ một lưu ý: Nếu có lỗi phân tích cú pháp nặng trong mã, 'token_get_all' có thể tự xuất lỗi, ví dụ: nếu bạn đang sử dụng '\\' trong nguồn (trong PHP <5.3). – NikiC

+1

Rất đẹp. Đáng buồn thay, nó vẫn không - không thể - cung cấp sự bảo vệ chống lại các cuộc gọi hàm sai, ví dụ: lỗi chính tả đơn giản trong tên hàm. PHP thực sự nên suy nghĩ về việc bắt lỗi nghiêm trọng trong eval(). –

12

Tôi không nghĩ rằng eval sẽ ném cho bạn một ngoại lệ, nó sẽ ném các lỗi bình thường.Để nắm bắt những nơi một

ob_start(); 

trước eval và và sau khi eval:

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

Tôi không nghĩ rằng có một chức năng như eval với xử lý lỗi tốt hơn. Có php_check_syntax nhưng nó chỉ xác thực tệp.

+0

Vâng, tôi đã xác định chúng, xin lỗi vì đã không đề cập đến. Sửa mã trên. Đối với trường hợp ngoại lệ, tôi khá chắc chắn nó không không, tuy nhiên, mã này có thể thất bại trong tương lai sau khi có lẽ thêm ngoại lệ cho nó, do đó, nó không làm tổn thương xử lý chúng anyway. – Christian

+0

Tôi không thể tìm thấy 'check_syntax'. – hakre

+0

@hakre thx cố định – NikiC

2

Bạn cũng có thể thử một cái gì đó như thế này:

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

Vì vậy, bất kỳ lỗi nào trong $ evalCode sẽ được xử lý bởi bộ xử lý lỗi.

4

Làm thế nào để kiểm tra lỗi phân tích cú pháp bên trong eval():

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

Nếu $result == false, $evalcode có một lỗi phân tích cú pháp và không thực hiện 'trở thành sự thật' phần. Rõ ràng $evalcode phải không trở bản thân điều gì đó, nhưng với thủ thuật này bạn có thể kiểm tra xem có lỗi phân tích cú pháp trong các biểu thức một cách hiệu quả ...

+0

Ý tưởng rất thông minh! Tôi chưa thử nghiệm nó nhưng tôi sẽ. – itoctopus

+0

giải pháp rất tốt – Hien

+1

Bạn không thể bắt lỗi Parse trong mã 'eval''d!Nó sẽ chỉ ngừng thực hiện kịch bản của bạn. – DUzun