isset () 및 empty ()를 피하는 방법


98

E_NOTICE 오류 수준에서 실행할 때 많은 "xyz is undefined"및 "undefined offset"메시지를 던지는 이전 응용 프로그램이 여러 개 있습니다. 왜냐하면 변수의 존재는 isset()and consorts를 사용하여 명시 적으로 확인하지 않기 때문 입니다.

누락 된 변수 또는 오프셋에 대한 알림이 생명의 은인이 될 수 있고, 약간의 성능 향상을 얻을 수 있으며, 전체적으로 더 깔끔한 방식이므로 E_NOTICE와 호환되도록 작업하는 것을 고려하고 있습니다.

그러나 수백 개의 isset() empty()array_key_exists()s가 내 코드에 미치는 영향이 마음에 들지 않습니다 . 가치 나 의미의 측면에서 아무것도 얻지 못하고 부풀어지고 가독성이 떨어집니다.

변수 검사를 초과하지 않고 E_NOTICE와 호환되는 코드를 어떻게 구성 할 수 있습니까?


6
전적으로 동의합니다. 이것이 제가 Zend Framework를 너무 좋아하는 이유입니다. 요청 모듈이 아주 좋습니다. 작은 앱에서 작업하는 경우 일반적으로 ZF의 요청과 유사하게 작동하는 매직 메서드 __set 및 __get으로 간단한 요청 클래스를 코딩합니다. 그렇게하면 코드에서 isset 및 empty의 모든 발생을 피할 수 있습니다. 이렇게하면 배열을 반복하기 전에 배열에 if (count ($ arr)> 0)을 사용하고 몇 가지 중요한 위치에서 if (null! == $ variable) 만 사용해야합니다. 훨씬 깨끗해 보입니다.
Richard Knop

답변:


128

관심있는 분들을 위해이 주제를 작은 기사로 확장했습니다. 아래 정보는 좀 더 구조화 된 형식으로 제공됩니다. The Definitive Guide To PHP isset And empty


IMHO 앱을 "E_NOTICE 호환"으로 만드는 것뿐만 아니라 모든 것을 재구성하는 것에 대해 생각해야합니다. 데 수백 코드에 포인트를 정기적으로 다소 심하게 구조화 된 프로그램과 같은 존재하지 않는 변수 소리를 사용하려고있다. 존재하지 않는 변수에 접근하려는 시도는 절대 일어나서는 안되며, 다른 언어는 컴파일 타임에이 상황을 방해합니다. PHP가 그렇게 할 수 있다는 사실이 그렇게해야한다는 의미는 아닙니다.

이러한 경고는 당신을 성가 시게하는 것이 아니라 당신 을 돕기 위한 것입니다. "존재하지 않는 작업을 시도하고 있습니다!" 라는 경고가 표시되는 경우 , 귀하의 반응은 "죄송합니다. 죄송합니다. 최대한 빨리 수정하겠습니다." "정의되지 않고 잘 작동하는 변수"심각한 오류를 유발할 수있는 정직하게 잘못된 코드 의 차이점을 어떻게 구분할 있습니까? 이것은 또한 항상 오류보고 를 11로 설정하여 개발 하고 단 하나가 아닐 때까지 코드를 계속 연결 하는 이유이기도합니다.NOTICE . 오류보고 기능을 끄는 것은 정보 유출을 방지하고 버그가있는 코드가있는 경우에도 더 나은 사용자 경험을 제공하기 위해 프로덕션 환경에만 해당됩니다.


자세히 설명하려면 :

항상 필요 isset하거나 empty코드의 어딘가에서 발생을 줄이는 유일한 방법은 변수를 올바르게 초기화하는 것입니다. 상황에 따라 다른 방법이 있습니다.

함수 인수 :

function foo ($bar, $baz = null) { ... }

여부를 확인 할 필요가 없습니다 $bar또는 $baz경우에 그 값들을 평가하는 것입니다에 대해 당신은 단지 그들을 설정하기 때문에 함수 내에서 설정이 모든 당신이 걱정할 필요 true하거나 false(또는 어떤 다른).

어디서나 일반 변수 :

$foo = null;
$bar = $baz = 'default value';

변수를 사용할 코드 블록의 맨 위에서 변수를 초기화하십시오. 이것은 문제를 해결하고 !isset, 변수가 항상 알려진 기본값을 가지도록 보장하고, 독자에게 다음 코드가 어떤 작업을 수행 할 것인지에 대한 아이디어를 제공하여 일종의 자체 문서화 역할도합니다.

어레이 :

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

위와 마찬가지로 기본값으로 배열을 초기화하고 실제 값으로 덮어 씁니다.

나머지 경우 컨트롤러에 의해 설정되거나 설정되지 않은 값을 출력하는 템플릿을 가정 해 보겠습니다. 다음 사항 만 확인하면됩니다.

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

을 정기적으로 사용 array_key_exists하고 있다면 어떤 용도로 사용하는지 평가해야합니다. 차이를 만드는 유일한 경우는 다음과 같습니다.

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

위에서 언급했듯이 변수를 올바르게 초기화하는 경우 키가 존재하는지 여부를 알기 때문에 확인할 필요가 없습니다. 외부 소스에서 배열을 얻는 경우, 값이 가장 가능성이되지 않습니다 null'', 0, '0', false또는 같은, 즉 가치 당신과 함께 평가할 수 있습니다 isset또는 empty귀하의 의도에 따라. 정기적으로 배열 키를 설정 한 경우 null와 평균 아무것도를 원하지만 false, 즉, 위의 예에서의 서로 다른 결과를 경우 isset와는 array_key_exists왜 프로그램 로직에 변화를, 당신은 자신을 요청해야합니다. 변수의 존재만으로는 중요하지 않으며 그 값만이 중요해야합니다. 키가 true/ false플래그이면true또는 false,하지 null. 이것에 대한 유일한 예외 null는 무언가를 의미 하려는 써드 파티 라이브러리 일 것입니다. 그러나 nullPHP에서 감지하기가 너무 어렵 기 때문에 아직 이것을 수행하는 라이브러리를 찾지 못했습니다.


4
사실,하지만 대부분 실패 액세스 시도의 라인을 따라있는 if ($array["xyz"])대신에 isset()또는 array_key_exists()(내가 잘못 해요 경우 올바른 나) 내가 문제가 다소 합법적, 확실히 구조 찾을 수있다. 추가 array_key_exists()하는 것은 나에게 끔찍한 낭비처럼 보입니다.
Pekka

9
array_key_exists간단한 isset($array['key'])또는 대신 사용할 경우를 생각할 수 없습니다 !empty($array['key']). 물론, 둘 다 코드에 7 자 또는 8자를 추가하지만 문제가되지는 않습니다. 또한 코드를 명확히하는 데 도움이됩니다. if (isset($array['key']))이는이 변수가 실제로 선택 사항이며 없을 수도 있음을 if ($array['key'])의미 하는 반면 "참인 경우"를 의미합니다. 후자에 대한 통지를 받으면 논리가 어딘가에 망가진 것을 알 수 있습니다.
deceze

6
isset ()과 array_key_exists ()의 차이점은 값이 NULL이면 후자가 true를 반환한다는 것입니다. isset ()은 그렇지 않습니다.
Htbaa

1
사실이지만 존재하지 않는 변수와 값이 null 인 세트 키를 구별해야하는 건전한 사용 사례를 생각할 수 없었습니다. 값이 FALSE로 평가되면 차이가 없어야합니다. :)
deceze

1
배열 키는 정의되지 않은 변수보다 확실히 더 귀찮습니다. 당신이 확실하지 배열 키를 포함 여부 있다면, 그것은 의미 중 하나 는 배열을 직접 정의하지 않았 거나 당신이 통제하지 않는 소스에서 잡아 당겨 있습니다. 두 시나리오 모두 자주 발생해서는 안됩니다. 그리고 그것이 발생하면 어레이에 자신이 생각하는 것을 포함하는지 확인할 모든 이유가 있습니다. 보안 조치 IMO입니다.
kijin

37

그것에 대한 함수를 작성하십시오. 다음과 같은 것 :

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

당신이 사용할 수있는

$username = get_string($_POST, 'username');

같은 사소한 것들에 대해 동일한 작업을 수행 get_number(), get_boolean(), get_array()과에 이렇게.


5
이것은 좋아 보이며 magic_quotes 검사도 수행합니다. 좋은!
Pekka

훌륭한 기능! 공유해 주셔서 감사합니다.
Mike Moore

3
$ _POST [ 'something']은 배열을 반환 할 수 있습니다 (예 : <input name="something[]" />. 사용한다이 경우 하나, 위의 코드를 사용하여 (트림이 배열에 적용 할 수 없기 때문에)이 오류가 발생합니다 is_string가능성이 strval. 이것은 단순히 get_array사용자 입력 (악성)이 무엇이든 사용할 수 있고 사용자 입력 파서가 오류를 발생시키지 않아야하기 때문에 둘 중 하나를 사용해야하는 경우가 아닙니다 .
Ciantic 2012

1
동일한 종류의 함수를 사용하지만 다음과 같이 정의합니다. function get_value (& $ item, $ default = NULL) {return isset ($ item)? $ item : $ default; }이 함수의 장점은 배열, 변수 및 객체를 사용하여 호출 할 수 있다는 것입니다. 단점은 $ item이 그렇지 않은 경우 나중에 초기화 (null로)된다는 것입니다.
Mat

마법 따옴표는 하나의 함수로 처리하는 대신 전역 적으로 꺼야합니다. 마법의 따옴표를 설명하는 인터넷 소스가 많이 있습니다.
Kayla

13

이 문제를 해결하는 가장 좋은 방법 중 하나는 클래스를 통해 GET 및 POST (COOKIE, SESSION 등) 배열의 값에 액세스하는 것입니다.

각 배열 및 선언 __get__set메서드에 대한 클래스를 만듭니다 ( overloading ). __get값의 이름이 될 하나의 인수를 허용합니다. 이 메서드는 다음을 사용하여 해당 전역 배열에서이 값을 확인해야합니다.isset() 하거나 empty()및 존재하는 경우, 또는 값을 반환 null달리 (또는 다른 디폴트 값).

그런 다음 다음과 같이 자신있게 배열 값에 액세스 할 수 있습니다. $POST->username 필요한 경우 isset()s 또는 empty()s를 사용하지 않고 유효성 검사를 수행 할 수 있습니다. 경우 username해당 전역 배열에 존재하지 않는 다음 null에는 경고 또는주의 사항이 발생되지 않도록, 반환됩니다.


1
이것은 훌륭한 아이디어이며 코드를 재구성 할 준비가 된 것입니다. +1
Pekka

안타깝게도 $ _GET 또는 $ _POST에 할당하지 않는 한 이러한 인스턴스를 전역으로 만들 수는 없습니다. 하지만 물론 정적 클래스를 사용할 수 있습니다 ...
ThiefMaster

1
"정적 클래스"에서는 getter 및 setter를 사용할 수 없습니다. 변수 당 하나의 클래스를 작성하는 것은 코드 중복을 의미하므로 나쁜 습관입니다. 이 솔루션이 가장 적절하다고 생각하지 않습니다.
Mat

클래스의 공개 정적 멤버는 슈퍼 글로벌처럼 작동합니다. 클래스 HTTP {public static $ POST = array (); ...}; HTTP :: $ POST = new someClass ($ _ POST); ...
velcrow

6

array_key_exists()기능을 사용해도 괜찮습니다. 사실, 난 사용하여 선호 이 특정 기능을 하는 대신에 의존 해킹 미래에 자신의 행동을 변경할 수 있습니다 기능 과 같은 emptyisset (회피에 strikedthrough 감수성 ).


그러나 나는 이것에 편리한 간단한 함수 와 배열 인덱스를 다루는 다른 상황 을 사용합니다 .

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

다음과 같은 배열이 있다고 가정 해 보겠습니다.

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

배열에서 "값"을 어떻게 얻습니까? 단순한:

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

우리는 이미 단일 및 다차원 배열을 다루고 있습니다. 다른 무엇을 할 수 있습니까?


예를 들어 다음 코드를 사용하십시오.

$url = '/programming/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}
else
{
    $domain = 'N/A';
}

꽤 지루하지 않나요? 다음은 Value()함수를 사용하는 또 다른 접근 방식입니다 .

$url = '/programming/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

추가 예를 들어, 테이크 RealIP()기능 테스트를 들어 :

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

깔끔 하죠? ;)


6
"앞으로 행동을 바꿀 수있는 해킹 기능에 의존"?! 미안하지만 그게 내가 일주일 내내들은 것 중 가장 우스꽝스러운 말에 관한 것입니다. 우선, isset하고 empty있는 언어 구조 가 아닌 기능. 경우 둘째, 어떤 핵심 라이브러리 함수 / 언어 구조가 자신의 동작을 변경, 당신은 나 나사 될 수도 있고 그렇지 않을 수도있다. array_key_exists행동 이 바뀌면 어떨까요? 대답은 문서화 된대로 사용하는 한 그렇지 않다는 것입니다. 그리고 isset정확히 그렇게 사용되는 것으로 기록되어 있습니다. 최악의 경우 기능은 주요 릴리스 버전 또는 두 개에서 더 이상 사용되지 않습니다. NIH 증후군은 나쁘다!
deceze

속임수 미안하지만, 먼저 알아 차리지 못한 경우를 대비 하여 해킹이탤릭체로 되어 있습니다. =) 둘째, 키가 배열array_key_exists()있는지 확인하는 데 의존해서는 안된다는 것을 의미 합니까?! 한 그것에 대해 정확히 작성 , 나는보다는 이러한 목적에 의존 특별히 누구의 공식 설명입니다 실제로 존재하는 경우 "변수가 비어 있는지 검사합니다"아무것도 언급하지 않습니다. 귀하의 의견과 반대 투표는 제가 한 달 동안 목격 한 것 중 가장 우스꽝스러운 것 중 하나입니다 . array_key_exists()isset()empty()
Alix Axel

3
나는 말없는거야 isset하고 empty있습니다 더 많거나 적은 안정적인 이상 array_key_exists과 똑같은 일을 할 수 있습니다. 두 번째, 장황한 예제는 $domain = isset($domain['host']) ? $domain['host'] : 'N/A';핵심 언어 기능 으로 작성 될 수 있으며 , 추가 함수 호출이나 선언이 필요하지 않습니다 (내가 반드시 삼항 연산자 사용을 옹호하지는 않습니다; o). 일반 스칼라 변수의 경우 당신은 여전히 사용해야합니다 isset또는 empty, 당신은 동일한 방법으로 배열을 위해 사용할 수 있습니다. "신뢰성"은 그렇게하지 않는 나쁜 이유입니다.
deceze

1
나는 당신이 말한 대부분의 내용에 동의하지 않지만 당신이 주장했습니다. 예를 들어 양식의 숨겨진 필드에 항상 "0"값을 사용하는 것과 같이 90 % 이상의 경우에 대해 잘못 이해했다고 생각합니다. 그래도 내가 제공 한 솔루션이 하향 투표 가치가 없으며 Pekka에 유용 있다고 생각합니다 .
Alix Axel

2
@deceze는 사용자 정의 함수에 대한 요점을 가지고 있지만 일반적으로 동일한 입장을 취합니다. value () 접근 방식은 충분히 흥미로워 서 살펴볼 것입니다. 그 답과 후속 조치를 통해 나중에 우연히 마주 치는 모든 사람이 스스로 결정을 내릴 수있을 것입니다. +1.
Pekka

3

난 당신과 함께 여기 있어요. 그러나 PHP 디자이너는 그보다 훨씬 더 나쁜 실수를 저질렀습니다. 값 읽기에 대한 사용자 지정 함수를 정의하는 데 부족하면 그 주위에 방법이 없습니다.


1
isset () 물건. 기본적으로 모든 것을 null로 설정하면 많은 문제를 줄일 수 있습니다.
vava

2
그리고 이것이 '모든 것'은 무엇입니까? PHP가 생각할 수있는 모든 변수 이름을 상상하고 각각을 NULL로 설정하여 게으른 개발자가 5 자 입력을 피할 수 있도록하는 것은 낭비처럼 보일 것입니다.
Lotus Notes

5
@Byron,보세요, 정말 간단합니다. 다른 많은 언어에서도 그렇게합니다. Ruby와 Perl은 몇 가지 예입니다. VM은 변수가 이전에 사용되었는지 여부를 알고 있습니다. 오류 메시지를 포함하거나 포함하지 않고 실패하는 대신 항상 null을 반환 할 수 있습니다. 그리고 이것은 형편없는 5 개의 문자에 관한 것이 아니라, 전화에 params["width"] = params["width"] || 5대한 모든 말도 안되는 대신 기본값을 설정하기 위해 작성 하는 것입니다 isset().
vava

3
오래된 스레드를 부활 시켜서 죄송합니다. PHP의 최악의 실수 중 두 있었다 register_globalsmagic_quotes. 이러한 조장 문제는 초기화되지 않은 변수를 비교해 보면 거의 무해한 것처럼 보입니다.
staticsan 2010

3

이 기능을 사용합니다

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
  // executes only if both login and pass were in POST
  // stored in $login and $pass variables
  $authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}

2
나도 이것을 사용하지만 경우에 따라 변수가 자동으로 초기화된다는 것을 기억하십시오. 예를 들어 load ($ array [ 'FOO'])는 $ array에 FOO 키를 생성합니다.
Mat

2

null 병합 연산자 (PHP> = 7.0.1)에 오신 것을 환영합니다 .

$field = $_GET['field'] ?? null;

PHP 말한다 :

isset ()과 함께 삼항을 사용해야하는 일반적인 경우를 위해 null 병합 연산자 (??)가 구문 설탕으로 추가되었습니다. 존재하고 NULL이 아닌 경우 첫 번째 피연산자를 리턴합니다. 그렇지 않으면 두 번째 피연산자를 반환합니다.


1

false설정되지 않은 경우 반환 하고 false비어있는 경우 반환하는 함수를 만듭니다 . 유효하면 변수를 반환합니다. 아래 코드와 같이 더 많은 옵션을 추가 할 수 있습니다.

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>

0

소프트웨어는 신의 은혜로 마술처럼 실행되지 않습니다. 누락 된 것이있을 것으로 예상하는 경우 적절하게 처리해야합니다.

이를 무시하면 응용 프로그램에 보안 허점이 생길 수 있습니다. 정의되지 않은 변수에 액세스하는 정적 언어에서는 불가능합니다. null 인 경우 단순히 응용 프로그램을 컴파일하거나 충돌하지 않습니다.

또한 응용 프로그램을 유지 관리 할 수 ​​없으며 예기치 않은 일이 발생하면 미쳐 버릴 것입니다. 언어의 엄격함은 필수이며 PHP는 설계 상 여러 측면에서 잘못되었습니다. 모르는 경우 나쁜 프로그래머가 될 것입니다.


저는 PHP의 부족함을 잘 알고 있습니다. 질문에서 지적했듯이 이전 프로젝트의 정밀 검사에 대해 이야기하고 있습니다.
Pekka

동의합니다. 유일 PHP 개발자이기 때문에 모든 것을 선언해야하는 Java와 같은 새로운 언어로 모험을 떠나는 것은 매우 어렵습니다.
Dzhuneyt

0

가독성에 대한 정의가 무엇인지 잘 모르겠지만 empty (), isset () 및 try / throw / catch 블록을 올바르게 사용하는 것은 전체 프로세스에서 매우 중요합니다.

E_NOTICE가 $ _GET 또는 $ _POST에서 오는 경우 해당 데이터가 통과해야하는 다른 모든 보안 검사와 함께 empty ()에 대해 검사해야합니다.

외부 피드 나 라이브러리에서 오는 경우 try / catch로 래핑해야합니다.

데이터베이스에서 오는 경우 $ db_num_rows () 또는 이에 상응하는 항목을 확인해야합니다.

내부 변수에서 오는 경우 제대로 초기화되어야합니다. 종종 이러한 유형의 알림은 실패시 FALSE를 반환하는 함수의 반환에 새 변수를 할당 할 때 발생합니다. 오류 발생시 코드가 처리 할 수있는 허용 가능한 기본값을 변수에 할당하거나 코드가 처리 할 수있는 예외를 throw 할 수있는 테스트로 래핑되어야합니다.

이러한 것들은 코드를 더 길게 만들고, 추가 블록을 추가하고, 추가 테스트를 추가하지만, 나는 그들이 가장 확실히 추가 가치를 추가한다고 생각한다는 점에 동의하지 않습니다.


-2

@운영자 를 사용하는 것은 어떻습니까?

예를 들면 :

if(@$foo) { /* Do something */ }

$ foo "내부"에서 일어나는 일을 제어 할 수 없기 때문에 이것이 나쁘다고 말할 수 있습니다 (예를 들어 PHP 오류가 포함 된 함수 호출 인 경우). 그러나이 기술을 변수에 대해서만 사용하는 경우 다음과 같습니다.

if(isset($foo) && $foo) { /* ... */ }

if(isset($foo))실제로 충분합니다. TRUE표현식이로 평가 되면 반환 됩니다 TRUE.
Dzhuneyt

2
@ ColorWP.com 표현식이 false로 평가되면 true를 반환합니다.
Jon Hulka

실제로 추가 개발 중이 아닌 코드, 또는 다른 사람에게 표시하고 싶지 않은 일회성 코드 또는 기존 프로젝트의 빠른 수정에 대해서만 @ 매개 변수를 사용해야합니다 (알림 무시). 그러나 빠른 해킹을위한 일반적인 해결 방법입니다.
rubo77
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.