PHP에서 어떤값이 배열안에 있는지 체크할 때 in_array() 함수를 사용한다.

$a = array('1', '2', '3', '4');
// 문자 'a' 가 배열 $a 안에 있는지 체크
if (in_array('a', $a)==true) ...
else ...

뭐 이런식으로 사용하는데 주의할 점이 있다.
모든 PHP의 비교 구문에서 있을 수 있는 문제인데 변수의 타입에 관한 문제이다.
결론부터 말하자면 비교대상이 문자열-숫자 0 일 경우엔 모든 비교가 TRUE가 된다.

// 배열이 모두 숫자만으로 이루어져 있다. 문제 없는 케이스다.
$a = array(1,2);
var_dump(in_array('a', $a)); // FALSE
var_dump(in_arary(0, $a));  // FALSE

// 배열 안에 문자열이 하나 있다. 숫자 0 을 사용할 경우 문제가 발생할 수 있다.
$a = array('a', 2);
var_dump(in_array('0', $a)); // FALSE (문자 '0' 은 문제가 없다)
var_dump(in_array(0, $a)); // TRUE (숫자 0 일 경우엔 배열에 0 이 없지만 TRUE를 반환한다)

// 배열 안에 숫자 0 이 있을 경우엔 in_array() 의 첫번째 파라메터가 문자열일 경우 무조건 TRUE를 반환하는 심각한 문제가 있다.
$a = array('a', 0);
var_dump(in_array('a', $a)); // TRUE (문자 'a'는 배열 안에 있으므로 기대값과 일치한다.)
var_dump(in_array('b', $a)); // TRUE (문자 'b'는 배열 안에 없지만 TRUE가 반환된다.)



이 문제는 PHP가 내부에서 비교를 수행할 때 타입이 다른 변수의 경우 형변환을 하는데서 발생할 수 있다.
숫자 0 과 문자열 'abc'를 비교할 경우 문자열 'abc' 를 숫자로 형변환하고,
문자열 'abc'는 아무 숫자도 가지고 있지 않으므로 기본적으로 숫자 0 이 반환된다.
그러므로 숫자 0 과 문자열 'abc'의 비교는 TRUE가 된다.

var_dump(intval('abc')); // 숫자 0

if (0=='abc') ... // TRUE

if (0==='abc') ... // FALSE


물론 변수의 타입까지 비교하는 구문에선 어림도 없다.

PHP의 기본 정책상 이렇다고는 하지만 이 문제는 좀 아닌 것 같다.
숫자 0 과 문자열 'abc'를 비교할 때 형변환이 반대로 되는 경우가 얼핏 떠오르기 때문이다. (나만 그럴 수도 있다...)
최대한 변수의 값을 바꾸지 않는다는 전제 하에 문자열 'abc'가 숫자 0 으로 변환되기 보다는 숫자 0 이 문자열 '0' 으로 변하는 걸 기대하지 않을까.
이런 점에서 PHP에서 이런 형태의 형변환을 거치는 비교 구문은 문제가 있다고 생각된다.
(참고로 자바스크립트도 if (0=='abc') 는 false다...)

in_array() 함수의 세번째 파라메터를 true로 하면 변수의 타입까지 비교할 수 있지만,
GET, POST, COOKIE 값이나 DB에서 가져온 값들을 그냥 배열에 때려넣고 in_array()를 잘 사용하던 입장에서는,
오히려 숫자 0 에 대한 부담으로 세번째 파라메터를 true로 하기엔 리스크가 더 크다고 생각된다.



참고1. http://php.net/manual/en/function.in-array.php (PHP 매뉴얼의 in_array 함수 페이지)
참고2. http://bugs.php.net/bug.php?id=14343 (버그가 아니라는 구차한 변명...-_-)




Posted by bloodguy
,