Validar XML usando uma DTD personalizada em PHP

Existe uma maneira (sem instalar bibliotecas) de validação de XML usando uma DTD personalizada em PHP?

Dê uma olhada no DOM do PHP , especialmente no DOMDocument :: schemaValidate e DOMDocument :: validate .

O exemplo para DOMDocument :: validate é bastante simples:

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

Se você tiver o dtd em uma string, você pode validar contra isso usando um wrapper de dados para o dtd:

 $xml = '< ?xml version="1.0"?> < !DOCTYPE note SYSTEM "note.dtd">  Tove Jani Reminder Don\'t forget me this weekend! '; $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"; } 

Minha interpretação da questão original é que temos um arquivo XML “on board” que queremos validar contra um arquivo DTD “on board”. Então, aqui é como eu implementaria a idéia “interpolar uma DTD local dentro do elemento DOCTYPE”, expressa nos comentários de Soren e PayamRWD:

 validação de function pública ($ xml_realpath, $ dtd_realpath = null) {
     $ xml_lines = arquivo ($ xml_realpath);
     $ doc = novo DOMDocument;
     se ($ dtd_realpath) {
         // Injectar DTD dentro da linha DOCTYPE:
         $ dtd_lines = arquivo ($ dtd_realpath);
         $ new_lines = array ();
         foreach ($ xml_lines como $ x) {
             // Assume DOCTYPE SYSTEM "blah blah" formato:
             se (preg_match ('/ DOCTYPE /', $ x)) {
                 $ y = preg_replace ('/ SYSTEM "(. *)" /', "[\ n". implodir ("\ n", $ dtd_lines). "\ n]", $ x);
                 $ new_lines [] = $ y;
             } outro {
                 $ new_lines [] = $ x;
             }
         }
         $ doc-> loadXML (implodir ("\ n", $ new_lines));
     } outro {
         $ doc-> loadXML (implodir ("\ n", $ xml_lines));
     }
     // Habilitar o tratamento de erros do usuário
     libxml_use_internal_errors (true);
     se (@ $ doc-> validate ()) {
         eco "Válido! \ n";
     } outro {
         eco "Não válido: \ n";
         $ errors = libxml_get_errors ();
         foreach ($ errors as $ error) {
             print_r ($ error, true);
         }
     }
 }

Observe que o tratamento de erros foi suprimido por brevidade, e pode haver uma maneira melhor / mais geral de lidar com a interpolação. Mas , na verdade, usei esse código com dados reais, e ele funciona com o PHP versão 5.2.17.

Tentando completar a resposta “owenmarshall”:

em xml-validator.php:

adicione html, header, corpo, …

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

template-format.xml:

 < ?xml version="1.0" encoding="utf-8"?>  < !DOCTYPE template-format [ 
< !ELEMENT template-format (template)>
< !ELEMENT template (background-color, color, font-size, header-image)>
< !ELEMENT background-color (#PCDATA)>
< !ELEMENT color (#PCDATA)>
< !ELEMENT font-size (#PCDATA)>
< !ELEMENT header-image (#PCDATA)>
]>