Отключить предупреждения при загрузке некорректного HTML с помощью DomDocument (PHP)

79

Мне нужно проанализировать некоторые файлы HTML, однако они не имеют правильного формата, и PHP выводит предупреждения для них. Я хочу программно избежать такого поведения отладки / предупреждения. Пожалуйста, порекомендуйте. Спасибо!

Код:

// create a DOM document and load the HTML data
$xmlDoc = new DomDocument;
// this dumps out the warnings
$xmlDoc->loadHTML($fetchResult);

Этот:

@$xmlDoc->loadHTML($fetchResult)

можно подавить предупреждения, но как я могу записать эти предупреждения программно?

Вьетнам
источник
Попробуйте это решение - кажется, намного проще - stackoverflow.com/questions/6090667/…
Marcin
Преобразование паршивого ввода в надлежащий вывод - вот что оплачивает счета;) Вариант восстановления указан в руководстве . это просто логическое значение. Вы можете просто позвонить, $dom->saveHTML()чтобы посмотреть, какой тип документа libxml пытается сделать из вашего $htmlввода, обычно это довольно близко / хорошо.
Wrikken 09

Ответы:

13

Вы можете установить временный обработчик ошибок с помощью set_error_handler

class ErrorTrap {
  protected $callback;
  protected $errors = array();
  function __construct($callback) {
    $this->callback = $callback;
  }
  function call() {
    $result = null;
    set_error_handler(array($this, 'onError'));
    try {
      $result = call_user_func_array($this->callback, func_get_args());
    } catch (Exception $ex) {
      restore_error_handler();        
      throw $ex;
    }
    restore_error_handler();
    return $result;
  }
  function onError($errno, $errstr, $errfile, $errline) {
    $this->errors[] = array($errno, $errstr, $errfile, $errline);
  }
  function ok() {
    return count($this->errors) === 0;
  }
  function errors() {
    return $this->errors;
  }
}

Применение:

// create a DOM document and load the HTML data
$xmlDoc = new DomDocument();
$caller = new ErrorTrap(array($xmlDoc, 'loadHTML'));
// this doesn't dump out any warnings
$caller->call($fetchResult);
if (!$caller->ok()) {
  var_dump($caller->errors());
}
Troelskn
источник
10
Похоже, что для этой ситуации много лишнего. Обратите внимание на функции PHP libxml2.
thomasrutter
Хорошее замечание, Томас. Я не знал об этих функциях, когда писал этот ответ. Если я не ошибаюсь, внутри он делает то же самое.
troelskn
1
В этом случае он имеет тот же эффект, да, хотя это делается на другом уровне: с помощью вышеупомянутого решения ошибки PHP генерируются, но подавляются, но в моем случае они не становятся ошибками PHP. Я лично считаю, что если что-то связано с подавлением ошибок PHP с помощью @ или set_error_handler (), то это неправильный способ. Но это только мое мнение. Обратите внимание, что ошибки и исключения PHP - это совсем другое дело - можно использовать try {} catch () {}.
thomasrutter
2
Я думаю, что видел несколько отчетов об ошибках, которые предполагают, что libxml_use_internal_errorsподключается к обработчику ошибок php.
troelskn
Я надеюсь, что люди прокрутят этот ответ до лучших ответов ниже.
thomasrutter
222

Вызов

libxml_use_internal_errors(true);

до обработки с $xmlDoc->loadHTML()

Это указывает libxml2 не отправлять ошибки и предупреждения в PHP. Затем, чтобы проверить ошибки и обработать их самостоятельно, вы можете обратиться к libxml_get_last_error () и / или libxml_get_errors (), когда будете готовы.

Thomasrutter
источник
1
Намного проще, чем добавить 20 строк кода, как это делает принятый ответ. Благодаря!
Брайан Клуг
94

Чтобы скрыть предупреждения, вы должны дать специальные инструкции, libxmlкоторые используются внутри для выполнения синтаксического анализа:

libxml_use_internal_errors(true);
$dom->loadHTML($html);
libxml_clear_errors();

Значок libxml_use_internal_errors(true)указывает, что вы собираетесь самостоятельно обрабатывать ошибки и предупреждения и не хотите, чтобы они испортили вывод вашего скрипта.

Это не то же самое, что @оператор. Предупреждения собираются за кулисами, и впоследствии вы можете получить их, используя, libxml_get_errors()если вы хотите выполнить регистрацию или вернуть список проблем вызывающему.

Независимо от того, используете ли вы собранные предупреждения, вы всегда должны очищать очередь, позвонив libxml_clear_errors().

Сохранение государства

Если у вас есть другой код, который использует, libxmlвозможно, стоит убедиться, что ваш код не изменяет глобальное состояние обработки ошибок; для этого вы можете использовать возвращаемое значение, libxml_use_internal_errors()чтобы сохранить предыдущее состояние.

// modify state
$libxml_previous_state = libxml_use_internal_errors(true);
// parse
$dom->loadHTML($html);
// handle errors
libxml_clear_errors();
// restore
libxml_use_internal_errors($libxml_previous_state);
Джек
источник
2
@Greeso: установлено предыдущее значение. Это сделано с учетом того, что он мог быть настроен для какого-то другого кода, глобально отличного от того, FALSEи установка его FALSEвпоследствии разрушила бы этот параметр. Использование предыдущего возвращаемого значения $libxml_previous_stateпредотвращает эти потенциальные побочные эффекты, поскольку исходная конфигурация была восстановлена ​​независимо от потребностей этого места. libxml_use_internal_errors()Установка является глобальной, так что стоит взять какую - то помощь.
hakre
Если уже есть ожидающие ошибки libxml, разве это их не съест?
cHao
@cHao разве не разумно предположить, что вы начинаете с чистого листа? :)
Ja͢ck
@ Як: Нет. Если что-то ранее вызывалось libxml_use_internal_errors(true), то оно может ожидать обработки возникших ошибок.
cHao
23

Установка параметров "LIBXML_NOWARNING" и "LIBXML_NOERROR" тоже отлично работает:

$dom->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR);
Джошуа Отт
источник