ความพยายามที่จะให้ภาพรวมของการอภิปรายและคำตอบต่าง ๆ :
ไม่มีคำตอบเดียวสำหรับคำถามที่สามารถแทนที่วิธีการทั้งหมดที่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
และมากกว่า,unset
reset_assignment_state
แต่ถ้าใช่ทำไมหยุดที่บูลีน? จะเป็นอย่างไรถ้าคุณต้องการทราบว่าการทดสอบผ่านไปกี่ครั้ง คุณสามารถขยายข้อมูลเมตาของคุณไปเป็นจำนวนเต็มและมีget_assignment_count
และreset_assignment_count
...
เห็นได้ชัดว่าการเพิ่มคุณสมบัติดังกล่าวจะมีการแลกเปลี่ยนในความซับซ้อนและประสิทธิภาพของภาษาดังนั้นมันจะต้องมีการชั่งน้ำหนักอย่างรอบคอบเทียบกับประโยชน์ที่คาดหวัง เช่นเดียวกับvery_null
ค่าคงที่มันจะมีประโยชน์เฉพาะในสถานการณ์ที่แคบมากเท่านั้นและจะต้านทานเช่นเดียวกันกับการกู้แฟคตอริ่งอีกครั้ง
คำถามที่ชัดเจนหวังว่าเป็นเพราะเหตุใด PHP รันไทม์เอ็นจิ้นควรคิดล่วงหน้าว่าคุณต้องการติดตามสิ่งต่าง ๆ แทนที่จะปล่อยให้คุณทำอย่างชัดเจนโดยใช้รหัสปกติ