E_NOTICE?== E_DEBUG, избегая isset() и @ с более изощренным обработчиком ошибок
Какие лучшие способы существуют, чтобы избежать изобилия isset()
в логике приложения, и сохранить способность видеть сообщения отладки (E_NOTICE) при необходимости?
Сначала презумпция: E_NOTICE - это не ошибка, это неправильное выражение, и на самом деле оно должно быть E_DEBUG. Однако, хотя это верно для неустановленных переменных (PHP по-прежнему является языком сценариев), некоторые функции файловой системы и т. Д. Тоже их выдают. Следовательно, желательно развивать с E_NOTICEs.
Тем не менее, не все уведомления об отладке полезны, вот почему это распространенная (к сожалению) идиома PHP isset()
и @ во всей логике приложения. Конечно, существует множество допустимых вариантов использования для isset / empty, но в целом это кажется синтаксической солью и может фактически препятствовать отладке.
Вот почему я сейчас использую букмарклет error_reporting и тупой переключатель вкл / выкл:
// javascript:(function(){document.cookie=(document.cookie.match(/error_reporting=1/)?'error_reporting=0':'error_reporting=1')})()
if (($_SERVER["REMOTE_ADDR"] == "127.0.0.1")
and $_COOKIE["error_reporting"])
{
error_reporting(E_ALL|E_STRICT);
}
else {/* less */}
Однако это все еще оставляет мне проблему с наличием слишком большого количества уведомлений для поиска после включения. В качестве обходного пути я мог бы использовать оператор подавления ошибок @. В отличие от isset() он не полностью уничтожает параметры отладки, потому что пользовательский обработчик ошибок все еще может получать подавленные E_NOTICEs. Таким образом, это может помочь отделить ожидаемые уведомления об отладке от потенциальных проблем.
Но это также неудовлетворительно. Отсюда и вопрос. Кто-нибудь использует или знает более сложный обработчик ошибок PHP. Я представляю себе что-то, что:
- выводит нефильтрованные ошибки / предупреждения / уведомления (с абсолютным позиционированием CSS?)
- и AJAX-этажерка для проверки и подавления на стороне клиента
- но также сохраняет список фильтрации ожидаемых и " одобренных " уведомлений или предупреждений.
Конечно, некоторые фреймворки уже должны иметь такой обработчик ошибок пользователя.
- В основном я заинтересован в управлении предупреждениями / уведомлениями.
- Полное подавление E_NOTICE действительно нежелательно.
- E_NOTICES нужны. Просто их меньше. По умолчанию выделите те, которые могут меня волновать, а не ожидаемые.
- Если я запускаю без параметра? Order =, происходит ожидаемое УВЕДОМЛЕНИЕ. О котором, как и следовало ожидать, мне не нужно сообщать более одного раза.
- Однако, когда я нахожусь в режиме полной отладки, я хочу видеть наличие неопределенных переменных через наличие (или, что более интересно, отсутствие) упомянутых уведомлений об отладке. -> Это то, что я думаю, что они для. Отказ от isset приносит неявные языковые операторы печати.
- Также следует понимать, что речь идет о случаях использования, в которых подходит обычная семантика обработки форм PHP, а не об областях приложения, где строгость обязательна.
О, кто-то, пожалуйста, помогите переписать это. Длительное объяснение не удаётся.
9 ответов
Что ж, если вы подождете PHP 7, у вас будет доступ к тернарному оператору null coalesce, который, в дополнение к самому крутому из существующих имен операторов (я называю моего следующего ребенка "Null Coalesce"), позволит вам сделать этот:
$var = $some_array[$some_value] ?? "default value";
Который заменяет вездесущий (и некрасивый)
$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
Можно разработать большое PHP-приложение, которое никогда не испускает E_NOTICE. Все, что вам нужно сделать, - это избегать всех ситуаций, когда уведомление может быть отправлено, подавляющее большинство из которых являются неинициализированными переменными и несуществующими ключами массива. К сожалению, это противоречит вашему желанию избежать isset()
- и, соответственно, array_key_exists()
- потому что они предназначены для решения именно этой проблемы.
В лучшем случае вы можете минимизировать их использование путем тщательного построения фреймворка. Это обычно означает (например) входной слой, который говорит, что GET
ожидаемые переменные и то, что по умолчанию недостающие. Таким образом, код для конкретной страницы всегда будет иметь значения для просмотра. В целом это полезный метод, который можно применять к различным API. Но я сомневаюсь, что это должно быть первоочередной целью дизайна.
В отличие от некоторых других языков, PHP различает переменную, которая не существует и обычно содержит "пустое" значение (обычно null
). Это, вероятно, артефакт дизайна из более ранней версии, но тем не менее он все еще присутствует, поэтому вы не можете избежать его.
Я использую isset()
только для $_GET
а также $_SERVER
переменные, откуда данные поступают из- за контроля моего приложения. И я использую его в другой ситуации, когда у меня нет времени, чтобы написать правильное решение ООП, чтобы избежать этого, но я уверен, что этого можно избежать в большинстве, если не во всех местах. Например, лучше использовать классы вместо ассоциативных массивов, так что вам не нужно проверять наличие ключа массива.
Мои советы:
- Избегайте использования
@
оператор. - Используйте Xdebug. Во-первых, он печатает легко читаемые и легко заметные сообщения о каждом уведомлении / предупреждении, а также печатает очень полезную трассировку стека для исключений (вы можете настроить его для печати каждого параметра метода и каждой локальной переменной (
xdebug.collect_params=4
а такжеxdebug.show_local_vars=on
параметры конфигурации). Во-вторых, он может отключить@
оператор сxdebug.scream=1
значение конфигурации Вы можете использовать Xdebug для профилирования, а также для анализа покрытия кода. Это должно быть на вашей машине разработки. - Для отладки я также использую FirePHP, потому что он работает с Firebug и может печатать сообщения на консоли Firebug, поэтому его можно использовать и для отладки AJAX.
- С помощью специального обработчика ошибок вы можете отлавливать и фильтровать любые ошибки и предупреждения, а также регистрировать их в файл или отображать их с помощью FirePHP, или вы можете использовать, например, jGrowl или Gritter, чтобы красиво отображать их на веб-странице.
Я использую модифицированную версию примера в руководстве по PHP:
<?php
//error_reporting(0);
set_error_handler("errorHandler");
function errorHandler($errno, $errstr, $errfile, $errline)
{
echo "errorHandler()<br />\n";
// filter out getImageSize() function with non existent files (because I'am avoiding using file_exists(), which is a costly operation)
if ( mb_stripos($errstr, 'getimagesize') !== false )
return true;
// filter out filesize() function with non existent files
if ( mb_stripos($errstr, 'filesize') !== false )
return true;
// consoleWriter is my class which sends the messages with FirePHP
if (class_exists('consoleWriter'))
consoleWriter::debug(array('errno'=>$errno, 'errstr'=>$errstr, 'errfile'=>$errfile, 'errline'=>$errline, 'trace'=>debug_backtrace()), "errorHandler");
switch ($errno) {
case E_USER_ERROR:
$out .= "<b>FATAL_ERROR</b> <i>$errno</i> $errstr<br />\n";
$out .= "Fatal error on line $errline in file $errfile";
echo "</script>$out"; // if we were in a script tag, then the print is not visible without this
//writeErrorLog($out);
echo "<pre>";
var_export(debug_backtrace());
echo "</pre>";
exit(1);
break;
case E_USER_WARNING:
$out .= "<b>WARNING</b> <i>$errno</i> $errstr<br />\n";
$out .= "On line $errline in file $errfile<br />\n";
break;
case E_USER_NOTICE:
$out .= "<b>NOTICE</b> <i>$errno</i> $errstr<br />\n";
$out .= "On line $errline in file $errfile<br />\n";
break;
default:
$out .= "<b>Unknown</b> <i>$errno</i> $errstr<br />\n";
$out .= "On line $errline in file $errfile<br />\n";
break;
}
if (!class_exists('consoleWriter'))
echo $out;
//writeErrorLog($out);
//addJGrowlMessage($out);
// Don't execute PHP internal error handler
return true;
}
function testNotice($a)
{
echo $a;
}
testNotice();
Еще один совет не использовать закрытие ?>
тег в конце файлов только для php, потому что это может привести к headers already sent
ошибки в конфигурациях, где выходная буферизация по умолчанию отключена.
Пытаться xdebug
- http://www.xdebug.org/docs/stack_trace
много isset
проверка не навредит тебе,
на самом деле, это поощряет объявлять переменные перед использованием
Я думаю, что следование лучшим практикам не является пустой тратой времени. Это правда, уведомление не является ошибкой, но при правильном объявлении и проверке переменных ваш код может быть более читабельным и безопасным. Но не так сложно написать пользовательский обработчик ошибок с помощью debug_backtrace, отсортировав E_NOTICE(8) с помощью регулярного выражения.
PHP определенно нарушает этот код, делая его менее читабельным. "ноль" означает "неопределенный" - достаточно просто.
Вот что я делаю, когда сталкиваюсь с этой проблемой, делая код нечитаемым:
/**
* Safely index a possibly incomplete array without a php "undefined index" warning.
* @param <type> $array
* @param <type> $index
* @return <type> null, or the value in the index (possibly null)
*/
function safeindex($array, $index) {
if (!is_array($array)) return null;
return (isset($array[$index])) ? $array[$index] : null;
}
// this might generate a warning
$configMenus = $config['menus'];
// WTF are you talking about!! 16 punctuation marks!!!
$configMenus = (isset($config['menus']) ? $config['menus'] : null;
// First-time awkward, but readible
$configMenus = safeindex($config, 'menus');
Перекрестная публикация этого ответа здесь. Это помогает спам-чекер?
У меня было похожее желание. Поэтому я начал использовать собственные обработчики ошибок.
http://php.net/manual/en/function.set-error-handler.php
Затем вы можете создавать свои собственные фильтры / механизмы для отображения / регистрации ошибок / уведомлений.
Ура!
Лучший способ избежать isset()
на мой взгляд, это определить ваши переменные, прежде чем использовать их. мне не нравится isset()
не столько потому, что это некрасиво, а потому, что способствует плохой практике программирования.
Что касается самой обработки ошибок, я помещаю всю эту информацию в журналы сервера. Я также использую php -l
в командной строке для синтаксиса проверьте программы перед рукой. Я делаю красивые сообщения для пользователей по умолчанию.
Вы можете заглянуть в одну из различных платформ, чтобы увидеть, подойдет ли какая-либо из них для вас. Большинство из них, на которые я смотрел, имеют процедуры обработки ошибок, которые упрощают работу, чем то, что предлагает PHP из коробки.
РЕДАКТИРОВАТЬ:@mario - мой ответ на ваш комментарий становился слишком длинным:-). Я не сторонник определения типов или использования какого-либо строгого формата, такого как Java или C. Я просто выступаю за объявление переменной в контексте, который она использует. ($foo = null;
не то же самое, что оставить переменную пустой).
Я думаю, что во многих случаях это скорее проблема с глобальными переменными, особенно суперглобальные для получения данных GET и POST. Мне бы очень хотелось, чтобы PHP отбросил суперглобальные перемены в пользу класса для получения входных данных. Как-то так (супер просто, но эй, ты хотел чего-то конкретного::))
<?php
class PostData {
private $data;
public function __construct() {
$this->data = $_POST;
unset($_POST);
}
public function param($name, $value = null) {
if( $value !== null ) {
$this->data[$name] = $value;
}
if( isset( $this->data[$name] ) ) {
return $this->data[$name];
}
return null;
}
}
?>
Включите класс, тогда вы можете получить и установить данные POST из param()
метод. Это также был бы хороший способ включить проверку во входные данные. И в качестве бонуса, не проверять все для isset()
(это уже есть).
Это своего рода устаревший ответ сейчас, но я изначально использовал гибкий диспетчер журналов, https://github.com/grosser/errorhandler (Не совсем то, что я искал IIRC, но, по крайней мере, немного сложнее, чем чередование между полное и частичное подавление.)
Во всяком случае, я тем временем использую $_GET->int["input"]
Обертка для самых распространенных случаев. Это просто тривиальная оболочка ArrayAccess, которая неявно перехватывает несуществующие переменные, что позволяет легче восстанавливать уведомления. (Просто побочный продукт. Прежде всего, предназначен для немедленной фильтрации.)
А для другого проекта я даже использую макрос препроцессора IFSET@($var)
, чтобы разрешить включение / отключение или перенаправление журнала в зависимости от параметров сборки.