简体   繁体   中英

Why DOMDocument::schemaValidate() over the same XML and XSD files have sometimes a execution time dramatically higher than usual?

It is extrange that a default class method fails that way, sometimes, even when the arguments passed to the method are the same that usualy takes a fraction of second.

Maximum execution time of 30 seconds exceeded in script_path on line script_line_number

In that exact line:

$result = $DOMDocument -> schemaValidate($schemaPath);

$DOMDocument is always the same. And it only references parts of the same XML with ID atrtibutes. It does not have any URL like attribute, besides Algorith and xmlns ones, which by nature does not call anyresource from anywhere, and we are talking about DOMDocument class of PHP and the XML starndards.

$schemaPath is always the same, and it is pointing to a server local XSD file, which is always there, before, and after the validation attempt, either it is successful or not. The schema is only pointing to other local xsd files, located in the same folder ie <xs:include schemaLocation="schema2.xsd"/>

The only possible answer I can think of, is that the XSD file is located by the method but for some reason it cannot be read, because the disc is busy.

What could be causing the execution of the method take so long?

What measures should be taken to prevent the error to happen besides increasing the maximum execution time limit of PHP?

The XML and the XSD files are pretty small, in fact the exact same XML and XSD usually takes less than ~ 0.1 seconds to validate, but a very few times (~ 1 out of 1000) the execution time exeeds 30 seconds.


EDIT

I isolated the problem so I post a samples.

Schema.xsd:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema targetNamespace="http://www.foo.bar/Car" xmlns:SiiDte="http://www.foo.bar/Car" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:include schemaLocation="schema2.xsd"/>
    <xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsignature_v10.xsd"/><!-- just the standar signature schema -->
    <xs:element name="ROOT" type="SiiDte:ROOTDefType"/>
    <xs:complexType name="ROOTDefType">
        <xs:sequence>
            <xs:element name="Element"></xs:element>
            <xs:element ref="ds:Signature">
                <xs:annotation>
                    <xs:documentation>Firma Digital sobre Documento</xs:documentation>
                </xs:annotation>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="version" type="xs:decimal" use="required" fixed="1.0"/>
    </xs:complexType>
</xs:schema>

Schema2.xsd:

<xs:schema targetNamespace="http://www.foo.bar/Car" xmlns:ns1="http://www.foo.bar/Car" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:simpleType name="MOOType">
        <xs:restriction base="xs:positiveInteger">
            <xs:enumeration value="1"/>
            <xs:enumeration value="2"/>
            <xs:enumeration value="3"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

Code:

// ... a bunch of ther code...

$XML =
    '<?xml version="1.0"?>
    <ROOT xmlns="http://www.foo.bar/Car" version="1.0">
        <Element ID="A1">hello</Element>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
                <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
                <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                <Reference URI="#A1">
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>base64string</DigestValue>
                </Reference>
            </SignedInfo>
            <SignatureValue>base64string</SignatureValue>
            <KeyInfo>
                <KeyValue>
                    <RSAKeyValue>
                        <Modulus>base64string</Modulus>
                        <Exponent>AQAB</Exponent>
                    </RSAKeyValue>
                </KeyValue>
                <X509Data>
                    <X509Certificate>base64string</X509Certificate>
                </X509Data>
            </KeyInfo>
        </Signature>
    </ROOT>'
;

$DD = new DOMDocument();
$DD -> loadXML($XML);
$i = 0;

while ($i < 100) {
    // ... a bunch of other code...

    libxml_use_internal_errors(true);
    $old_libxml_disable_entity_loader = libxml_disable_entity_loader(false);        $result = $DD -> schemaValidate(__DIR__ . '/schema.xsd');
    libxml_disable_entity_loader($old_libxml_disable_entity_loader); // Se desactiva nuevamente carga de entidades para descartar entidades maliciosas
    $i++;
    echo str_pad($i, 5) . ($result ? 'true' : 'false') . '<br>';

    // ... a bunch of other code...
}

The problem is that the whole script is reaching the 30 seconds mark, not a DOMDocument::schemaValidate() execution alone.

The execution time corresponds to the full script executing, with all its includes and iterations in case it has.

Consider the execution time does not count any time spent outside the script, such as in stream operations, database queries, amongst others. So for example the script may look as taking 1 minut when in reality it is taking only 15 or 30. See http://php.net/manual/en/function.set-time-limit.php which states:

Note: The set_time_limit() function and the configuration directive max_execution_time only affect the execution time of the script itself. Any time spent on activity that happens outside the execution of the script such as system calls using system(), stream operations, database queries, etc. is not included when determining the maximum time that the script has been running. This is not true on Windows where the measured time is real.

It is not ::schemaValidate() which is taking 30 seconds to execute, it is the full script. Then why once the script reach 30 seconds the error falls just inside schemaValidate()? Because altought ::schemaValidate() it is a relatively fast method to execute, it must be the most complex code inside the iteration, hence the biggest chance is the error falls when schemaValidate is executing, after N repetitions (in the real case N must be a lot).

So the answer is ::schemaValidate() is consuming most of the execution time, hence the error ocurs virtualy always when ::schemaValidate() is executing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM