У меня есть несколько старых приложений, которые бросают много сообщений «xyz undefined» и «undefined offset» при запуске на уровне ошибки E_NOTICE, поскольку существование переменных явно не проверяется с помощью isset()
и сущностей.
Я рассматриваю возможность работы с ними, чтобы сделать их совместимыми с E_NOTICE, поскольку уведомления о недостающих переменных или смещениях могут быть lifesavers, могут быть некоторые незначительные улучшения производительности, и это в целом более чистый способ.
Тем не менее, мне не нравится то, что приводит к сотням isset()
empty()
и array_key_exists()
s для моего кода. Он становится раздутым, становится менее читаемым, не приобретая ничего с точки зрения ценности или смысла.
Как я могу структурировать свой код без избыточного контроля переменных, а также быть совместимым с E_NOTICE?
Для тех, кого это интересует, я расширил эту тему в небольшой статье, в которой приведена ниже информация в несколько лучше структурированной форме: окончательное руководство по PHP iset и пустое
ИМХО вам следует подумать не только о том, чтобы приложение «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, мне еще не найти любую библиотеку, которая это делает.
Просто напишите для этого функцию. Что-то вроде:
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()
и т. Д.
Я считаю, что одним из лучших способов справиться с этой проблемой является доступ к значениям массивов GET и POST (COOKIE, SESSION и т. Д.) Через класс.
Создайте класс для каждого из этих массивов и объявите методы __get
и __set
( перегрузка ). __get
принимает один аргумент, который будет именем значения. Этот метод должен проверять это значение в соответствующем глобальном массиве либо с использованием isset()
либо с помощью empty()
и возвращать значение, если оно существует, или null
(или другое значение по умолчанию) в противном случае.
После этого вы можете уверенно обращаться к значениям массива таким образом: $POST->username
и выполнять любую проверку, если это необходимо, без использования каких-либо isset()
s или empty()
s. Если username
не существует в соответствующем глобальном массиве, тогда возвращается null
, поэтому никаких предупреждений или уведомлений не будет создано.
Я не возражаю против использования array_key_exists()
, на самом деле я предпочитаю использовать эту конкретную функцию, а не полагаться на функции взлома, которые могут изменить их поведение в будущем, как (strikedthrough, чтобы избежать восприимчивости ). 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');
У нас уже есть uni и многомерные массивы, что еще мы можем сделать?
Возьмем следующий фрагмент кода, например:
$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')));
Я здесь с тобой. Но разработчики PHP сделали гораздо более худшие ошибки, чем это. За исключением определения пользовательской функции для любого значения чтения, нет никакого способа обойти это.
Я использую эти функции
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; ... }
Сделайте функцию, которая возвращает 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"]; } ?>
Я не уверен, что такое определение читаемости, но правильное использование пустых (), isset () и блоков try / throw / catch довольно важно для всего процесса. Если ваш E_NOTICE поступает из $ _GET или $ _POST, то они должны быть проверены на пустое () прямо вместе со всеми другими проверками безопасности, которые должны пройти эти данные. Если он поступает из внешних каналов или библиотек, он должен быть завернут в try / catch. Если он исходит из базы данных, необходимо проверить значение $ db_num_rows () или его эквивалент. Если это происходит из внутренних переменных, они должны быть правильно инициализированы. Часто эти типы уведомлений связаны с назначением новой переменной возврату функции, которая возвращает FALSE при сбое, которые должны быть завернуты в тесте, который в случае сбоя может либо присвоить переменной приемлемое значение по умолчанию что код может обрабатывать или бросать исключение, которое может обрабатывать код. Эти вещи делают код длиннее, добавляют дополнительные блоки и добавляют дополнительные тесты, но я не согласен с вами в том, что я думаю, что они определенно добавляют дополнительную ценность.
программное обеспечение не волшебным образом управляется благодатью Бога, если вы ожидаете чего-то, что вам не хватает, вам нужно правильно его обработать. если вы игнорируете это, вы, вероятно, создаете дыры в безопасности в своих приложениях. на статических языках доступ к неопределенной переменной просто невозможно, он не будет просто компилировать или разбивать ваше приложение, если оно равно null. кроме того, делает ваше приложение незаменимым, и вы сойдете с ума, когда произойдут неожиданные вещи. строгость языка является обязательным и php, по дизайну, является неправильным во многих аспектах. это сделает вас плохим программистом, если вы не знаете.
Как насчет использования оператора @? например:
if(@$foo) { /* do something */ }
Вы можете сказать, что это плохо, потому что у вас нет контроля над тем, что происходит «внутри» $ foo (если это вызов функции, который содержит ошибку PHP, например), но если вы используете этот метод только для переменных, это эквивалентно:
if(isset($foo) && $foo) { /* ... */ }