ฉันควรใช้การยืนยันรหัส PHP ของฉันหรือไม่


87

เพื่อนร่วมงานได้เพิ่มคำสั่งassertสองสามครั้งภายในไลบรารีของเราในสถานที่ที่ฉันจะใช้คำสั่ง if และทิ้งข้อยกเว้น (ฉันไม่เคยได้ยินเรื่องการยืนยันมาก่อนเลย) นี่คือตัวอย่างของวิธีที่เขาใช้:

assert('isset($this->records); /* Records must be set before this is called. */');

ฉันจะทำ:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

จากการอ่านเอกสาร PHP ในการยืนยันดูเหมือนว่าขอแนะนำให้คุณตรวจสอบให้แน่ใจว่ามีการใช้งานอยู่และเพิ่มตัวจัดการก่อนที่จะใช้การยืนยัน ฉันไม่พบสถานที่ที่เขาทำแบบนี้

ดังนั้นคำถามของฉันคือใช้การยืนยันความคิดที่ดีที่ได้รับข้างต้นและฉันควรใช้บ่อยขึ้นแทนที่จะใช้ if's และข้อยกเว้นหรือไม่?

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


เป็นจริง'isset(สายรหัสด้วยassert)? ไม่ใช่แค่isset(ไม่มีเครื่องหมายคำพูดเดียว')?
Peter Mortensen

คำตอบ:


79

หลักการทั่วไปที่ใช้ได้กับภาษาส่วนใหญ่ (ทั้งหมดที่ฉันรู้อย่างคลุมเครือ) คือassertใช้เพื่อยืนยันว่าเงื่อนไขเป็นจริงเสมอในขณะที่ifเหมาะสมหากเป็นไปได้ว่าบางครั้งจะล้มเหลว

ในกรณีนี้ผมจะบอกว่าassertมีความเหมาะสม (ตามความเข้าใจของผมที่อ่อนแอของสถานการณ์) เพราะrecordsควรเสมอถูกตั้งค่าก่อนที่จะได้รับวิธีการที่เรียกว่า ดังนั้นความล้มเหลวในการตั้งค่าเรกคอร์ดจะเป็นจุดบกพร่องในโปรแกรมแทนที่จะเป็นเงื่อนไขรันไทม์ นี่assertคือการช่วยให้แน่ใจว่า (ด้วยการทดสอบที่เพียงพอ) ว่าไม่มีเส้นทางการเรียกใช้โปรแกรมที่เป็นไปได้ที่อาจทำให้รหัสที่ถูกปกป้องด้วยการassertเรียกโดยไม่recordsได้รับการตั้งค่า

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


4
ในการเพิ่มสิ่งนี้คุณอาจไม่ต้องการปิดใช้งานการยืนยันในรหัสการผลิตของคุณเนื่องจากจะช่วยให้แน่ใจว่าเงื่อนไข "สิ่งนี้ไม่ควรเกิดขึ้น" ยังคงเป็นเช่นนั้น อาจเป็นการดีกว่าที่จะปล่อยให้แอปพลิเคชันของคุณหยุดจากการยืนยันแทนที่จะปล่อยให้ผู้ใช้ของคุณดำเนินต่อไปตามเส้นทางการดำเนินการที่ไม่ควรมีอยู่
derekerdmann

2
@derekerdmann: จริง. สำหรับการยืนยันบางอย่างอาจเพียงพอที่จะบันทึก (ในการผลิต) หรือพิมพ์คำเตือน (ในสภาพแวดล้อมการพัฒนา) แต่เนื่องจากอาจเกิดการอ้างยังรักษาความปลอดภัยป้องกันรหัสที่เกี่ยวข้อง, assert_options(ASSERT_BAIL)คุณอาจรวมทั้งเปิดใช้งาน มันเร็วกว่าวิธีแก้ปัญหาด้วยตนเองถ้า / โยนอยู่ดี
มาริโอ

4
@derekerdmann ฉันไม่เห็นด้วยกับสิ่งนี้ (ในบริบทของการใช้ assert () ใน php) นี่เป็นช่องโหว่ขนาดใหญ่เนื่องจาก assert () ถือว่าอาร์กิวเมนต์สตริงทั้งหมดเป็นโค้ด PHP ดังนั้นจึงเป็นไปได้ (ในทางทฤษฎี) ที่จะฉีดและเรียกใช้รหัสโดยอำเภอใจ IMHO ควรปิดการยืนยันในการผลิต
Vitaliy Lebedev

2
@VitaliyLebedev ถ้าคุณไม่ต้องการที่จะอ่อนแอต่อการฉีดอย่าผ่านเงื่อนไขเพื่อยืนยัน
Damon Snyder

9
เข้าร่วมงานเลี้ยงล่าช้า แต่ PHP.net ระบุว่า: "การยืนยันควรใช้เป็นคุณลักษณะการแก้ไขข้อบกพร่องเท่านั้น"
โคเอ็น.

25

คิดว่าการยืนยันเป็น "พลังความคิดเห็น" แทนที่จะแสดงความคิดเห็นเช่น:

// Note to developers: the parameter "a" should always be a number!!!

ใช้:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

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

เมื่อเห็นวิธีนี้การยืนยันเป็นแนวคิดที่แตกต่างอย่างสิ้นเชิงกับ if (ข้อผิดพลาด) ... และข้อยกเว้นและสามารถอยู่ร่วมกันได้

ใช่คุณควรแสดงความคิดเห็นเกี่ยวกับรหัสของคุณและใช่คุณควรใช้ "ความคิดเห็นที่มีอำนาจ" (ยืนยัน) ทุกครั้งที่ทำได้


จะเกิดอะไรขึ้นหากในการพัฒนาเมื่อทำการทดสอบคุณจะส่งเงื่อนไขที่ดีไปยังผู้ยืนยันเสมอ แต่ในการผลิตหากปิดการยืนยัน - ผู้ใช้บางรายผ่านเงื่อนไขอื่นที่คุณไม่ได้คิดเมื่อทำการทดสอบ หรือไม่เช่นนั้นคุณต้องยืนยันอยู่เสมอ แต่มันไม่เหมือนกับการเขียนเช็คของคุณเองหรือ?
Dariux

จากนั้นโปรแกรมของคุณจะล้มเหลว แก้ไขอย่างเหมาะสมด้วยคำสั่ง if และคุณสมบัติการจัดการข้อผิดพลาดของภาษาและสภาพแวดล้อมการพัฒนาของคุณ การยืนยันสามารถค้นพบปัญหามีวิธีที่ดีกว่าในการแก้ไขปัญหา
DaveWalley

โปรดทราบว่าในขณะที่ PHP 7.2 การส่งสตริงไปยังการยืนยันเพื่อประเมินได้เลิกใช้งานแล้ว เศร้าเพราะมันดูมีประโยชน์มาก
Jannie Theunissen

16

ทั้งหมดขึ้นอยู่กับกลยุทธ์การพัฒนาของคุณ นักพัฒนาส่วนใหญ่ไม่ทราบassert()และใช้การทดสอบหน่วยดาวน์สตรีม แต่แผนการทดสอบเชิงรุกและในตัวบางครั้งอาจเป็นประโยชน์

ยืนยันมีประโยชน์เพราะสามารถเปิดและปิดใช้งานได้ จะไม่ทำให้ประสิทธิภาพหมดไปหากไม่มีการกำหนดตัวจัดการการยืนยันดังกล่าว เพื่อนร่วมงานของคุณไม่มีและคุณควรสร้างรหัสที่จะเปิดใช้งานชั่วคราวในสภาพแวดล้อมการพัฒนา (ถ้า E_NOTICE / E_WARNINGs เปิดอยู่ดังนั้นควรเป็นตัวจัดการการยืนยัน) ฉันใช้เป็นครั้งคราวโดยที่รหัสของฉันไม่สามารถผสมประเภทตัวแปรผสมได้ - โดยปกติแล้วฉันไม่ได้มีส่วนร่วมในการพิมพ์ที่เข้มงวดใน PHP ที่พิมพ์ผิดปกติ แต่มีกรณีการใช้งานแบบสุ่ม:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

ซึ่งสำหรับตัวอย่างจะชดเชยการขาดชนิด string $a, array $bspecifiers PHP5.4 จะรองรับ แต่ไม่ตรวจสอบ


"php 5.4 จะมีให้ แต่ไม่ได้ตรวจสอบ" หมายความว่าอย่างไร
Kzqai

1
PHP 5.4 รองรับและตรวจสอบการยืนยัน
DaveWalley

7

Assert ไม่ได้ใช้แทนการควบคุมการไหลตามปกติเช่นifหรือข้อยกเว้นเนื่องจากมีไว้เพื่อใช้สำหรับการดีบักระหว่างการพัฒนาเท่านั้น


6

หมายเหตุสำคัญเกี่ยวกับการยืนยันใน PHP ก่อนหน้า 7 ซึ่งแตกต่างจากภาษาอื่น ๆ ที่มีโครงสร้างการยืนยัน PHP จะไม่ส่งข้อความยืนยันออกไปทั้งหมด - ถือว่าเป็นฟังก์ชัน (ทำ debug_backtrace () ในฟังก์ชันที่เรียกโดยการยืนยัน) การปิดการยืนยันดูเหมือนว่าจะทำให้ฟังก์ชันไม่ทำอะไรเลยในเครื่องยนต์ โปรดทราบว่า PHP 7 สามารถสร้างขึ้นเพื่อเลียนแบบพฤติกรรมนี้ได้โดยตั้งค่า zend.assertions เป็น 0 แทนที่จะเป็นค่าปกติที่มากกว่า 1 (เปิด) หรือ -1 (ปิด)

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

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

เนื่องจากเจตนาในการยืนยันว่านี่เป็นข้อผิดพลาดและเป็นข้อผิดพลาดที่มีมายาวนานเนื่องจากเป็นภาษาตั้งแต่ยืนยันได้รับการแนะนำใน PHP 4

สตริงที่ส่งไปยืนยันจะได้รับการประเมินโดยมีผลกระทบด้านประสิทธิภาพและอันตรายทั้งหมดที่มาพร้อมกับสิ่งนั้น แต่เป็นวิธีเดียวที่จะได้รับข้อความยืนยันเพื่อให้ทำงานได้ตามที่ควรใน PHP (พฤติกรรมนี้เลิกใช้ใน PHP 7.2)

แก้ไข: เปลี่ยนแปลงด้านบนเพื่อบันทึกการเปลี่ยนแปลงใน PHP 7 และ 7.2


1
ใน PHP 7 จะมีzend.assertionsการตั้งค่า/ จะปิดassert()โดยสมบูรณ์
Kontrollfreak

นั่นเป็นข่าวดี - แต่จากเอกสารที่นั่นดูเหมือนว่าแพตช์ของ PHPUnit เป็นคำสั่งโดยเพิ่มตัวจัดการการเรียกกลับเพื่อยืนยัน AssertionException เมื่อการยืนยันล้มเหลวภายใต้ PHP 5.x วิธีนี้การทดสอบหน่วยสามารถใช้คำอธิบายประกอบ @ คาดไม่ถึง AssertionException ไม่ว่าจะรันบน PHP 5.x หรือ 7
Michael Morris

3

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


7
แต่จะยังคงมีการยืนยันในรหัส พวกเขาจะไม่ทำงานในสภาพแวดล้อมการผลิต
aaronasterling

1
ฉันจะเก็บไว้ในการผลิตและปรับแต่งตัวจัดการข้อผิดพลาดตามนั้นแทน
Daniel W.

3

ไม่เพื่อนร่วมงานของคุณไม่ควรใช้เป็นตัวจัดการข้อผิดพลาดสำหรับวัตถุประสงค์ทั่วไป ตามคู่มือ:

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

ไม่ควรใช้การยืนยันสำหรับการดำเนินการรันไทม์ปกติเช่นการตรวจสอบพารามิเตอร์อินพุต ตามกฎทั่วไปรหัสของคุณควรจะทำงานได้อย่างถูกต้องเสมอหากไม่ได้เปิดใช้งานการตรวจสอบการยืนยัน

หากคุณคุ้นเคยกับชุดทดสอบอัตโนมัติคำกริยา "assert" มักใช้เพื่อตรวจสอบผลลัพธ์ของวิธีการหรือฟังก์ชันบางอย่าง ตัวอย่างเช่น:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

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


3

เพื่อนร่วมงานของคุณพยายามใช้การออกแบบตามสัญญา (DbC) จากภาษาไอเฟลและอ้างอิงจากหนังสือ: Object Oriented Software Construction, 2nd Edition

การยืนยันตามที่เขาใช้จะเป็น {P} - ส่วนหนึ่งของ Hoare Logic หรือ Hoare Triple: {P} C {Q} โดยที่ {P} คือการยืนยันเงื่อนไขล่วงหน้า (ไอออน) และ {Q} คือ หลังเงื่อนไขยืนยัน (ไอออน) s.

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

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

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

ยิ่งไปกว่านั้น - ฉันขอแนะนำให้ผู้ผลิต PHP ทำการศึกษาการออกแบบอย่างละเอียดตามสัญญาและพยายามที่จะใส่ลงใน PHP ASAP! จากนั้นเราทุกคนจะได้รับประโยชน์จากการมีคอมไพเลอร์ / ล่ามที่รับรู้ DbC ซึ่งจะจัดการกับปัญหาที่ระบุไว้ในคำตอบ (ด้านบน):

  1. คอมไพเลอร์ที่รับรู้การออกแบบตามสัญญาที่นำไปใช้อย่างถูกต้อง (หวังว่า) จะปราศจากข้อผิดพลาด (ไม่เหมือนกับการยืนยัน PHP ในปัจจุบัน)
  2. คอมไพเลอร์ที่ตระหนักถึงการออกแบบตามสัญญาที่นำไปใช้อย่างถูกต้องจะจัดการกับความแตกต่างของการจัดการตรรกะการยืนยันแบบหลายรูปแบบให้คุณแทนที่จะใช้สมองของคุณในเรื่องนี้

หมายเหตุ: แม้แต่การใช้if-statement เพื่อทดแทนการยืนยัน (เงื่อนไขเบื้องต้น) จะได้รับผลกระทบที่เลวร้ายหากใช้เพื่อเสริมสร้างเงื่อนไขเบื้องต้นหรือทำให้สภาวะหลังอ่อนแอลง เพื่อให้เข้าใจถึงความหมายคุณจะต้องศึกษาการออกแบบตามสัญญาเพื่อทราบ! :-)

มีความสุขในการเรียนและเรียนรู้

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