다양한 토론과 답변에 대한 개요를 제공하려고합니다.
isset
사용할 수있는 모든 방법을 대체 할 수있는 질문에 대한 단일 답변은 없습니다 . 일부 유스 케이스는 다른 기능으로 해결되는 반면, 다른 유스 케이스는 정밀 조사를지지하거나 코드 골프를 넘어 모호한 가치를 지니지 않습니다. "파손 된"또는 "일관되지 않은"것과는 달리, 다른 유스 케이스는 왜 isset
반응 null
이 논리적 행동 인지를 보여줍니다 .
실제 사용 사례 (솔루션 포함)
1. 배열 키
배열은 마치 변수처럼 취급 unset
하고 변수의 집합처럼 취급 될 수 있습니다 isset
. 그러나 반복, 계산 등이 가능하므로 결 측값은 값이 아닌 값과 다릅니다 null
.
이 경우의 대답은 대신에 사용하는 array_key_exists()
것입니다isset()
.
이것은 배열을 함수 인수로 검사하기 때문에 배열 자체가 존재하지 않으면 PHP는 여전히 "알림"을 발생시킵니다. 경우에 따라 각 차원을 먼저 초기화해야한다고 주장 할 수 있으므로 통지가 제대로 수행되고 있습니다. 다른 경우 array_key_exists
에는 배열의 각 차원을 차례로 확인한 "재귀" 함수는이를 피하지만 기본적으로와 동일합니다 @array_key_exists
. 또한 null
값을 처리하는 데 다소 접 합니다.
2. 객체 속성
"객체 지향 프로그래밍"의 전통적인 이론에서 캡슐화와 다형성은 객체의 주요 속성입니다. PHP의 같은 클래스 기반 OOP 구현에서, 캡슐화 된 속성은 클래스 정의의 일부로 선언하고, 주어진 액세스 수준 ( public
, protected
또는 private
).
그러나 PHP를 사용하면 배열의 키처럼 객체에 속성을 동적으로 추가 할 수 있으며 일부 사람들은 비슷한 클래스에서 객체가없는 객체 (기술적으로는 내장 stdClass
또는 인스턴스 가없는 메소드)를 사용합니다 연관 배열 방법. 이것은 특정 속성이 주어진 객체에 추가되었는지 함수가 알고 싶어하는 상황으로 이어집니다.
배열 키와 마찬가지로 객체 속성을 확인하기위한 솔루션이 언어에 합리적으로 충분히 포함되어 있습니다property_exists
.
토론이 가능한 정당하지 않은 사용 사례
3. register_globals
글로벌 네임 스페이스의 기타 오염
이 register_globals
기능은 HTTP 요청 (GET 및 POST 매개 변수 및 쿠키)의 측면에 따라 이름이 결정되는 전역 범위에 변수를 추가했습니다. 이로 인해 버그가 많고 안전하지 않은 코드가 생길 수 있으며, 이는 2000 년 8 월에 릴리스 된 PHP 4.2 이후 기본적으로 비활성화되어 2012 년 3 월에 출시 된 PHP 5.4 에서 완전히 제거 된 이유 입니다. 그러나 일부 시스템은이 기능을 사용하거나 에뮬레이트 한 상태로 계속 실행 중일 수 있습니다. global
키워드 또는 $GLOBALS
배열을 사용하여 다른 방식으로 전역 네임 스페이스를 "폴링"할 수도 있습니다 .
첫째, register_globals
자체가 예기치 않게 생산할 가능성이 null
는 GET, POST 이후, 변수 및 쿠키 값은 항상 (와 문자열이 될 것입니다 ''
여전히 반환 true
에서 isset
), 세션 변수는 프로그래머의 통제하에 완전히 있어야한다.
둘째, 값 null
이 있는 변수의 오염은 이전 초기화를 덮어 쓰는 경우에만 문제가됩니다. 초기화되지 않은 변수를 "덮어 쓰기"하는 null
것은 다른 곳의 코드가 두 상태를 구별하는 경우에만 문제가 될 수 있으므로, 이러한 가능성은 그 자체 로 그러한 구별을 만드는 것에 반대 하는 주장 입니다.
4. get_defined_vars
그리고compact
PHP에서 드물게 사용되는 몇 가지 함수 (예 : get_defined_vars
and compact
)를 사용하면 변수 이름을 배열의 키인 것처럼 취급 할 수 있습니다. 전역 변수의 경우 수퍼 전역 배열$GLOBALS
은 비슷한 액세스를 허용하며 더 일반적입니다. 변수가 관련 범위에 정의되어 있지 않으면 이러한 액세스 방법은 다르게 작동합니다.
이러한 메커니즘 중 하나를 사용하여 변수 집합을 배열로 취급하기로 결정한 후에는 일반 배열에서와 동일한 모든 작업을 수행 할 수 있습니다. 따라서 1을 참조하십시오.
이러한 함수의 동작을 예측하기 위해서만 존재했던 기능 (예 : " get_defined_vars
?에 의해 반환 된 배열에 키 'foo'가 있을까요?")은 단순히 함수를 실행하고 악영향없이 찾을 수 있기 때문에 불필요한 기능입니다.
4a. 변수 변수 ( $$foo
)
변수 세트를 연관 배열로 바꾸는 함수와 완전히 동일하지는 않지만 "변수 변수" ( "이 다른 변수를 기반으로 이름 지정된 변수에 지정")를 사용하는 대부분의 경우 연관 배열을 대신 사용하도록 변경해야합니다. .
변수 이름은 기본적으로 프로그래머가 값에 부여한 레이블입니다. 런타임에 결정하는 경우 실제로 레이블이 아니라 일부 키-값 저장소의 키입니다. 실제로는 배열을 사용하지 않으면 계산, 반복 등의 기능이 손실됩니다. 키-값 저장소에 변수를 "외부"로 덮어 쓰는 것도 불가능할 수 있습니다 $$foo
.
연관 배열을 사용하도록 변경되면 코드는 솔루션 1을 준수 할 수 있습니다. 간접 오브젝트 특성 액세스 (예 $foo->$property_name
:)는 솔루션 2로 처리 될 수 있습니다.
5. isset
입력하는 것보다 훨씬 쉽다array_key_exists
나는 이것이 실제로 관련이 있는지 확실하지 않지만, 그렇습니다. PHP의 함수 이름은 때로는 길고 일관성이 없을 수 있습니다. 분명히, 이전 버전의 PHP는 함수 이름의 길이를 해시 키로 사용했기 때문에 Rasmus는 의도적으로 함수 이름 htmlspecialchars
을 구성하여 특이한 수의 문자를 갖습니다 ...
그래도 적어도 우리는 Java를 작성하지 않습니다. ;)
6. 초기화되지 않은 변수에는 유형이 있습니다
변수 기본 사항에 대한 매뉴얼 페이지 에는 다음 내용이 포함되어 있습니다.
초기화되지 않은 변수는 사용되는 컨텍스트에 따라 유형이 기본값입니다.
Zend Engine에 "초기화되지 않았지만 알려진 유형"이라는 개념이 있는지 또는 문을 너무 많이 읽었는지 확실하지 않습니다.
초기화되지 않은 변수에 대해 해당 페이지에 설명 된 동작은 값이 인 변수의 동작과 동일하므로 동작에 실질적인 차이가 없습니다 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
되더라도 원에 도달하지 못할 가능성이 있습니다 . 프로그래머의 의도는 설정되지 않은 시간 을 감지하는 것이지만 PHP는 그렇게 할 수 없습니다.some_test
true
$result
그러나이 방법에는 다른 문제가 있으며 외부 루프를 추가하면 분명해집니다.
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
오는 것만 가능합니다. 특정 내장 기능에 의해 감지됩니다.
실험 2 : 할당 카운터
마지막 if
질문 에 대해 생각하는 또 다른 방법 은 "무엇이 할당되어 $result
있습니까?"입니다. 이 값을의 특별한 값으로 생각하기보다는 $result
이것을 Perl의 "variable tainting"과 같은 변수 에 대한 "메타 데이터"로 생각할 수 있습니다. 그래서보다는 isset
당신이 그것을 호출 할 수 있습니다 has_been_assigned_to
, 그리고보다는 unset
, reset_assignment_state
.
그렇다면 그렇다면 부울에 멈추는 이유는 무엇입니까? 시험이 몇 번이나 통과 했는지 알고 싶다면 어떻게해야합니까? 당신은 단순히 메타 데이터를 정수로 확장 할 수 get_assignment_count
있고 reset_assignment_count
...
분명히 이러한 기능을 추가하면 언어의 복잡성과 성능이 저하 될 수 있으므로 예상되는 유용성과 비교하여 신중하게 평가해야합니다. very_null
상수 와 마찬가지로 매우 좁은 환경에서만 유용하며 리팩토링에 유사하게 저항합니다.
희망적으로 명백한 질문은 PHP 런타임 엔진이 일반 코드를 사용하여 명시 적으로 수행하지 않고 그러한 것들을 추적하고 싶다고 미리 가정해야하는 이유입니다.