使用 PHP 处理 XML 配置文件

使用 XML 配置文件轻易地配置 PHP 应用程序和对象

XML 为应用程序配置文件提供了一种便捷、易用的表达语言。但有时候将这些信息提取到 PHP 脚本中将会面对一个不小的挑战。这正是 XJConf for PHP 包出现的原因:它提供了一种读取 XML 编码信息并直接将其用于配置标量、数组和 PHP 对象这类 PHP 数据结构的 API。本文介绍了这个包,并通过在一些实际应用程序中的应用来示范其用法,包括配置复杂的类树和构建基于 Web 的配置界面。

Vikram Vaswani, 创始人, Melonfire

Vikram Vaswani 是 Melonfire 的创始人和 CEO,该公司是一家专门研究开源工具和技术的咨询服务公司。他还著有 PHP Programming SolutionsHow to do Everything with PHP and MySQL 等著作。



2007 年 11 月 29 日

简介

作为一条通用规则,开发任何稍微复杂的软件时,最好花点时间确定产品的重要配置变量,并将其从标准变量名称空间中取出来放到单独的位置。这样处理后就能形成集中的应用程序配置信息存储库,从而简化在不同环境中工作时对产品的修改。还有助于开发人员熟悉和理解产品成功运行所需要的重要信息。

传统上,配置变量存储在一个(或多个)配置文件中。这些变量常常用 XML 表示,XML 提供了一个灵活的框架允许文档作者使用自定义的标签和标记描述内容。多数情况下,开发人员还需要建立与这些 XML 配置文件交互的用户接口,读取和使用包含的配置数据。

XJConf for PHP 可帮助 PHP 开发人员完成这项任务。这个开源的包提供了一种 API,从配置文件中提取 XML 编码的信息,转化成原生数据结构直接在应用程序中使用。同样地,它也为所有的 PHP 应用程序提供了一种健壮的、易用的部件。


安装需要的软件

XJConf 包由 Frank Kleine 和 Stephan Schmidt 维护,按照 GNU LGPL 协议在 PHP 社区发布。它需要 PHP 5.0(或更高版本),最简单的安装方法是使用 PEAR 自动安装程序,后者默认情况下包含在 PHP 中。安装只需在 shell 提示符下输入下列命令即可:

shell> pear channel-discover pear.php-tools.net

shell> pear install pat/XJConfForPHP-alpha

PEAR 安装程序将连接到新的通道,下载这个程序包并安装到系统中适当的位置。本文使用的是 XJConf for PHP V. 0.2.0。

手动安装需要访问主页,下载源代码压缩包,手动解压到目标位置。可以在本文的 参考资料 部分找到该程序包的主页以及正在开发版本的链接。需要注意的是手工安装之前必须对 PEAR 程序包的组织结构有所了解。

最后一点要求:XJConf for PHP 需要启用 PHP 的 xmlreader 扩展。PHP 5.1.0 或以上版本默认启用该扩展。老版本 PHP 用户可从 PHP 手册(链接见 参考资料)的适当章节获取详细信息和激活方法。

本文假设您使用过 PHP 和 XML,熟悉 PHP 的简单和复杂数据类型。假设对一般 OOP 概念和 PHP 5 的类模型有一定的了解,对 PHP 文档对象模型(DOM)扩展生成 XML 树也有所了解。


理解基本用法

XJConf 为 PHP 应用程序开发人员提供了一个 API,可以读取 XML 格式配置文件并转化成原生 PHP 数据类型或自定义对象。然后即可按照通常的方式在 PHP 脚本中使用这些类型或对象。任何基于 XJConf 的程序都包括三部分:

  • XML 格式的配置文件
  • 解析和提取配置文件数据的 PHP 脚本
  • 将配置文件中的元素映射到原生 PHP 结构的定义文件

最好用一个例子来说明三者之间的联系。清单 1 显示了关于宠物猫的配置文件:

清单 1. XML 配置文件(cat.xml)
<?xml version='1.0'?>
<cat>
 <name>Martha</name>
 <age>4</age>
 <breed>Siamese</breed>
</cat>

假设需要将这些配置值读入 PHP 应用程序。首先要创建一个定义文件,将 清单 1 中的每个元素映到一种 PHP 原生数据类型。清单 2 给出了一个例子:

清单 2. XJConf 定义文件(defs.xml)
<defines>
 <tag name="name" type="string"/>
 <tag name="breed" type="string"/>
 <tag name="age" type="integer"/>
</defines>

先研究一下定义文件的结构,因为理解该文件对后面所有的例子至关重要。外层的 <defines> 元素包含一系列 <tag> 元素,分别代表 XML 配置文件中的一个元素。每个 <tag> 必须至少具有 nametype 属性。name 属性指定配置文件中的元素名,type 属性则指定对应的 PHP 数据类型。常见的基本类型有 stringintegerboolean,不过,后面将看到还能将元素映射为数组或自定义类。

最后还需要一个 PHP 脚本初始化 XJConf 实例,并使用 清单 2 中的定义检索配置数据。清单 3 显示了需要的 PHP 代码:

清单 3. 读取 XML 配置数据的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$conf = new XJConfFacade();

// attach definitions to parser
$conf->addDefinition('defs.xml');

// parse XML
$conf->parse('cat.xml');

// access XML element values
echo $conf->getConfigValue('name') . " is " . $conf->getConfigValue('breed') 
. " and " . $conf->getConfigValue('age') . " year(s) old.";
?>

脚本的输出结果如 清单 4 所示:

清单 4. 清单 3 的输出结果
Martha is Siamese and 4 year(s) old.

仔细看看 清单 3,很容易理解其作用。清单 3 首先读入需要的类定义文件,然后加载和初始化 XJConf facade 对象。在 XJConf 中,该对象为 DefinitionParser 方法和 XMLParser 方法提供了通用接口,前者处理 清单 2 中的定义文件,后者处理 清单 1 中的 XML 格式配置文件。

首先,脚本调用 XJConfFacade 对象的 addDefinition() 方法从 清单 2 中读取各种标签定义并将这些定义添加到对象中。然后,该对象的 parse() 方法解析 清单 1 中的配置数据文件。解析过程中,配置文件中的元素和数据转化成类似 PHP 关联数组的键-值对。

解析一旦完成(如果 XML 文件定义有错误则终止并报告错误),就可以用 XJConfFacade 对象的 getConfigValue() 方法访问键和值了,该方法接收键名作为参数返回对应的值。因此,在 清单 4 中,对 getConfigValue('name') 的调用将返回配置文件 cat.xml清单 1)中 <name> 元素的值,即 Martha

现在修改配置文件(清单 1)中的值,如 清单 5 所示:

清单 5. 修改后的配置文件
<?xml version='1.0'?>
<cat>
 <name>Tom</name>
 <age>1</age>
 <breed>Norwegian</breed>
</cat>

现在重新运行 清单 4 中的 PHP 脚本,结果如 清单 6 所示:

清单 6. 修改后的输出结果
Tom is Norwegian and 1 year(s) old.

注意,访问修改后的配置数据不需要改变 清单 4 中的代码。因而,在单独的文件中存储配置变量并使用 XJConf 这样的工具对其进行访问的优点就很清楚了:全局性的修改只需变动一个文件,修改会立即反映到整个应用程序。


配置数组

简单的数据类型仅仅是冰山一角 — XJConf 还能够定义相关配置变量的集合,并将其转化成原生 PHP 数组。XJConf 同时支持数字索引数组和关联数组。

为说明其原理,清单 7 创建了一个包含一组值的配置文件。

清单 7. 包含一组相关项的数据文件(collection.xml)
<?xml version='1.0'?>
<root>
 <collection>
 <item letter="a">apples</item>
 <item letter="p">pears</item>
 <item letter="o">oranges</item>
 </collection>
</root>

清单 8 是对应的定义文件:

清单 8. 定义文件(defs.xml)
<defines>
 <tag name="collection" type="array" />
 <tag name="item" type="string" keyAttribute="letter" />
</defines>

注意定义文件中 type 属性的值:<collection> 元素映射到一个数组,嵌套的 <item> 映射为简单字符串值。清单中还引入了一个新属性:keyAttribute 属性,它告诉 XJConf 设置输出关联数组的键时使用哪个元素属性。

清单 9 中的 PHP 代码解析和显示上述配置数据:

清单 9. 将 XML 配置数据读入 PHP 数组的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('collection.xml');

// access XML element values
print_r($xml->getConfigValue('collection'));
?>

清单 10 显示了输出结果:

清单 10. 清单 9 的输出结果
Array
(
 [a] => apples
 [p] => pears
 [o] => oranges
)

通过把 keyAttribute 设置为 letter,XJConf 自动将每个 <item> 元素的 letter 属性的值赋给输出关联数组对应的键。

喜欢用一般的数字索引数组吗?只需要修改定义文件并将 keyAttribute 值设为特殊符号 __none

清单 11. 修改后的定义文件(defs.xml)
<defines>
 <tag name="collection" type="array" />
 <tag name="item" type="string" keyAttribute="__none" />
</defines>

输出结果如 清单 12 所示:

清单 12. 清单 9 的输出结果
Array
(
 [0] => apples
 [1] => pears
 [2] => oranges
)

现在看一个实际的应用:根据 XML 配置文件中的变量配置数据库连接。假设配置文件如 清单 13 所示:

清单 13. XML 数据库配置文件(conf.xml)
<?xml version='1.0'?>
<conf>
 <database>
 <user>root</user>
 <pass>mysql123</pass>
 <host>localhost</host>
 <db>test</db>
 </database>
</conf>

并且,假设目标是将这些配置值读入一个数组,使用这个数组连接到一个 MySQL 数据库服务器。首先要使用定义文件将 XML 映射到数组,如 清单 14 所示:

清单 14. 定义文件(defs.xml)
<defines>
 <tag name="database" type="array">
 <tag name="user" type="string" />
 <tag name="pass" type="string" />
 <tag name="host" type="string" />
 <tag name="db" type="string" />
 </tag>
</defines>

清单 15 中的 PHP 脚本尝试使用这些数据连接到 MySQL 数据库:

清单 15. 从 XML 文件读取数据库配置数据的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('conf.xml');

// access database configuration array
$db = $xml->getConfigValue('database');

// test MySQL connection
$conn = mysql_connect($db['host'], $db['user'], $db['pass']) 
or die('Connection error!');
mysql_select_db($db['db'], $conn) or die('Database selection error!');
mysql_close($conn);
echo 'Database test successful';
?>

清单 15 使用 XJConf 创建数组 $db,并使用配置文件 conf.xml 中的数据填充该数组。然后使用该数组打开到 MySQL 数据库服务器的连接,并选择要使用的数据库。如前面的例子所说明的那样,脚本的关键在于定义文件 defs.xml,它告诉 XJConf 如何将配置文件中的元素映射到 PHP 数组。


配置类和对象

然而,XJConf 的强大功能在于对 PHP 类和对象的支持。使用 XJConf,从 XML 配置文件实例化自定义对象非常简单,甚至可使用配置文件中的数据表示配置对象的属性。

作为一个例子,首先定义简单的 RectangularObject 类,它向配置 API 公开了三个主要属性:长、宽和高。该类还公开了一个 getVolume() 方法,可根据这些属性计算对象的体积。代码如 清单 16 所示:

清单 16. PHP 类定义(RectangularObject.php)
<?php
class RectangularObject {

 // declare properties
 private $length;
 private $height;
 private $width;
 
 // declare constructor
 function __construct() {
 return true; 
 }
 
 // declare setter methods
 function setLength($l) {
 $this->length = $l;
 }
 
 function setHeight($h) {
 $this->height = $h;
 }
 
 function setWidth($w) {
 $this->width = $w;
 }
 
 // declare other methods
 function getVolume() {
 return ($this->length * $this->width * $this->height);
 }
}
?>

清单 17 包含 RectangularObject 类对应的 XML 格式的配置数据:

清单 17. XML 配置文件(data.xml)
<?xml version='1.0'?>
<conf>
 <rectangularobject>
 <height>20</height>
 <width>15</width>
 <length>10</length>
 </rectangularobject>
</conf>

将数据映射到对象实例很简单。只需将 type 属性设置为需要实例化的类名。清单 18 显示了对应的定义:

清单 18. XJConf 定义文件(defs.xml)
<defines>
 <tag name="rectangularobject" type="RectangularObject">
 <tag name="length" type="integer"/>
 <tag name="width" type="integer"/>
 <tag name="height" type="integer"/> 
 </tag>
</defines>

只要正确命名了 setter 方法,XJConf 将做两件事:

  1. 实例化该类的一个对象。
  2. 对嵌套的每个元素调用 setter 方法自动配置对象的特性。

因此,对 <length> 元素自动调用 setLength() 方法,对 <height> 元素自动调用 setHeight() 方法,依此类推。

清单 1920 分别显示了 PHP 代码和输出结果:

清单 19. 根据 XML 数据实例化和配置对象的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load class definition
include_once 'RectangularObject.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('data.xml');

// access object instance
$instance = $xml->getConfigValue('rectangularobject');
print 'The volume of the object is: ' . $instance->getVolume() . ' units';
?>

清单 20 显示了输出结果:

清单 20. 清单 19 的输出结果
The volume of the object is: 3000 units

配置默认值和强制值

也可用属性代替嵌套标记编写 XML 配置文件,用这些属性配置对象实例。要查看实际效果,首先修改 清单 17 以使用属性代替嵌套元素:

清单 21. 修改后的 XML 配置文件(data.xml)
<?xml version='1.0'?>
<conf>
 <rectangularobject height="20" width="15" length="10" />
</conf>

显然,还必须修改定义文件:

清单 22. 修改后的 XJConf 定义文件(defs.xml)
<defines>
 <tag name="rectangularobject" type="RectangularObject">
 <attribute name="length" type="integer"/>
 <attribute name="width" type="integer"/>
 <attribute name="height" type="integer"/> 
 </tag>
</defines>

所做的修改包括:使用 <attribute> 元素替换 <tag> 元素,以通知 XJConf 在设置对象特性时考虑指定标记的属性,而非其子元素的 CDATA 内容。

您还可以为特定的配置变量设置默认值,或将某个配置变量标记为强制值,这可通过在定义文件中使用 defaultrequired 属性实现。看一下 清单 23,它将 lengthwidth 配置变量设置为强制值,而将 height 设置为默认值:

清单 23. 包含 ‘默认’ 和 ‘强制’ 限制的 XJConf 定义文件
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <attribute name="length" type="integer" required="true" />
        <attribute name="width" type="integer" required="true" />
        <attribute name="height" type="integer" default="10" />    
    </tag>
</defines>

如果您现在尝试读取一个缺少强制值的配置文件,XJConf 将抛出 MissingAttributeException 异常(参见图 1):

图 1. 缺少强制值时将抛出一个 XJConf 异常
缺少强制值时将抛出一个 XJConf 异常

在前面的示例中,您了解了 XJConf 可以自动调用合适的 setter 方法来配置对象实例。setter 方法的自动调用主要取决于对 setter 方法的正确命名,从而能够轻易地进行识别并映射到元素/属性名。如果您不喜欢使用这种方法,那么还可以通过定义文件中的 setter 属性,显式地为每个元素/属性定义 setter 方法。清单 24 演示了对 lengthwidth 属性使用定制的 setter 方法:

清单 24. XJConf 定义文件使用了定制的 setter 方法
<defines>
    <tag name="rectangularobject" type="RectangularObject">
        <attribute name="length" setter="setL" />
        <attribute name="width" setter="setW" />
        <attribute name="height" />    
    </tag>
</defines>

您可能已经了解到,PHP 5 的一个新特性就是引入了特殊的 __set()__get() 重载方法。即使没有为某个对象特性显式定义 setter 或 getter 方法,您也可以使用这两种重载方法设置对象特性。清单 25清单 16 中的类定义进行了修改,演示了这种方法的功能:

清单 25. 使用了 __set() 和 __get() 的 PHP 类定义
<?php
class RectangularObject {

    // declare properties
    public $length;
    public $height;
    public $width;
    
    // declare constructor
    function __construct() {
        return true;    
    }
    
    // declare generic setter method
    public function __set($property, $value) {
        $this->$property = $value;
    }
    
    // declare generic getter method
    public function __get($property) {
        return $this->property;
    }
    
    // declare other methods
    function getVolume() {
        return ($this->length * $this->width * $this->height);
    }
}
?>

假设数据和定义文件分别与 清单 17清单 18 相同,并运行 清单 26 中的 PHP 脚本:

清单 26. 使用 XJConf 配置文件配置对象的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// load class definition
include_once 'RectangularObject.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('data.xml');

// access object instance
$instance = $xml->getConfigValue('rectangularobject');
print 'The volume of the object is: ' . $instance->getVolume() . ' units';
?>

将得到如下输出:

清单 27. 清单 26 的输出
The volume of the object is: 3000 units

您将注意到,尽管类定义中缺少显式定义的 setter 方法,XJConf 仍然能够通过 __set() 方法配置对象的特性。同样,这个特性可以显著减少类定义的长度,并使您的应用程序代码更容易维护和读取。

在 V. 0.2.0 版本以前,XJConf for PHP 并不支持这些特殊的重载方法,并且尝试使用这些方法将使 PHP 脚本出现致命错误。

最后,您将看到,在解析 XML 定义和数据之前,上文中的所有清单都可以显式地加载类定义文件。通过其独有的类加载器,XJConf 可为您自动完成这一任务;可从 XJConf for PHP 包获得这些特性的使用示例(参见 参考资料)。如果您的应用程序中包含了大量类的话,那么您一定要查看这些示例用法。


项目:配置复杂的类树

如上文中的清单所示,XJConf 可以使用属性或子元素正确配置实例化的对象。因此,除了一种情况以外,使用这两种技巧可以很方便地定义方法。然而,当将复杂树的一个或多个对象链接到另一对象时,这种情况下使用子元素要胜于使用属性。

为进一步解释,请看 清单 28,它定义了一些类:

清单 28. 一些 PHP 类定义(App.php)
<?php
// configuration class
class Config {
    // declare properties
    private $host;
    private $port;
    private $user;
    private $pass;
    
    // declare methods
    function setConfig($confArray) {
        if (isset($confArray['host'])) {
            $this->host = $confArray['host'];
        }    
        if (isset($confArray['port'])) {
            $this->port = $confArray['port'];
        }    
        if (isset($confArray['user'])) {
            $this->user = $confArray['user'];
        }    
        if (isset($confArray['pass'])) {
            $this->pass = $confArray['pass'];
        }    
    }    
}

// these inherit from the Config class
class DBConfig extends Config {
}

class SMTPConfig extends Config {
}

// application class
class App {
    // declare properties
    public $DBConfig;
    public $SMTPConfig;
    
    // declare constructor
    public function __construct() {
        return true;    
    }
    
    // declare setter methods
    function setDBConfig($db) {
        $this->DBConfig = $db;    
    }    
    function setSMTPConfig($smtp) {
        $this->SMTPConfig = $smtp;    
    }    
    
    // declare getter methods
    function getDBConfig() {
        return $this->DBConfig;    
    }    
    function getSMTPConfig() {
        return $this->SMTPConfig;    
    }    
}
?>

清单 28 首先定义了 Config 类,它公开了一个通用 API 来接收一组有关的服务器访问参数。DBConfigSMTPConfig 类是 Config 类的子类,分别用来保存一个数据库服务器和一个 SMTP 服务器的访问参数。DBConfigSMTPConfig 对象用于 App class,后者表示一个应用程序。这个 App 类公开 setter 和 getter 方法,以在需要时设置和检索对应的 Config 类。

这些类的配置通过一个 XML 配置文件完成,如 清单 29 所示:

清单 29. 一个 XML 配置文件(conf.xml)
<?xml version='1.0'?>
<configuration>
  <app>
    <db>
      <array>
        <host>localhost</host>
        <user>guest</user>
        <pass>mysql123</pass>
      </array>
    </db>
    
    <smtp>
      <array>
        <host>mail.domain.com</host>
        <user>sluggo</user>
        <pass>mypass</pass>
        <port>25</port>
      </array>
    </smtp>
  </app>
</configuration>

清单 29 中的 XML 数据转换为完整配置的 App 对象所需的胶合代码(glue)位于定义文件(清单 30)和对应的 PHP 脚本(清单 31)中。首先查看定义文件:

清单 30. 一个 XJConf 定义文件(defs.xml)
<defines>
  <tag name="app" type="App">  
    <tag name="db" type="DBConfig" setter="setDBConfig">
        <tag name="array" type="array" keyAttribute="name" setter="setConfig">
            <tag name="host" type="string" />
            <tag name="user" type="string" />
            <tag name="pass" type="string" />
        </tag>
    </tag>
    <tag name="smtp" type="SMTPConfig" setter="setSMTPConfig">
        <tag name="array" type="array" keyAttribute="name" setter="setConfig">
            <tag name="host" type="string" />
            <tag name="port" type="integer" />
            <tag name="user" type="string" />
            <tag name="pass" type="string" />
        </tag>
    </tag>
  </tag>
</defines>

它是如何工作的呢?实际上没有看上去那么复杂:

  1. 当 XJConf 遇到配置文件中的 <app> 元素时,它将对 App 对象实例进行实例化。从 清单 28 中的类定义可以看出,App 对象公开了 setDBConfig()setSMTPConfig() 方法。
  2. 当 XJConf 接下来遇到 <db><smtp> 元素时,它将对 DBConfigSMTPConfig 对象进行实例化,并调用该对象的 setConfig() setter 方法。这个 setter 方法的输入为一个配置参数数组,由 <array> 元素及其子元素生成。
  3. 然后将 步骤 2 创建的 DBConfigSMTPConfig 对象通过 setter 方法 setDBConfig()setSMTPConfig() 连接回 App 对象。您将注意到,定义文件通过 setter 属性也对这些方法进行了命名。

    然后,通过调用 getConfigValue() 从 PHP 脚本检索生成的 App 对象实例。在类似的结构中,通过调用 App::getDBConfig()App::getSMTPConfig() 也可以检索经过实例化和配置的 DBConfigSMTPConfig 对象。如 清单 31 所示:

清单 31. 配置复杂类树的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

// include class file
include_once 'App.php';

// load facade
XJConfLoader::load('XJConfFacade');
$xml = new XJConfFacade();

// attach definitions to parser
$xml->addDefinition('defs.xml');

// parse XML
$xml->parse('conf.xml');

// access objects
$app = $xml->getConfigValue('app');
$db = $app->getDBConfig();
$smtp = $app->getSMTPConfig();

// display App object
print_r($app);
?>

清单 32 显示了输出:

清单 32. 清单 31 的输出
App Object
(
    [DBConfig] => DBConfig Object
        (
            [host:private] => localhost
            [port:private] => 
            [user:private] => guest
            [pass:private] => mysql123
        )

    [SMTPConfig] => SMTPConfig Object
        (
            [host:private] => mail.domain.com
            [port:private] => 25
            [user:private] => sluggo
            [pass:private] => mypass
        )

)

项目:构建一个基于 web 的配置接口

对于很多项目来说,另一个常见的实际需求就是基于 Web 的配置工具,它允许用户通过一个 Web 表单输入配置数据并将数据以 XML 格式保存到文件。XJConf 在此方面功能有限。虽然它提供了一个功能强大的 API 将 XML 格式的配置数据读取到 PHP 数据结构,它并没有提供任何方法将数据写回文件中。因此,在这些情形下,需要在 PHP 中为 XJConf 补充 DOM 扩展,这个扩展可以提供 API 动态构建 XML 树并将它写入文件。

清单 33 演示了这两种工具的使用:

清单 33. 编辑并将数据保存到文件的 PHP 脚本
<?php
// include XJConf class file
include_once 'XJConf/XJConfLoader.php';

$dataFile = 'config.xml';
$defsFile = 'defs.xml';
    
// if form is not submitted
if (!isset($_POST['submit'])) {
    
    // check to see if config file exists
    if (file_exists($dataFile)) {

        // load facade
        XJConfLoader::load('XJConfFacade');
        $xml = new XJConfFacade();

        // attach definitions to parser
        $xml->addDefinition($defsFile);

        // parse XML
        $xml->parse($dataFile);
    } 
?>
<html>
 <head><basefont face="Arial"></head>
 <body>
  <h2>Configuration</h2>
  <table border="0" cellspacing="5" cellpadding="5">
   <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
    <tr>
     <td>POP3 host name</td>
     <td><input type="text" name="pop3_host" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('host') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 port</td>
     <td><input type="text" name="pop3_port" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('port') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 user name</td>
     <td><input type="text" name="pop3_user" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('user') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td>POP3 user password</td>
     <td><input type="text" name="pop3_pass" 
     value="<?php echo (isset($xml)) ? $xml->getConfigValue('pass') : null; ?>"
     ></td>
    </tr>
    <tr>
     <td colspan="2" align="center"><input type="submit" name="submit" 
     value="Save Configuration"></td>
    </tr>
   </form>
  </table>           
 </body>
</html>
<?php
} else {
    // do some input validation here
    
    // write the submitted values to a configuration file
    // generate DOM tree and root node
    $dom = new DOMDocument('1.0', 'iso-8859-1');
    $conf = $dom->createElement('conf');
    $dom->appendChild($conf);    
    
    // attach child nodes
    $host = new DOMElement('host', $_POST['pop3_host']);
    $port = new DOMElement('port', $_POST['pop3_port']);
    $user = new DOMElement('user', $_POST['pop3_user']);
    $pass = new DOMElement('pass', $_POST['pop3_pass']);
    $conf->appendChild($host);
    $conf->appendChild($port);
    $conf->appendChild($user);
    $conf->appendChild($pass);
    
    // save to configuration file
    $dom->formatOutput = true;
    if ($dom->save($dataFile)) {
        echo 'Configuration successfully saved!';
    }
}
?>

清单 33 分为两大部分:用一个表单显示当前配置(如果存在)并允许用户进行编辑,用一个表单处理程序接受新配置并保存为文件。脚本的前半部分使用 XJConf 解析 XML 配置数据并将其内容检索到 PHP 变量中;然后使用这些变量预填充 Web 表单的字段。

清单 34 包含了 清单 33 所使用的简单定义文件:

清单 34. 一个 XJConf 定义文件(defs.xml)
<defines>
  <tag name="host" type="string"/>  
  <tag name="port" type="integer"/>  
  <tag name="user" type="string"/>  
  <tag name="pass" type="string"/>  
</defines>

提交表单后,将由 PHP 中的 DOM 扩展处理。将创建一个 DOMDocument 实例,然后将子元素连接到其上以保存用户输入的配置数据。当完成整个 DOM 树的构建后,将使用 DOMDocument::save() 方法将它写回到配置文件。

图 2 演示了表单的外观:

图 2. 用来编辑和保存配置数据的 Web 表单
用来编辑和保存配置数据的 Web 表单

清单 35 演示了由这个脚本编写的配置文件:

清单 35. 清单 34 创建的示例文件
<?xml version="1.0" encoding="iso-8859-1"?>
<conf>
  <host>localhost</host>
  <port>110</port>
  <user>joelle</user>
  <pass>guessme</pass>
</conf>

如前面的清单所示,XJConf 包提供了易于使用的灵活的 API,它可以读取 XML 格式的配置文件并将其中的值转换为 PHP 数据结构。除了简单的字符串和数字值外,它还支持使用数组和对象,并提供了内置的智能,可以使用 setter 方法自动配置最新实例化的对象。综上所述,任何 PHP 开发人员都值得将其集成到工具包中。如果您需要在 PHP 应用程序和 XML 配置文件之间建立一个接口,那么请亲自尝试使用它吧!

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Open source
ArticleID=272072
ArticleTitle=使用 PHP 处理 XML 配置文件
publish-date=11292007