PHP : different behaviour of XSLT Processor in Windows WRT to Linux

331 views Asked by At

I've got this php snippet:

$xsltPath = $argv[1];
$xmlPath = $argv[2];

$xslt = file_get_contents($xsltPath);
$xml = file_get_contents($xmlPath);

$templateCMSObj = new \DOMDocument();
$templateCMSObj->loadXML($xslt);
$ekbXMLObj = new \DOMDocument();
$ekbXMLObj->loadXML($xml);
$proc = new \XSLTProcessor();
$proc->importStylesheet($templateCMSObj);
$html = $proc->transformToXML($ekbXMLObj);
echo($html);
exit;

Which simply applies an XSLT to a given XML document.

When I apply the below XSLT to the same XML doc I got a different behaviour of Windows wrt to Linux PHP version.

Here's php and libxml version detail:

Windows:

PHP 7.1.6 (cli) (built: Jun  8 2017 02:06:32) ( ZTS MSVC14 (Visual C++ 2015) x86 )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

XML Support => active
XML Namespace Support => active
libxml2 Version => 2.9.4
XMLReader => enabled
XMLWriter => enabled
XSL => enabled
libxslt Version => 1.1.29
libxslt compiled against libxml Version => 2.9.4
EXSLT => enabled
libexslt Version => 0.8.17

Linux:

PHP 7.0.32-1~dotdeb+8.1 (cli) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
    with Zend OPcache v7.0.32-1~dotdeb+8.1, Copyright (c) 1999-2017, by Zend Technologies
    with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans

XML Support => active
XML Namespace Support => active
libxml2 Version => 2.9.1
XMLReader => enabled
XMLWriter => enabled
XSL => enabled
libxslt Version => 1.1.28
libxslt compiled against libxml Version => 2.9.1
EXSLT => enabled
libexslt Version => 1.1.28

Here's is the XSLT code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://new.webservice.namespace">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    <xsl:param name="searchPath" select="11"/>
    <xsl:variable name="slash" select="'/'"/>
    <xsl:variable name="dot" select="'.'"/>
    <xsl:variable name="open_bracket" select="'{'"/>
    <xsl:variable name="closed_bracket" select="'}'"/>
    <xsl:template match="/">
        <ns:flat_pallet>
            <xsl:attribute name="tipo"><xsl:value-of select="//ns:EKB_piatto/@tipo"/></xsl:attribute>
            <xsl:apply-templates select=".//ns:gruppo_logico/ns:versione/ns:contenuto/ns:riferimento_oi"/>
        </ns:flat_pallet>
    </xsl:template>

    <!--restituisco tipo del box e l'indice relativo a nel sottoalbero-->
    <xsl:template match="ns:gruppo_logico">
        <xsl:variable name="tipo">
            <xsl:value-of select="@tipo"/>
        </xsl:variable>
        <xsl:variable name="index">
            <xsl:number level="single" count="node()[@tipo=$tipo]" format="1"/>
        </xsl:variable>
        <xsl:value-of select="normalize-space($tipo)" disable-output-escaping="yes"/>
        <xsl:value-of select="$open_bracket"/>
        <xsl:value-of select="$index -1" disable-output-escaping="yes"/>
        <xsl:value-of select="$closed_bracket"/>
        <xsl:value-of select="$slash"/>
    </xsl:template>
    <!--quando sono in una unit risalgo i box progenitori-->
    <xsl:template match="ns:riferimento_oi">
        <xsl:variable name="tipoUnit" select="./ns:tipo"/>
        <xsl:variable name="index">
            <xsl:number level="single" count="node()[node()/ns:tipo=$tipoUnit]" format="1"/>
        </xsl:variable>
        <xsl:variable name="labelPath">
            <xsl:apply-templates select="ancestor::ns:gruppo_logico"/>
            <xsl:value-of select="$tipoUnit"/>
            <xsl:value-of select="$open_bracket"/>
            <xsl:value-of select="$index -1"/>
            <xsl:value-of select="$closed_bracket"/>
        </xsl:variable>
        <xsl:copy>
            <xsl:attribute name="labelPath"><xsl:value-of select="$labelPath" disable-output-escaping="yes"/></xsl:attribute>
            <xsl:attribute name="unitIndex"><xsl:value-of select="$index" disable-output-escaping="yes"/></xsl:attribute>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <!-- just copy all my attributes and child nodes, except if there's a better template for some of them -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!--template per rimuovere nodi vuoti-->
    <xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']"/>
</xsl:stylesheet>

And the XML Doc which gaves different results in win and linux XSLT:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:dettaglioFormEKBSOUT xmlns:ns="http://new.webservice.namespace">
    <ns:EKB_piatto>
        <ns:campo_GL>
            <ns:gruppo_logico tipo="Standard">
                <ns:versione id="1">
                    <ns:contenuto>
                        <ns:riferimento_oi>
                            <ns:tipo>Standard</ns:tipo>
                            <ns:natura_OI>
                                <ns:UNI>2.1</ns:UNI>
                            </ns:natura_OI>
                        </ns:riferimento_oi>
                    </ns:contenuto>
                    <ns:contenuto>
                        <ns:riferimento_oi>
                            <ns:tipo>Standard</ns:tipo>
                            <ns:natura_OI>
                                <ns:UNI>2.2</ns:UNI>
                            </ns:natura_OI>
                        </ns:riferimento_oi>
                    </ns:contenuto>
                </ns:versione>
            </ns:gruppo_logico>
            <ns:gruppo_logico tipo="Standard_due">
                <ns:versione id="1">
                    <ns:contenuto>
                        <ns:riferimento_oi>
                            <ns:tipo>Standard</ns:tipo>
                            <ns:natura_OI>
                                <ns:UNI>2.1</ns:UNI>
                            </ns:natura_OI>
                        </ns:riferimento_oi>
                    </ns:contenuto>
                    <ns:contenuto>
                        <ns:riferimento_oi>
                            <ns:tipo>Standard_due</ns:tipo>
                            <ns:natura_OI>
                                <ns:UNI>2.2</ns:UNI>
                            </ns:natura_OI>
                        </ns:riferimento_oi>
                    </ns:contenuto>
                </ns:versione>
            </ns:gruppo_logico>
        </ns:campo_GL>
    </ns:EKB_piatto>
</ns:dettaglioFormEKBSOUT>

Linux output This is the desired output which actually results from applying XSLT on Linux:

<?xml version="1.0"?>
<ns:flat_pallet xmlns:ns="http://new.webservice.namespace" tipo="">
    <ns:riferimento_oi labelPath="Standard{0}/Standard{0}" unitIndex="1">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.1</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard{0}/Standard{1}" unitIndex="2">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.2</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard_due{0}/Standard{0}" unitIndex="1">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.1</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard_due{0}/Standard_due{0}" unitIndex="1">
        <ns:tipo>Standard_due</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.2</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
</ns:flat_pallet>

Windows output This is the wrong result produces on Windows:

<?xml version="1.0"?>
<ns:flat_pallet xmlns:ns="http://new.webservice.namespace" tipo="">
    <ns:riferimento_oi labelPath="Standard{0}/Standard{0}" unitIndex="1">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.1</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard{0}/Standard{1}" unitIndex="2">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.2</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard_due{NaN}/Standard{0}" unitIndex="1">
        <ns:tipo>Standard</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.1</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
    <ns:riferimento_oi labelPath="Standard_due{NaN}/Standard_due{NaN}" unitIndex="">
        <ns:tipo>Standard_due</ns:tipo>
        <ns:natura_OI>
            <ns:UNI>2.2</ns:UNI>
        </ns:natura_OI>
    </ns:riferimento_oi>
</ns:flat_pallet>

The problem lies in the NaN put instead of relative index: it surely has to do with xsl:number element, but I can't figure out how to fix this...


Edit after accepted answer

By changing xsl:number count attribute to * instead of node() the xslt template works as expected on both Win and Linux.

Here's the updated code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://new.webservice.namespace">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    <xsl:param name="searchPath" select="11"/>
    <xsl:variable name="slash" select="'/'"/>
    <xsl:variable name="dot" select="'.'"/>
    <xsl:variable name="open_bracket" select="'{'"/>
    <xsl:variable name="closed_bracket" select="'}'"/>
    <xsl:template match="/">
        <ns:flat_pallet>
            <xsl:attribute name="tipo"><xsl:value-of select="//ns:EKB_piatto/@tipo"/></xsl:attribute>
            <xsl:apply-templates select=".//ns:gruppo_logico/ns:versione/ns:contenuto/ns:riferimento_oi"/>
        </ns:flat_pallet>
    </xsl:template>

    <!--restituisco tipo del box e l'indice relativo a nel sottoalbero-->
    <xsl:template match="ns:gruppo_logico">
        <xsl:variable name="tipo">
            <xsl:value-of select="@tipo"/>
        </xsl:variable>
        <xsl:variable name="index">
            <xsl:number level="single" count="*[@tipo=$tipo]" format="1"/>
        </xsl:variable>
        <xsl:value-of select="normalize-space($tipo)" disable-output-escaping="yes"/>
        <xsl:value-of select="$open_bracket"/>
        <xsl:value-of select="$index -1" disable-output-escaping="yes"/>
        <xsl:value-of select="$closed_bracket"/>
        <xsl:value-of select="$slash"/>
    </xsl:template>
    <!--quando sono in una unit risalgo i box progenitori-->
    <xsl:template match="ns:riferimento_oi">
        <xsl:variable name="tipoUnit" select="./ns:tipo"/>
        <xsl:variable name="index">
            <xsl:number level="single" count="*[*/ns:tipo=$tipoUnit]" format="1"/>
        </xsl:variable>
        <xsl:variable name="labelPath">
            <xsl:apply-templates select="ancestor::ns:gruppo_logico"/>
            <xsl:value-of select="$tipoUnit"/>
            <xsl:value-of select="$open_bracket"/>
            <xsl:value-of select="$index -1"/>
            <xsl:value-of select="$closed_bracket"/>
        </xsl:variable>
        <xsl:copy>
            <xsl:attribute name="labelPath"><xsl:value-of select="$labelPath" disable-output-escaping="yes"/></xsl:attribute>
            <xsl:attribute name="unitIndex"><xsl:value-of select="$index" disable-output-escaping="yes"/></xsl:attribute>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <!-- just copy all my attributes and child nodes, except if there's a better template for some of them -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!--template per rimuovere nodi vuoti-->
    <xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']"/>
</xsl:stylesheet>
1

There are 1 answers

3
Daniel Haley On BEST ANSWER

In the select attribute of xsl:number, use * instead of node().

When you use node(), that can be an element, text, comment, or processing instruction node.

When you use *, that's only an element. That makes more sense in your use of xsl:number.

See https://xsltfiddle.liberty-development.net/nc4NzRq/1 for a working fiddle. (Thanks @parfait!)