Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
339 views
in Technique[技术] by (71.8m points)

xslt - How to filter a select nodeset with a PHP function?

I wonder if and how it is possible to register a PHP userspace function with the XSLT processor that is able not only to take an array of nodes but also to return it?

Right now PHP complains about an array to string conversion using the common setup:

function all_but_first(array $nodes) {        
    array_shift($nodes);
    shuffle($nodes);
    return $nodes;
};

$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStylesheet($xslDoc);
$buffer = $proc->transformToXML($xmlDoc);

The XMLDocument ($xmlDoc) to transform can for example be:

<p>
   <name>Name-1</name>
   <name>Name-2</name>
   <name>Name-3</name>
   <name>Name-4</name>
</p>

Within the stylesheet it's called like this:

<xsl:template name="listing">
    <xsl:apply-templates select="php:function('all_but_first', /p/name)">
    </xsl:apply-templates>
</xsl:template>

The notice is the following:

Notice: Array to string conversion

I don't understand why if the function gets an array as input is not able to return an array as well?

I was also trying other "function" names as I've seen there is php:functionString but all tried so far (php:functionArray, php:functionSet and php:functionList) did not work.

In the PHP manual it's written I can return another DOMDocument containing elements, however then those elements aren't from the original document any longer. That does not make much sense to me.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Something that works for me is to return an instance of DOMDocumentFragment instead of an array. So to try it on your example, I saved your input as foo.xml. Then I made foo.xslt look like this:

<xsl:stylesheet version="1.0" xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
        xmlns:php="http://php.net/xsl">
    <xsl:template match="/">
        <xsl:call-template name="listing" />
    </xsl:template>
    <xsl:template match="name">
        <bar> <xsl:value-of select="text()" /> </bar>
    </xsl:template>
    <xsl:template name="listing">
        <foo>
            <xsl:for-each select="php:function('all_but_first', /p/name)">
                <xsl:apply-templates />
            </xsl:for-each>
        </foo>
    </xsl:template>
</xsl:stylesheet>

(This is mostly just your example with a xsl:stylesheet wrapper to invoke it.) And the real heart of the matter, foo.php:

<?php

function all_but_first($nodes) {
    if (($nodes == null) || (count($nodes) == 0)) {
        return ''; // Not sure what the right "nothing" return value is
    }
    $returnValue = $nodes[0]->ownerDocument->createDocumentFragment();
    array_shift($nodes);
    shuffle($nodes);
    foreach ($nodes as $node) {
        $returnValue->appendChild($node);
    }
    return $returnValue;
};

$xslDoc = new SimpleXMLElement('./foo.xslt', 0, true);
$xmlDoc = new SimpleXMLElement('./foo.xml', 0, true);

$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStylesheet($xslDoc);
$buffer = $proc->transformToXML($xmlDoc);
echo $buffer;

?>

The important part being the call to ownerDocument->createDocumentFragment() to make the object that gets returned from the function.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...