ทำไมความชั่วร้ายจึงเป็นจริง?


141

ฉันรู้ว่าโปรแกรมเมอร์ Lisp และ Scheme มักจะบอกว่าevalควรหลีกเลี่ยงเว้นแต่จำเป็นอย่างเคร่งครัด ผมเคยเห็นคำแนะนำเหมือนกันสำหรับการเขียนโปรแกรมภาษาหลาย evalแต่ฉันยังไม่เห็นรายชื่อของการขัดแย้งที่ชัดเจนกับการใช้งานของ ฉันจะหาบัญชีของปัญหาที่อาจเกิดขึ้นจากการใช้งานได้evalที่ไหน

ตัวอย่างเช่นผมรู้ว่าปัญหาของGOTOในการเขียนโปรแกรมขั้นตอน (โปรแกรมทำให้อ่านได้ยากและยากที่จะรักษาทำให้ปัญหาความปลอดภัยยากที่จะหา ฯลฯ ) evalแต่ผมไม่เคยเห็นมีปากเสียงกับ

ที่น่าสนใจข้อโต้แย้งที่เหมือนกันGOTOนั้นน่าจะใช้ได้กับการต่อเนื่อง แต่ฉันเห็นว่า Schemers นั้นจะไม่พูดว่าการต่อเนื่องนั้นเป็น "ความชั่วร้าย" - คุณควรระวังเมื่อใช้มัน พวกเขามีแนวโน้มที่จะขมวดคิ้วเมื่อใช้โค้ดevalมากกว่าใช้โค้ดต่อเนื่อง (เท่าที่ฉันเห็น - ฉันอาจผิด)


5
eval ไม่ใช่ความชั่วร้าย แต่ความชั่วร้ายคือสิ่งที่ eval ทำ
Anurag

9
@yar - ฉันคิดว่าความคิดเห็นของคุณแสดงให้เห็นมุมมองโลกที่จัดส่งวัตถุ - เป็นศูนย์กลางมาก มันอาจใช้ได้กับภาษาส่วนใหญ่ แต่จะแตกต่างกันใน Common LISP ที่ซึ่งวิธีการไม่ได้อยู่ในคลาสและแตกต่างกันมากขึ้นใน Clojure ซึ่งคลาสจะได้รับการสนับสนุนผ่านฟังก์ชัน Java interop เท่านั้น เจย์ติดแท็กคำถามนี้ในชื่อ Scheme ซึ่งไม่มีความคิดในตัวของชั้นเรียนหรือวิธีการใด ๆ (มี OO หลากหลายรูปแบบให้บริการในรูปแบบไลบรารี)
Zak

3
@ แซ็คถูกต้องฉันรู้แค่ภาษาที่ฉันรู้จัก แต่ถึงแม้ว่าคุณจะทำงานกับเอกสาร Word โดยไม่ใช้สไตล์คุณก็ไม่แห้ง ประเด็นของฉันคือการใช้เทคโนโลยีเพื่อไม่ทำซ้ำตัวเอง OO ไม่ใช่สากลจริง ...
Dan Rosenstark

4
ฉันใช้เสรีภาพในการเพิ่มแท็ก clojure ในคำถามนี้เนื่องจากฉันเชื่อว่าผู้ใช้ Clojure อาจได้รับประโยชน์จากการได้รับคำตอบที่ยอดเยี่ยมที่โพสต์ที่นี่
Michał Marczyk

... ดีสำหรับ Clojure มีเหตุผลเพิ่มเติมอย่างน้อยหนึ่งข้อ: คุณสูญเสียความเข้ากันได้กับ ClojureScript และตราสารอนุพันธ์
Charles Duffy

คำตอบ:


148

EVALมีเหตุผลหลายประการที่หนึ่งไม่ควรใช้

เหตุผลหลักสำหรับผู้เริ่มต้นคือคุณไม่ต้องการมัน

ตัวอย่าง (สมมติว่า Common LISP):

หาค่านิพจน์ด้วยตัวดำเนินการอื่น:

(let ((ops '(+ *)))
  (dolist (op ops)
    (print (eval (list op 1 2 3)))))

นั่นเขียนได้ดีกว่า:

(let ((ops '(+ *)))
  (dolist (op ops)
    (print (funcall op 1 2 3))))

มีตัวอย่างมากมายที่ผู้เริ่มต้นเรียนรู้ Lisp คิดว่าพวกเขาต้องการEVALแต่พวกเขาไม่ต้องการ - เนื่องจากมีการประเมินนิพจน์และสามารถประเมินส่วนของฟังก์ชันได้เช่นกัน เวลาส่วนใหญ่ที่ใช้EVALแสดงให้เห็นถึงการขาดความเข้าใจของผู้ประเมิน

มันเป็นปัญหาเดียวกันกับมาโคร บ่อยครั้งที่ผู้เริ่มต้นเขียนแมโครซึ่งพวกเขาควรจะเขียนฟังก์ชั่น - ไม่เข้าใจว่ามาโครตัวใดมีไว้เพื่ออะไรจริงๆและไม่เข้าใจว่าฟังก์ชั่นทำงานอยู่แล้ว

บ่อยครั้งเป็นเครื่องมือที่ไม่ถูกต้องสำหรับงานที่ใช้EVALและมักจะระบุว่าผู้เริ่มต้นไม่เข้าใจกฎการประเมินผลเสียงกระเพื่อมตามปกติ

หากคุณคิดว่าคุณต้องEVALแล้วตรวจสอบว่าสิ่งที่ชอบFUNCALL, REDUCEหรือAPPLYอาจจะนำมาใช้แทน

  • FUNCALL - เรียกใช้ฟังก์ชันที่มีอาร์กิวเมนต์: (funcall '+ 1 2 3)
  • REDUCE - เรียกใช้ฟังก์ชันในรายการค่าและรวมผลลัพธ์: (reduce '+ '(1 2 3))
  • APPLY- (apply '+ '(1 2 3))เรียกใช้ฟังก์ชันที่มีรายชื่อเป็นข้อโต้แย้งนี้:

ถาม: ฉันต้องการ eval จริง ๆ หรือคอมไพเลอร์ / ผู้ประเมินแล้วสิ่งที่ฉันต้องการจริงๆ

เหตุผลหลักที่ควรหลีกเลี่ยงEVALสำหรับผู้ใช้ขั้นสูงเล็กน้อย:

  • คุณต้องการตรวจสอบให้แน่ใจว่าโค้ดของคุณถูกคอมไพล์เพราะคอมไพเลอร์สามารถตรวจสอบรหัสสำหรับปัญหาต่าง ๆ และสร้างรหัสได้เร็วขึ้นบางครั้งจำนวนมากมาก (นั่นคือตัวคูณ 1,000 ;-))

  • รหัสที่สร้างขึ้นและต้องได้รับการประเมินไม่สามารถรวบรวมได้เร็วที่สุด

  • การป้อนข้อมูลผู้ใช้โดยพลการจะทำให้เกิดปัญหาด้านความปลอดภัยขึ้น

  • การใช้การประเมินผลบางอย่างกับEVALสามารถเกิดขึ้นในเวลาที่ไม่ถูกต้องและสร้างปัญหาการสร้าง

หากต้องการอธิบายจุดสุดท้ายด้วยตัวอย่างที่ง่ายขึ้น:

(defmacro foo (a b)
  (list (if (eql a 3) 'sin 'cos) b))

ดังนั้นฉันอาจต้องการที่จะเขียนแมโครว่าขึ้นอยู่กับการใช้พารามิเตอร์แรกอย่างใดอย่างหนึ่งหรือSINCOS

(foo 3 4)ไม่(sin 4)และไม่(foo 1 4)(cos 4)

ตอนนี้เราอาจมี:

(foo (+ 2 1) 4)

สิ่งนี้ไม่ได้ให้ผลลัพธ์ที่ต้องการ

จากนั้นหนึ่งอาจต้องการซ่อมแซมแมโครFOOโดยการประเมินตัวแปร:

(defmacro foo (a b)
  (list (if (eql (eval a) 3) 'sin 'cos) b))

(foo (+ 2 1) 4)

แต่นี่ก็ยังใช้งานไม่ได้:

(defun bar (a b)
  (foo a b))

ค่าของตัวแปรไม่เป็นที่รู้จักในเวลารวบรวม

เหตุผลสำคัญทั่วไปที่ควรหลีกเลี่ยงEVAL:มักใช้เพื่อการแฮ็กที่น่าเกลียด


3
ขอบคุณ! ฉันไม่เข้าใจจุดสุดท้าย (การประเมินในเวลาผิดหรือเปล่า?) - คุณช่วยอธิบายหน่อยได้ไหม?
เจย์

41
+1 เนื่องจากนี่เป็นคำตอบที่แท้จริง - ผู้คนล้มevalเหลวเพราะพวกเขาไม่รู้ว่ามีภาษาหรือไลบรารีที่เฉพาะเจาะจงที่จะทำสิ่งที่พวกเขาต้องการทำ ตัวอย่างที่คล้ายกันจาก JS: ฉันต้องการได้รับทรัพย์สินจากวัตถุโดยใช้ชื่อแบบไดนามิกดังนั้นฉันเขียน: eval("obj.+" + propName)เมื่อฉันสามารถเขียนobj[propName]ได้
Daniel Earwicker

ฉันเห็นสิ่งที่คุณหมายถึงตอนนี้เรนเนอร์! Thansk!
เจย์

@ Daniel: "obj.+"? ที่ฉันตรวจสอบล่าสุด+ไม่ถูกต้องเมื่อใช้การอ้างอิงดอทใน JS
Hello71

2
@Daniel อาจหมายถึง eval ("obj." + propName) ซึ่งควรใช้งานได้ตามที่คาดไว้
claj

41

eval(ในภาษาใด ๆ ) ไม่ใช่ความชั่วร้ายในลักษณะเดียวกับที่ลูกโซ่ไม่ใช่ความชั่วร้าย มันเป็นเครื่องมือ มันเป็นเครื่องมือที่ทรงพลังที่เมื่อนำไปใช้ในทางที่ผิดสามารถตัดแขนขาและ eviscerate (พูดเชิงเปรียบเทียบ) แต่สามารถพูดได้เหมือนกันสำหรับเครื่องมือมากมายในกล่องเครื่องมือของโปรแกรมเมอร์รวมถึง:

  • goto และเพื่อน ๆ
  • เกลียวล็อคที่ใช้
  • มาโคร (hygenic หรืออื่น ๆ )
  • ชี้
  • ข้อยกเว้นที่เริ่มต้นได้ใหม่
  • รหัสแก้ไขตัวเอง
  • ... และนักแสดงนับพัน

หากคุณพบว่าตัวเองต้องใช้เครื่องมือที่ทรงพลังและอันตรายเหล่านี้ให้ถามตัวเองสามครั้งว่า "ทำไม" ในห่วงโซ่ ตัวอย่างเช่น:

"ทำไมฉันต้องใช้eval?" "เพราะ foo" "ทำไมถึงต้องฟู" "เพราะ ..."

หากคุณไปถึงจุดสิ้นสุดของห่วงโซ่นั้นและเครื่องมือยังคงดูเหมือนว่าเป็นสิ่งที่ถูกต้องให้ทำเช่นนั้น บันทึก Hell ออกมา ทดสอบนรกออกมาจากมัน ตรวจสอบความถูกต้องและความปลอดภัยซ้ำแล้วซ้ำอีก แต่ทำมัน


ขอบคุณ - นั่นคือสิ่งที่ฉันเคยได้ยิน eval มาก่อน ("ถามตัวเองว่าทำไม") แต่ฉันยังไม่เคยได้ยินหรืออ่านสิ่งที่อาจเป็นปัญหา ฉันเห็นตอนนี้จากคำตอบที่นี่สิ่งที่พวกเขา (ปัญหาด้านความปลอดภัยและประสิทธิภาพ)
Jay

8
และการอ่านรหัสได้ Eval สามารถไขรหัสทั้งหมดและทำให้เข้าใจไม่ได้
เพียงความคิดเห็นที่ถูกต้องของฉัน

ฉันไม่เข้าใจว่าทำไม "เกลียวล็อคตาม" [sic] อยู่ในรายการของคุณ มีรูปแบบของการทำงานพร้อมกันที่ไม่เกี่ยวข้องกับการล็อคและโดยทั่วไปปัญหาเกี่ยวกับการล็อคเป็นที่รู้จักกันดี แต่ฉันไม่เคยได้ยินใครอธิบายว่าใช้ล็อคเป็น "ความชั่วร้าย"
asveikau

4
asveikau: การทำเกลียวบนล็อคเป็นสิ่งที่ฉาวโฉ่ยากที่จะทำให้ถูกต้อง (ฉันเดาว่า 99.44% ของรหัสการผลิตที่ใช้ล็อคไม่ดี) มันไม่ได้เขียน มีแนวโน้มที่จะเปลี่ยนรหัส "มัลติเธรด" ของคุณให้เป็นรหัสซีเรียล (การแก้ไขเพียงแค่นี้ทำให้โค้ดช้าและป่องแทน) มีทางเลือกที่ดีในการทำเกลียวแบบล็อคเช่น STM หรือนางแบบนักแสดงที่ใช้มันในทุกสิ่งยกเว้นโค้ดระดับต่ำที่สุด
เพียงความคิดเห็นที่ถูกต้องของฉัน

"ทำไมห่วงโซ่" :) ให้แน่ใจว่าได้หยุดหลังจาก 3 ขั้นตอนมันอาจเจ็บ
szymanowski

27

Eval ใช้ได้ดีตราบใดที่คุณรู้ว่ามีอะไรเกิดขึ้น การป้อนข้อมูลของผู้ใช้ใด ๆ จะต้องตรวจสอบและตรวจสอบและทุกอย่าง หากคุณไม่ทราบวิธีการตรวจสอบให้แน่ใจ 100% อย่าทำเช่นนั้น

โดยทั่วไปผู้ใช้สามารถพิมพ์รหัสใด ๆ สำหรับภาษาที่เป็นปัญหาและมันจะดำเนินการ คุณสามารถจินตนาการถึงความเสียหายที่เขาสามารถทำได้


1
ดังนั้นหากฉันกำลังสร้าง S-expressions โดยยึดตามการป้อนข้อมูลของผู้ใช้โดยใช้อัลกอริทึมที่จะไม่คัดลอกอินพุตของผู้ใช้โดยตรงและถ้ามันง่ายขึ้นและชัดเจนขึ้นในบางสถานการณ์ที่เฉพาะเจาะจงมากกว่าการใช้มาโครหรือเทคนิคอื่น ๆ " เกี่ยวกับมัน? ในคำอื่น ๆ ปัญหาเฉพาะกับ eval จะเหมือนกันกับแบบสอบถาม SQL และเทคนิคอื่น ๆ ที่ใช้การป้อนข้อมูลผู้ใช้โดยตรง?
เจย์

10
เหตุผลที่เรียกว่า "ความชั่วร้าย" ก็เพราะการทำผิดนั้นแย่กว่าการทำสิ่งอื่น ๆ และอย่างที่เรารู้กันว่ามือใหม่จะทำสิ่งผิด
Tor Valamo

3
ฉันจะไม่พูดว่าต้องตรวจสอบรหัสก่อนที่จะทำการตรวจสอบในทุกสถานการณ์ เมื่อใช้งาน REPL อย่างง่ายคุณอาจแค่ป้อนข้อมูลลงในช่องว่างที่ไม่ถูกทำเครื่องหมาย Eval และนั่นจะไม่เป็นปัญหา (แน่นอนเมื่อเขียน REPL บนเว็บคุณต้องใช้ Sandbox แต่ไม่ใช่กรณีปกติ CLI-REPL ที่ทำงานบนระบบของผู้ใช้)
sepp2k

1
อย่างที่ฉันบอกไปคุณต้องรู้แน่ชัดว่าเกิดอะไรขึ้นเมื่อคุณป้อนสิ่งที่คุณป้อนเข้าสู่การทดลอง หากนั่นหมายความว่า "มันจะดำเนินการคำสั่งบางอย่างภายในขอบเขตของ sandbox" นั่นคือสิ่งที่มันหมายถึง ;)
Tor Valamo

@TorValamo เคยได้ยินเรื่องการพักคุกหรือไม่?
Loïc Faure-Lacroix

21

"ฉันควรใช้เมื่อevalไหร่" อาจเป็นคำถามที่ดีกว่า

คำตอบสั้น ๆ คือ "เมื่อโปรแกรมของคุณต้องการเขียนโปรแกรมอื่นเมื่อรันไทม์แล้วเรียกใช้" โปรแกรมทางพันธุกรรมevalเป็นตัวอย่างของสถานการณ์ที่มันอาจจะทำให้ความรู้สึกกับการใช้


14

IMO, คำถามนี้ไม่ได้เฉพาะการ LISP นี่คือคำตอบสำหรับคำถามเดียวกันสำหรับ PHP และใช้กับ LISP, Ruby และภาษาอื่น ๆ ที่มี eval:

ปัญหาหลักของ eval () คือ:

  • การป้อนข้อมูลที่อาจไม่ปลอดภัย การส่งพารามิเตอร์ที่ไม่น่าเชื่อถือเป็นวิธีที่ล้มเหลว มันมักจะไม่ใช่งานที่ไม่สำคัญเพื่อให้แน่ใจว่าพารามิเตอร์ (หรือส่วนหนึ่งของมัน) นั้นเชื่อถือได้อย่างสมบูรณ์
  • Trickyness การใช้ eval () ทำให้โค้ดฉลาดขึ้นดังนั้นจึงยากที่จะติดตาม ในการอ้างถึง Brian Kernighan " การดีบักนั้นยากกว่าการเขียนรหัสสองเท่าในตอนแรกดังนั้นถ้าคุณเขียนรหัสอย่างชาญฉลาดที่สุดเท่าที่จะทำได้คุณจะเป็นผู้นิยามไม่ฉลาดพอที่จะทำการแก้ไข "

ปัญหาหลักที่เกิดขึ้นจริงกับการใช้ eval () เป็นเพียงปัญหาเดียว:

  • นักพัฒนาที่ไม่มีประสบการณ์ซึ่งใช้งานโดยไม่พิจารณาอย่างเพียงพอ

ที่นำมาจากที่นี่

ฉันคิดว่าชิ้นเล่ห์เหลี่ยมเป็นจุดที่น่าอัศจรรย์ ความหลงใหลในโค้ดกอล์ฟและโค้ดที่กระชับนั้นส่งผลให้เกิดโค้ด "ฉลาด" (ซึ่ง evals เป็นเครื่องมือที่ยอดเยี่ยม) แต่คุณควรเขียนโค้ดเพื่อให้สามารถอ่านได้ IMO ไม่ใช่เพื่อแสดงว่าคุณฉลาดและไม่ประหยัดกระดาษ (คุณจะไม่พิมพ์)

จากนั้นใน LISP มีปัญหาบางอย่างที่เกี่ยวข้องกับบริบทที่มีการเรียกใช้งาน Eval ดังนั้นโค้ดที่ไม่น่าเชื่อถือจึงสามารถเข้าถึงสิ่งต่างๆได้มากขึ้น ปัญหานี้ดูเหมือนจะเป็นเรื่องธรรมดาอยู่ดี


3
ปัญหา "การป้อนข้อมูลที่ชั่วร้าย" กับ EVAL มีผลกับภาษาที่ไม่ใช่เสียงกระเพื่อมเท่านั้นเนื่องจากในภาษาเหล่านั้น eval () มักใช้อาร์กิวเมนต์สตริงและข้อมูลของผู้ใช้โดยทั่วไปจะถูก spliced ​​ระบบผู้ใช้สามารถใส่เครื่องหมายคำพูดในอินพุต รหัสที่สร้างขึ้น แต่ใน Lisp อาร์กิวเมนต์ของ EVAL ไม่ใช่สตริงและอินพุตของผู้ใช้ไม่สามารถหลบหนีเข้าไปในโค้ดได้เว้นแต่คุณจะประมาทอย่างแน่นอน (ในขณะที่คุณแยกวิเคราะห์อินพุตด้วย READ-FROM-STRING เพื่อสร้าง S-expression ซึ่งคุณจะรวมไว้ใน รหัส EVAL โดยไม่ต้องอ้างถึงถ้าคุณพูดมันไม่มีทางที่จะหนีจากคำพูด)
ทิ้งบัญชี

12

มีคำตอบที่ยอดเยี่ยมมากมาย แต่นี่เป็นอีกคำตอบหนึ่งจาก Matthew Flatt หนึ่งในผู้พัฒนาแร็กเก็ต:

http://blog.racket-lang.org/2011/10/on-eval-in-dynamic-languages-generally.html

เขาทำหลายจุดที่ได้รับการคุ้มครองแล้ว แต่บางคนอาจพบว่าเขาสนใจ แต่อย่างไรก็ตาม

สรุป: บริบทที่ใช้มีผลต่อผลลัพธ์ของการประเมินผล แต่มักไม่ได้รับการพิจารณาโดยโปรแกรมเมอร์ซึ่งนำไปสู่ผลลัพธ์ที่ไม่คาดคิด


11

คำตอบที่ยอมรับคือการอยู่ห่าง ซึ่งฉันพบว่าแปลกเพราะมันเป็นแบบดั้งเดิมและหนึ่งในเจ็ดแบบดั้งเดิม (ส่วนอื่น ๆ คือข้อเสียรถยนต์ cdr ถ้า eq และคำพูด) มันได้รับการใช้งานและความรักน้อยที่สุด

จากOn Lisp : "โดยปกติแล้วการโทรหา eval อย่างชัดเจนก็เหมือนกับการซื้อของในร้านขายของที่ระลึกที่สนามบินเมื่อรอจนถึงวินาทีสุดท้าย

ดังนั้นฉันจะใช้ eval เมื่อไหร่? หนึ่งในการใช้งานปกติคือการมี REPL ภายใน REPL (loop (print (eval (read))))ของคุณโดยการประเมิน ทุกคนใช้ได้ดีกับการใช้งานนั้น

แต่คุณยังสามารถกำหนดฟังก์ชั่นในแง่ของมาโครที่จะถูกประเมินหลังการคอมไพล์โดยรวม eval กับ backquote คุณไป

(eval `(macro ,arg0 ,arg1 ,arg2))))

และมันจะฆ่าบริบทสำหรับคุณ

Swank (สำหรับ emacs slime) เต็มไปด้วยกรณีเหล่านี้ พวกเขามีลักษณะเช่นนี้:

(defun toggle-trace-aux (fspec &rest args)
  (cond ((member fspec (eval '(trace)) :test #'equal)
         (eval `(untrace ,fspec))
         (format nil "~S is now untraced." fspec))
        (t
         (eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args))
         (format nil "~S is now traced." fspec))))

ฉันไม่คิดว่ามันเป็นการแฮ็คที่สกปรก ฉันใช้มันตลอดเวลาเพื่อคืนค่าแมโครใหม่ให้เป็นฟังก์ชั่น


1
คุณอาจต้องการตรวจสอบภาษาเคอร์เนล;)
artemonster

7

อีกสองสามคะแนนใน Lisp eval:

  • มันประเมินภายใต้สภาพแวดล้อมของโลกสูญเสียบริบทท้องถิ่นของคุณ
  • บางครั้งคุณอาจถูกล่อลวงให้ใช้ Eval เมื่อคุณตั้งใจจะใช้มาโคร read ''. ซึ่งประเมินในเวลาอ่าน

ฉันเข้าใจว่าการใช้ env ทั่วโลกนั้นเป็นจริงทั้ง Common Lisp และ Scheme มันเป็นความจริงสำหรับ Clojure หรือไม่
Jay

2
ใน Scheme (อย่างน้อยสำหรับ R7RS บางทีสำหรับ R6RS) คุณต้องผ่านสภาพแวดล้อมเพื่อให้เห็นความจริง
csl

4

เช่นเดียวกับกฎ "GOTO": หากคุณไม่รู้ว่าคุณกำลังทำอะไรอยู่

นอกเหนือจากการสร้างข้อมูลที่เป็นที่รู้จักและปลอดภัยแล้วยังมีปัญหาที่บางภาษา / การใช้งานไม่สามารถเพิ่มประสิทธิภาพของรหัสได้อย่างเพียงพอ evalคุณอาจจะจบลงด้วยภายในรหัสตีความ


กฎนั้นเกี่ยวข้องกับ GOTO อย่างไร มีฟีเจอร์ใด ๆ ในภาษาการเขียนโปรแกรมที่คุณไม่สามารถทำให้ยุ่งเหยิงได้หรือไม่?
Ken

2
@Ken: ไม่มีกฎ GOTO ดังนั้นเครื่องหมายคำพูดในคำตอบของฉัน มีเพียงความเชื่อสำหรับคนที่กลัวที่จะคิดด้วยตนเอง เหมือนกันสำหรับ eval ฉันจำได้ว่าการเร่งสคริปต์ Perl ให้เร็วขึ้นอย่างมากโดยใช้ eval เป็นเครื่องมือหนึ่งในกล่องเครื่องมือของคุณ มือใหม่มักใช้การประเมินผลเมื่อการสร้างภาษาอื่นง่ายกว่า / ดีกว่า แต่การหลีกเลี่ยงมันจะต้องเจ๋งและทำให้ผู้คนดื้อรั้น?
stesch

4

Eval นั้นไม่ปลอดภัย ตัวอย่างเช่นคุณมีรหัสต่อไปนี้:

eval('
hello('.$_GET['user'].');
');

ขณะนี้ผู้ใช้มาที่ไซต์ของคุณและป้อน URL http://example.com/file.php?user= ); $ is_admin = true; echo (

จากนั้นรหัสผลลัพธ์จะเป็น:

hello();$is_admin=true;echo();

6
เขากำลังพูดถึง Lisp ไม่คิด php
fmsf

4
@fmsf เขากำลังพูดถึง Lisp โดยเฉพาะ แต่โดยทั่วไปเกี่ยวกับevalภาษาใด ๆ ก็ตามที่มี
Skilldrick

4
@fmsf - นี่เป็นคำถามที่ไม่ขึ้นกับภาษา มันยังใช้กับภาษาที่รวบรวมแบบคงที่ในขณะที่พวกเขาสามารถจำลอง eval โดยการโทรออกไปยังคอมไพเลอร์ที่รันไทม์
Daniel Earwicker

1
ในกรณีนั้นภาษาจะซ้ำกัน ฉันเห็นสิ่งนี้มากมายที่นี่
fmsf

9
PHP eval ไม่เหมือน Lisp eval ดูมันทำงานบนสายอักขระและการใช้ประโยชน์ใน URL ขึ้นอยู่กับความสามารถในการปิดวงเล็บข้อความต้นฉบับและเปิดอีกหนึ่ง เสียงกระเพื่อม eval ไม่ไวต่อสิ่งประเภทนี้ คุณสามารถประเมินข้อมูลที่เป็นอินพุตจากเครือข่ายถ้าคุณทำการ sandbox อย่างถูกต้อง (และโครงสร้างนั้นง่ายพอที่จะเดินเพื่อทำสิ่งนั้น)
Kaz

2

Eval ไม่ใช่ความชั่วร้าย Eval ไม่ซับซ้อน มันเป็นฟังก์ชั่นที่รวบรวมรายชื่อที่คุณส่งให้ ในภาษาอื่น ๆ ส่วนใหญ่การคอมไพล์โค้ดโดยพลการจะหมายถึงการเรียนรู้ AST ของภาษาและการขุดในคอมไพเลอร์ภายในเพื่อหา API คอมไพเลอร์ ในเสียงกระเพื่อมคุณเพียงเรียก eval

คุณควรใช้เมื่อใด เมื่อใดก็ตามที่คุณต้องการที่จะรวบรวมสิ่งที่มักจะใช้โปรแกรมที่ยอมรับสร้างหรือปรับเปลี่ยนโค้ดที่รันไทม์

เมื่อใดที่คุณไม่ควรใช้ กรณีอื่น ๆ ทั้งหมด

ทำไมคุณไม่ควรใช้มันเมื่อคุณไม่ต้องการ? เพราะคุณจะทำอะไรบางอย่างในลักษณะที่ซับซ้อนเกินความจำเป็นซึ่งอาจทำให้เกิดปัญหาในการอ่านประสิทธิภาพและการดีบัก

ใช่ แต่ถ้าฉันเป็นผู้เริ่มฉันจะรู้ได้อย่างไรว่าฉันควรใช้มัน? พยายามใช้สิ่งที่คุณต้องการด้วยฟังก์ชั่นเสมอ หากไม่ได้ผลให้เพิ่มมาโคร หากยังไม่ได้ผลให้ลองดู!

ปฏิบัติตามกฎเหล่านี้และคุณจะไม่ทำชั่วกับการประเมิน :)


0

ฉันชอบคำตอบของแซคเป็นอย่างมากและเขาก็มีความสำคัญกับเรื่องนี้: evalใช้เมื่อคุณกำลังเขียนภาษาใหม่สคริปต์หรือการดัดแปลงภาษา เขาไม่ได้อธิบายเพิ่มเติมดังนั้นฉันจะยกตัวอย่าง:

(eval (read-line))

ในโปรแกรม Lisp แบบง่ายนี้ผู้ใช้จะได้รับพร้อมต์ให้ป้อนข้อมูลจากนั้นสิ่งที่พวกเขาป้อนจะถูกประเมิน สำหรับการทำงานทั้งชุดของคำจำกัดความของสัญลักษณ์ที่จะนำเสนอมีถ้าโปรแกรมจะรวบรวมเพราะคุณมีความคิดที่ฟังก์ชั่นการใช้อาจป้อนไม่ดังนั้นคุณจะต้องรวมถึงพวกเขาทั้งหมด ซึ่งหมายความว่าหากคุณรวบรวมโปรแกรมอย่างง่ายนี้ไบนารีที่ได้จะเป็นขนาดใหญ่

ตามหลักการแล้วคุณไม่สามารถพิจารณาคำแถลงที่รวบรวมได้ด้วยเหตุผลนี้ โดยทั่วไปเมื่อคุณใช้evalคุณจะทำงานในสภาพแวดล้อมที่ถูกตีความและรหัสจะไม่สามารถรวบรวมได้อีกต่อไป ถ้าคุณไม่ใช้evalคุณสามารถคอมไพล์โปรแกรม Lisp หรือ Scheme เหมือนกับโปรแกรม C ดังนั้นคุณต้องการที่จะให้แน่ใจว่าคุณต้องการและความจำเป็นที่จะอยู่ในสภาพแวดล้อมที่ตีความก่อน committing ใช้EVAL

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.