问题 使用PHP中的自定义DTD验证XML


是否有一种方法(没有安装任何库)使用PHP中的自定义DTD验证XML?


1443
2017-09-19 13:46


起源

因此,只是为了澄清 - “自定义DTD”是否意味着“DTD与XML文件内容中指定的任何DTD独立/不同”? - Peter
看到 stackoverflow.com/questions/1274173/... - MPV


答案:


看一眼 PHP的DOM特别是 DOM文档:: schemaValidate 和 DOM文档::验证

DOMDocument :: validate的示例非常简单:

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

6
2017-09-19 13:50



获取验证错误的唯一方法是使用自定义错误处理程序。真的很难看php糟透了处理错误 - Andrei Savu
uk3.php.net/manual/en/domdocument.schemavalidate.php#62032 看起来有一个比自定义错误处理程序更好的方法 - Andrei Savu
@Andrei - 看到验证错误正确显示肯定有帮助,所以在验证之前调用libxml_use_internal_errors(true),在失败之后调用libxml_get_errors()。 - Peter
@owenmarshall - 我不认为这真的回答了原来的问题,因为“book.xml”只会针对book.xml内容中指定的任何DTD进行验证,而不是调用者在运行时指定的“自定义”DTD。 - Peter
仅供参考,PHP中有一个错误 DOMDocument::validate()  bugs.php.net/bug.php?id=48080 - Krystian


如果你有一个字符串中的dtd,你可以使用a来验证它 数据包装器 对于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";
}

3
2018-06-30 09:48



那么为什么这段代码会产生输出“无效”?从捕获的libxml我看到以下错误:<B>错误517 </ B>:无法加载外部子集 “的数据://文本/无格式; BASE64,PCFFTEVNRU5UIG5vdGUgKHRvLGZyb20saGVhZGluZyxib2R5KT4KICAgICAgICA8IUVMRU1FTlQgdG8gKCNQQ0RBVEEpPgogICAgICAgIDwhRUxFTUVOVCBmcm9tICgjUENEQVRBKT4KICAgICAgICA8IUVMRU1FTlQgaGVhZGluZyAoI1BDREFUQSk + CiAgICAgICAgPCFFTEVNRU5UIGJvZHkgKCNQQ0RBVEEpPg ==” 上线的<b> 0 </ b> - Peter
我希望我可以为破坏的代码(或至少撤销我的upvote)进行此操作。 - Peter
我对上面代码的问题似乎是在createDocumentType()调用中,它生成了DOCTYPE元素。这就是我想要的(例如):<!DOCTYPE note [<!ELEMENT note(to,from,heading,body)> <!ELEMENT to(#PCDATA)> ... <!ELEMENT body(#PCDATA) >]>但是这是我得到什么:<!DOCTYPE符号系统 “的数据:// text / plain的; BASE64,PCFFTEVNRU5UIG5vdGUgKHRvLGZyb20saGVhZGluZyxib2R5KT4KICAgICAgICA8IUVMRU1FTlQgdG8gKCNQQ0RBVEEpPgogICAgICAgIDwhRUxFTUVOVCBmcm9tICgjUENEQVRBKT4KICAgICAgICA8IUVMRU1FTlQgaGVhZGluZyAoI1BDREFUQSk + CiAgICAgICAgPCFFTEVNRU5UIGJvZHkgKCNQQ0RBVEEpPg ==”!> - Peter
@Peter - 看起来这个代码来自w3schools(w3fools.com)。 - Ben
看起来更像是已复制的代码 从这里 然后与数据包装器结合使用。 @Peter:您的配置可能会禁用外部子集加载,但它确实有效。 - hakre


我对原始问题的解释是,我们有一个“板载”XML文件,我们希望根据“板载”DTD文件进行验证。因此,我将如何实现Soren和PayamRWD在评论中表达的“插入DOCTYPE元素内的本地DTD”的想法:

public function validate($ xml_realpath,$ dtd_realpath = null){
    $ xml_lines = file($ xml_realpath);
    $ doc = new DOMDocument;
    if($ dtd_realpath){
        //在DOCTYPE行中注入DTD:
        $ dtd_lines = file($ dtd_realpath);
        $ new_lines = array();
        foreach($ xml_lines为$ x){
            //假设DOCTYPE SYSTEM“blah blah”格式:
            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));
    }
    //启用用户错误处理
    libxml_use_internal_errors(真);
    if(@ $ doc-> validate()){
        echo“有效!\ n”;
    } else {
        echo“无效:\ n”;
        $ errors = libxml_get_errors();
        foreach($ errors as $ error){
            print_r($ error,true);
        }
    }
}

请注意,为简洁起见,已经抑制了错误处理,并且可能有更好/更通用的方法来处理插值。但是我  实际上将此代码与真实数据一起使用,它适用于PHP 5.2.17版。


3
2017-09-12 14:15





试图完成“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/>
}

?>

模板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>

0
2018-03-03 16:11



同样,您不能在任何地方加载DTD。 - Znarkus
在他的例子中,他在DOCTYPE元素内部本地插入DTD(这是Soren的代码试图做的,但它似乎不起作用)。 - Peter