В настоящее время я переписываю класс PHP, который пытался разбить XML-файл на более мелкие куски, чтобы использовать XMLReader и XMLWriter вместо текущей базовой файловой системы и регулярного подхода.
Однако я не могу понять, как получить версию, кодировку и автономные флаги из преамбулы XML.
Начало моего тестового XML-файла выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE fakedoctype SYSTEM "fake_doc_type.dtd"> <!-- This is a comment, it's here to try and get the parser to break in some way --> <root attribute="value" otherattribute="othervalue">
Я могу открыть его с читателем и перейти через документ с помощью read (), next () и т. Д., Но я просто не могу получить что-то в <?xml ... ?>
. Первое, что я могу получить, – это поддельный DOCTYPE.
Мой тестовый код выглядит следующим образом:
$a = new XMLReader (); var_dump ($a -> open ('/path/to/test/file.xml')) // true var_dump ($a -> nodeType); // 0 var_dump ($a -> name); // "" var_dump ($a -> readOuterXML ()); // '' var_dump ($a -> read ()); // true var_dump ($a -> nodeType); // 10 var_dump ($a -> readOuterXML ()); // <!DOCTYPE fakedoctype SYSTEM "fake_doc_type.dtd">
Конечно, я всегда мог предположить, что XML 1.0, кодирование UTF8 и standalone = yes, но для правильности я бы скорее смог захватить то, что значения в моем исходном канале, и использовать их при создании разделенных файлов.
Документация по XMLReader и XMLwriter кажется очень плохой, поэтому есть все шансы, что я просто что-то пропустил в документах. Кто-нибудь знает, что делать в этом случае?
Что я знаю из XMLReader
даже если он имеет XMLReader::XML_DECLARATION
, я никогда не испытывал этого при перемещении документа с помощью XMLReader::read()
в XMLReader::$nodeType
.
Похоже, что он пропускается, и я также задавался вопросом, почему это так, и я еще не нашел никакого флага или возможности изменить это поведение.
Для вывода XMLReader
всегда возвращает строки с кодировкой UTF-8. Это то же самое, что и в других частях на основе libxml в PHP. Так что с этой стороны все ясно. Но я предполагаю, что это не та часть, которая вам интересна, но конкретный ввод строки в файле, который вы открываете с помощью XMLReader::open()
.
Не специально для XMLReader
я однажды создал класс утилиты, который я назвал XMLRecoder
который способен обнаруживать кодировку строки XML на основе объявления XML, а также на основе спецификации. Я думаю, вы должны сделать то и другое. Это одна из частей, я думаю, что вам все еще нужно использовать регулярные выражения, но поскольку объявление XML должно быть первым, а также является инструкцией по обработке (PI), которая очень хорошо и строго определена, вы должны быть в состоянии заглянуть туда.
Это часть связанной части кода XMLRecoder
:
### excerpt from https://gist.github.com/hakre/5194634 /** * pcre pattern to access EncodingDecl, see <http://www.w3.org/TR/REC-xml/#sec-prolog-dtd> */ const DECL_PATTERN = '(^<\?xml\s+version\s*=\s*(["\'])(1\.\d+)\1\s+encoding\s*=\s*(["\'])(((?!\3).)*)\3)'; const DECL_ENC_GROUP = 4; const ENC_PATTERN = '(^[A-Za-z][A-Za-z0-9._-]*$)'; ... ($result = preg_match(self::DECL_PATTERN, $buffer, $matches, PREG_OFFSET_CAPTURE)) && $result = $matches[self::DECL_ENC_GROUP];
Поскольку это показывает, что это идет до кодирования, поэтому оно не является полным. Однако для того, чтобы извлечь кодировку (и для вашей версии потребностей), она должна выполнить эту работу. Я провел это против тонн (тысяч) случайных XML-документов для тестирования.
Другая часть – обнаружение спецификации:
### excerpt from https://gist.github.com/hakre/5194634 const BOM_UTF_8 = "\xEF\xBB\xBF"; const BOM_UTF_32LE = "\xFF\xFE\x00\x00"; const BOM_UTF_16LE = "\xFF\xFE"; const BOM_UTF_32BE = "\x00\x00\xFE\xFF"; const BOM_UTF_16BE = "\xFE\xFF"; ... /** * @param string $string string (recommended length 4 characters/octets) * @param string $default (optional) if none detected what to return * @return string Encoding, if it can not be detected defaults $default (NULL) * @throws InvalidArgumentException */ public function detectEncodingViaBom($string, $default = NULL) { $len = strlen($string); if ($len > 4) { $string = substr($string, 0, 4); } elseif ($len < 4) { throw new InvalidArgumentException(sprintf("Need at least four characters, %d given.", $len)); } switch (true) { case $string === self::BOM_UTF_16BE . $string[2] . $string[3]: return "UTF-16BE"; case $string === self::BOM_UTF_8 . $string[3]: return "UTF-8"; case $string === self::BOM_UTF_32LE: return "UTF-32LE"; case $string === self::BOM_UTF_16LE . $string[2] . $string[3]: return "UTF-16LE"; case $string === self::BOM_UTF_32BE: return "UTF-32BE"; } return $default; }
С обнаружением BOM я также выполнил это в отношении одного и того же набора документов XML, однако не многие из них имели спецификации. Как вы можете видеть, порядок обнаружения оптимизирован для более распространенных сценариев при одновременном рассмотрении дублирующих двоичных паттернов между различными спецификациями. Большинство документов, с которыми я столкнулся, не имеют спецификации, и вам в основном нужно выяснить, закодирован ли документ UTF-32.
Надеюсь, это по крайней мере дает некоторые идеи.