PHP DOM UTF-8 проблема - PullRequest
       29

PHP DOM UTF-8 проблема

2 голосов
/ 23 августа 2010

Прежде всего, моя база данных использует Windows-1250 в качестве нативной кодировки. Я вывожу данные как UTF-8. Я использую функцию iconv () по всему сайту для преобразования строк Windows-1250 в строки UTF-8, и она отлично работает.

Проблема в том, что я использую PHP DOM для анализа некоторого HTML-кода, хранящегося в базе данных (HTML является выходным сигналом редактора WYSIWYG и недопустим, он не имеет HTML, заголовок, теги тела и т. Д.).

HTML может выглядеть примерно так, например:

<p>Hello</p>

Вот метод, который я использую для анализа определенного HTML из базы данных:

 private function ParseSlideContent($slideContent)
 {
        var_dump(iconv('Windows-1250', 'UTF-8', $slideContent)); // this outputs the HTML ok with all special characters

  $doc = new DOMDocument('1.0', 'UTF-8');

  // hack to preserve UTF-8 characters
  $html = iconv('Windows-1250', 'UTF-8', $slideContent);
  $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
  $doc->preserveWhiteSpace = false;

  foreach($doc->getElementsByTagName('img') as $t) {
   $path = trim($t->getAttribute('src'));
   $t->setAttribute('src', '/clientarea/utils/locate-image?path=' . urlencode($path));
  }
  foreach ($doc->getElementsByTagName('object') as $o) {
   foreach ($o->getElementsByTagName('param') as $p) {
    $path = trim($p->getAttribute('value'));
    $p->setAttribute('value', '/clientarea/utils/locate-flash?path=' . urlencode($path));
   }
  }
  foreach ($doc->getElementsByTagName('embed') as $e) {
   if (true === $e->hasAttribute('pluginspage')) {
    $path = trim($e->getAttribute('src'));
    $e->setAttribute('src', '/clientarea/utils/locate-flash?path=' . urlencode($path));
   } else {
    $path = end(explode('data/media/video/', trim($e->getAttribute('src'))));
    $path = 'data/media/video/' . $path;
    $path = '/clientarea/utils/locate-video?path=' . urlencode($path);
    $width = $e->getAttribute('width') . 'px';
    $height = $e->getAttribute('height') . 'px';
    $a = $doc->createElement('a', '');
    $a->setAttribute('href', $path);
    $a->setAttribute('style', "display:block;width:$width;height:$height;");
    $a->setAttribute('class', 'player');
    $e->parentNode->replaceChild($a, $e);
    $this->slideContainsVideo = true;
   }
  }

  $html = trim($doc->saveHTML());

  $html = explode('<body>', $html);
  $html = explode('</body>', $html[1]);
  return $html[0];
 }

Выходные данные из вышеприведенного метода - это мусор, в котором все специальные символы заменены на странные вещи вроде ÃšÄ *.

Еще одна вещь. Это действительно работает на моем сервере разработки.

Хотя это не работает на рабочем сервере.

Есть предложения?

PHP-версия производственного сервера: PHP версия 5.2.0RC4-dev

PHP версия сервера разработки: PHP версия 5.2.13


UPDATE:

Я сам работаю над решением проблемы. У меня есть вдохновение из этого отчета об ошибках PHP (хотя на самом деле это не ошибка): http://bugs.php.net/bug.php?id=32547

Это мое предложенное решение. Завтра попробую и сообщу, работает ли он:

 private function ParseSlideContent($slideContent)
 {
        var_dump(iconv('Windows-1250', 'UTF-8', $slideContent)); // this outputs the HTML ok with all special characters

  $doc = new DOMDocument('1.0', 'UTF-8');

  // hack to preserve UTF-8 characters
  $html = iconv('Windows-1250', 'UTF-8', $slideContent);
  $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
  $doc->preserveWhiteSpace = false;

  // this might work
  // it basically just adds head and meta tags to the document
  $html = $doc->getElementsByTagName('html')->item(0);
  $head = $doc->createElement('head', '');
  $meta = $doc->createElement('meta', '');
  $meta->setAttribute('http-equiv', 'Content-Type');
  $meta->setAttribute('content', 'text/html; charset=utf-8');
  $head->appendChild($meta);
  $body = $doc->getElementsByTagName('body')->item(0);
  $html->removeChild($body);
  $html->appendChild($head);
  $html->appendChild($body);

  foreach($doc->getElementsByTagName('img') as $t) {
   $path = trim($t->getAttribute('src'));
   $t->setAttribute('src', '/clientarea/utils/locate-image?path=' . urlencode($path));
  }
  foreach ($doc->getElementsByTagName('object') as $o) {
   foreach ($o->getElementsByTagName('param') as $p) {
    $path = trim($p->getAttribute('value'));
    $p->setAttribute('value', '/clientarea/utils/locate-flash?path=' . urlencode($path));
   }
  }
  foreach ($doc->getElementsByTagName('embed') as $e) {
   if (true === $e->hasAttribute('pluginspage')) {
    $path = trim($e->getAttribute('src'));
    $e->setAttribute('src', '/clientarea/utils/locate-flash?path=' . urlencode($path));
   } else {
    $path = end(explode('data/media/video/', trim($e->getAttribute('src'))));
    $path = 'data/media/video/' . $path;
    $path = '/clientarea/utils/locate-video?path=' . urlencode($path);
    $width = $e->getAttribute('width') . 'px';
    $height = $e->getAttribute('height') . 'px';
    $a = $doc->createElement('a', '');
    $a->setAttribute('href', $path);
    $a->setAttribute('style', "display:block;width:$width;height:$height;");
    $a->setAttribute('class', 'player');
    $e->parentNode->replaceChild($a, $e);
    $this->slideContainsVideo = true;
   }
  }

  $html = trim($doc->saveHTML());

  $html = explode('<body>', $html);
  $html = explode('</body>', $html[1]);
  return $html[0];
 }

Ответы [ 3 ]

5 голосов
/ 23 августа 2010

Ваш "хак" не имеет смысла.

Вы конвертируете HTML-файл Windows-1250 в UTF-8, а затем добавляете <?xml encoding="UTF-8">.Это не сработает.Расширение DOM для файлов HTML:

  • Принимает кодировку, указанную в мета-http-эквиваленте для "content-type".
  • В противном случае предполагается, что ISO-8859-1

Я предлагаю вам вместо этого преобразовать Windows-1250 в ISO-8859-1 и ничего не добавлять.

EDIT Предложение не очень хорошее, поскольку в Windows-1250 есть символыкоторые не в ISO-8859-1.Поскольку вы имеете дело с фрагментами без meta элементов для типа контента, вы можете добавить свой собственный для принудительной интерпретации как UTF-8:

<?php
//script and output are in UTF-8

/* Simulate HTML fragment in Windows-1250 */
$html = <<<XML
<p>ĄĽź ‰ ‡ … á (some exist on win-1250, but not LATIN1 or even win-1252)</p>
XML;
$htmlInterm = iconv("UTF-8", "Windows-1250", $html); //convert

/* Append meta header to force UTF-8 interpretation and convert into UTF-8 */
$htmlInterm =
    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" .
    iconv("Windows-1250", "UTF-8", $htmlInterm);

/* Omit libxml warnings */
libxml_use_internal_errors(true);

/* Build DOM */
$d = new domdocument;
$d->loadHTML($htmlInterm);
var_dump($d->getElementsByTagName("body")->item(0)->textContent); //correct UTF-8

дает:

string(79) "ĄĽź ‰ ‡ … á (some exist on win-1250, but not LATIN1 or even win-1252)"
1 голос
/ 23 августа 2010

Два решения.

Вы можете установить кодировку в качестве заголовка:

<?php header("Content-Type", "text/html; charset=utf-8"); ?>

Или установить метатег:

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

РЕДАКТИРОВАТЬ: в случае, еслиоба они установлены правильно, выполните следующие действия:

  • Создайте небольшую страницу с символом UTF-8.
  • Напишите страницу втот же метод, который у вас уже есть.
  • Используйте Fiddler или Wireshark для проверки необработанных байтов, передаваемых в ваших средах DEV и PROD.Вы также можете дважды проверить заголовки, используя Fiddler / Wireshark.

Если вы уверены, что отправляется правильный заголовок, то ваш лучший шанс найти ошибку - начать смотреть на необработанные байты.Одинаковые байты, отправленные в один и тот же браузер, дадут тот же результат, поэтому вам нужно начать искать, почему они не идентичны.Скрипач / Wireshark поможет с этим.

0 голосов
/ 19 августа 2013

У меня была такая же проблема. Мое исправление заключалось в использовании notepad ++ и установке кодировки php-документа на «UTF-8 без спецификации». Надеюсь, что это поможет любому из других.

...