ความพยายามที่จะให้ภาพรวมของการอภิปรายและคำตอบต่าง ๆ :
ไม่มีคำตอบเดียวสำหรับคำถามที่สามารถแทนที่วิธีการทั้งหมดที่issetสามารถใช้ได้ กรณีการใช้งานบางอย่างได้รับการแก้ไขโดยฟังก์ชั่นอื่น ๆ ในขณะที่กรณีอื่น ๆ ไม่สามารถตรวจสอบได้หรือมีมูลค่าที่น่าสงสัยเกินกว่าการตีกอล์ฟ ไกลจากการถูก "แตก" หรือ "ไม่สอดคล้อง" กรณีการใช้งานอื่น ๆ แสดงให้เห็นว่าทำไมissetปฏิกิริยาของมันถึงnullเป็นพฤติกรรมเชิงตรรกะ
กรณีการใช้งานจริง (พร้อมโซลูชัน)
1. คีย์ Array
อาร์เรย์สามารถได้รับการปฏิบัติเหมือนคอลเลกชันของตัวแปรด้วยunsetและissetปฏิบัติกับพวกเขาราวกับว่าพวกเขาเป็น แต่เนื่องจากพวกเขาสามารถซ้ำนับ ฯลฯ nullเป็นค่าที่ขาดหายไปจะไม่เหมือนกันเป็นหนึ่งที่มีค่า
คำตอบในกรณีนี้คือการใช้array_key_exists()isset()แทน
เนื่องจากสิ่งนี้ใช้เวลาอาร์เรย์เพื่อตรวจสอบว่าเป็นอาร์กิวเมนต์ของฟังก์ชัน, PHP จะยังคงเพิ่ม "ประกาศ" ถ้าอาร์เรย์นั้นไม่มีอยู่ ในบางกรณีสามารถโต้แย้งได้อย่างถูกต้องว่าแต่ละส่วนข้อมูลควรได้รับการกำหนดค่าเริ่มต้นก่อนดังนั้นการแจ้งเตือนจึงกำลังทำงานอยู่ สำหรับกรณีอื่น ๆ "recursive" array_key_existsฟังก์ชั่นซึ่งการตรวจสอบขนาดของอาร์เรย์ในการเปิดแต่ละจะหลีกเลี่ยงปัญหานี้ @array_key_existsแต่โดยทั่วไปจะเป็นเช่นเดียวกับ นอกจากนี้ยังค่อนข้างสัมผัสกับการจัดการnullค่า
2. คุณสมบัติของวัตถุ
ในทฤษฎีดั้งเดิมของ "การเขียนโปรแกรมเชิงวัตถุ" การห่อหุ้มและ polymorphism เป็นคุณสมบัติที่สำคัญของวัตถุ ในการดำเนินงาน OOP ระดับ-based เช่น PHP ของคุณสมบัติห่อหุ้มที่มีการประกาศให้เป็นส่วนหนึ่งของการกำหนดชั้นและได้รับระดับการเข้าถึง ( public, protectedหรือprivate)
อย่างไรก็ตาม PHP ยังอนุญาตให้คุณเพิ่มคุณสมบัติให้กับวัตถุแบบไดนามิกเช่นเดียวกับที่คุณทำกุญแจให้กับอาเรย์และบางคนใช้ออบเจ็กต์ที่ไม่ใช้คลาส (ในทางเทคนิค, อินสแตนซ์ของบิวด์อินstdClass) วิธีการเชื่อมโยงอาร์เรย์ สิ่งนี้นำไปสู่สถานการณ์ที่ฟังก์ชันอาจต้องการทราบว่ามีการเพิ่มคุณสมบัติเฉพาะลงในวัตถุที่กำหนดให้หรือไม่
เช่นเดียวกับปุ่มอาร์เรย์โซลูชั่นสำหรับการตรวจสอบคุณสมบัติของวัตถุจะถูกรวมอยู่ในภาษาที่เรียกว่ามีเหตุผลเพียงพอproperty_exists
กรณีการใช้งานที่ไม่สมเหตุสมผลพร้อมการอภิปราย
3. register_globalsและมลภาวะอื่น ๆ ของ namespace ทั่วโลก
register_globalsคุณสมบัติเพิ่มตัวแปรเพื่อขอบเขตทั่วโลกที่มีรายชื่อได้รับการพิจารณาจากแง่มุมของการร้องขอ HTTP (GET และ POST พารามิเตอร์และคุกกี้) นี้สามารถนำไปสู่รถและรหัสที่ไม่ปลอดภัยซึ่งเป็นเหตุผลที่จะได้รับการยกเลิกโดยปริยายตั้งแต่PHP 4.2 ปล่อยสิงหาคม 2000และลบออกอย่างสมบูรณ์ในPHP 5.4 ปล่อยมีนาคม 2012 อย่างไรก็ตามเป็นไปได้ว่าบางระบบยังคงทำงานอยู่โดยเปิดใช้งานหรือเลียนแบบคุณลักษณะนี้ นอกจากนี้ยังเป็นไปได้ที่จะ "สร้างมลภาวะ" เนมสเปซส่วนกลางด้วยวิธีอื่นโดยใช้globalคำหลักหรือ$GLOBALSอาร์เรย์
ประการแรกregister_globalsตัวเองไม่น่าจะสร้างnullตัวแปรโดยไม่คาดคิดเนื่องจากค่า GET, POST และคุกกี้จะเป็นสตริง (ซึ่ง''ยังคงกลับมาtrueจากisset) และตัวแปรในเซสชันควรอยู่ภายใต้การควบคุมของโปรแกรมเมอร์
ประการที่สองมลพิษของตัวแปรที่มีค่าnullเป็นเพียงปัญหาถ้าสิ่งนี้มากเกินไปเขียนเริ่มต้นก่อนหน้านี้บางส่วน "การเขียนทับ" ตัวแปรที่ไม่มีการกำหนดค่าเริ่มต้นด้วยnullจะเป็นปัญหาหากโค้ดที่อื่นถูกแยกความแตกต่างระหว่างสองสถานะดังนั้นด้วยตัวของมันเองความเป็นไปได้นี้เป็นข้อโต้แย้งต่อต้านการสร้างความแตกต่าง
4. get_defined_varsและcompact
ฟังก์ชั่นที่ไม่ค่อยได้ใช้งานใน PHP เช่นget_defined_varsและcompactอนุญาตให้คุณปฏิบัติต่อชื่อตัวแปรราวกับว่ามันเป็นกุญแจในอาเรย์ สำหรับตัวแปรโกลบอลอาเรย์ซุปเปอร์โกลบอล$GLOBALSอนุญาตการเข้าถึงที่คล้ายกันและเป็นเรื่องปกติมากขึ้น วิธีการเข้าถึงเหล่านี้จะทำงานแตกต่างกันหากไม่ได้กำหนดตัวแปรในขอบเขตที่เกี่ยวข้อง
เมื่อคุณตัดสินใจที่จะปฏิบัติต่อชุดของตัวแปรเป็นอาเรย์โดยใช้หนึ่งในกลไกเหล่านี้คุณสามารถทำการดำเนินการเดียวกันทั้งหมดในอาเรย์ตามปกติ ดังนั้นโปรดดูที่ 1
ฟังก์ชั่นที่มีอยู่เพียงเพื่อคาดการณ์ว่าฟังก์ชั่นเหล่านี้จะทำงานอย่างไร (เช่น "จะมีคีย์ 'foo' ในอาเรย์ที่ส่งคืนโดยget_defined_vars?") นั้นไม่จำเป็นเพราะคุณสามารถเรียกใช้งานฟังก์ชั่น
4a ตัวแปรตัวแปร ( $$foo)
แม้ว่าจะไม่เหมือนกับฟังก์ชั่นที่เปลี่ยนชุดตัวแปรเป็นอาเรย์แบบเชื่อมโยง แต่ส่วนใหญ่ใช้"ตัวแปรตัวแปร" ("กำหนดให้กับตัวแปรที่ตั้งชื่อตามตัวแปรอื่นนี้") สามารถและควรเปลี่ยนเป็นอาเรย์แบบเชื่อมโยงแทน .
ชื่อตัวแปรพื้นฐานคือฉลากที่กำหนดให้กับค่าโดยโปรแกรมเมอร์ หากคุณกำลังพิจารณาในขณะใช้งานจริง ๆ มันไม่ใช่ป้ายกำกับ แต่เป็นคีย์ในที่เก็บคีย์ - ค่าบางตัว คุณจะสูญเสียความสามารถในการนับวนซ้ำ ฯลฯ ก็ยังสามารถเป็นไปไม่ได้ที่จะมีตัวแปร "นอก" $$fooเก็บค่าคีย์เพราะมันอาจจะมากกว่าที่เขียนโดย
เมื่อเปลี่ยนเป็นใช้อาเรย์แบบเชื่อมโยงแล้วโค้ดจะสามารถแก้ไขได้ด้วยวิธีแก้ปัญหา 1. การเข้าถึงคุณสมบัติวัตถุทางอ้อม (เช่น$foo->$property_name) สามารถแก้ไขได้ด้วยโซลูชัน 2
5. issetพิมพ์ได้ง่ายกว่ามากarray_key_exists
ฉันไม่แน่ใจว่าสิ่งนี้มีความเกี่ยวข้องจริงๆ แต่ใช่ชื่อฟังก์ชั่นของ PHP นั้นค่อนข้างยืดยาวและบางครั้งก็ไม่สอดคล้องกัน เห็นได้ชัดว่า PHP รุ่นก่อนประวัติศาสตร์ใช้ความยาวของชื่อฟังก์ชั่นเป็นคีย์แฮชดังนั้น Rasmus จึงสร้างชื่อฟังก์ชั่นอย่างจงใจhtmlspecialcharsเพื่อให้พวกเขามีจำนวนอักขระที่ผิดปกติ ...
ยังอย่างน้อยเราก็ไม่ได้เขียน Java ใช่มั้ย ;)
6. ตัวแปร Uninitialized มีชนิด
หน้าคู่มือที่พื้นฐานตัวแปรรวมถึงคำสั่งนี้:
ตัวแปร Uninitialized มีค่าเริ่มต้นของพวกเขาขึ้นอยู่กับบริบทที่พวกเขาจะใช้
ฉันไม่แน่ใจว่ามีความคิดบางอย่างใน Zend Engine ของ "uninitialized แต่รู้จักชนิด" หรือว่านี่เป็นการอ่านมากเกินไปในคำสั่ง
nullอะไรคือสิ่งที่ชัดเจนก็คือว่ามันทำให้ไม่แตกต่างการปฏิบัติเพื่อให้พฤติกรรมของพวกเขาเนื่องจากพฤติกรรมที่อธิบายไว้ในหน้านั้นสำหรับตัวแปรเตรียมจะเหมือนกับพฤติกรรมของตัวแปรที่มีค่าเป็น ที่จะเลือกตัวอย่างหนึ่งทั้งสอง$aและ$bในรหัสนี้จะจบลงเช่นจำนวนเต็ม42:
unset($a);
$a += 42;
$b = null;
$b += 42;
(อันแรกจะแจ้งให้ทราบเกี่ยวกับตัวแปรที่ไม่ได้ประกาศในความพยายามที่จะทำให้คุณเขียนโค้ดได้ดีขึ้น แต่จะไม่สร้างความแตกต่างใด ๆ กับวิธีที่โค้ดทำงานจริง)
99. การตรวจสอบว่าฟังก์ชั่นทำงานหรือไม่
(การทำให้อันนี้อยู่ได้นานกว่าเพราะนานกว่าคนอื่นบางทีฉันจะแก้ไขมันทีหลัง ... )
พิจารณารหัสต่อไปนี้:
$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
หากsome_functionสามารถกลับมาnullมีความเป็นไปได้ว่าechoจะไม่ได้ถึงแม้ว่าจะกลับมาsome_test trueความตั้งใจของโปรแกรมเมอร์คือการตรวจสอบเมื่อ$resultไม่เคยตั้งค่า แต่ PHP ไม่อนุญาตให้ทำเช่นนั้น
อย่างไรก็ตามมีปัญหาอื่น ๆ เกี่ยวกับวิธีการนี้ซึ่งจะชัดเจนหากคุณเพิ่มการวนรอบนอก:
foreach ( $list_of_tests as $test_value ) {
// something's missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
}
เนื่องจาก$resultไม่ได้กำหนดค่าเริ่มต้นอย่างชัดเจนจะใช้ค่าเมื่อการทดสอบครั้งแรกผ่านไปทำให้ไม่สามารถบอกได้ว่าการทดสอบครั้งต่อไปผ่านหรือไม่ นี่เป็นข้อผิดพลาดที่พบบ่อยมากเมื่อตัวแปรไม่ได้เริ่มต้นอย่างถูกต้อง
ในการแก้ไขปัญหานี้เราต้องทำอะไรบางอย่างในบรรทัดที่ฉันได้แสดงความคิดเห็นว่ามีบางอย่างขาดหายไป ทางออกที่ชัดเจนที่สุดคือตั้งค่า$resultเป็น "ค่าเทอร์มินัล" ที่some_functionไม่สามารถย้อนกลับได้ หากเป็นnullเช่นนั้นรหัสที่เหลือจะทำงานได้ดี หากไม่มีตัวเลือกตามธรรมชาติสำหรับค่าเทอร์มินัลเนื่องจากsome_functionมีประเภทส่งคืนที่คาดเดาไม่ได้อย่างมาก (ซึ่งอาจเป็นสัญญาณที่ไม่ดีในตัวเอง) ดังนั้นค่าบูลีนเพิ่มเติมเช่น$foundสามารถใช้แทนได้
การทดลองทางความคิดที่หนึ่ง: very_nullค่าคงที่
PHP สามารถให้ค่าคงที่ในทางทฤษฎีเช่นเดียวกับnull- เพื่อใช้เป็นค่าเทอร์มินัลที่นี่ สันนิษฐานว่ามันจะผิดกฎหมายที่จะส่งคืนสิ่งนี้จากฟังก์ชั่นหรือมันจะถูกบังคับให้nullทำเช่นเดียวกัน นั่นจะทำให้กรณีที่เฉพาะเจาะจงนี้ง่ายขึ้นเล็กน้อย แต่ทันทีที่คุณตัดสินใจที่จะคำนึงถึงโค้ดอีกครั้ง - ตัวอย่างเช่นการใส่ลูปด้านในเป็นฟังก์ชันแยกต่างหาก - มันจะไร้ประโยชน์ หากค่าคงที่สามารถส่งผ่านระหว่างฟังก์ชั่นคุณไม่สามารถรับประกันได้ว่าsome_functionจะไม่ส่งคืนดังนั้นจึงไม่มีประโยชน์เป็นค่าเทอร์มินัลอเนกประสงค์อีกต่อไป
อาร์กิวเมนต์สำหรับการตรวจสอบตัวแปรที่ไม่มีค่าเริ่มต้นในกรณีนี้จะลดลงเป็นอาร์กิวเมนต์สำหรับค่าคงที่พิเศษนั้น: หากคุณแทนที่ความคิดเห็นด้วยunset($result)และปฏิบัติต่อสิ่งนั้นแตกต่างไปจาก$result = nullนี้คุณกำลังแนะนำ "ค่า" สำหรับ$resultสิ่งที่ไม่สามารถผ่านได้ ตรวจพบโดยฟังก์ชั่นในตัวที่เฉพาะเจาะจง
การทดลองทางความคิดที่สอง: เคาน์เตอร์ที่ได้รับมอบหมาย
อีกวิธีหนึ่งในการคิดเกี่ยวกับสิ่งที่ifถามครั้งสุดท้ายคือ "มีการมอบหมาย$resultอะไรให้" แทนที่จะคิดว่ามันเป็นค่าพิเศษ$resultคุณอาจจะคิดว่านี่เป็น "ข้อมูลเมตา" เกี่ยวกับตัวแปรเล็กน้อยเช่น "ตัวแปร tainting" ของ Perl ดังนั้นแทนที่จะissetคุณอาจเรียกมันhas_been_assigned_toและมากกว่า,unsetreset_assignment_state
แต่ถ้าใช่ทำไมหยุดที่บูลีน? จะเป็นอย่างไรถ้าคุณต้องการทราบว่าการทดสอบผ่านไปกี่ครั้ง คุณสามารถขยายข้อมูลเมตาของคุณไปเป็นจำนวนเต็มและมีget_assignment_countและreset_assignment_count...
เห็นได้ชัดว่าการเพิ่มคุณสมบัติดังกล่าวจะมีการแลกเปลี่ยนในความซับซ้อนและประสิทธิภาพของภาษาดังนั้นมันจะต้องมีการชั่งน้ำหนักอย่างรอบคอบเทียบกับประโยชน์ที่คาดหวัง เช่นเดียวกับvery_nullค่าคงที่มันจะมีประโยชน์เฉพาะในสถานการณ์ที่แคบมากเท่านั้นและจะต้านทานเช่นเดียวกันกับการกู้แฟคตอริ่งอีกครั้ง
คำถามที่ชัดเจนหวังว่าเป็นเพราะเหตุใด PHP รันไทม์เอ็นจิ้นควรคิดล่วงหน้าว่าคุณต้องการติดตามสิ่งต่าง ๆ แทนที่จะปล่อยให้คุณทำอย่างชัดเจนโดยใช้รหัสปกติ