XSL 转换
XSLT(Extensible Stylesheet Language Transformations)是一种将 XML 文档转换为其他格式(XML、HTML、文本)的语言。PHP 的 XSL 扩展基于 libxslt 库,提供了完整的 XSLT 1.0 处理支持。本节将讲解如何使用 PHP 进行 XSL 转换。
前置知识
阅读本节前,建议先了解:DOM 操作、XMLReader / XMLWriter
基础概念
XSLT 的用途
- XML 转 HTML:将数据 XML 转换为网页
- XML 到 XML:文档格式转换(如 RSS 转 Atom)
- 数据提取:从复杂 XML 中提取特定数据
- 报告生成:将 XML 数据转换为可读的报告
安装
bash
# 编译安装
./configure --with-xsl
# Ubuntu/Debian
sudo apt-get install php-xslXSLT 转换基础
基本转换流程
php
<?php
declare(strict_types=1);
// XML 数据
$xmlString = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book category="fiction">
<title>Harry Potter</title>
<author>J.K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="programming">
<title>Clean Code</title>
<author>Robert C. Martin</author>
<year>2008</year>
<price>39.99</price>
</book>
</books>
XML;
// XSL 样式表
$xslString = <<<XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html>
<head><title>书籍列表</title></head>
<body>
<h1>书籍列表</h1>
<table border="1">
<tr>
<th>书名</th>
<th>作者</th>
<th>年份</th>
<th>价格</th>
</tr>
<xsl:for-each select="books/book">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="author"/></td>
<td><xsl:value-of select="year"/></td>
<td>$<xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSL;
// 加载 XML
$xml = new DOMDocument();
$xml->loadXML($xmlString);
// 加载 XSL
$xsl = new DOMDocument();
$xsl->loadXML($xslString);
// 创建 XSLT 处理器
$processor = new XSLTProcessor();
$processor->importStyleSheet($xsl);
// 执行转换
$result = $processor->transformToXML($xml);
echo $result;转换为 DOMDocument
php
<?php
declare(strict_types=1);
$processor = new XSLTProcessor();
$processor->importStylesheet($xslDoc);
// 转换为 DOMDocument(可进一步操作)
$domResult = $processor->transformToDoc($xmlDoc);
// 转换为文件
$success = $processor->transformToURI($xmlDoc, '/tmp/output.html');
// 转换为字符串
$html = $processor->transformToXML($xmlDoc);传递参数
设置 XSLT 参数
php
<?php
declare(strict_types=1);
$xslString = <<<XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:param name="title" select="'书籍列表'"/>
<xsl:param name="currency" select="'USD'"/>
<xsl:param name="minPrice" select="0"/>
<xsl:template match="/">
<html>
<body>
<h1><xsl:value-of select="$title"/></h1>
<xsl:for-each select="books/book[price >= $minPrice]">
<p>
<xsl:value-of select="title"/>
- <xsl:value-of select="$currency"/> <xsl:value-of select="price"/>
</p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSL;
$processor = new XSLTProcessor();
$processor->importStylesheet($xslDoc);
// 传递参数
$processor->setParameter(null, 'title', '我的书架');
$processor->setParameter(null, 'currency', 'CNY');
$processor->setParameter(null, 'minPrice', 30);
// 也可以指定命名空间
// $processor->setParameter('http://example.com/ns', 'param', 'value');
$html = $processor->transformToXML($xmlDoc);
echo $html;注册 PHP 函数
在 XSLT 中调用 PHP 函数
php
<?php
declare(strict_types=1);
// PHP 函数:将价格格式化为人民币
function formatPrice(string $price): string
{
return '¥' . number_format((float) $price, 2);
}
// PHP 函数:计算折扣价
function applyDiscount(string $price, string $discount): string
{
return number_format((float) $price * (float) $discount, 2);
}
$processor = new XSLTProcessor();
$processor->importStylesheet($xslDoc);
// 注册 PHP 函数供 XSLT 调用
$processor->registerPHPFunctions(['formatPrice', 'applyDiscount']);
$xslWithPhp = <<<XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl"
exclude-result-prefixes="php">
<xsl:output method="html" encoding="UTF-8"/>
<xsl:template match="/">
<html><body>
<xsl:for-each select="books/book">
<p>
<xsl:value-of select="title"/>
- <xsl:value-of select="php:function('formatPrice', price)"/>
</p>
<p>
折扣价: <xsl:value-of select="php:function('applyDiscount', price, '0.8')"/>
</p>
</xsl:for-each>
</body></html>
</xsl:template>
</xsl:stylesheet>
XSL;安全警告
registerPHPFunctions 允许 XSLT 调用任意 PHP 函数,存在安全风险。仅在可信的 XSL 样式表中使用,或使用白名单限制可调用的函数。
详细说明
XSLTProcessor 方法一览
| 方法 | 说明 |
|---|---|
importStyleSheet() | 导入 XSL 样式表 |
transformToDoc() | 转换为 DOMDocument |
transformToURI() | 转换并写入文件/URI |
transformToXML() | 转换为 XML 字符串 |
setParameter() | 设置 XSLT 参数 |
getParameter() | 获取 XSLT 参数 |
removeParameter() | 移除参数 |
registerPHPFunctions() | 注册 PHP 函数 |
hasExsltSupport() | 检查 EXSLT 扩展支持 |
setProfiling() | 启用性能分析 |
getSecurityPrefs() | 获取安全设置 |
setSecurityPrefs() | 设置安全选项 |
安全配置
php
<?php
declare(strict_types=1);
$processor = new XSLTProcessor();
// 安全选项
// XSL_SECPREF_NONE - 无限制
// XSL_SECPREF_READ_FILE - 允许读取文件
// XSL_SECPREF_WRITE_FILE - 允许写入文件
// XSL_SECPREF_CREATE_DIRECTORY - 允许创建目录
// XSL_SECPREF_READ_NETWORK - 允许网络访问
// XSL_SECPREF_WRITE_NETWORK - 允许网络写入
// 禁止所有文件和网络操作(最安全)
$processor->setSecurityPrefs(XSL_SECPREF_NONE);
// 只允许读取文件
$processor->setSecurityPrefs(XSL_SECPREF_READ_FILE);EXSLT 扩展
php
<?php
declare(strict_types=1);
$processor = new XSLTProcessor();
// 检查 EXSLT 支持
if ($processor->hasExsltSupport()) {
echo "支持 EXSLT" . PHP_EOL;
// EXSLT 命名空间
// xmlns:math="http://exslt.org/math"
// xmlns:set="http://exslt.org/sets"
// xmlns:str="http://exslt.org/strings"
// xmlns:date="http://exslt.org/dates-and-times"
// xmlns:dyn="http://exslt.org/dynamic"
// 示例 XSL 使用 EXSLT 数学函数
// <xsl:value-of select="math:max(price)"/>
// <xsl:value-of select="math:min(price)"/>
// <xsl:value-of select="str:uppercase(title)"/>
}实战示例
XML 数据报表生成
php
<?php
declare(strict_types=1);
class ReportGenerator
{
private XSLTProcessor $processor;
public function __construct()
{
$this->processor = new XSLTProcessor();
}
/**
* 从 XML 数据和 XSL 模板生成 HTML 报表
*/
public function generate(
string $xmlContent,
string $xslContent,
array $params = []
): string {
$xml = new DOMDocument();
$xml->loadXML($xmlContent);
$xsl = new DOMDocument();
$xsl->loadXML($xslContent);
$this->processor->importStylesheet($xsl);
// 设置参数
foreach ($params as $name => $value) {
$this->processor->setParameter(null, $name, $value);
}
return $this->processor->transformToXML($xml);
}
/**
* 生成并保存到文件
*/
public function generateToFile(
string $xmlPath,
string $xslPath,
string $outputPath,
array $params = []
): void {
$xml = new DOMDocument();
$xml->load($xmlPath);
$xsl = new DOMDocument();
$xsl->load($xslPath);
$this->processor->importStylesheet($xsl);
foreach ($params as $name => $value) {
$this->processor->setParameter(null, $name, $value);
}
$this->processor->transformToURI($xml, "file://{$outputPath}");
}
}注意事项
常见错误
php
<?php
declare(strict_types=1);
// XSL 编译错误 - 检查 XSL 语法
try {
$processor->importStylesheet($xslDoc);
} catch (\Throwable $e) {
echo "XSL 错误: " . $e->getMessage();
}
// 转换错误
libxml_use_internal_errors(true);
$result = $processor->transformToXML($xmlDoc);
foreach (libxml_get_errors() as $error) {
echo "错误: {$error->message}" . PHP_EOL;
}
libxml_clear_errors();最佳实践
- 启用 profiling:开发时使用
setProfiling()分析转换性能 - 使用参数:通过
setParameter动态控制输出 - 安全配置:生产环境设置
setSecurityPrefs - 缓存样式表:避免重复加载 XSL 文件
- 使用 EXSLT:利用扩展函数增强 XSLT 能力
下一节
继续学习:libxml