PHP Conference Japan 2024

XSLTProcessor::registerPHPFunctions

(PHP 5 >= 5.0.4, PHP 7, PHP 8)

XSLTProcessor::registerPHPFunctions啟用將 PHP 函式用作 XSLT 函式的功能

描述

public XSLTProcessor::registerPHPFunctions(array|string|null $functions = null): void

此方法啟用在 XSL 樣式表中將 PHP 函式用作 XSLT 函式的功能。

參數

functions

使用此參數僅允許從 XSLT 呼叫特定函式。

此參數可以是下列之一:字串 (函式名稱)、索引陣列的函式名稱,或具有索引鍵為函式名稱且關聯值為可呼叫的關聯陣列

回傳值

不回傳值。

變更記錄

版本 描述
8.4.0 現在可以使用可呼叫的物件,當使用帶有陣列條目的 restrict時作為回呼。

範例

範例 #1 從樣式表呼叫簡單 PHP 函式

<?php
$xml
= <<<EOB
<allusers>
<user>
<uid>bob</uid>
</user>
<user>
<uid>joe</uid>
</user>
</allusers>
EOB;
$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.dev.org.tw/xsl">
<xsl:output method="html" encoding="utf-8" indent="yes"/>
<xsl:template match="allusers">
<html><body>
<h2>使用者</h2>
<table>
<xsl:for-each select="user">
<tr><td>
<xsl:value-of
select="php:function('ucfirst',string(uid))"/>
</td></tr>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>
EOB;
$xmldoc = new DOMDocument();
$xmldoc->loadXML($xml);
$xsldoc = new DOMDocument();
$xsldoc->loadXML($xsl);

$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsldoc);
echo
$proc->transformToXML($xmldoc);
?>

參見

新增筆記

使用者貢獻的筆記 20 則筆記

heinemann dot juergen at hjcms dot de
18 年前
您可以在 PHP 原始碼 php-5.*/ext/xsl/tests 中找到更多範例
<?php

$xform
= <<<EOT
<?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.dev.org.tw/xsl"
xsl:extension-element-prefixes="php"
>
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:namespace-alias stylesheet-prefix="php" result-prefix="xsl" />
<xsl:template match="root">
<html>
<head>
<title>日期格式</title>
</head>
<body>
<xsl:for-each select="datenode">
<li>
<xsl:value-of select="php:functionString('convertDate', . )" />
</li>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
EOT;

function
convertDate( $i )
{
setlocale( LC_TIME, 'de_DE' );
return
utf8_encode( strftime( '%B %d %A %Y %H:%M:%S CET', $i ) );
}

$xsl = new XSLTProcessor;
$xsl->registerPHPFunctions();
$xsl->setParameter( 'DOCTYPE', 'PUBLIC', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' );
$xsl->setParameter( 'html', 'xmlns', 'http://www.w3.org/1999/xhtml' );

$xdom = new DomDocument( '1.0', 'utf-8' );
$xdom->loadXML( $xform );

$xsl->importStyleSheet( $xdom );
unset(
$xdom );

$dom = new DomDocument( '1.0', 'utf-8' );
$r = $dom->appendChild( $dom->createElement( 'root' ) );
foreach (
range( 1, 12 ) AS $i ) {
$r->appendChild( $dom->createElement( 'datenode', mktime( date('G'), date('i'), date('s'), $i, date('d'), date('Y') ) ) );
}

header( "Content-Type: text/html; charset=utf-8;" );
header( "Content-Encoding: utf-8" );
echo
$xsl->transformToXML( $dom );

?>
Matthew, info at mathias-schaefer dot de
14 年前
=> 不需要你的類別實例,你必須注意命名空間 (使用 5.3)。

<!-- xsl
<xsl:value-of select="php:function ('My\Name\Space\MyClassName::methodName',1+2)"/>
--->

<?php
namespace My\Name\Space;

class
MyClassName
{
static public function
methodName($someArgument)
{
return
"resultis:".$someArgument;
}
}
?>

<!-- 結果 -->
resultis:3
-->

=> 不需要對參數進行字串轉換
=> 也可以回傳數字 (或許 DOM 節點也可以)

繼續努力開發吧!
Demerit
14 年前
各位,讓我們把術語弄正確。

如果您呼叫的是類別內部的函式,請使用它的「實例」名稱來參考它,而不是它的類別名稱。

class.my_class.php

<?php
class class_name {
static function
function_name($in){
return (
$out);
}
}
?>

calling_document.php

<?php
require_once('class.my_class.php');
$instance_name=new class_name();
?>

my_xslt.xsl

<xsl:value-of select="php:function ('instance_name::function_name','garbage')" />

順帶一提,在 5.1.6 版本中,只有雙冒號 (::) 才能在呼叫類別內部的函式時正常運作。不過,您必須將您的函式宣告為 static。使用某些人建議的 -> (例如,php:function ('instance_name->function_name','garbage')) 完全無效,無論您如何宣告您的函式。
taylorbarstow at that google mail thingy
18 年前
關於從 PHP 函式回傳節點集給 XSLT 的先前說明 (如下) 的補充

您不必回傳 DOMDocument,DOMElement 也能正常運作。此外,回傳 DOMElement 可以解決捨棄根節點的問題,這是我在下面討論的,也是 "Ingram" 提到的。
Ingram
18 年前
經測試回傳節點集是由以下貢獻者提供的:

taylorbarstow at that google mail thingy

(運作良好,感謝您!)

我發現使用以下方式:

===
"據推測,建立一個範本來進行捨棄是值得的

<xsl:template select="*" mode="discardRoot">
<xsl:apply-templates select="./*" />
</xsl:template>

您可以這樣呼叫它

<xsl:apply-templates select="php:function('getNodeSet')" mode="discardRoot" /> "
===

我只能輸出文字,而無法在套用範本後輸出任何標籤,也就是說,它會移除文字周圍的所有元素。

改為使用

===
<xsl:template match="/">
<xsl:for-each select="php:function('getNodeSet')" />
<xsl:apply-templates />
</xsl:for-each>
</xsl:template>

這實際上會捨棄根節點。
===

運作良好,並且讓我可以在回傳的節點集上順利套用範本。
hopeseekr at gmail dot com
18 年前
我遇到最麻煩的事情之一就是編碼 URL 參數。我的意思是,假設您想要使用 "search+terms" 而不是僅僅 "search terms" 來填入連結。我當時在 XML 中包含了兩個獨立的 URL,這太荒謬了。

以下是一個更優雅的 PHP+XSLT 解決方案。您還會看到它使用了 registerPHPFunctions() 的兩個*未公開*功能,也就是 php:functionString() 和將參數傳遞給函式。我透過反覆試驗才弄清楚這一點;我真的希望這個註解對您有所幫助,因為它*大大地*擴展了 XSLT 中 PHP 函式的功能!

<?php
/* --- XML 輸入 --- */
<search_results>
<
query>演唱會門票</query>
</
search_results>

/* --- XSL 程式碼 --- */
<!-- 顯示查詢 -->
<
xsl:template match="search_results">
<!--
透過 PHP 取得 URL 編碼字串 -->
<
xsl:variable name="safeurl" as="xs:string" select="php:functionString('urlencode', query)" />
<
p>您搜尋的 <em><xsl:value-of select="query"/></em> 可以在 <a href="http://www.tixtix.com/search.php?q={$safeurl}">我們的搜尋引擎</a></p>
</
xsl:template>

/* --- XHTML 輸出 --- */
<p>您搜尋的 <em>演唱會門票</em> 可以在 <a href="http://www.tixtix.com/search.php?q=space+cowboy">我們的搜尋引擎</a></p>
?>

很酷吧?
taylorbarstow at that google mail thingy
19 年前
從 PHP 函式,您可以使用 DOMDocument 將節點集傳遞回 XSL。例如:

<?php

function getNodeSet() {
$xml =
"<test>" .
"<a-node>這是一個節點</a-node>" .
"<a-node>這是另一個節點</a-node>" .
"</test>";
$doc = new DOMDocument;
$doc->loadXml($xml);
return
$doc;
}

?>

我發現的唯一問題是,您回傳的 DOM 文件中的根層級節點的作用就像原始文件的根層級節點一樣。因此,很容易引入無限迴圈,例如:

<xsl:template match="/">
<xsl:apply-templates select="php:function('getNodeSet')" />
</xsl:template>

為了避免這種情況,我一直在使用類似以下的結構:

<xsl:template match="/">
<xsl:for-each select="php:function('getNodeSet')" />
<xsl:apply-templates />
</xsl:for-each>
</xsl:template>

這實際上會捨棄根節點。據推測,建立一個範本來進行捨棄是值得的

<xsl:template select="*" mode="discardRoot">
<xsl:apply-templates select="./*" />
</xsl:template>

您可以這樣呼叫它

<xsl:apply-templates select="php:function('getNodeSet')" mode="discardRoot" />
zac at zacbowling dot com
20 年前
如果人們在使用此函式時遇到問題,我已經告訴很多人檢查是否有任何 'xmlns' 屬性
這些屬性是由某些流行的軟體類型產生並新增到您的 xml 頁面中的。
這些屬性是由某些流行的軟體類型產生並新增到您的 xml 頁面中的。
這些屬性是由某些流行的軟體類型產生並新增到您的 xml 頁面中的。

<?php
$file
= "http://data.map***.net/m***ck.asmx/GetMessages?IMEI=$id";

$docxml = file_get_contents($file);

//您可能需要執行類似以下的操作,
//我會移除我的 ASP.NET SOAP 回應所回傳的 xmlns 標籤的所有實例。

$docxml =
str_replace("xmlns=\"http://data.map***.net/m***ck.asmx?WSDL\"",
"",$docxml);

$xslt = new xsltProcessor;

//如果您不移除它們,此函式將會爆炸。
$xslt->registerPHPFunctions();
$xslt->importStyleSheet(DomDocument::load('../xsl/message.xsl'));
print
$xslt->transformToXML(DomDocument::loadXML($docxml));

?>

此外,此函式還有一些很酷的技巧,您可以呼叫
內建的 PHP 函式。例如:

<xsl:value-of
select="php:function('nl2br',string(MessageContent/Message))"
disable-output-escaping="yes"/>

現在,該 XSL 值將會回傳您的正常字串,但會將您 XML 中的所有換行符號取代為 '<br />'。
您的新行字元取代為 '<br />'。

另請注意 'disable-output-escaping="yes"' 語句。如果
您不呼叫它,則該繫結的輸出將會執行
基本上是 "htmlencode()" 類型的函式。

最後但並非最不重要的一點,請看一下我在傳回它之前在 XSL 中呼叫的 'string()' 函式。那是因為如果沒有
呼叫該函式,當它執行時,它會嘗試傳遞節點物件,
而不是它的值 (這很可能是您想要的)。
而不是它的值 (這很可能是您想要的)。

這個函式非常棒,可能會導致一些非常
有趣的程式碼開發。可以遠端載入外觀樣式。
您可以不用太多程式碼就可以用 PHP 撰寫 RSS 檢視器。
您可以將 XHTML 頁面剖析為另一個檢視 (在本機或遠端)。然後,您可以使用相同的 XML
然後,您可以使用相同的 XML
內容,並使用相同的 XSL 樣式表將它傳遞給 ASP.NET、Java 或甚至是命令列處理工具,並為您的頁面產生前端,而無需
內容,並使用相同的 XSL 樣式表將它傳遞給 ASP.NET、Java 或甚至是命令列處理工具,並為您的頁面產生前端,而無需
太多變更。我非常興奮。
太多變更。我非常興奮。

程式碼愉快。
zac at zacbowling dot com
20 年前
關於此函式,有一點需要注意的是。很多值需要在使用 XSL 中的 "string()" 函式將其轉換為 XSLT 字串,然後再將其傳遞給您的函式,而且當您回傳它們時,請確保如果它們是字串,則在執行此操作之前在 php 中呼叫 "strval()"。這為我節省了數小時。

希望這有幫助。

Zac Bowling
Ariz Jacinto
16 年前
如果您想使用類似 XPath 2.0[1] 的 replace() 函數,並且想避免像這個[2]一樣冗長的 XSLT 函數,您可以使用 PHP 的 str_replace() 來替代。

<xsl:value-of select="php:functionString('str_replace','pattern', 'replacement' , . )">

連結
[1] http://www.w3.org/TR/2004/WD-xpath-functions-20041029/#func-replace
[2] http://aspn.activestate.com/ASPN/Cookbook/XSLT/Recipe/65426

_
thsoft at vipmail dot hu
16 年前
很遺憾地,您無法從 XLST 傳遞陣列到 PHP。
但是您可以傳遞任意數量的參數到 PHP 函數,然後使用 func_get_args() 將它們作為陣列取得。
[編寫一個與 array() 結構相同的函數是沒有意義的,因為函數的返回值在作為參數傳遞給另一個函數時會被轉換為字符串。這意味著無法以這種方式處理深層陣列。]
junkmail at eighteyes dot com
20 年前
當編寫使用回呼函數的樣式表時,請務必包含 php 的命名空間宣告,如下所示:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="https://php.dev.org.tw/xsl" version='1.0'>
begemot at php dot com dot ua
20 年前
我想這對您有幫助。
<?php

function dateLang () {
return
strftime("%A");
}

$xsl = new DomDocument();
$xsl->load("datetime.xsl");
$inputdom = new DomDocument();
$inputdom->load("today.xml");

$proc = new XsltProcessor();
$proc->registerPhpFunctions();

// 載入文件並使用 $xslt 進行處理
$xsl = $proc->importStylesheet($xsl);

/* 轉換並輸出 xml 文檔 */
$newdom = $proc->transformToDoc($inputdom);

print
$newdom->saveXML();

?>

這是會呼叫該函數的 XSLT 樣式表,datetime.xsl:

<?xml version="1.0" encoding="iso-8859-1" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="https://php.dev.org.tw/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('dateLang')" />
</xsl:template>
</xsl:stylesheet>

這是個絕對精簡的 XML 檔案 today.xml,用於傳遞到樣式表 (雖然 articles.xml 也能達到相同結果)

<?xml version="1.0" encoding="iso-8859-1" ?>
<today></today>
Nic Luciano
15 年前
Ritch 說:「如果您想使用類別裡面的函數,請使用雙冒號 (::) 表示法...」

另請注意,類別函數必須宣告為靜態 (static)。
thsoft at vipmail dot hu
17 年前
從 PHP 接收返回的 DOM 節點 (無論是元素、文檔、文檔片段等) 的正確方法是使用 <xsl:copy-of select="php:function...

我在此找到這個想法和範例:http://bugs.php.net/bug.php?id=29409

有關原因的技術細節,請參閱http://www.w3.org/TR/xslt#copy-of
phil at worldpoolfederation dot com
18 年前
不是真的

使用 :: 呼叫靜態函數
使用 -> 呼叫非靜態函數

您必須在 php 5.1 中將函數定義為靜態,才能夠以靜態方式呼叫它。
mark at thirst dot org
18 年前
Ritch 說:「如果您想使用類別裡面的函數,請使用雙冒號 (::) 表示法...」這在 5.0.4 中可行,但在 5.1.6 中已不再適用。
franp at free dot fr
18 年前
請注意,如果您希望您的輸出驗證通過某些 xhtml dtd,您必須將以下屬性添加到 xslt 樣式表的 xsl:stylesheet 元素:
exclude-result-prefixes="php"。

否則,您會收到「無效屬性 xmlns:php」錯誤。
benbarnett
19 年前
您可以在 XSL 中使用 php:functionString(),它會自動將輸出轉換為字符串!
Ritch at Bugsoftware dot co dot uk
19 年前
如果您想使用類別裡面的函數,請使用雙冒號 (::) 表示法,例如:

php:functionString('classname::function')

該函數會以靜態方式觸發,因此就像全域命名空間中的函數一樣運作。
To Top