7

同じ単一の XML スキーマ (XSD) に対して XML ファイルのバッチを検証するプロセスを高速化したいと考えています。唯一の制限は、私が PHP 環境にいるということです。

現在の問題は、検証したいスキーマに 2755 行のかなり複雑な xhtml スキーマが含まれていることです (http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd)。非常に単純なデータであっても、これには長い時間がかかります (検証前に約 30 秒)。私のバッチには何千もの XML ファイルがあるので、これは実際にはうまくスケーリングできません。

XML ファイルの検証には、標準の php-xml ライブラリからこれらの両方の方法を使用します。

  • DOMDocument::schemaValidate
  • DOMDocument::schemaValidateSource

PHP 実装は HTTP 経由で XHTML スキーマをフェッチし、内部表現 (おそらく DOMDocument) を構築し、検証が完了すると破棄されると考えています。私は、XML-libs のいくつかのオプションがこの動作を変更して、再利用のためにこのプロセスで何かをキャッシュする可能性があると考えていました。

私の問題を説明する簡単なテスト設定を作成しました。

テストスキーマ.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>

テストデータ.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.

この問題を解決する方法に関するヘルプや提案は大歓迎です!

4

2 に答える 2

17

オーバーヘッドとして、タイミング値から 30 秒を安全に差し引くことができます。

ほとんどのライブラリはドキュメントのキャッシュを反映していないため (HTTP ヘッダーでさえそれを示唆しています)、W3C サーバーへのリモート リクエストが遅延しています。しかし、あなた自身を読んでください

W3C サーバーは DTD を返すのが遅いです。遅延は意図的なものですか?

はい。さまざまなソフトウェア システムが 1 日に何百万回もサイトから DTD をダウンロードするため (サーバーのキャッシュ ディレクティブにもかかわらず)、DTD とスキーマ (DTD、XSD、ENT、MOD など) をサイトから提供し始めました。人工遅延。そうすることの目標は、現在進行中の過剰な DTD トラフィックの問題にもっと注意を向け、サイトの残りの部分の安定性と応答時間を保護することです。パフォーマンスを向上させるために、HTTP キャッシュまたはカタログ ファイルをお勧めします。

W3.org はリクエストを低く抑えようとしています。それは理解できます。PHPDomDocumentは 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>

2 つのファイルのコピーを、.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',
];

これが示すように、これら 2 つの 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 ファイルをロードしてディスクから検証し、XSD URI をローカル ファイル名に「解決」するために作成されています。他の種類の操作 (DTD ベースの検証など) では、コードの変更や拡張が必要になる場合があります。より望ましいのは、XML カタログです。また、さまざまなツールでも機能します。

于 2012-12-13T18:00:18.120 に答える
0

@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;
    }
);
于 2015-03-16T16:09:15.533 に答える