php DOM XSLT transform

tug

Новичок
php DOM XSLT transform

PHP 5.0.4
Делаю так:
Код:
<?
$content = new DomDocument('1.0', [b]'windows-1251'[/b]);
//либо так, причём в первом случай предпочтительней
//из-за возможности контролировать кодировку
$content = new DomDocument;
$content->load('/home/some_xml_template_with_header_and_encoding_windows1251').xml;

$content->formatOutput = true;

$page = $content->createElement('page');
$page = $content->appendChild($page);

//...  и т.д. создаю xml документ

$template = new DomDocument();
//правила преобразования подгружаются
$template->load('/home/some_xsl_template_with_header_and_encoding_windows1251.xsl');

$processor = new XsltProcessor();

$xsl = $processor->importStylesheet($xsl);

$result = $processor->transformToXML($xml);
if ($result) echo $result;

echo highlight_string($content->saveXML()); //Может кто заодно знает как xml покрасить?

?>
Проблема в том, что переодически возникает это дрянное предупреждение - Warning: output conversion failed due to conv error in ... и далее Bytes: 0xD1 0xEE 0xEE 0xE1 in ...

Чего хочу-то
Где найти нормальное объяснения того, как это всё работает(на php.net слишком скудно)? (ссылки, книги, FAQ, manuals) на буржуйском тоже сгодится.
как лучше поступать с кодировками при выполнении подобных операций?
Как избежать приведенной выше ошибки?
Как в итоге получить документ в нужной мне кодировке (windows-1251, koi8-r, utf-8), просто подставив в нужное место переменную и ничего не меняя в коде?

Может кто поделится своими наработками/хинтами в данной области...
я думаю многим будет интересно.

crossposted in http://raleigh.ru в форуме Программирование
 

slach

Новичок
суть ошибки - не может сконвертировать из внутреннего представления utf-8

когда ты делаешь
createElement
и
appentChild
ты должен заносить в них значения как ? обычные 8bit?
надо в utf8


лучше всего НЕ ПАРИТЬСЯ и _все_ держать в UTF8
в том числе и базу
 

tug

Новичок
Хорошо будет у меня всё в utf8.
А как дальше всё вывести в том, чём нужно?
 

DiTHER

bang bang
все держать в utf-8 это самое здравое решение.

база, внутренние переменные, подключаемые файлы - все в UTF-8.

при выводе результирующего дерева просто:

$finaldom->encoding = 'cp1251';
echo $finaldom->saveXml();

дом автоматически подставит кодировку cp1251 в декларацию xml результирующего дерева и переконвертит все содержимое соответственно. Естественно meta-теги для броузеров нужно рисовать в шаблонах - и всегда то что будет выводится (cp1251 в данном случае).

p.s. главное не наступи на грабли: если все в utf тогда строковые операции требуют внимания - php уникод не поддерживает на низком уровне. Соответственно strlen, например, будет возвращать число байт, но НЕ число символов. у pcre - флажок 'u'. Для строковых функций - mb_*
Либо несколько из iconv_*.

И ещё не забывай конвертить post-данные и прочую клиентскую шалупонь. Ибо accept-charset в формах не поддерживается (?) ie, а без него все броузеры считают что отправлять серверу надо в той кодировке в которой они от него получили. И тому подобное :)

-~{}~ 28.06.05 12:21:

небольшой туториал. как работает дом со всей своей аравой:

данные в dom представляются ТОЛЬКО в UTF-8. Т.е. например:

создается дом документ // utf-8
у него пишется кодировка // плевать на кодировку он все равно в utf-8
вывод // конвертится в кодировку указанную до этого, либо никуда не конвертится (utf-8 остаётся)

Следовательно ВСЕ что пишется в домдокумент должно быть заранее в utf-8. Проше всего все держать в utf-8 и кодировать только при выводе (см выше), иначе - кодировать перед каждым записыванием. (ругаться на однобайтовые символы - до 128 включительно он не будет (циферки и англ буквы). Но от греха подальше лучше конвертить все. Ибо ошибку конвертации на выводе у домдокумента не перехватить (почти, но об этом позже :))

Как дом читает файлы:
тут уже интереснее.. любой xml документ теоретически должен иметь декларацию вида <?xml encoding="cp1251" version="1.0" ?> Оба параметра необязательные (есть ещё standalone). По умолчанию версия - 1.0, кодировка - utf-8).

Когда дом читает xml документ ему важно чтобы кодировка в декларации совпадала с кодировкой самого xml. При чтении ($dom->load($xml), $dom->loadXml($xmlstr)) он посмотрит на кодировку. Переконвертит (попытаецца :)) все содержимое xml из той кодировки что указана в utf-8 и запихнет в себя. Если не сможет - будет орать. Если кодировка не указана - будет считать что все и так в utf-8.

интересный момент:

$dom = new DOMDocument('1.0','cp1251')
$dom->load($xml) // $xml в кодировке koi8-r, указано в декларации
echo $dom->saveXml()
echo $dom->encoding;

xml будет в koi8-r а не в cp1251 как задумывалось, ибо декларация имеет приоритет перед указанными в конструкторе класса значениями. Выход: перед выводом сменить кодировку: $dom->encoding = 'cp1251'. В конструкторе класса кодировка имеет значение до конца в случае когда дом документ и все его деревья создаются внутри php не загружая ни откдута xml.

Да и ещё: я применяю практику когда преобразование xml => {xslt} => xml даёт на выводе DOMDocument, а не xml-строку

Код:
[php]function xsltParser ($xml, $xslfile, $mode) {
    /**
    * mode:
    *  xml: xml to browser
    *  xhtml: xhtml to browser
    **/
	global $timers;
	
    $xml->preserveWhiteSpace = false;
    $xml->formatOutput = true;

	if($mode == 'xhtml') 
    {
		$timers->start("XsltParser",__FILE__,__LINE__,"XSLT_PARSER");
		
        $xsl = new DOMDocument('1.1');
		$xsl->formatOutput = true;
		$xsl->preserveWhiteSpace = false;
		
		
		$fp = fopen($xslfile,"r");
		$fa = fread($fp,filesize($xslfile));
		$fa = str_replace("\n",'',$fa);
		$fa = str_replace("\r",'',$fa);
		$fa = preg_replace("/(\t+)/i"," ",$fa);
		$fa = preg_replace('/( +)/i',' ',$fa);
		
		$xsl->loadXML($fa);
		
		
        $proc = new XSLTProcessor;
        $proc->importStyleSheet($xsl);
        $proc->registerPhpFunctions();
		
        $result = $proc->transformToDOC($xml);
		
		$timers->stop("XsltParser");
		return $result;
    } 
        else if($mode == 'xml') 
        {
            header("Content-type: application/xml\r\n");
            return $xml->saveXML();
        }
}

note: документ xslt загружается в libxslt с помощью
предварительного fopen из соображений гарантировано
правильной обработки flock (не уверен на счет внутреннего
загрузчика libxslt) в другом скрипте который с завидной 
переодичностью меняет xslt-шаблон. Плюс вырезание всех
переносов строк и пробелов. А то знаю орать будете как 
черти.. %)
[/php]
СРАЗУ СКАЖУ ПОЛЬЗОВАТЬСЯ ЭТИМ БЕЗ ДОВОДКИ ОПАСНО! Выдрано из существующего проекта, но написано давно. Просто как пример. Вырезает помимо всего прочего пробелы внутри текста (которые броузер все равно игнорирует)

например

"фыв фыв {тут перенос строки и скажем 20 пробелов} фыв фыв" => "фыв фыв {теперь тут 1 пробел} фыв фыв"

$dom->preserveWhiteSpace = false уберёт только пустые теги, но не повторяющиеся пробелы.

и ещё. люди часто путаются:

Код:
<?xml encoding="utf-8" ?>
<xmlroot>
  <xmlnode>abc</xmlnode>
  <xmlnode2>
    <xmlnode3>bcd</xmlnode3>
  </xmlnode2>
</xmlroot>

на самом деле представляет из себя вот что:
<?xml encoding="utf-8" ?>
<xmlroot>
<xml:text>{перенос строки и два пробела, пустой нод}</xml:text>
<xmlnode><xml:text>abc</xml:text></xmlnode>
<xml:text>{перенос строки и два пробела, пустой нод}</xml:text>
<xmlnode2>
<xml:text>{перенос строки и 4 пробела, пустой нод}</xml:text>
<xmlnode3><xml:text>bcd</xml:text></xmlnode3>
<xml:text>{перенос строки и 2 пробела, пустой нод}</xml:text>
</xmlnode2>
<xml:text>{перенос строки, пустой нод}</xml:text>
<xmlroot>

но только все в одну строчку (т.е. между тегами нет переносов строк и пробелов). я раписал построчно лишь для наглядности
в последнем случае - если написать $dom->preserveWhiteSpace = false (ПОСЛЕ ЗАГРУЗКИ XML ЕСЛИ ТАКОВОЕ ИМЕЕТСЯ). То все теги имеющие пометку 'пустой нод', вырежуться автоматически.

Пустыми считаются те что не содержат ни одного непробельного символа (\n,\r,\t, пробел - считаются пробельными, посему вырезаются). Адрес непробельного пробела ("неразрывный пробел") не помню в упор. Ибо не использую вовсе

Ещё хинт:

в xslt запись вида
<div></div>

переконвертится в complete-нод вида <div/> (ошибочно с точки зрения всех стандартов html и xhtml, которые имеют div).

как вариант номер раз:
Код:
<нод_повыше xml:space="preserve">
  <div>{!ТУТ ПРОБЕЛ!}</div>
  <div> </div>
</нод_повыше>
в итоге будет именно то что ожидали - <div> содержащий один пробел.

Но это не прокатит с textarea (все что в нем будет вписываться в форму). Посему вариант номер два (более предпочтительный)

Код:
<нод_повыше xml:space = "preserve">
  <div><![!CDATA[]]></div>
</нод_повыше>
Пока дом документ шарахается между libxml и libxslt эта запись будет оставаться, посему нод пустым считаться не будет. При выводе - естественно просто <div></div>

Благодарю за внимание. Вопросы в личку.

-~{}~ 28.06.05 13:05:

%/ а тут в форуме же масса линнков на доки. Думал, что нету, раз автар интересуется..
 

slach

Новичок
;) тут в форуме много чего полезного есть

но тебе спасибо за ОБСТОЯТЕЛЬНЫЙ хороший ответ
добро пожаловать
 
Сверху