Eval

在一些程式語言中，eval 是一個把字串當作表示式執行而返回一個結果的函式；在另一些之中，它執行多行的程式碼就好像它們被包括在其中，而不是包括 eval 的這一行。eval 的輸入不一定是字串；在支援句法抽象的語言（如 Lisp）中，eval 的輸入將會由抽象句法形式組成。

安全風險

當使用 eval 而資料來自不可信任的來源時，一定要特別的注意。比如說，假設 get_data 函式從 Internet 獲取資料，這個 Python 程式碼就是不安全的:

session'authenticated' = False

data = get_data

foo = eval (data)

一個攻擊者可以讓字串 "session.update (authenticated=True)" 作為資料提供給這個程式，它將會更新 session 字典以設定一個透過身份驗證的鍵為 True。為了補救這一點，所有將會被 eval 使用的資料必須被轉義，或者必須運行於無法訪問可能有害的函式的環境下。

使用

對 eval 的呼叫有時被沒經驗的程式設計師在所有種類的事物上使用。在絕大多數情況下，有更加靈活而不會造成解析程式碼時速度損失的可替代的方法。

比如說，eval 有時被用於一個簡單的設施上，就像如下 PHP 程式碼所示:

$name = 'John Doe';

$greeting = 'Hello';

$template = '"$greeting, $name! How can I help you today?"';

print eval ("return $template;");

儘管這確實有用，它可能導致一些安全問題 (見安全風險)，而且比其它的解決方案慢很多。一個更快和更安全的解決方案可以是改變最後一行為 print $template; 和移除前一行的單引號，或者使用 printf。

eval 有時也被用於需要對數學表示式求值的應用中，如電子試算表。這比寫一個表示式解析器簡單多了，但是發現或者寫出一個表示式解析器通常是一個更好的選擇。除了可修復的安全風險，使用這種語言的求值特性通常很有可能更慢，也沒有那麼高的可定製性。

也許 eval 的最佳使用是在 一個新的語言的過程中 (就像 Lisp 那樣)，以及作為語言的允許使用者在受控制的環境下執行他們自己的程式的指導程式。

出於表示式求值的目的，eval 相比表示式解析器的主要優勢在於，在 eval 受支援的絕大多數程式設計環境下，這個表示式可能是任意的複雜，而且可能包括對使用者所寫的不可能被解析器的創造者所預先知道的函式的呼叫。這個能力允許你用一個你可以按需要增強的函式庫有效的增加 eval 引擎，而不必持續的維持一個表示式解析器。然而，如果你不需要這麼終極的靈活性，表示式解析器遠遠更加有效和輕量。

實施

在直譯語言中，eval 幾乎總是被和正常的程式碼使用一樣的解析器實現。在編譯語言中，用於編譯程式的編譯器可能被嵌入在使用 eval 的程式中；分開來的直譯器有時也被使用，儘管這可能會導致重複程式碼。

程式語言

4.1 JavaScript

在 JavaScript 中，eval 是某種介於表示式求值器和語句執行器的混合體。它返回最後一個被求值的表示式的值 (在 JavaScript 中，所有語句都是表示式)，也允許最後一個分號省略。

如下示例是一個表示式求值器:

foo = 2;

alert (eval ('foo + 2'));

如下示例則是一個語句執行器。

foo = 2;

eval ('foo = foo + 2;alert (foo);');

JavaScript 的 eval 的一個應用是解析 JSON 文字，也許是作為 Ajax 框架中的一部分。然而，現代的瀏覽器提供 JSON.parse 作為這個任務的一個更加安全的替代品。

4.2 ActionScript

在 ActionScript (Flash 的程式語言) 中，eval 不能用於計算任意的表示式。根據 Flash 8 文件，它的使用僅限於代表 “變數名，屬性，物件或者要檢索的影片剪輯。這個引數可以是一個字串或者對物件例項的一個直接引用” 的表示式。

ActionScript 3 不支援 eval。

ActionScript 3 Eval Library 和 D.eval API 是進行中的用以在 ActionScript 3 中建立 eval 的等價物的開發專案。

4.3 Lisp

Lisp 是首先使用 eval 函式的語言。事實上，對 eval 函式的定義導致了該語言直譯器的最初的實施。在 eval 函式被定義之前，Lisp 函式只是手動被編譯成組合語言語句。然而，一旦 eval 函式被手動編譯，它隨後就被用於組成第一個 Lisp 直譯器的基礎的 Read–eval–print 迴圈的一部分。

Lisp eval 函式的後來版本也被作為編譯器實施。

Lisp 中的 eval 函式期望一個形式作為一個引數被求值和執行。給定形式的返回值將會是對 eval 的呼叫的返回值。

這是一個示例 Lisp 程式碼:

 A form which calls the + function with 1,2 and 3 as arguments.

 It returns 6.

(+ 1 2 3)

 In lisp any form is meant to be evaluated, therefore

 the call to + was performed.

 We can prevent Lisp from performing evaluation

 of a form by prefixing it with "'", for example:

(setq form1 '(+ 1 2 3))

 Now form1 contains a form that can be used by eval, for

 example:

(eval form1)

 eval evaluated (+ 1 2 3) and returned 6.

Lisp 眾所周知的非常靈活，eval 函式也是。例如，為了對字串的內容求值，這個字串首先必須使用 read-from-string 函式轉化為 Lisp 格式，隨後這個結果的格式將會被傳給 eval:

(eval (read-from-string "(format t \"Hello World

~%\")"))

主要造成混淆的一點是這個問題，即在哪個上下文中這個形式中的符號會被求值。在上述示例中，form1 包含符號 +。對該符號的求值必然會產生一個用於加法的函式以使得該示例像預期那樣工作。因而 Lisp 的某些方言允許為 eval 傳入一個額外的引數以指定求值的上下文 (類似於 Python 的 eval 函式的可選引數 - 如下所示)。一個用 Lisp 的 Scheme 方言 (R5RS 和以後版本) 寫出的示例:

; Define some simple form as in the above example.

(define form2 '(+ 5 2))

Value: form2

; Evaluate the form within the initial context.

; A context for evaluation is called an "environment" in Scheme slang.

(eval form2 user-initial-environment)

Value: 7

; Confuse the initial environment, so that + will be

; a name for the subtraction function.

(environment-define user-initial-environment '+ -)

Value: +

; Evaluate the form again.

; Notice that the returned value has changed.

(eval form2 user-initial-environment)

Value: 3

4.4 Perl

在 Perl 中，eval 函式是某種介於表示式求值器和語句執行器的混合體。它返回最後一個被求值的表示式的結果 (在 Perl 程式設計中，所有的語句都是表示式)，且允許最後一個分號省略。

一個表示式求值器的示例:

$foo = 2;

print eval ('$foo + 2'), "\n";

一個語句執行器的示例:

$foo = 2;

eval ('$foo += 2; print "$foo\n";');

(注意字串的引號。注意單引號在上述示例中被用來引用字串。如果使用的是雙引號，它將會在把該字串傳入 eval 之前把變數的值插入字串，破壞了 eval 原本的目的，而且在賦值的情況下，有可能引起句法錯誤。)

Perl 也有 eval 塊，作為它的異常處理機制。這與上述對 eval 傳入字串的用法不同，在於 eval 內的程式碼在編譯時而不是執行時解釋，所以它不是本文中使用的 eval 的含義。

PHP

在 PHP 中，eval 執行在一個字串中的程式碼幾乎就像它被放進了檔案中，而不是對 eval 的呼叫一樣。唯一的區別是錯誤被報道為來自對 eval 的一個呼叫，而返回語句則成為函式的結果。