Ускорение проверки XML-схемы пакета XML-файлов по одной и той же XML-схеме (XSD)


Я хотел бы ускорить процесс проверки пакета XML-файлов по одной и той же схеме XML (XSD). Единственные ограничения заключаются в том, что я нахожусь в среде PHP.

Моя текущая проблема заключается в том, что схема, которую я хотел бы проверить, включает довольно сложную схему xhtml из 2755 строк(http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd). Даже для очень простых данных это занимает много времени (около 30 секунд для проверки). Поскольку у меня есть тысячи XML-файлов в моя партия, это не очень хорошо масштабируется.

Для проверки XML-файла я использую оба этих метода из стандартных библиотек php-xml.

  • DOMDocument::Проверка схемы
  • DOMDocument::schemavalidatesource
  • домдокумент::schemavalidatesource

Я думаю, что реализация PHP извлекает схему XHTML через HTTP и создает некоторое внутреннее представление (возможно, DOMDocument), и это отбрасывается после завершения проверки. Я думал, что какой-нибудь вариант для библиотеки XML могут изменить это поведение, чтобы кэшировать что-то в этом процессе для повторного использования.

Я построил простую тестовую установку, которая иллюстрирует мою проблему:

Тест-схема.xsd

<xs:schema attributeFormDefault="unqualified"
    elementFormDefault="qualified"
    targetNamespace="http://myschema.example.com/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:myschema="http://myschema.example.com/"
    xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <xs:import
        schemaLocation="http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd"
        namespace="http://www.w3.org/1999/xhtml">
    </xs:import>
    <xs:element name="Root">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="MyHTMLElement">
                    <xs:complexType>
                        <xs:complexContent>
                            <xs:extension base="xhtml:Flow"></xs:extension>
                        </xs:complexContent>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Test-data.xml

<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns="http://myschema.example.com/" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://myschema.example.com/ test-schema.xsd ">
  <MyHTMLElement>
    <xhtml:p>This is an XHTML paragraph!</xhtml:p>
  </MyHTMLElement>
</Root>

Schematest.php

<?php
$data_dom = new DOMDocument();
$data_dom->load('test-data.xml');

// Multiple validations using the schemaValidate method.
for ($attempt = 1; $attempt <= 3; $attempt++) {
    $start = time();
    echo "schemaValidate: Attempt #$attempt returns ";
    if (!$data_dom->schemaValidate('test-schema.xsd')) {
        echo "Invalid!";
    } else {
        echo "Valid!";
    }
    $end = time();
    echo " in " . ($end-$start) . " seconds.\n";
}

// Loading schema into a string.
$schema_source = file_get_contents('test-schema.xsd');

// Multiple validations using the schemaValidate method.
for ($attempt = 1; $attempt <= 3; $attempt++) {
    $start = time();
    echo "schemaValidateSource: Attempt #$attempt returns ";
    if (!$data_dom->schemaValidateSource($schema_source)) {
        echo "Invalid!";
    } else {
        echo "Valid!";
    }
    $end = time();
    echo " in " . ($end-$start) . " seconds.\n";
}

Запуск этого schematest.php файл выдает следующие выходные данные:

schemaValidate: Attempt #1 returns Valid! in 30 seconds.
schemaValidate: Attempt #2 returns Valid! in 30 seconds.
schemaValidate: Attempt #3 returns Valid! in 30 seconds.
schemaValidateSource: Attempt #1 returns Valid! in 32 seconds.
schemaValidateSource: Attempt #2 returns Valid! in 30 seconds.
schemaValidateSource: Attempt #3 returns Valid! in 30 seconds.

Любая помощь и предложения о том, как решить эту проблему, очень приветствуются!

Author: hakre, 2012-12-13

2 answers

Вы можете безопасно вычесть 30 секунд из значений времени в качестве накладных расходов.

Удаленные запросы к серверам W3C задерживаются, потому что большинство библиотек не отражают кэширование документов (даже заголовки HTTP предполагают это). Но читайте свои собственные:

Серверы W3C медленно возвращают DTD. Является ли задержка преднамеренной?

Да. Из-за различных программных систем, загружающих DTD с нашего сайта миллионы раз в день (несмотря на директивы кэширования наших серверов), мы начали обслуживать DTD и схемы (DTD, XSD, ENT, MOD и т.д.) с нашего сайта с искусственной задержкой. Наши цели при этом заключаются в том, чтобы привлечь больше внимания к нашим текущим проблемам с чрезмерным трафиком DTD, а также защитить стабильность и время отклика остальной части нашего сайта. Мы рекомендуем кэширование HTTP или файлы каталога для повышения производительности.

W3.org старается держать запросы на низком уровне. Это понятно. PHP DomDocument основан на libxml. И libxml позволяет установить внешний загрузчик сущностей. Весь В этом случае интересен раздел поддержки каталога.

Чтобы решить данную проблему, настройте файл catalog.xml:

<?xml version="1.0"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
    <system systemId="http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd"
            uri="xhtml1-transitional.xsd"/>
    <system systemId="http://www.w3.org/2001/xml.xsd"
            uri="xml.xsd"/>
</catalog>

Сохраните копию двух файлов .xsd с именами, указанными в этом файле каталога рядом с каталогом (относительные, а также абсолютные пути file:///... работают, если вы предпочитаете другой каталог).

Затем убедитесь, что для вашей системной переменной среды XML_CATALOG_FILES задано имя файла из файла catalog.xml. Когда все настроено, проверка просто выполняется:

schemaValidate: Attempt #1 returns Valid! in 0 seconds.
schemaValidate: Attempt #2 returns Valid! in 0 seconds.
schemaValidate: Attempt #3 returns Valid! in 0 seconds.
schemaValidateSource: Attempt #1 returns Valid! in 0 seconds.
schemaValidateSource: Attempt #2 returns Valid! in 0 seconds.
schemaValidateSource: Attempt #3 returns Valid! in 0 seconds.

Если это все еще занимает много времени, это просто признак того, что переменная среды не установлена в нужном месте. Я обработал переменную, а также некоторые крайние случаи, а также в сообщении в блоге:

Он должен заботиться о различных крайних случаях, таких как имена файлов, содержащие пробелы.

В качестве альтернативы можно создать простую функцию обратного вызова загрузчика внешних сущностей, которая использует сопоставление URL =>файлов для локальной файловой системы в виде массива:

$mapping = [
     'http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd'
         => 'schema/xhtml1-transitional.xsd',

     'http://www.w3.org/2001/xml.xsd'                          
         => 'schema/xml.xsd',
];

Как видно из этого, я поместил дословную копию этих двух XSD-файлов в подкаталог под названием schema. Следующий шаг - использовать libxml_set_external_entity_loader чтобы активировать функцию обратного вызова с сопоставлением. Файлы, которые уже существуют на диске, предпочтительны и загружаются напрямую. Если рутина при обнаружении файла, не имеющего сопоставления, будет выдано RuntimeException с подробным сообщением:

libxml_set_external_entity_loader(
    function ($public, $system, $context) use ($mapping) {

        if (is_file($system)) {
            return $system;
        }

        if (isset($mapping[$system])) {
            return __DIR__ . '/' . $mapping[$system];
        }

        $message = sprintf(
            "Failed to load external entity: Public: %s; System: %s; Context: %s",
            var_export($public, 1), var_export($system, 1),
            strtr(var_export($context, 1), [" (\n  " => '(', "\n " => '', "\n" => ''])
        );

        throw new RuntimeException($message);
    }
);

После установки этого загрузчика внешних сущностей больше нет задержки для удаленных запросов.

И это все. См. Суть. Будьте осторожны: Этот загрузчик внешних сущностей был написан для загрузки XML-файла для проверки с диска и "разрешения" URI XSD в локальные имена файлов. Для других операций (например, проверка на основе DTD) могут потребоваться некоторые изменения кода / расширение. Более предпочтительным является XML-каталог. Это также работает для различных инструментов.

 13
Author: hakre, 2012-12-17 04:48:09

В качестве альтернативы @hakre: Загрузите внешний ресурс (DTD) с первой попытки, затем используйте загруженную версию:

libxml_set_external_entity_loader(    
    function ($public, $system, $context) {
        if(is_file($system)){
            return $system;
        }
        $cached_file= tempnam(sys_get_temp_dir(), md5($system));
        if (is_file($cached_file)) {
            return $cached_file;
        }
        copy($system,$cached_file);
        return $cached_file;
    }
);
 0
Author: markmarijnissen, 2015-03-16 16:09:15