Проверка XML с помощью пользовательского DTD в PHP

Есть ли способ (без установки каких-либо библиотек) проверки XML с помощью пользовательского DTD в PHP?


person michael    schedule 19.09.2008    source источник
comment
Итак, просто для уточнения - означает ли пользовательское DTD DTD, которое является независимым/отличным от любого DTD, которое может быть указано в содержимом файла XML?   -  person Peter    schedule 11.09.2011
comment
См. stackoverflow.com/questions/ 1274173/   -  person MPV    schedule 10.10.2012


Ответы (4)


Взгляните на DOM PHP, особенно DOMDocument::schemaValidate и DOMDocument::validate.

Пример для DOMDocument::validate довольно прост:

<?php
$dom = new DOMDocument;
$dom->Load('book.xml');
if ($dom->validate()) {
    echo "This document is valid!\n";
}
?>
person owenmarshall    schedule 19.09.2008
comment
единственный способ получить ошибку проверки — использовать собственный обработчик ошибок. действительно некрасиво. php не справляется с обработкой ошибок - person Andrei Savu; 02.04.2009
comment
uk3.php.net/manual/en/domdocument.schemavalidate.php# 62032 похоже, что есть лучший способ, чем собственный обработчик ошибок - person Andrei Savu; 02.04.2009
comment
@Andrei - Это, безусловно, помогает правильно отображать ошибки проверки, поэтому это выигрышный вызов libxml_use_internal_errors(true) перед проверкой и libxml_get_errors() после сбоя. - person Peter; 09.09.2011
comment
@owenmarshall - я не думаю, что это действительно отвечает на исходный вопрос, потому что book.xml будет просто проверяться на соответствие любому DTD, указанному в содержимом book.xml, а не настраиваемому DTD, указанному вызывающей стороной во время выполнения. - person Peter; 09.09.2011
comment
К вашему сведению, в PHP есть ошибка с DOMDocument::validate() bugs.php.net/bug.php?id =48080 - person Krystian; 08.03.2013
comment
Это проверяет, находится ли xml в правильном формате как xml. Но я думаю, он спрашивает, как проверять пользовательские правила, такие как DTD? Я имею в виду, что вам нужно проверить, соответствуют ли теги имен элементов xml правильным именам элементов xml? - person themhz; 06.11.2014

Если у вас есть dtd в строке, вы можете проверить его с помощью оболочки данных для DTD:

$xml = '<?xml version="1.0"?>
        <!DOCTYPE note SYSTEM "note.dtd">
        <note>
            <to>Tove</to>
            <from>Jani</from>
            <heading>Reminder</heading>
            <body>Don\'t forget me this weekend!</body>
        </note>';

$dtd = '<!ELEMENT note (to,from,heading,body)>
        <!ELEMENT to (#PCDATA)>
        <!ELEMENT from (#PCDATA)>
        <!ELEMENT heading (#PCDATA)>
        <!ELEMENT body (#PCDATA)>';


$root = 'note';

$systemId = 'data://text/plain;base64,'.base64_encode($dtd);

$old = new DOMDocument;
$old->loadXML($xml);

$creator = new DOMImplementation;
$doctype = $creator->createDocumentType($root, null, $systemId);
$new = $creator->createDocument(null, null, $doctype);
$new->encoding = "utf-8";

$oldNode = $old->getElementsByTagName($root)->item(0);
$newNode = $new->importNode($oldNode, true);
$new->appendChild($newNode);

if (@$new->validate()) {
    echo "Valid";
} else {
    echo "Not valid";
}
person Søren Jacobi    schedule 30.06.2011
comment
Так почему же этот код выдает Not valid? Отлов ошибок от Libxml я вижу следующее: <б> Ошибка 517 : Не удалось загрузить внешние данные подмножества: // текст / равнину; base64, PCFFTEVNRU5UIG5vdGUgKHRvLGZyb20saGVhZGluZyxib2R5KT4KICAgICAgICA8IUVMRU1FTlQgdG8gKCNQQ0RBVEEpPgogICAgICAgIDwhRUxFTUVOVCBmcm9tICgjUENEQVRBKT4KICAgICAgICA8IUVMRU1FTlQgaGVhZGluZyAoI1BDREFUQSk + CiAgICAgICAgPCFFTEVNRU5UIGJvZHkgKCNQQ0RBVEEpPg == в строке <б> 0 - person Peter; 09.09.2011
comment
Я хотел бы понизить это за неработающий код (или, по крайней мере, отозвать свой голос). - person Peter; 09.09.2011
comment
Проблема, с которой я столкнулся с приведенным выше кодом, связана с вызовом createDocumentType(), который генерирует элемент DOCTYPE. Вот что я хочу (для примера): ‹!DOCTYPE note [‹!ELEMENT note (to,from,heading,body)› ‹!ELEMENT to (#PCDATA)› ... ‹!ELEMENT body (#PCDATA) >]> но это то, что я получаю: <данные SYSTEM DOCTYPE примечание: // текст / равнину; base64, PCFFTEVNRU5UIG5vdGUgKHRvLGZyb20saGVhZGluZyxib2R5KT4KICAgICAgICA8IUVMRU1FTlQgdG8gKCNQQ0RBVEEpPgogICAgICAgIDwhRUxFTUVOVCBmcm9tICgjUENEQVRBKT4KICAgICAgICA8IUVMRU1FTlQgaGVhZGluZyAoI1BDREFUQSk + CiAgICAgICAgPCFFTEVNRU5UIGJvZHkgKCNQQ0RBVEEpPg ==> - person Peter; 09.09.2011
comment
больше похоже на то, что код был скопирован отсюда, а затем объединен с оболочкой данных. @Peter: загрузка внешнего подмножества может быть отключена в вашей конфигурации, она работает. - person hakre; 18.10.2014

Моя интерпретация исходного вопроса заключается в том, что у нас есть «встроенный» файл XML, который мы хотим проверить на соответствие «встроенному» файлу DTD. Итак, вот как бы я реализовал идею «интерполировать локальный DTD внутри элемента DOCTYPE», выраженную в комментариях Soren и PayamRWD:

public function validate($xml_realpath, $dtd_realpath=null) {
    $xml_lines = file($xml_realpath);
    $doc = new DOMDocument;
    if ($dtd_realpath) {
        // Inject DTD inside DOCTYPE line:
        $dtd_lines = file($dtd_realpath);
        $new_lines = array();
        foreach ($xml_lines as $x) {
            // Assume DOCTYPE SYSTEM "blah blah" format:
            if (preg_match('/DOCTYPE/', $x)) {
                $y = preg_replace('/SYSTEM "(.*)"/', " [\n" . implode("\n", $dtd_lines) . "\n]", $x);
                $new_lines[] = $y;
            } else {
                $new_lines[] = $x;
            }
        }
        $doc->loadXML(implode("\n", $new_lines));
    } else {
        $doc->loadXML(implode("\n", $xml_lines));
    }
    // Enable user error handling
    libxml_use_internal_errors(true);
    if (@$doc->validate()) {
        echo "Valid!\n";
    } else {
        echo "Not valid:\n";
        $errors = libxml_get_errors();
        foreach ($errors as $error) {
            print_r($error, true);
        }
    }
}

Обратите внимание, что обработка ошибок была подавлена ​​для краткости, и может быть лучший/более общий способ обработки интерполяции. Но я использовал этот код на реальных данных, и он работает с PHP версии 5.2.17.

person Peter    schedule 12.09.2011

Попытка завершить ответ "owenmarshall":

в xml-validator.php:

добавить html, заголовок, тело, ...

<?php

$dom = new DOMDocument; <br/>
$dom->Load('template-format.xml');<br/>
if ($dom->validate()) { <br/>
    echo "This document is valid!\n"; <br/>
}

?>

template-format.xml:

<?xml version="1.0" encoding="utf-8"?>

<!-- DTD to Validate against (format example) -->

<!DOCTYPE template-format [  <br/>
  <!ELEMENT template-format (template)>  <br/>
  <!ELEMENT template (background-color, color, font-size, header-image)>  <br/>
  <!ELEMENT background-color   (#PCDATA)>  <br/>
  <!ELEMENT color (#PCDATA)>  <br/>
  <!ELEMENT font-size (#PCDATA)>  <br/>
  <!ELEMENT header-image (#PCDATA)>  <br/>
]>

<!-- XML example -->

<template-format>

<template>

<background-color>&lt;/background-color>  <br/>
<color>&lt;/color>  <br/>
<font-size>&lt;/font-size>  <br/>
<header-image>&lt;/header-image>  <br/>

</template> 

</template-format>
person PayamRWD    schedule 03.03.2011
comment
То же самое здесь, вы нигде не загружаете DTD. - person Znarkus; 24.03.2011
comment
В своем примере он интерполировал DTD локально внутри элемента DOCTYPE (это то, что пытается сделать код Сорена, но, похоже, это не работает). - person Peter; 09.09.2011