PHP 编程/XSL/registerPHPFunctions
The XSLTProcessor::registerPHPFunctions() 方法**允许将 PHP (v5.0.4+) 函数用作 XSLT-v1 函数**。是“由 PHP 调用的 XSLT 解析器”的 XSLT/registerFunction 功能。
它是XSLTProcessor的一个特性,用于将 PHP 函数或方法暴露给 XSLT 脚本(由 importStyleSheet 方法处理)。对 PHP 用户来说非常重要,因为 PHP(以及任何 依赖 libxml2 的)没有 XSLT-v2 引擎,并且可以通过使用registerPHPFunctions克服部分功能缺失。但是,即使在 2013 年,大多数程序员都认为
... 文档编写和支持都很糟糕,并且有很多不完善的地方。尽量少依赖它...
本章的目标,**使用 PHP 函数与 XSLT 的教程**,是**试图改变这种“现状”**。
注意:另一个功能补充是使用 PHP 对 EXSLT 库的支持(参见 http://www.exslt.org/)。另请参见 [1]、[2]、[3] ...以及其他提示(不要与 具有相似名称的函数库 混淆)。通用模块 是与registerPHPFunctions一起使用最重要的模块,在所有 XML 解析器中都有完整的实现。
XSLTProcessor 调用需要一些初始化操作,因此,我们可以将这些初始化操作封装在一个函数中,该函数使用 XML 数据和 XSLT 脚本作为输入,并打印XSLTProcessor的结果。
function XSL_transf($xml,$xsl) {
$xmldoc = DOMDocument::loadXML($xml);
$xsldoc = DOMDocument::loadXML($xsl);
$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsldoc);
echo $proc->transformToXML($xmldoc);
}
为了将XSLT 脚本发送到此XSL_transf()
函数,XSLT 脚本必须是字符串,因此我们可以使用内联声明(参见 PHP 的 Nowdoc 和 Heredoc),
$xsl = <<<'EOB'
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="https://php.ac.cn/xsl">
<xsl:template match="/">
...
</xsl:template>
</xsl:stylesheet>
EOB;
XSL_transf('<root/>',$xsl);
另一种方法是从文件中获取它,
XSL_transf('<root/>',file_get_contents('xslt_script.xsl'));
甚至可以更改XSL_transf()
,
function XSL_transf($xmlFile,$xslFile) {
$xmldoc = DOMDocument::load($xml);
$xsldoc = DOMDocument::load($xsl);
... remaining same code...
}
在<xsl:stylesheet version="1.0" ...>
声明之后,您可以通过例如更改默认输出,
<xsl:output method="text"/>
在本教程的示例中,始终使用 XML 方法,
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
并且,为了在无需在浏览器中打开源代码的情况下查看所有标签,请使用以下命令启动 PHP 脚本:
header("Content-Type: text/plain; charset=utf-8");
在“将 PHP 暴露给 XSLT”功能的初步概述中,我们可以忽略 XML 输入数据,使用 XSLT 脚本作为静态模板。
声明和使用带有 PHP 函数调用的 XSLT 脚本(参见xsl:value-of
),该脚本从 PHP 函数(直接或参数化)导入字符串值。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="https://php.ac.cn/xsl" exclude-result-prefixes="php">
<xsl:template match="/">
PHP time()=<xsl:value-of select="php:function('time')" />,
PHP rand()=<xsl:value-of select="php:function('rand')" />,
PHP rand(11,99)=<xsl:value-of select="php:function('rand',11,99)" />,
PHP xsl_myF1()=<xsl:value-of select="php:function('xsl_myF1_StrConstant')" />,
PHP xsl_myF2(XX)=<xsl:value-of select="php:function('xsl_myF2_id','XX')" />.
</xsl:template>
</xsl:stylesheet>
前两个函数可以在没有任何参数的情况下被调用,后两个函数是用户声明的
function xsl_myF1_StrConstant() { return "123"; }
function xsl_myF2_id($str) { return $str; }
XSL_transf 结果
PHP time()=1365869487, PHP rand()=1410713536, PHP rand(11,99)=20, PHP xsl_myF1()=123, PHP xsl_myF2(XX)=XX.
<xsl:value-of ... />
子句通常接收字符串值,但使用disable-output-escaping
属性,它可以接收整个 XML 片段。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="https://php.ac.cn/xsl">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
PHP xsl_myF2('<someTag/>')=<xsl:value-of select="php:function('xsl_myF2_id','<someTag/>')" />
PHP xsl_myF2('<someTag/>')=<xsl:value-of select="php:function('xsl_myF2_id','<someTag/>')" disable-output-escaping="yes"/>
PHP xsl_myF3()=<xsl:value-of select="php:function('xsl_myF1_XmlConstant')" disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>
其中
function xsl_myF1_XmlConstant() {
return '<aBigFragment> text <someTag val="123"/> text </aBigFragment>';
}
XSL_transf 结果
PHP xsl_myF2=<someTag/>
PHP xsl_myF2=<someTag/>
PHP xsl_myF3=
<aBigFragment> text <someTag val="123"/> text </aBigFragment>
所有XSLTProcessor活动都依赖于DOMDocument操作,因此,为了获得最佳性能,最好发送DOMElement对象而不是字符串。
子句<xsl:copy-of ... />
接收DOMElement或DOMDocument,而<xsl:for-each ...>
接收 DOMNodeList。因此,如果我们有一个返回DOMDocument的 PHP 函数,我们可以使用它。
function xsl_myF4_DOMConstant() {
static $xdom = DOMDocument::loadXML('<t> foo <tt val="123"/> bar </t>');
return $xdom;
}
在 XSLT 脚本中调用xsl_myF4
,
<xsl:template match="/">
PHP xsl_myF4()=<xsl:copy-of select="php:function('xsl_myF4_DOMConstant')" />
</xsl:template>
结果
PHP xsl_myF4()=<t> foo <tt val="123"/> bar </t>
一个常见的需求是处理DOM 片段,即没有根元素的 XML。在上面的示例中,函数xsl_myF4_DOMConstant()
我们使用了<t> foo <tt val="123"/> bar </t>
。如果 needle 的返回值仅为foo <tt val="123"/> bar
,则必须将函数更改为,
function xsl_myF4b_DOMFrag() {
$dom = new DOMDocument;
$tmp = $dom->createDocumentFragment();
$tmp->appendXML(' <t> foo <tt val="123"/> bar </t> TEST');
return $tmp;
}
但现在,在 XSLT 脚本中调用xsl_myF4b
与调用xsl_myF4
不同,现在我们需要更改 XPath 表达式以引用一组节点。
<xsl:template match="/">
PHP xsl_myF4b()=<xsl:copy-of select="php:function('xsl_myF4b_DOMFrag')/node()" />
</xsl:template>
注意:这可能是 LibXML2 的一个错误,此处有解释。
结果
PHP xsl_myF4b()= <t> foo <tt val="123"/> bar </t> TEST
"真实"模板使用XML输入数据进行输出。假设以下XML
<allusers>
<user> <uid>bob</uid> </user>
<user> <uid>joe</uid> </user>
</allusers>
要将输入节点作为字符串发送,您可以使用核心函数库的XPath-v1.0 string()函数,该函数将节点转换为字符串。如果参数是XML片段(具有多个值的节点),则“强制转换为字符串”会将标签替换为空格。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="https://php.ac.cn/xsl">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="allusers">
Users:
<xsl:for-each select="user"> <xsl:value-of select="position()"/>:
myF2(uid)="<xsl:value-of select="php:function('xsl_myF2_id',string(uid))" />",
myF2(.)="<xsl:value-of select="php:function('xsl_myF2_id',string(.))" />",
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XSL_transf 结果
Users: 1: myF2(uid)="BOB", myF2(.)=" BOB textTest ", 2: myF2(uid)="JOE", myF2(.)=" JOE ",
XSLT脚本将节点作为PHP函数参数发送的最完整方法是,在不进行字符串转换的情况下发送它。PHP函数将接收一个DOMElement数组作为参数,并且PHP可以将一个DOMElement发送回XSLT脚本。
这是使用此“DOM通信”实现的标识函数
function xsl_myF5_id($m) { // $m is always an array
$ele = $m[0]; // get_class($m[0])==DOMElement
return $ele; // XSLT accepts only DOMElement or DOMDocument
}
在输入节点循环中使用此函数,
<xsl:for-each select="user"> <xsl:value-of select="position()"/>:
copy-of myF5(uid)="<copy-of select="php:function('xsl_myF5_id', uid)" />",
value-of myF5(uid)="<xsl:value-of select="php:function('xsl_myF5_id', uid)" />",
copy-of myF5(.)=<xsl:copy-of select="php:function('xsl_myF5_id', . )" />.
</xsl:for-each>
XSL_transf 结果
1: copy-of myF5(uid)="<uid>BOB</uid>", value-of myF5(uid)="BOB", copy-of myF5(.)=<user> <uid>BOB</uid> textTest </user>. 2: copy-of myF5(uid)="<uid>JOE</uid>", value-of myF5(uid)="JOE", copy-of myF5(.)=<user> <uid>JOE</uid> </user>.
用于列表:像这样的函数xsl_myF5_id可以返回NULL,不会产生干扰。这对于数组(或数据库)组合很有用,稍后可以通过另一个函数检索到XSLT。
有多种方法可以将PHP变量作为全局参数传递到XSLT中
- 调用php:function返回变量的PHP值;
- 在解析器中使用setparameter,从xsl:parameter声明创建真正的XSLT变量。
- 在XML输入中注入“参数-XML”。
第一个可能是最好的,但每个都有其优缺点。
与setParamter(下面的部分)相比,函数具有携带XML片段(不仅仅是字符串值)的优势,但XSLT无法将其作为普通变量访问。XSLT中的典型用法
<xsl:value-of select="php:function('xsl_strParam','param1')" />
在PHP中使用类似的内容
function xsl_strParam($paramName) {global $PARAMS; return $PARAMS[$paramName];}
要返回DOM片段,请参阅“XSL接收外部片段”部分。
全局参数在样式表级别定义
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="param1" select="'default-string1'"/>
</xsl:stylesheet>
它们可以具有默认值,由select语句指定。全局参数可用于将值从外部应用程序传递到样式表。
- 提示:您可以在select属性中使用XPath,
<xsl:param name="p1" select="."/>
,因此,当它不是XPath时,请注意使用"'string'"
。
要使用XSLTProcessor::setParameter,请重写XSL_transf(),在准备部分
function XSL_transf($xml,$xsl,$param1val) {
$xmldoc = DOMDocument::loadXML($xml);
$xsldoc = DOMDocument::loadXML($xsl);
$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsldoc);
$proc->setParameter('', 'param1', $param1val); // add here, $param1val will overwrites 'default-string1'
echo $proc->transformToXML($xmldoc);
}
另一种读取外部参数的自然方法是将其作为XML输入字符串的一部分。必须检查某些DTD约定,采用一些“数组到XML”约定,并且主函数(例如上面的XSL_transf()
函数)必须处理DOM一次插入或替换。
当有很多参数或XML片段时,建议使用此方法。smallest-php-xml-xsl-framework的2.0.2版本和在那里进行的“状态注入”就是使用此策略的一个示例。
... 标准库建议 ...
... 请参阅XSLT/标准注册函数 ...
请协作测试
- PHP 5.3.10-1ubuntu3.6 (Zend Engine v2.3.0)。所有示例均运行。