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.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”功能的初步概述中,我們可以忽略 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.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.
<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=<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.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 ",
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)。所有示例均執行。