Как избежать isset () и empty () - PullRequest
       56

Как избежать isset () и empty ()

97 голосов
/ 25 декабря 2009

У меня есть несколько старых приложений, которые выдают много сообщений "xyz is undefined" и "undefined offset" при работе на уровне ошибки E_NOTICE, потому что существование переменных не проверяется явно с помощью isset() и consorts. *

Я подумываю проработать их, чтобы сделать их совместимыми с E_NOTICE, поскольку уведомления об отсутствующих переменных или смещениях могут быть спасителями, могут быть достигнуты некоторые незначительные улучшения производительности, и в целом это более чистый путь.

Тем не менее, мне не нравится то, что сотни isset() empty() и array_key_exists() s делают с моим кодом. Он становится раздутым, становится менее читаемым, не приобретая ничего с точки зрения ценности или значения.

Как я могу структурировать свой код без лишних проверок переменных, а также быть совместимым с E_NOTICE?

Ответы [ 11 ]

127 голосов
/ 25 декабря 2009

Для тех, кто заинтересован, я расширил эту тему в небольшую статью, в которой приведенная ниже информация представлена ​​в несколько лучше структурированной форме: Полное руководство по isset в PHP И пусто


ИМХО, вам следует подумать не только о том, чтобы сделать приложение "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 что-то значило, но так как null так трудно обнаружить в PHP, мне еще не удалось найти какую-либо библиотеку, которая делает это.

37 голосов
/ 25 декабря 2009

Просто напишите функцию для этого. Что-то вроде:

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() и так далее.

13 голосов
/ 25 декабря 2009

Я считаю, что один из лучших способов справиться с этой проблемой - получить доступ к значениям массивов GET и POST (COOKIE, SESSION и т. Д.) Через класс.

Создайте класс для каждого из этих массивов и объявите методы __get и __set ( перегрузка ). __get принимает один аргумент, который будет именем значения. Этот метод должен проверить это значение в соответствующем глобальном массиве, используя isset() или empty() и вернуть значение, если оно существует, или null (или другое значение по умолчанию) в противном случае.

После этого вы можете уверенно обращаться к значениям массива следующим образом: $POST->username и выполнять любую проверку, если это необходимо, без использования isset() с или empty() с. Если username не существует в соответствующем глобальном массиве, то будет возвращено null, поэтому предупреждения или уведомления не будут генерироваться.

6 голосов
/ 25 декабря 2009

Я не против использования array_key_exists(), на самом деле я предпочитаю использовать эту конкретную функцию , а не полагаться на hack функции, которые могут изменить их поведение в будущем , как empty и isset (поражение, чтобы избежать восприимчивости ).


Однако я использую простую функцию, которая пригодится в этом и в некоторых других ситуациях при работе с индексами массива :

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 = 'https://stackoverflow.com/questions/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 = 'https://stackoverflow.com/questions/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')));

Аккуратно, а? ;)

3 голосов
/ 17 июля 2011

Я использую эти функции

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")) // really =, not ==
if($pass=POST("pass"))
if($login=="Admin" && $pass==...) {
  // login and pass are not empty, login is "Admin" and pass is ...
  $authorized = true;
  ...
}
3 голосов
/ 25 декабря 2009

Я здесь с тобой. Но PHP дизайнеры допустили гораздо больше ошибок, чем это. Если не считать пользовательскую функцию для любого чтения значения, то нет никакого пути к этому.

2 голосов
/ 05 января 2018

Добро пожаловать в оператор объединения нулей (PHP> = 7.0.1):

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

PHP говорит:

Нулевой оператор объединения (??) был добавлен в качестве синтаксического сахара для общего случая необходимости использования троичного в сочетании с isset (). Возвращает свой первый операнд, если он существует и не равен NULL; в противном случае возвращается второй операнд.

1 голос
/ 07 сентября 2014

Создает функцию, которая возвращает 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 голосов
/ 14 августа 2012

А как насчет использования оператора @? e.g.:

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

Вы можете сказать, что это плохо, потому что у вас нет контроля над тем, что происходит "внутри" $ foo (если это был вызов функции, например, с ошибкой PHP), но если вы используете этот метод только для переменных, это эквивалентно чтобы:

if(isset($foo) && $foo) { /* ... */ }
0 голосов
/ 25 декабря 2009

программное обеспечение магическим образом не работает по милости Бога, если вы ожидаете чего-то, чего не хватает, вам необходимо правильно с этим справиться. если вы игнорируете это, вы, вероятно, создаете дыры в безопасности в своих приложениях. на статических языках доступ к неопределенной переменной просто невозможен, он не просто скомпилирует или завершит работу вашего приложения, если оно пустое. кроме того, делает ваше приложение неприемлемым, и вы будете сходить с ума, когда происходят неожиданные вещи. Строгость языка обязательна, а php, по замыслу, неверен во многих аспектах. это сделает вас плохим программистом, если вы не будете знать.

...