如何在PHP中检查一个字符串是否是有效的XML而不显示警告

65
我尝试使用 simplexml_load_string()文档 函数来检查一个字符串是否为有效的 XML,但它显示了许多警告信息。
我该如何在不抑制错误(在开头加上@)的情况下,检查一个字符串是否为有效的 XML,并显示一个不会产生警告的函数呢?
7个回答

93

使用libxml_use_internal_errors()来阻止所有的XML错误,然后使用libxml_get_errors()来逐个迭代。

Simple XML加载字符串

libxml_use_internal_errors(true);

$doc = simplexml_load_string($xmlstr);
$xml = explode("\n", $xmlstr);

if (!$doc) {
    $errors = libxml_get_errors();

    foreach ($errors as $error) {
        echo display_xml_error($error, $xml);
    }

    libxml_clear_errors();
}

7
接近完美...只需要补充一下,display_xml_error 函数只是一个自定义函数,用于以漂亮的方式输出错误信息,可以在这里找到 http://php.net/manual/zh/function.libxml-get-errors.php。 起初我以为它是我遗漏的内部函数。 - Carlton
10
小心使用 if (!$doc)!例如,PHP会将有命名空间的文档视为空,并且 !$doc === TRUE - David
6
我遇到了与@David提到的问题相同的情况,我需要明确检查 if($doc !== FALSE) 而不是通常足够的 if($doc) - Samsquanch

28

来自文档:

在加载文档时处理XML错误是一项非常简单的任务。使用libxml功能可以在加载文档时抑制所有XML错误,然后迭代遍历这些错误。

libxml_get_errors()返回的libXMLError对象包含多个属性,包括错误的messagelinecolumn(位置)。

libxml_use_internal_errors(true);
$sxe = simplexml_load_string("<?xml version='1.0'><broken><xml></broken>");
if (!$sxe) {
    echo "Failed loading XML\n";
    foreach(libxml_get_errors() as $error) {
        echo "\t", $error->message;
    }
}

Reference: libxml_use_internal_errors


14

我的版本是这样的:

//validate only XML. HTML will be ignored.

function isValidXml($content)
{
    $content = trim($content);
    if (empty($content)) {
        return false;
    }
    //html go to hell!
    if (stripos($content, '<!DOCTYPE html>') !== false) {
        return false;
    }

    libxml_use_internal_errors(true);
    simplexml_load_string($content);
    $errors = libxml_get_errors();          
    libxml_clear_errors();  

    return empty($errors);
}

测试:

//false
var_dump(isValidXml('<!DOCTYPE html><html><body></body></html>'));
//true
var_dump(isValidXml('<?xml version="1.0" standalone="yes"?><root></root>'));
//false
var_dump(isValidXml(null));
//false
var_dump(isValidXml(1));
//false
var_dump(isValidXml(false));
//false
var_dump(isValidXml('asdasds'));

请注意,isValidXml('<html>Im XML!</html>')仍然会返回true。 - undefined

13

试试这个

//check if xml is valid document
public function _isValidXML($xml) {
    $doc = @simplexml_load_string($xml);
    if ($doc) {
        return true; //this is valid
    } else {
        return false; //this is not valid
    }
}

11
问题特别要求一个不使用@来抑制错误的解决方案。 - GenericJon

3

这是我一段时间前写的一个小类:

/**
 * Class XmlParser
 * @author Francesco Casula <fra.casula@gmail.com>
 */
class XmlParser
{
    /**
     * @param string $xmlFilename Path to the XML file
     * @param string $version 1.0
     * @param string $encoding utf-8
     * @return bool
     */
    public function isXMLFileValid($xmlFilename, $version = '1.0', $encoding = 'utf-8')
    {
        $xmlContent = file_get_contents($xmlFilename);
        return $this->isXMLContentValid($xmlContent, $version, $encoding);
    }

    /**
     * @param string $xmlContent A well-formed XML string
     * @param string $version 1.0
     * @param string $encoding utf-8
     * @return bool
     */
    public function isXMLContentValid($xmlContent, $version = '1.0', $encoding = 'utf-8')
    {
        if (trim($xmlContent) == '') {
            return false;
        }

        libxml_use_internal_errors(true);

        $doc = new DOMDocument($version, $encoding);
        $doc->loadXML($xmlContent);

        $errors = libxml_get_errors();
        libxml_clear_errors();

        return empty($errors);
    }
}

对于测试目的,使用流和vfsStream一样可以很好地工作。


1
添加了检查以避免“DOMDocument :: loadXML():提供空字符串作为输入”的错误。 - Francesco Casula

2

案例

偶尔检查Google Merchant XML供稿的可用性。

该供稿没有DTD,因此 validate() 无法起作用。

解决方案

// disable forwarding those load() errors to PHP
libxml_use_internal_errors(true);
// initiate the DOMDocument and attempt to load the XML file
$dom = new \DOMDocument;
$dom->load($path_to_xml_file);
// check if the file contents are what we're expecting them to be
// `item` here is for Google Merchant, replace with what you expect
$success = $dom->getElementsByTagName('item')->length > 0;
// alternatively, just check if the file was loaded successfully
$success = null !== $dom->actualEncoding;

length 包含了文件中实际列出的产品数量。您可以使用自己的标签名称。

逻辑

您可以在任何其他标签名称上调用 getElementsByTagName()(我使用的是 Google Merchant 的 item,您的情况可能有所不同),或者读取 $dom 对象本身的其他属性。逻辑保持不变:与其检查加载文件时是否存在错误,我认为实际尝试操作它(或特别检查它是否包含您实际需要的值)会更可靠。

最重要的是:与 validate() 不同,这不需要您的 XML 具有 DTD。


0

解决方案

<?php
/**
* 檢查XML是否正確
* 
* @param string $xmlstr
* @return bool
*/
public function checkXML($xmlstr)
{
    libxml_use_internal_errors(true);
    $doc = simplexml_load_string($xmlstr);
    if (!$doc) {
        $errors = libxml_get_errors();
        if (count($errors)) {
            libxml_clear_errors();
            return false;
        }
    }
    return true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接