วิธีที่ดีที่สุดในการทดสอบการมีอยู่ของตัวแปรใน PHP isset () เสียอย่างชัดเจน


187

จากisset()เอกสาร :

isset() will return FALSE if testing a variable that has been set to NULL.

โดยทั่วไปisset()ไม่ได้ตรวจสอบไม่ว่าจะเป็นตัวแปรที่มีการตั้งค่าที่ทุกคน แต่ไม่ว่าจะเป็นชุดอะไร NULLแต่

ระบุว่าวิธีที่ดีที่สุดในการตรวจสอบการมีอยู่ของตัวแปรคืออะไร? ฉันลองทำสิ่งที่ชอบ:

if(isset($v) || @is_null($v))

( @จำเป็นต้องหลีกเลี่ยงการเตือนเมื่อ$vไม่ได้ตั้งค่า) แต่is_null()มีปัญหาคล้ายกับisset(): มันกลับมาTRUEที่ตัวแปร unset! นอกจากนี้ยังปรากฏว่า:

@($v === NULL)

ทำงานเหมือน@is_null($v)อย่างนั้นก็เช่นกัน

เราควรจะตรวจสอบการมีอยู่ของตัวแปรใน PHP ได้อย่างไร?


แก้ไข: มีความแตกต่างอย่างชัดเจนใน PHP ระหว่างตัวแปรที่ไม่ได้ตั้งค่าและตัวแปรที่ตั้งค่าเป็นNULL:

<?php
$a = array('b' => NULL);
var_dump($a);

PHP แสดงว่า$a['b']มีอยู่และมีNULLค่า หากคุณเพิ่ม:

var_dump(isset($a['b']));
var_dump(isset($a['c']));

คุณสามารถเห็นความคลุมเครือที่ฉันพูดถึงด้วยisset()ฟังก์ชั่น นี่คือผลลัพธ์ของทั้งสามข้อต่อไปนี้var_dump()s:

array(1) {
  ["b"]=>
  NULL
}
bool(false)
bool(false)

แก้ไขเพิ่มเติม: สองสิ่ง

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

ประการที่สองคำตอบ Zoredache ของ , array_key_exists()ทำงานอย่างถูกต้องสำหรับกรณีการใช้งานดังกล่าวข้างต้นของฉันและสำหรับตัวแปรใด ๆ ทั่วโลก:

<?php
$a = NULL;
var_dump(array_key_exists('a', $GLOBALS));
var_dump(array_key_exists('b', $GLOBALS));

เอาท์พุท:

bool(true)
bool(false)

ตั้งแต่ที่ถูกจับเพียงเกี่ยวกับทุกที่ฉันสามารถดูมีเป็นความคลุมเครือระหว่างตัวแปรที่ไม่อยู่และตัวแปรที่มีการกำหนดให้NULL, ฉันโทรarray_key_exists()วิธีที่ง่ายที่สุดอย่างเป็นทางการใน PHP การตรวจสอบอย่างแท้จริงสำหรับการดำรงอยู่ของตัวแปร

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


คุณไม่สามารถตรวจสอบ - แต่ทำไมคุณต้องทำ
php มากเกินไป

12
NULL มีความหมายที่เฉพาะเจาะจงมากใน PHP และเป็นแนวคิดที่แยกจากกันโดยสิ้นเชิงว่าตัวแปรถูกตั้งค่าหรือไม่
chazomaticus

33
มีเหตุผลที่แตกต่างระหว่าง null และ nonexistent ตัวอย่างเช่นคุณกำลังสร้างวัตถุเพื่อแสดงแถวในตารางฐานข้อมูล สำหรับทุกคอลัมน์ในแถวคุณสร้างตัวแปรส่วนตัวเข้าถึงได้เฉพาะผ่านวิธีการ getter ของวัตถุ สมมติว่าค่าคอลัมน์เป็นโมฆะ ทีนี้เมธอด getter จะรู้ได้อย่างไรว่าไม่มีคอลัมน์ดังกล่าวในตารางหรือว่าวัตถุนี้เพิ่งมีค่า Null อยู่ที่นั่น โชคดีที่ในกรณีของฉันตัวแปรส่วนตัวเป็นรายการในอาร์เรย์ส่วนตัวดังนั้นฉันสามารถใช้ array_key_exists แต่นี่เป็นปัญหาจริง
Nathan Long

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

2
@chazomaticus แต่ตัวแปรและองค์ประกอบมากมายที่เป็นสิ่งที่แตกต่างกัน ; เพียงเพราะคุณสามารถทำสิ่งเดียวกันกับพวกเขาไม่ได้หมายความว่าพวกเขาเป็นหรือควรจะเปลี่ยนได้ 100% ไม่มี "ความไม่สอดคล้องกันในภาษา PHP" ที่นี่เพียงบางสิ่งที่คุณไม่ชอบ / เข้าใจ สำหรับregister_globalsฉันยังคงดิ้นรนที่จะคิดว่าสถานการณ์ที่แม้แต่ที่จะต้องมีความแตกต่างดังกล่าวตั้งแต่จดทะเบียนอะไรจากการร้องขอ HTTP nullมักจะเป็นสตริงไม่
IMSoP

คำตอบ:


97

หากตัวแปรที่คุณกำลังตรวจสอบอยู่ในขอบเขตทั่วโลกคุณสามารถทำได้:

array_key_exists('v', $GLOBALS) 

3
อ้า! ตอนนี้คุณกำลังพูดอยู่! คุณจะทำสิ่งนั้นให้พูดคุณสมบัติของชั้นเรียนได้อย่างไร?
chazomaticus

22
ในฐานะที่เป็นรูปแบบถ้าการตรวจสอบจำเป็นต้องทำงานกับตัวแปรขอบเขตภายในเช่นกันบนสามารถทำ$defined_vars = get_defined_vars();และทดสอบผ่านarray_key_exists('v', $defined_vars);ได้
Henrik Opel

1
มันดูน่าเกลียดสำหรับฉัน แต่ในกรณีที่คุณกำลังตรวจสอบองค์ประกอบอาเรย์จริง ๆ แล้วมันก็สมเหตุสมผลดีกว่า: isset($foo[$bar])กลายเป็นarray_key_exists($bar, $foo)
Arild

property_existsดูเหมือนจะมีแนวโน้มยกเว้นเรื่องนี้:> ฟังก์ชั่น property_exists () ไม่สามารถตรวจจับคุณสมบัติที่สามารถเข้าถึงได้อย่างน่าอัศจรรย์โดยใช้วิธี __get magic
alexw

@alexw ตัวแปร "สร้าง" ผ่าน __get ไม่มีอยู่จริง __get เป็นรหัสโดยพลการที่ใช้เป็นทางเลือกสำหรับตัวแปรที่ไม่มีอยู่ซึ่งสามารถส่งคืนสิ่งที่ต้องการโดยไม่คำนึงว่ามีการจัดเก็บข้อมูลที่เกี่ยวข้องหรือไม่
Brilliand

46

ความพยายามที่จะให้ภาพรวมของการอภิปรายและคำตอบต่าง ๆ :

ไม่มีคำตอบเดียวสำหรับคำถามที่สามารถแทนที่วิธีการทั้งหมดที่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 รันไทม์เอ็นจิ้นควรคิดล่วงหน้าว่าคุณต้องการติดตามสิ่งต่าง ๆ แทนที่จะปล่อยให้คุณทำอย่างชัดเจนโดยใช้รหัสปกติ


เกี่ยวกับคลาสและคุณสมบัติ, property_exists เศร้า () ไม่ทำงานเมื่อคุณสมบัติเป็นอาร์เรย์ตัวอย่างเช่น: Class {public $ property = array ()} โยนข้อผิดพลาด
แอนดรู

1
@Andrew ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน: 3v4l.org/TnAY5สนใจที่จะให้ตัวอย่างที่สมบูรณ์หรือไม่
IMSoP

ใช่ดูเหมือนว่าจะทำงานได้ดีมีอะไรผิดปกติกับการตั้งค่าของฉัน ขออภัยสำหรับการเตือนที่ผิดพลาด :)
แอนดรูว์

20

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

ถึงกระนั้นก็เป็นเรื่องยากที่จะคิดดังนั้นนี่เป็นเมทริกซ์ง่าย ๆ เปรียบเทียบว่าค่าต่าง ๆ จะได้รับการประเมินโดยการดำเนินการที่แตกต่างกันอย่างไร:

|           | ===null | is_null | isset | empty | if/else | ternary | count>0 |
| -----     | -----   | -----   | ----- | ----- | -----   | -----   | -----   |
| $a;       | true    | true    |       | true  |         |         |         |
| null      | true    | true    |       | true  |         |         |         |
| []        |         |         | true  | true  |         |         |         |
| 0         |         |         | true  | true  |         |         | true    |
| ""        |         |         | true  | true  |         |         | true    |
| 1         |         |         | true  |       | true    | true    | true    |
| -1        |         |         | true  |       | true    | true    | true    |
| " "       |         |         | true  |       | true    | true    | true    |
| "str"     |         |         | true  |       | true    | true    | true    |
| [0,1]     |         |         | true  |       | true    | true    | true    |
| new Class |         |         | true  |       | true    | true    | true    |

เพื่อให้พอดีกับตารางฉันบีบอัดฉลากเล็กน้อย:

  • $a; อ้างถึงตัวแปรที่ประกาศ แต่ยังไม่ได้กำหนด
  • ทุกอย่างอื่นในคอลัมน์แรกหมายถึงค่าที่กำหนดเช่น:
    • $a = null;
    • $a = [];
    • $a = 0;
    • ...
  • คอลัมน์อ้างถึงการดำเนินการเปรียบเทียบเช่น:
    • $a === null
    • isset($a)
    • empty($a)
    • $a ? true : false
    • ...

ผลลัพธ์ทั้งหมดเป็นบูลีนtrueถูกพิมพ์และfalseถูกละเว้น

คุณสามารถทำการทดสอบด้วยตัวเองตรวจสอบสรุปสาระสำคัญนี้:
https://gist.github.com/mfdj/8165967


อาจจะไม่อยู่ในขอบเขตของคำถามนี้ แต่คุณอาจต้องการเพิ่มลง"0"ในตารางเพื่อความสมบูรณ์และความชัดเจนของการemptyดำเนินการ
Rik Schaaf

17

คุณสามารถใช้โครงสร้างภาษากะทัดรัดเพื่อทดสอบการมีอยู่ของตัวแปร null ตัวแปรที่ไม่มีอยู่จะไม่ปรากฏในผลลัพธ์ในขณะที่ค่า Null จะแสดง

$x = null;
$y = 'y';

$r = compact('x', 'y', 'z');
print_r($r);

// Output:
// Array ( 
//  [x] => 
//  [y] => y 
// ) 

ในกรณีตัวอย่างของคุณ:

if (compact('v')) {
   // True if $v exists, even when null. 
   // False on var $v; without assignment and when $v does not exist.
}

แน่นอนว่าตัวแปรในขอบเขตทั่วโลกคุณสามารถใช้ array_key_exists ()

ด้วยตนเองฉันจะหลีกเลี่ยงสถานการณ์เช่นโรคระบาดที่มีความหมายแตกต่างระหว่างตัวแปรที่ไม่มีอยู่และตัวแปรที่มีค่า Null PHP และภาษาอื่น ๆ ส่วนใหญ่ไม่คิดว่าจะมี


3
PHP ไม่ได้ แต่ฉันจะไม่พูดภาษาอื่นส่วนใหญ่ไม่ได้ ภาษาส่วนใหญ่ที่ประกาศตัวแปรจะมีข้อผิดพลาดหากไม่มีการประกาศตัวแปร แต่คุณสามารถตั้งค่าNULLได้ ความหมายNULLควรหมายถึง "ไม่มีทรัพยากร" แต่ไม่ได้กำหนดตัวแปรเป็นข้อผิดพลาดของโปรแกรมเมอร์
M Miller

1
@MMiller แน่นอน แต่การเขียนโค้ดที่ตามหลังหนึ่งพา ธ ในกรณีของ "ไม่มีทรัพยากร" และพา ธ ที่แตกต่างกันในกรณีของ "โปรแกรมเมอร์ผิดพลาด" นั้นค่อนข้างไร้สาระ หากคุณต้องการตรวจจับตัวแปรที่ไม่ได้ประกาศในระหว่างการดีบักให้ใช้เครื่องมือวิเคราะห์แบบคงที่เช่นคุณต้องการค้นหาข้อผิดพลาดที่อาจเกิดขึ้นในภาษาใด ๆ
IMSoP

@ MMiller เจ๋งคุณคิดยังไงกับเรื่องนี้
Pacerier

1
@MMiller แต่ก็ไม่ได้ทำงานตามที่โต้แย้งเพราะคำสั่งในคำตอบที่เป็นที่ชัดเจนเกี่ยวกับ "ตัวแปรที่มีอยู่ไม่ได้" และเคาน์เตอร์ตัวอย่างของคุณเป็นเรื่องเกี่ยวกับทรัพย์สินวัตถุ / คีย์กัญชาที่มีอยู่ไม่ได้ ความแตกต่างระหว่างกรณีเหล่านี้ไม่เพียง แต่บังเอิญ
IMSoP

1
@MMiller - แน่นอนว่าเป็นตัวอย่างที่ดีกว่า ถึงกระนั้นหลังจาก 20 ปีของการเขียนโปรแกรมในภาษาที่เข้มงวดสถานการณ์ที่ฉันต้องการความแตกต่างระหว่างundefinedและnullเป็นของหายากมากจนฉันไม่ควรพลาด IMHO การใช้งานหลักสำหรับundefinedคือ "ข้อผิดพลาดของโปรแกรมเมอร์ในภาษาที่ไม่เข้มงวด" ในภาษาที่เข้มงวดถ้าฉันต้องการสถานะที่ชัดเจนสำหรับclient did not state a valueฉันก็จะประกาศค่าที่เหมาะสมกับสถานการณ์และทดสอบสำหรับมัน กรณีที่แย่ที่สุดต้องเพิ่มตัวแปรแฟล็กแยกต่างหาก แต่การทำเช่นนั้นไม่ค่อยดีไปกว่าการรับมือกับสถานะที่ไม่ใช่ค่าที่แตกต่างกันสองครั้งเสมอ !!
ToolmakerSteve

15

อธิบาย NULL, คิดอย่างมีเหตุผล

ฉันเดาว่าคำตอบที่ชัดเจนของสิ่งนี้คือ ... อย่าเริ่มต้นตัวแปรของคุณในฐานะ NULL, ทำให้พวกเขาเป็นสิ่งที่เกี่ยวข้องกับสิ่งที่พวกเขาตั้งใจจะเป็น

ปฏิบัติต่อ NULL อย่างถูกต้อง

NULL ควรถือว่าเป็น "ไม่มีค่า" ซึ่งเป็นความหมายของ NULL ตัวแปรไม่สามารถจัดประเภทเป็นที่มีอยู่ใน PHP ได้เนื่องจากยังไม่ได้รับการบอกว่าเป็นชนิดของเอนทิตีที่พยายามเป็น มันอาจจะไม่มีอยู่จริงดังนั้น PHP ก็แค่บอกว่า "ไม่เป็นไรเพราะมันไม่ได้มีประโยชน์อะไรเลยและ NULL ก็เป็นวิธีของฉันในการพูดแบบนี้"

ข้อโต้แย้ง

มาเถียงกันเถอะ "แต่ค่า NULL นั้นเหมือนกับการบอกว่า 0 หรือ FALSE หรือ ''

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

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

สรุป

มันไม่ได้เกี่ยวกับการสร้างฟังก์ชั่นแปลกประหลาดเพื่อแก้ไขปัญหา แต่มันเปลี่ยนวิธีที่สมองของคุณมอง NULL หากเป็นค่า NULL ให้ถือว่าไม่ได้ตั้งค่าเป็นอะไร หากคุณกำหนดตัวแปรไว้ล่วงหน้าให้กำหนดไว้ล่วงหน้าเป็น 0, FALSE หรือ "" ขึ้นอยู่กับประเภทการใช้งานที่คุณต้องการใช้

อย่าลังเลที่จะเสนอราคานี้ มันอยู่ด้านบนของหัวตรรกะของฉัน :)


5
คำตอบที่ดี หลายต่อหลายครั้งที่ฉันเห็นคนพูดรุนแรงเกี่ยวกับวิธีที่พวกเขาเกลียดเรื่องนี้หรือคุณลักษณะของภาษา แต่ดูเหมือนว่าพวกเขาจะสันนิษฐานว่า "ถ้ามันไม่ทำในแบบของฉันมันก็จะพัง" ใช่มีการตัดสินใจในการออกแบบที่ไม่ดี แต่ก็มีนักพัฒนาที่มีความคิดใกล้ชิดมาก!
curtisdf

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

2
ฉันรู้ว่าเรา "ไม่ควร" เพื่อตรวจสอบการมีอยู่ของตัวแปรใน php, นรก, ไม่มีแม้แต่วิธีที่แท้จริงในการตรวจสอบ ฉันจะไม่เขียนโค้ดที่ deppends เพราะมันเป็นไปไม่ได้ใน PHP นั่นเป็นข้อ จำกัด ของ php เห็นได้ชัดว่ามีความแตกต่างระหว่างตัวแปรที่ไม่ได้ตั้งค่าและเป็นโมฆะ แต่ php ไม่มีวิธีแยกแยะตัวแปรเหล่านั้น ยังมีฟังก์ชั่น meta จำนวนมากขึ้นอยู่กับการทำงานของมัน: การอ่าน var ที่ไม่มีอยู่จะแจ้งให้ทราบว่าisset($a['x'])จะเป็นเท็จถ้าxมันเป็นโมฆะ แต่มันจะปรากฏขึ้นcount($a)ค่ะcompactจะทำงานกับตัวแปรที่ตั้งไว้ทั้งหมดรวมถึงnullsและอื่น ๆ
enrey

3
คำตอบนี้มีข้อบกพร่องในวิธีการหนึ่งที่สำคัญ: ในการเขียนโปรแกรม OO, null เป็นตัวเลือกเชิงตรรกะที่หมายถึง "ไม่มีวัตถุ" ตัวอย่างเช่นในสถานการณ์ที่ไม่มีข้อยกเว้นเมื่อฟังก์ชันสามารถส่งคืนวัตถุหรือไม่มีวัตถุใด ๆ ก็ได้ null เป็นตัวเลือกที่ชัดเจน ในทางเทคนิคใน PHP, ค่า false หรือค่าอื่น ๆ ที่ถือว่าเป็นเท็จในบริบทบูลีนสามารถนำมาใช้ได้ ดังนั้น null เป็นค่าที่เหมาะสมในการเริ่มต้นที่ดีเลิศตัวแปรที่ควรถือวัตถุในที่สุดเพราะมันเป็นที่เกี่ยวข้องกับสิ่งที่มันตั้งใจที่จะกลายเป็น
chazomaticus

3
ตราบใดที่ PHP โยนข้อผิดพลาดสำหรับตัวแปรที่ไม่ได้กำหนด แต่ไม่ใช่สำหรับโมฆะก็มีความแตกต่าง ถ้า null และ undefined เป็นแนวคิดเดียวกันจริง ๆ แล้ว PHP ควรถือว่าตัวแปรที่ไม่ได้นิยาม / ไม่ได้ประกาศเป็น null และไม่มีข้อผิดพลาด แต่ไม่มีใครต้องการมันเพราะมันเป็นฝันร้ายของการพัฒนา Null และ undefined อาจไม่แตกต่างกันในบริบทของ semantics แต่มันแตกต่างกันมากเมื่อเขียนโค้ดที่ชัดเจนและ debuggable
Chris Middleton

9

คุณสมบัติของวัตถุสามารถตรวจสอบได้สำหรับการมีอยู่ของproperty_exists

ตัวอย่างจากการทดสอบหน่วย:

function testPropertiesExist()
{
    $sl =& $this->system_log;
    $props = array('log_id',
                   'type',
                   'message',
                   'username',
                   'ip_address',
                   'date_added');

    foreach($props as $prop) {
        $this->assertTrue(property_exists($sl, $prop),
                           "Property <{$prop}> exists");
    }
}

4

นอกเหนือจากการอภิปรายของ greatbigmassive ว่า NULL หมายถึงอะไรให้พิจารณาว่า "การดำรงอยู่ของตัวแปร" หมายถึงอะไร

ในหลายภาษาที่คุณต้องชัดเจนทุกประกาศตัวแปรก่อนที่คุณจะใช้มัน ; นี้อาจกำหนดชนิด แต่ที่สำคัญกว่าก็ประกาศของขอบเขต ตัวแปร "มีอยู่" ทุกที่ในขอบเขตของมันและไม่มีที่อยู่ข้างนอก - ไม่ว่าจะเป็นฟังก์ชั่นทั้งหมดหรือ "บล็อก" เดียว

ภายในขอบเขตของตัวแปรจะกำหนดความหมายบางอย่างให้กับป้ายกำกับซึ่งคุณโปรแกรมเมอร์ได้เลือกไว้ นอกขอบเขตของฉลากนั้นไม่มีความหมาย (ไม่ว่าคุณจะใช้ป้ายกำกับเดียวกันในขอบเขตที่แตกต่างกันนั้นโดยทั่วไปจะไม่เกี่ยวข้อง)

ใน PHP ตัวแปรไม่จำเป็นต้องประกาศ - มันจะมีชีวิตขึ้นมาทันทีที่คุณต้องการ เมื่อคุณเขียนไปยังตัวแปรเป็นครั้งแรก PHP จะจัดสรรรายการในหน่วยความจำสำหรับตัวแปรนั้น ถ้าคุณอ่านจากตัวแปรที่ยังไม่มีรายการที่ PHP NULLเห็นว่าตัวแปรที่จะมีค่า

อย่างไรก็ตามเครื่องตรวจจับคุณภาพรหัสอัตโนมัติจะเตือนคุณหากคุณใช้ตัวแปรโดยไม่ต้อง "เริ่มต้น" ก่อน ประการแรกสิ่งนี้จะช่วยตรวจจับความผิดพลาดเช่นการกำหนด$thingIdแต่อ่านจาก$thing_id; แต่ประการที่สองมันบังคับให้คุณพิจารณาขอบเขตที่ตัวแปรนั้นมีความหมายเช่นเดียวกับการประกาศ

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

เนื่องจากวิธีการทำงานของ PHP จึงเป็นไปได้ที่จะเขียนโค้ดที่ปฏิบัติต่อเนมสเปซของตัวแปรที่มีอยู่ไม่ใช่ขอบเขตของป้ายกำกับที่คุณให้ความหมาย แต่เป็นที่เก็บคีย์ - ค่าบางชนิด $var = $_GET['var_name']; $$var = $_GET['var_value'];คุณสามารถเช่นเรียกใช้รหัสเช่นนี้ เพียงเพราะคุณทำได้ไม่ได้หมายความว่าเป็นความคิดที่ดี

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

นอกจากนี้คุณยังสามารถใช้วัตถุในลักษณะที่คล้ายกันตั้งค่าคุณสมบัติแบบไดนามิกซึ่งในกรณีนี้คุณสามารถใช้property_exists()ในลักษณะเดียวกัน แน่นอนถ้าคุณกำหนดชั้นเรียนคุณสามารถประกาศคุณสมบัติที่จะได้ - คุณยังสามารถเลือกระหว่างpublic, privateและprotectedขอบเขต

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


1
คะแนนที่ดีมากแม้ว่าจะไม่ใช่คำตอบที่แน่นอน
chazomaticus

1
สำหรับคำถามที่ชัดเจน "เราควรจะตรวจสอบการมีอยู่ของตัวแปรใน PHP ได้อย่างไร?" คำตอบของฉันคือ "คุณไม่ได้และนี่คือเหตุผล" ทั้งคำตอบนี้และ greatbigmassive ยังตอบคำถามโดยปริยายว่า "ทำไมจึงทำisset()เช่นนั้น"
IMSoP

"ถ้าคุณอ่านจากตัวแปรที่ไม่มีรายการในปัจจุบัน PHP จะพิจารณาว่าตัวแปรนั้นมีค่า NULL" นี่เป็นเท็จ ตัวแปรที่ไม่ได้กำหนดนั้นไม่ได้ถูกกำหนดไว้อย่างง่าย มันอาจส่งคืนค่าว่างเมื่อคุณพยายามเข้าถึง แต่ไม่เกี่ยวข้อง
Hugo Zink

@HugoZink ไม่เกี่ยวข้องกับอะไร การทดสอบใด ๆ ที่คุณทำกับค่าของตัวแปรที่ไม่ได้กำหนดจะบอกคุณว่าค่านั้นเป็นnullเท่าใด ไม่ว่าจะเป็นค่าที่มีอยู่ก่อนที่คุณจะมองไปที่มันเป็นคำถามสำหรับนักปรัชญา แต่เท่าที่ติดตามพฤติกรรมใด ๆ nullที่เป็นห่วงค่าเป็นอย่างต่อเนื่อง
IMSoP

3

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

นี่คือทางออกหนึ่งที่เป็นไปได้:

$e1 = error_get_last();
$isNULL = is_null(@$x);
$e2 = error_get_last();
$isNOTSET = $e1 != $e2;
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

วิธีแก้ปัญหาอื่น ๆ คือการตรวจสอบผลลัพธ์ของget_defined_vars():

$vars = get_defined_vars();
$isNOTSET = !array_key_exists("x", $vars);
$isNULL = $isNOTSET ? true : is_null($x);
echo sprintf("isNOTSET: %d, isNULL: %d", $isNOTSET, $isNULL);

// Sample output:
// when $x is not set: isNOTSET: 1, isNULL: 1
// when $x = NULL:     isNOTSET: 0, isNULL: 1
// when $x = false:    isNOTSET: 0, isNULL: 0

2

ฉันไม่เห็นด้วยกับเหตุผลของคุณเกี่ยวกับ NULLและบอกว่าคุณต้องเปลี่ยนความคิดของคุณเกี่ยวกับ NULL นั้นแปลก

ฉันคิดว่า isset () ไม่ได้ถูกออกแบบอย่างถูกต้อง isset () ควรบอกคุณว่าตัวแปรถูกตั้งค่าไว้แล้วและไม่ควรเกี่ยวข้องกับมูลค่าที่แท้จริงของตัวแปร

จะเกิดอะไรขึ้นถ้าคุณกำลังตรวจสอบค่าที่ส่งคืนจากฐานข้อมูลและหนึ่งในคอลัมน์มีค่า NULL คุณยังต้องการทราบว่ามีอยู่แม้ว่าค่านั้นจะเป็น NULL ... nope dont trust isset () ที่นี่

ในทำนองเดียวกัน

$a = array ('test' => 1, 'hello' => NULL);

var_dump(isset($a['test']));   // TRUE
var_dump(isset($a['foo']));    // FALSE
var_dump(isset($a['hello']));  // FALSE

isset () ควรได้รับการออกแบบให้ทำงานเช่นนี้:

if(isset($var) && $var===NULL){....

ด้วยวิธีนี้เราปล่อยให้โปรแกรมเมอร์ตรวจสอบชนิดและไม่ปล่อยให้ isset () สมมติว่ามันไม่ได้อยู่ที่นั่นเพราะค่าเป็น NULL - การออกแบบที่โง่เพียง


ตัวอย่างของคุณไม่ได้ตรวจสอบการมีอยู่ของตัวแปร แต่เป็นคีย์อาร์เรย์ array_key_existsวิธีการแก้ปัญหาที่มีอยู่ในรูปแบบของ คุณไม่ควรอยู่ในสถานการณ์ที่คุณไม่รู้ว่ารันไทม์หากมีตัวแปรจริงอยู่
IMSoP

@chazomaticus คุณไม่ควรอยู่ในสถานการณ์ที่เปิดใช้งาน register_globals ดังนั้นฉันจึงต้องยืนหยัดด้วยคำพูดนั้น
IMSoP

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

@chazomaticus หากปัญหาของคุณคือแล้วคำตอบคือไม่ได้มีการเปลี่ยนแปลงregister_globals คู่มือ PHPกล่าวถึง "โดยทั่วไปแล้วมันเป็นวิธีการเขียนโปรแกรมที่ดีในการเริ่มต้นตัวแปรก่อน" ซึ่งแก้ปัญหาในเวลาออกแบบมากกว่าเวลาทำงาน นอกจากนี้ยังมีรายการคำถามที่พบบ่อยให้ฟังก์ชั่นที่จะจัดการกับมันในเวลาทำงาน isset()register_globalsunregister_globals()
IMSoP

2

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

$a = null;
var_dump($a); // NULL
var_dump($b); // NULL

คุณสามารถสันนิษฐานได้จากผลลัพธ์นี้ว่าความแตกต่างระหว่าง$a = nullและไม่ได้กำหนด$bอะไรเลย

การรายงานข้อผิดพลาดของข้อเหวี่ยงขึ้น:

NULL

Notice: Undefined variable: b in xxx on line n
NULL

หมายเหตุ:มันโยนข้อผิดพลาดที่ไม่ได้กำหนดตัวแปร แต่มูลค่าการส่งออกของยังคงเป็นvar_dumpNULL

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

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


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

1

ถ้าฉันเรียกใช้ต่อไปนี้:

echo '<?php echo $foo; ?>' | php

ฉันได้รับข้อผิดพลาด:

PHP Notice:  Undefined variable: foo in /home/altern8/- on line 1

ถ้าฉันเรียกใช้ต่อไปนี้:

echo '<?php if ( isset($foo) ) { echo $foo; } ?>' | php

ฉันไม่ได้รับข้อผิดพลาด

หากฉันมีตัวแปรที่ควรตั้งค่าฉันมักจะทำสิ่งต่อไปนี้

$foo = isset($foo) ? $foo : null;

หรือ

if ( ! isset($foo) ) $foo = null;

ด้วยวิธีนี้ในสคริปต์ฉันสามารถใช้ $ foo ได้อย่างปลอดภัยและรู้ว่ามัน "ถูกตั้งค่า" และมันจะเป็นค่าเริ่มต้นเป็นโมฆะ หลังจากนี้ฉันสามารถif ( is_null($foo) ) { /* ... */ }ถ้าฉันต้องการและทราบว่าตัวแปรมีอยู่แม้ว่ามันจะเป็นโมฆะ

เอกสารของ บริษัท ผู้ออกหลักทรัพย์ฉบับเต็มอ่านมากกว่าสิ่งที่วางไว้ในตอนแรก ใช่มันจะคืนค่าเท็จสำหรับตัวแปรที่ตั้งค่าไว้ก่อนหน้านี้ แต่ตอนนี้เป็นโมฆะ แต่ก็กลับเท็จถ้าตัวแปรยังไม่ได้ตั้งค่า (เคย) และตัวแปรใด ๆ ที่ถูกทำเครื่องหมายว่าไม่ได้ตั้งค่า นอกจากนี้ยังตั้งข้อสังเกตว่า NULL ไบต์ ("\ 0") ไม่ถือเป็นโมฆะและจะส่งกลับค่าจริง

ตรวจสอบว่ามีการตั้งค่าตัวแปร

หากตัวแปรไม่มีการตั้งค่าด้วย unset () ตัวแปรจะไม่ถูกตั้งค่าอีกต่อไป isset () จะคืนค่า FALSE หากทดสอบตัวแปรที่ตั้งค่าเป็น NULL โปรดทราบว่าไบต์ NULL ("\ 0") ไม่เท่ากับค่าคงที่ PHP NULL


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

นี่ไม่ใช่วิธีปฏิบัติที่ไม่ดีสำหรับสคริปต์แบบง่าย ๆ แต่ในโครงการที่ซับซ้อน (เช่น OO ขนาดใหญ่) มันจะไม่สามารถทำได้ ตามที่ฉันได้กล่าวไว้ข้างต้น is_null () จะส่งกลับค่า TRUE สำหรับตัวแปรที่ไม่ได้ตั้งค่าดังนั้นจึงไม่มีเหตุผลที่จะทำสิ่งที่คุณพูดยกเว้นเพื่อหลีกเลี่ยงคำเตือน PHP
chazomaticus

1
สำหรับโครงการ "OO ขนาดใหญ่" ที่ออกแบบมาอย่างดีเหตุใดจึงเป็นปัญหาทั้งหมด ทำไมคุณถึงมี $ foo ในส่วนของเมธอดที่อาจไม่ได้ตั้งค่าก่อนการใช้งานครั้งแรก
Beau Simensen

1

ลองใช้ดู

unset($v)

ดูเหมือนว่าเวลาเดียวที่ตัวแปรไม่ได้ตั้งค่าคือเมื่อไม่ได้ตั้งค่าโดยเฉพาะ ($ v) ดูเหมือนความหมายของ 'การมีอยู่' นั้นแตกต่างจากคำจำกัดความของ PHP NULL มีอยู่จริงมันเป็น NULL


ฉันไม่แน่ใจว่าคุณหมายถึงอะไร หากคุณมีอาร์เรย์ด้วยองค์ประกอบหนึ่ง 'a' คุณไม่จำเป็นต้องยกเลิกการตั้งค่า () องค์ประกอบ 'b' สำหรับองค์ประกอบ 'b' ที่จะไม่มีอยู่ใน PHP มันจะไม่มีอยู่จริง สิ่งเดียวกันกับเช่นตัวแปรทั่วโลกซึ่งคุณสามารถคิดว่าเป็นองค์ประกอบของอาร์เรย์ $ GLOBALS
chazomaticus

1
แต่ฉันยอมรับว่ามีตัวแปรที่มีค่า NULL อยู่จริง
chazomaticus

0

ฉันต้องพูดในทุก ๆ ปีของการเขียนโปรแกรม PHP ฉันไม่เคยพบปัญหากับการisset()คืนค่าเท็จในตัวแปร null OTOH ฉันพบปัญหากับisset()ความล้มเหลวในรายการอาร์เรย์ว่าง แต่array_key_exists()ทำงานได้อย่างถูกต้องในกรณีนั้น

สำหรับการเปรียบเทียบบางอย่างไอคอนจะกำหนดตัวแปรที่ไม่ได้ใช้เป็นการส่งคืน&nullดังนั้นคุณใช้การทดสอบ is-null ในไอคอนเพื่อตรวจสอบตัวแปรที่ยังไม่ได้ตั้งค่า สิ่งนี้ทำให้สิ่งต่าง ๆ ง่ายขึ้น ในทางกลับกัน Visual Basic มีตัวแปรหลายสถานะสำหรับตัวแปรที่ไม่มีค่า (Null, Empty, Nothing, ... ) และบ่อยครั้งที่คุณต้องตรวจสอบมากกว่าหนึ่งรายการ เรื่องนี้เป็นที่รู้จักกันว่าเป็นแหล่งของแมลง


0

ตามคู่มือ PHP สำหรับฟังก์ชั่นที่ว่างเปล่า () ฟังก์ชั่น "ตรวจสอบว่าตัวแปรถูกพิจารณาว่าว่างเปล่าหรือไม่ตัวแปรนั้นถือว่าว่างถ้ามันไม่มีอยู่หรือถ้าค่าของมันเท่ากับ FALSE. empty () จะไม่สร้างคำเตือนหาก ตัวแปรไม่มีอยู่ " (ความสำคัญของฉัน) นั่นหมายความว่าฟังก์ชัน empty () ควรมีคุณสมบัติเป็น "วิธีที่ดีที่สุดในการทดสอบการมีอยู่ของตัวแปรใน PHP" ตามคำถามชื่อ

อย่างไรก็ตามนี่ไม่ดีพอเนื่องจากฟังก์ชัน empty () สามารถถูกหลอกได้โดยตัวแปรที่มีอยู่และตั้งค่าเป็น NULL

ฉันขัดจังหวะคำตอบก่อนหน้าของฉันเพื่อนำเสนอสิ่งที่ดีกว่าเพราะมันยุ่งยากน้อยกว่าคำตอบเดิมของฉัน (ซึ่งตามหลังการขัดจังหวะนี้เพื่อเปรียบเทียบ)

  function undef($dnc) //do not care what we receive
  { $inf=ob_get_contents();             //get the content of the buffer
    ob_end_clean();                     //stop buffering outputs, and empty the buffer
    if($inf>"")                         //if test associated with the call to this function had an output
    { if(false!==strpos($inf, "Undef"); //if the word "Undefined" was part of the output
        return true;                    //tested variable is undefined
    }
    return false;                       //tested variable is not undefined
  }

โค้ดสองบรรทัดอย่างง่ายสามารถใช้ฟังก์ชันด้านบนเพื่อเปิดเผยว่าตัวแปรไม่ได้ถูกกำหนด:

  ob_start();                           //pass all output messages (including errors) to a buffer
  if(undef($testvar===null))            //in this case the variable being tested is $testvar

คุณสามารถติดตามทั้งสองบรรทัดด้วยสิ่งที่เหมาะสมเช่นตัวอย่างนี้:

    echo("variable is undefined");
  else
    echo("variable exists, holding some value");

ฉันต้องการวางสายเพื่อ ob_start () และ ($ testvar === null) ภายในฟังก์ชั่นและเพียงแค่ส่งตัวแปรไปยังฟังก์ชั่น แต่มันไม่ทำงาน แม้ว่าคุณจะพยายามใช้ "pass by reference" ของตัวแปรไปยังฟังก์ชันตัวแปร BECOMES ที่กำหนดไว้และจากนั้นฟังก์ชันจะไม่สามารถตรวจพบว่าเคยมีการไม่ได้กำหนดไว้ สิ่งที่นำเสนอที่นี่เป็นการประนีประนอมระหว่างสิ่งที่ฉันต้องการจะทำและสิ่งที่ได้ผลจริง

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

   function inst(&$v) { return; }  //receive any variable passed by reference; instantiates the undefined

เพียงเรียกใช้ฟังก์ชันนั้นก่อนที่จะทำอะไรบางอย่างกับ $ testvar ของคุณ:

   inst($testvar);                //The function doesn't affect any value of any already-existing variable

ค่าของตัวแปรที่ถูกสร้างอินสแตนซ์ใหม่ถูกตั้งค่าเป็นโมฆะแน่นอน!

(การขัดจังหวะสิ้นสุด)

ดังนั้นหลังจากการศึกษาและทดลองบางอย่างนี่คือสิ่งที่รับประกันว่าจะทำงาน:

 function myHndlr($en, $es, $ef, $el)
 { global $er;
   $er = (substr($es, 0, 18) == "Undefined variable");
   return;
 }

 $er = false;
 if(empty($testvar))
 { set_error_handler("myHndlr");
   ($testvar === null);
   restore_error_handler();
 }
 if($er)  // will be 1 (true) if the tested variable was not defined.
 { ; //do whatever you think is appropriate to the undefined variable
 }

คำอธิบาย: ตัวแปร $ er ถูกเตรียมใช้งานเป็นค่าเริ่มต้นของ "ไม่มีข้อผิดพลาด" มีการกำหนด "ฟังก์ชันตัวจัดการ" ถ้า $ testvar (ตัวแปรที่เราต้องการทราบว่าไม่ได้กำหนดหรือไม่) ผ่านการทดสอบฟังก์ชั่น empty () เบื้องต้นเราจะทำการทดสอบอย่างละเอียดมากขึ้น เราเรียกใช้ฟังก์ชัน set_error_handler () เพื่อใช้ฟังก์ชันตัวจัดการที่กำหนดไว้ก่อนหน้านี้ จากนั้นเราทำการเปรียบเทียบเอกลักษณ์อย่างง่าย ๆ ที่เกี่ยวข้องกับ $ testvar ซึ่งหากไม่ได้กำหนดจะมีข้อผิดพลาดเกิดขึ้น ฟังก์ชั่นการจัดการจับข้อผิดพลาดและการทดสอบโดยเฉพาะเพื่อดูว่าสาเหตุของข้อผิดพลาดคือความจริงที่ว่าตัวแปรไม่ได้กำหนด ผลลัพธ์ถูกวางในตัวแปร $-error ซึ่งเป็นข้อมูลข้อผิดพลาดซึ่งเราสามารถทดสอบในภายหลังเพื่อทำสิ่งที่เราต้องการจากการรู้ว่ามีการกำหนด $ testvar ไว้หรือไม่ เนื่องจากเราต้องการฟังก์ชันตัวจัดการสำหรับจุดประสงค์ที่ จำกัด นี้เท่านั้นเราจึงกู้คืนฟังก์ชันการจัดการข้อผิดพลาดดั้งเดิม ฟังก์ชัน "myHndlr" จำเป็นต้องประกาศเพียงครั้งเดียว รหัสอื่นสามารถคัดลอกไปยังสถานที่ใดก็ได้ที่เหมาะสมสำหรับ $ testvar หรือตัวแปรอื่น ๆ ที่เราต้องการทดสอบด้วยวิธีนี้


1
หากความตั้งใจที่จะหลีกเลี่ยงการเตือนว่าตัวแปรของคุณยังไม่ได้ประกาศวิธีการแก้ปัญหาคือการแก้ไขรหัสของคุณเพื่อประกาศอย่างถูกต้อง instฟังก์ชั่นของคุณนั้นเหมือนกับ@โอเปอเรเตอร์ปราบปรามข้อผิดพลาด: "ฉันรู้ว่าฉันกำลังทำอะไรผิดพลาดที่นี่ แต่ฉันแค่อยากให้ข้อความหายไปโดยไม่เปลี่ยนการทำงานของโค้ดในทางใดทางหนึ่ง"
IMSoP

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

0

ฉันคิดว่าทางออกเต็มรูปแบบเพียงอย่างเดียวคือการรายงานการแจ้งเตือนด้วย

error_reporting(E_ALL); // Enables E_NOTICE

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

การเปิดใช้งานการรายงานการแจ้งเตือนอาจไม่ใช่ทางเลือกที่ดีในทุกสถานการณ์ แต่มีเหตุผลที่ดีในการเปิดใช้งาน:

เหตุใดฉันจึงควรแก้ไขข้อผิดพลาด E_NOTICE

ในกรณีของฉันเป็นเวลามากกว่าหนึ่งปีที่ทำงานใน proyect ที่ไม่มีมัน แต่เคยระมัดระวังเกี่ยวกับการประกาศตัวแปรดังนั้นมันจึงเป็นช่วงเปลี่ยนผ่านที่รวดเร็ว


0

วิธีเดียวที่จะทราบว่าตัวแปรถูกกำหนดในขอบเขตปัจจุบัน ( $GLOBALSไม่น่าเชื่อถือ) array_key_exists( 'var_name', get_defined_vars() )หรือไม่


1
ฉันคิดว่านั่นเป็นสิ่งที่คนอื่นพูดมาก่อนหรือว่าฉันผิด
Stephan Vierkant

-1

ฉันชอบใช้ไม่ว่างเปล่าเป็นวิธีที่ดีที่สุดในการตรวจสอบการมีอยู่ของตัวแปรที่ a) มีอยู่และ b) ไม่ใช่ null

if (!empty($variable)) do_something();

2
empty()ไม่ตรวจสอบว่าตัวแปรเป็นโมฆะหรือไม่ตรวจสอบว่าเป็นเท็จ -y หรือไม่เช่นหนึ่งใน""(สตริงว่าง), 0(0 เป็นจำนวนเต็ม), 0.0(0 เป็นทศนิยม), "0"(0 เป็นสตริง) NULL,, FALSE, array()(อาร์เรย์ว่าง) และ$var;(ตัวแปรที่ประกาศ แต่ไม่มีค่า) สมมติว่าคุณมีข้อมูลที่จำเป็นวิทยุในรูปแบบที่มีสองปัจจัยการผลิตที่มีค่าและ0 1หากคุณใช้empty()สำหรับการตรวจสอบความถูกต้องและผู้ใช้เลือก0อันใดอันหนึ่งคุณจะผิดพลาดโดยไม่ได้ตั้งใจ "ช่องที่จำเป็นต้องไม่ว่างเปล่า" ดูคู่มือphp.net/manual/en/function.empty.php
Halil Özgür
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.