E_NOTICE? == E_DEBUG, избегая isset () и @ с более изощренным обработчиком ошибок - PullRequest
11 голосов
/ 11 ноября 2010

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

Сначала презумпция: E_NOTICE - это не ошибка, это неправильное число, и на самом деле оно должно быть E_DEBUG. Однако, хотя это верно для неустановленных переменных (PHP по-прежнему является языком сценариев), некоторые функции файловой системы и т. Д. Тоже их выбрасывают. Следовательно, желательно разработать с включенными E_NOTICE.

Тем не менее, не все уведомления об отладке полезны, вот почему это распространенная (к сожалению) идиома 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 ]

9 голосов
/ 15 ноября 2010

Можно разработать большое PHP-приложение, которое никогда не испускает E_NOTICE. Все, что вам нужно сделать, - это избегать всех ситуаций, когда уведомление может быть отправлено, подавляющее большинство из которых - неинициализированные переменные и несуществующие ключи массива. К сожалению, это противоречит вашему желанию избегать isset() - и, соответственно, array_key_exists() - потому что они предназначены для решения именно этой проблемы.

В лучшем случае вы можете минимизировать их использование путем тщательного построения фреймворка. Как правило, это означает (например) входной слой, в котором сообщается, какие GET переменные следует ожидать и какие по умолчанию отсутствуют. Таким образом, код для конкретной страницы всегда будет иметь значения для просмотра. В целом это полезный метод, который можно применять к различным API. Но я сомневаюсь, что это должно быть первоочередной целью проектирования.

В отличие от некоторых других языков, PHP различает переменную, которая не существует и обычно содержит «пустое» значение (обычно null). Вероятно, это артефакт дизайна из более ранней версии, но тем не менее он все еще присутствует, поэтому вы не можете его избежать.

6 голосов
/ 18 ноября 2010

Я использую isset() только для $_GET и $_SERVER переменных, где данные поступают из вне контроля моего приложения. И я использую его в какой-то другой ситуации, когда у меня нет времени написать правильное решение ООП, чтобы избежать этого, но я уверен, что этого можно избежать в большинстве, если не во всех местах. Например, лучше использовать классы вместо ассоциативных массивов, так что вам не нужно проверять наличие ключа массива.

Мои советы:

  • Избегайте использования оператора @ .
  • Использование Xdebug . Во-первых, он печатает легко читаемые и легко заметные сообщения о каждом уведомлении / предупреждении, а также печатает очень полезную трассировку стека для исключений (вы можете настроить его для печати каждого параметра метода и каждой локальной переменной (* 1024) * и xdebug.show_local_vars=on параметры конфигурации.) Во-вторых, он может отключить оператор @ с помощью значения конфигурации xdebug.scream=1. Вы можете использовать Xdebug для профилирования , а также для анализа покрытия кода. Это на вашем компьютере разработчика должно быть .
  • Для отладки я также использую FirePHP , потому что он работает с Firebug и способен печатать сообщения на Консоль Firebug , поэтому ее можно использовать и для AJAX-отладки .
  • С помощью пользовательского обработчика ошибок вы можете перехватывать и фильтровать любые ошибки и предупреждения, а также регистрировать их в файл или отображать их с помощью FirePHP, или вы можете использовать, например, jGrowl или Gritter , чтобы красиво отображать их на веб-странице.

Я использую модифицированную версию примера в руководстве по PHP :

<code><?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 "
"; выход (1); перерыв; case E_USER_WARNING: $ out. = " ПРЕДУПРЕЖДЕНИЕ $ errno $ errstr
\ n"; $ out. = "В строке $ errline в файле $ errfile
\ n"; перерыв; case E_USER_NOTICE: $ out. = " УВЕДОМЛЕНИЕ $ errno $ errstr
\ n"; $ out. = "В строке $ errline в файле $ errfile
\ n"; перерыв; дефолт: $ out. = " Неизвестно $ errno $ errstr
\ n"; $ out. = "В строке $ errline в файле $ errfile
\ n"; перерыв; } if (! class_exists ('consoleWriter')) эхо $ out; // writeErrorLog ($ из); // addJGrowlMessage ($ из); // Не выполнять PHP-обработчик внутренних ошибок вернуть истину; } функция testNotice ($ a) { эхо $ а; } testNotice ();

Еще один совет - не использовать закрывающий тег ?> в конце файлов только для php, потому что это может вызвать headers already sent ошибок в конфигурациях, где выходная буферизация по умолчанию отключена .

3 голосов
/ 18 декабря 2014

Что ж, если вы подождете PHP 7, у вас будет доступ к null coalesce ternary оператору, который, в дополнение к самому крутому имени оператора из существующих (я называю своего следующего ребенка) «Null Coalesce») позволит вам сделать это:

$var = $some_array[$some_value] ?? "default value";

Что заменяет вездесущий (и некрасивый)

$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
2 голосов
/ 06 апреля 2011

PHP определенно нарушает этот код, делая его менее читабельным.«null» означает «undefined» - достаточно просто.

Вот что я делаю, когда сталкиваюсь с этой проблемой, делая код нечитаемым:

/**
 * 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');

Перекрестная публикация этого ответа здесь.Помогает ли это проверка спама?

2 голосов
/ 16 ноября 2010

У меня было похожее желание.Поэтому я начал использовать собственные обработчики ошибок.

http://php.net/manual/en/function.set-error-handler.php

Затем вы можете создавать свои собственные фильтры / механизмы для отображения / регистрации ошибок / уведомлений.

Cheers!

2 голосов
/ 15 ноября 2010

Я думаю, что следование лучшим практикам не является пустой тратой времени. Это правда, уведомление не является ошибкой, но при правильном объявлении и проверке переменных ваш код может быть более читабельным и безопасным. Но не так сложно написать определяемый пользователем обработчик ошибок с debug_backtrace sort E_NOTICE (8) с регулярным выражением.

2 голосов
/ 11 ноября 2010

try xdebug - http://www.xdebug.org/docs/stack_trace

много isset проверок не наносит вам вреда,

на самом деле, это поощряет объявление переменных перед использованием

1 голос
/ 22 ноября 2010

Это своего рода устаревший ответ сейчас, но я тогда использовал гибкий диспетчер журналов, https://github.com/grosser/errorhandler (Не совсем то, что я искал IIRC, но, по крайней мере, немного сложнее, чем чередование между полным ичастичное подавление.)

В любом случае, я использую $_GET->int["input"] упаковщик для наиболее распространенных случаев.Это просто тривиальная оболочка ArrayAccess, которая неявно перехватывает несуществующие переменные, что позволяет легче восстанавливать уведомления.(Просто побочный продукт. Первоначально предназначен для немедленной фильтрации.)

А для другого проекта я даже использую макрос препроцессора IFSET@($var), чтобы разрешить включение / отключение или перенаправление журнала в зависимости от параметров сборки.

1 голос
/ 11 ноября 2010

На мой взгляд, лучший способ избежать 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() (это уже есть).

...