跳转到内容

PHP 编程/XSL/registerPHPFunctions

来自 Wikibooks,开放世界中的开放书籍

The XSLTProcessor::registerPHPFunctions() 方法**允许将 PHP (v5.0.4+) 函数用作 XSLT-v1 函数**。是“由 PHP 调用的 XSLT 解析器”的 XSLT/registerFunction 功能

它是XSLTProcessor的一个特性,用于将 PHP 函数或方法暴露给 XSLT 脚本(由 importStyleSheet 方法处理)。对 PHP 用户来说非常重要,因为 PHP(以及任何 依赖 libxml2 的)没有 XSLT-v2 引擎,并且可以通过使用registerPHPFunctions克服部分功能缺失。但是,即使在 2013 年,大多数程序员都认为

... 文档编写和支持都很糟糕,并且有很多不完善的地方。尽量少依赖它...

F. Avila 所述

本章的目标,**使用 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

[编辑 | 编辑源代码]

在“将 PHP 暴露给 XSLT”功能的初步概述中,我们可以忽略 XML 输入数据,使用 XSLT 脚本作为静态模板。

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.

XSLT 接收外部 XML 作为字符串

[编辑 | 编辑源代码]

<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=&lt;someTag/&gt;
   PHP xsl_myF2=<someTag/>
   PHP xsl_myF3=
        <aBigFragment> text <someTag val="123"/> text </aBigFragment>

XSLT 接收外部 XML 作为 DOMElement

[编辑 | 编辑源代码]

所有XSLTProcessor活动都依赖于DOMDocument操作,因此,为了获得最佳性能,最好发送DOMElement对象而不是字符串。

子句<xsl:copy-of ... />接收DOMElementDOMDocument,而<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>

XSLT 接收外部片段

[编辑 | 编辑源代码]

一个常见的需求是处理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

使用 PHP 函数与动态 XSLT

[编辑 | 编辑源代码]

"真实"模板使用XML输入数据进行输出。假设以下XML

<allusers>
 <user> <uid>bob</uid> </user>
 <user> <uid>joe</uid> </user>
</allusers>

XSL发送和接收字符串值

[编辑 | 编辑源代码]

要将输入节点作为字符串发送,您可以使用核心函数库的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 ",

XSL-注册函数通过DOM通信

[编辑 | 编辑源代码]

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。

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接收外部片段”部分

设置XSLT全局参数

[编辑 | 编辑源代码]

全局参数在样式表级别定义

  <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注入作为参数

[编辑 | 编辑源代码]

另一种读取外部参数的自然方法是将其作为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)。所有示例均运行。
[编辑 | 编辑源代码]


华夏公益教科书