跳轉到內容

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.net.tw/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.net.tw/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.net.tw/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.net.tw/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)。所有示例均執行。
[編輯 | 編輯原始碼]


華夏公益教科書