As a general rule, when you develop any reasonably-complex piece of software, it's a good idea to take time to identify the product's key configuration variables, and then separate these from the standard variable namespace and place them in a separate area. With this process, you can create a centralized repository of application configuration information and simplify the task of modifying the product to work in different environment. It can also help increase a developer's familiarity with, and understanding of, the key pieces of information needed to get the product up and running.
Traditionally, configuration variables are stored in one (or more) configuration files. The variables are often expressed using XML, which provides a flexible framework for document authors to describe content using custom tags and markup. In most cases, developers also need to construct a user interface to interact with these XML configuration files, to read and use the configuration data contained therein.
XJConf for PHP helps PHP developers with this task. This open-source package provides an API to extract XML-encoded information from a configuration file and turn it into a native data structure, for immediate use within an application. As such, it provides a robust, easy-to-use widget for any PHP application.
The XJConf package is maintained by Frank Kleine and Stephan Schmidt, and released to the PHP community under the GNU LGPL. It requires PHP 5.0 (or better), and the easiest way to install it is with the automated PEAR installer, which should have been included by default with your PHP build. To install it, simply issue the following commands at your shell prompt:
shell> pear channel-discover pear.php-tools.net
shell> pear install pat/XJConfForPHP-alpha
The PEAR installer will now connect to the new channel, download the package, and install it to the appropriate location on your system. This article uses XJConf for PHP V. 0.2.0.
To install the package manually, visit its home page, download the source code archive, and manually uncompress the files to the desired location. You'll find links to the package home page, as well as to in-development builds, in the Resources of this article. Note that this manual installation process presupposes some knowledge of PEAR's package organization structure.
One final dependency: the XJConf for PHP package requires that you enable the
xmlreader extension of PHP. This extension is enabled
by default in PHP 5.1.0 or later. Users of older PHP versions can obtain
detailed information and activation instructions from the appropriate section of
the PHP manual (see Resources for links).
This article assumes that you have a working knowledge of PHP and XML, and are comfortable using PHP's simple and complex data types. It assumes a reasonable degree of knowledge of both general OOP concepts and the specific class model used by PHP 5, as well as some knowledge of XML tree generation with PHP's Document Object Model (DOM) extension.
XJConf provides PHP application developers with an API that reads XML-formatted configuration files and converts the data within them to native PHP data types or custom objects. These data types or objects can then be used within a PHP script in the normal fashion. Any XJConf-based program has three components:
- The XML-formatted configuration file
- A PHP script that parses and extracts data from the configuration file
- A definition file, which maps elements from the configuration file into native PHP structures
The best way to illustrate the interaction of these three components is with an example. Consider Listing 1, which shows an XML configuration file for a pet housecat:
Listing 1. An XML configuration file (cat.xml)
<?xml version='1.0'?>
<cat>
<name>Martha</name>
<age>4</age>
<breed>Siamese</breed>
</cat>
|
Now, suppose you need to read these configuration values into your PHP application. The first step is to create a definition file, which maps each element from Listing 1 to a native PHP data type. Listing 2 illustrates such a file:
Listing 2. An XJConf definition file (defs.xml)
<defines>
<tag name="name" type="string"/>
<tag name="breed" type="string"/>
<tag name="age" type="integer"/>
</defines>
|
Take a moment to study the structure of this definition file, because
understanding it will be critical for all subsequent examples in this article.
An outer <defines> element encloses a series of
<tag> elements, each of which represents an element from the XML configuration file. Each <tag> must have at least the name and type attributes. The name attribute specifies an element name from the configuration file, while the type attribute specifies the corresponding PHP data type. Common primitive types are string, integer and boolean; however, as you'll see a little further along in this article, you can also map an element to an array or a custom class.
All that's left now is a PHP script to initialize an instance of XJConf and use the definitions in Listing 2 to retrieve configuration data. Listing 3 illustrates the required PHP code:
Listing 3. A PHP script to read XML configuration data
<?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.";
?>
|
Listing 4 illustrates the output of this script:
Listing 4. The output generated by Listing 3
Martha is Siamese and 4 year(s) old.
|
Look closely at Listing 3, and it's easy to understand what
it does. The listing first reads in the required class definition files, and
then loads and initializes an XJConf facade object. In XJConf, this object provides a
common interface to the DefinitionParser method, which handles the definition file in Listing 2; and
the XMLParser method, which handles the XML-formatted configuration file in Listing 1.
First, the script calls the addDefinition() method
of the XJConfFacade object to read the various tag
definitions from Listing 2 and attach these definitions to the
object. Next, the parse() method of the object parses the configuration data file in Listing 1. During this parsing process, the elements and data in the configuration file are converted into key-value pairs, similar to that found in a PHP associative array.
Once parsing is complete (the process will stop with an error message if your
XML file definition has an error), you can access the keys and values with
the getConfigValue() method of the XJConfFacade object, which accepts a key name as argument and returns the corresponding value. Thus, in Listing 4, a call to getConfigValue('name') returns the value of the <name> element from the configuration file cat.xml (Listing 1), which in this case happens to be Martha.
Now, try to edit the values in the configuration file (Listing 1) such that it looks like Listing 5:
Listing 5. A revised configuration file
<?xml version='1.0'?>
<cat>
<name>Tom</name>
<age>1</age>
<breed>Norwegian</breed>
</cat>
|
And now, when you re-run the PHP script in Listing 4, you'll see the revised output shown in Listing 6:
Listing 6. The revised output of Listing 4
Tom is Norwegian and 1 year(s) old.
|
Notice that you didn't have to change a single line of Listing 4 to access the revised configuration data. Thus, the benefits of storing configuration variables in a separate file and using a tool like XJConf to access them should be clear: Global changes become as simple as altering a single file, with the changes reflecting instantly across the application.
Simple data types are just the tip of the iceberg—XJConf also lets you define collections of related configuration variables, and converts these into native PHP arrays. XJConf supports both numerically-indexed and associative arrays.
To see how this works, consider Listing 7, which sets up a configuration file containing a collection of values:
Listing 7. A data file containing a collection of related items (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>
|
Listing 8 contains the corresponding definition file:
Listing 8. A definition file (defs.xml)
<defines>
<tag name="collection" type="array" />
<tag name="item" type="string" keyAttribute="letter" />
</defines>
|
Notice the values of the type attribute in this
definition file: The <collection> element is
mapped to an array, while the nested <item>s
are mapped to simple string values. Notice also a new attribute introduced in
this listing: The keyAttribute attribute, which tells
XJConf which element attribute to consider when it sets the keys of the output associative array.
Listing 9 has the PHP code that parses and displays the configuration data:
Listing 9. A PHP script to read XML configuration data into a PHP array
<?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'));
?>
|
Listing 10 displays the output:
Listing 10. The output of Listing 9
Array
(
[a] => apples
[p] => pears
[o] => oranges
)
|
By setting the keyAttribute to letter, XJConf automatically assigns the value of the letter attribute of every <item> element to the corresponding key of the output associative array.
Prefer a regular numerically-indexed array? Simply alter the definition file and set the value of the keyAttribute to the special symbol __none:
Listing 11. The revised definition file (defs.xml)
<defines>
<tag name="collection" type="array" />
<tag name="item" type="string" keyAttribute="__none" />
</defines>
|
And Listing 12 now displays the revised output:
Listing 12. The revised output of Listing 9
Array
(
[0] => apples
[1] => pears
[2] => oranges
)
|
Now consider a realistic application of this feature: Configuring a database connection from variables present in an XML configuration file. Assume that the configuration file looks something like Listing 13:
Listing 13. A XML database configuration file (conf.xml)
<?xml version='1.0'?>
<conf>
<database>
<user>root</user>
<pass>mysql123</pass>
<host>localhost</host>
<db>test</db>
</database>
</conf>
|
Further, assume that the task is to read these configuration values into an array, and then use this array to connect to a MySQL database server. The first step, then, is to map the XML into an array using a definitions file like the one in Listing 14:
Listing 14. A definition file (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>
|
Listing 15 has the PHP script that uses this data to attempt a MySQL database connection:
Listing 15. A PHP script to read and use database configuration data from an XML file
<?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';
?>
|
Listing 15 uses XJConf to create and populate an array named
$db with data from the configuration file conf.xml. This array is then used to open a connection to the MySQL database server and select a database for use. As explained in earlier examples, the key to the script lies in the definition file, defs.xml, which tells XJConf how to map elements from the configuration file into a PHP array.
One of the most powerful features in XJConf, however, lies in its support for PHP classes and objects. With XJConf, it's quite simple to instantiate custom objects from an XML configuration file, and even configure the properties of said object using the data present in the configuration file.
To illustrate this, let's first define a simple RectangularObject class, which exposes three key properties to the configuration API: the length, width, and height. This class also exposes a getVolume() method, which uses these properties to calculate the volume of the object. Listing 16 has the code:
Listing 16. A PHP class definition (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);
}
}
?>
|
Listing 17 contains XML-formatted configuration data for one
such RectangularObject class:
Listing 17. An XML configuration file (data.xml)
<?xml version='1.0'?>
<conf>
<rectangularobject>
<height>20</height>
<width>15</width>
<length>10</length>
</rectangularobject>
</conf>
|
Mapping data into an object instance is fairly easy. All you need to do is set the type attribute to the name of the class you wish to instantiate. Listing 18 illustrates the corresponding definition:
Listing 18. An XJConf definition file (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>
|
Assuming that the setter methods are correctly named, XJConf will do two things
- Instantiate an object of that class.
- Call setter methods for each nested element to automatically configure the properties of the object.
Thus, the setLength() method is automatically invoked for the <length> element, the setHeight() method for the <height> element, and so on.
Listings 19 and 20 illustrate the PHP code and output respectively:
Listing 19. A PHP script to instantiate and configure an object from XML data
<?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';
?>
|
Listing 20 displays the output:
Listing 20. The output of Listing 19
The volume of the object is: 3000 units
|
Configure default and mandatory Values
You can write an XML configuration file using attributes instead of nested tags, and use these attributes to configure an object instance. To see how this works in practice, first revise Listing 17 such that it uses attributes instead of nested elements:
Listing 21. The revised XML configuration file (data.xml)
<?xml version='1.0'?>
<conf>
<rectangularobject height="20" width="15" length="10" />
</conf>
|
Obviously, this necessitates a change in the definition file as well:
Listing 22. The revised XJConf definition file (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>
|
This change consists of replacing the <tag> elements with <attribute> elements, thus telling XJConf to consider attributes of the named tag when setting object properties, rather than the CDATA content of its child elements.
You can also possible to set default values for a particular configuration
variable, or mark a certain configuration variable as mandatory, through skillful
use of the default and required attributes in the definition file. Consider Listing 23, which makes the length and width configuration variables mandatory and sets a default value for the height:
Listing 23. An XJConf definition file with 'default' and 'required' constraints
<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>
|
If you now try to read a configuration file which is missing a required value, XJConf will throw a MissingAttributeException (Figure 1):
Figure 1. An XJConf exception thrown when a required attribute is missing
In previous examples, you saw that XJConf can automatically call the
appropriate setter methods to configure object instances. This automatic
invocation of setter methods depends greatly on the correct naming of the setter
methods for easy identification and mapping to element/attribute names. If you
don't like this, you can also explicitly define the setter method for each
element/attribute, with the setter attribute in the
definition file. Listing 24 illustrates this use of custom setter methods for the length and width attributes:
Listing 24. An XJConf definition file with custom setter methods
<defines>
<tag name="rectangularobject" type="RectangularObject">
<attribute name="length" setter="setL" />
<attribute name="width" setter="setW" />
<attribute name="height" />
</tag>
</defines>
|
As you might know, a new feature in PHP 5 is the introduction of the special
__set() and __get()
overloading methods, which you can use to set object properties even if a setter or getter method is not explicitly defined for that property. Listing 25 revises the class definition from Listing 16 to illustrate how this works in practice:
Listing 25. A PHP class definition with __set() and __get()
<?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);
}
}
?>
|
Assume the same data and definition files as in Listing 17 and Listing 18 respectively and try to run the PHP script in Listing 26:
Listing 26. A PHP script to configure an object using an XJConf configuration file
<?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';
?>
|
You should see the following output:
Listing 27. The output of Listing 26
The volume of the object is: 3000 units
|
You'll notice that despite the lack of explicitly-defined setter methods in the class definition, XJConf was still able to configure the object's properties through the __set() method. This feature can, again, significantly reduce the length of your class definitions and make your application code simpler to maintain and easier to read.
In versions of XJConf for PHP prior to V. 0.2.0, these special overloading methods are not supported and attempts to use them will generate a fatal error in your PHP script.
Finally, you'll see that all the previous listings explicitly load the class definition files, before parsing the XML definitions and data. XJConf can automatically do this task for you using its own class loader as needed; examples illustrating this feature are available in the XJConf for PHP package (See Resources). You should definitely look at them if you work with numerous classes in your application.
Project: Configure a complex class tree
As the previous listings demonstrated, XJConf can use either attributes or child elements to correctly configure an instantiated object. In all cases but one, therefore, it is your relative comfort level with these two techniques that will define your approach. However, in one case, the use of child elements offers an advantage over attributes: When you link one or more objects to another in a complex tree.
To illustrate, consider Listing 28, which defines some classes:
Listing 28. Some PHP class definitions (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;
}
}
?>
|
Listing 28 begins with the Config class, which exposes a generic API to receive an associative array of server access parameters. The DBConfig and SMTPConfig classes are sub-classes of this Config class, and are intended specifically to store access parameters for a database server and an SMTP server respectively. DBConfig and SMTPConfig objects are, in turn, used by the App class, which represents an application. This App class exposes setter and getter methods to set and retrieve the corresponding Config class as needed.
Configuration of these classes occurs through an XML configuration file, such as the one in Listing 29:
Listing 29. An XML configuration file (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>
|
The glue needed to convert the XML data in Listing 29 into a fully-configured App object instance lies in the definition file (Listing 30) and the corresponding PHP script (Listing 31). Look at the definition file first:
Listing 30. An XJConf definition file (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>
|
How does this work? It's not really as difficult as it looks:
- When XJConf encounters an
<app>element in the configuration file, it instantiates anAppobject instance. From the class definitions in Listing 28, you'll see that thisAppobject exposes thesetDBConfig()andsetSMTPConfig()methods. -
When XJConf next encounters a
<db>or<smtp>element, it instantiates aDBConfigorSMTPConfigobject and invokes thesetConfig()setter method of the object. The input to this setter method is an array of configuration parameters, which is natively created from the<array>element and its children. -
The
DBConfigandSMTPConfigobjects created in step 2 are then attached back to theAppobject through the setter methodssetDBConfig()andsetSMTPConfig(). You'll notice that these methods are also named in the definition file through thesetterattribute.The resulting
Appobject instance is then retrieved from within a PHP script by callinggetConfigValue(). In a similar vein, the instantiated and configuredDBConfigandSMTPConfigobjects can also be retrieved, by callingApp::getDBConfig()orApp::getSMTPConfig(). You can see this in Listing 31:
Listing 31. A PHP script to configure a complex class tree
<?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);
?>
|
Listing 32 displays the output:
Listing 32. The output of Listing 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
)
)
|
Project: Build a web-based configuration interface
Another common real-world requirement for many projects is a Web-based configuration tool, which allows users to input configuration data through a Web form and saves this to a file in XML format. XJConf has limited usefulness in this context. While the package provides a full-featured API to read XML-formatted configuration data into PHP data structures, it does not offer any methods to write data back to a file. Thus, in these situations, it is necessary to supplement XJConf with the DOM extension in PHP, which provides an API to dynamically construct an XML tree and write it to a file.
Listing 33 demonstrates the use of these two tools:
Listing 33. A PHP script to edit and save configuration data to a file
<?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!';
}
}
?>
|
Listing 33 is divided into two parts: a form which displays the current configuration (if available) and allows the user to edit it, and a form processor, which accepts the new configuration and saves it to a file. XJConf is used in the first half of the script, to parse the XML configuration file and retrieve its contents into PHP variables; these variables are then used to pre-fill the fields of the Web form.
Listing 34 contains the simple definition file used by Listing 33:
Listing 34. An XJConf definition file (defs.xml)
<defines>
<tag name="host" type="string"/>
<tag name="port" type="integer"/>
<tag name="user" type="string"/>
<tag name="pass" type="string"/>
</defines>
|
Once the form is submitted, the DOM extension in PHP takes over. A DOMDocument instance is created, and child elements are attached to it holding the configuration values input by the user. Once the entire DOM tree has been constructed, it's written back to the configuration file using the DOMDocument::save() method.
Figure 2 illustrates what the form looks like:
Figure 2. A Web form to edit and save configuration data
And Listing 35 demonstrates one such configuration file written by this script:
Listing 35. An example file created by Listing 34
<?xml version="1.0" encoding="iso-8859-1"?>
<conf>
<host>localhost</host>
<port>110</port>
<user>joelle</user>
<pass>guessme</pass>
</conf>
|
As the previous listings illustrate, the XJConf package provides an easy-to-use, flexible API that reads XML-formatted configuration files and converts the values found therein into PHP data structures. In addition to simple string and numeric values, it also supports the use of arrays and objects, and includes built-in intelligence to automatically configure newly-instantiated objects through setter methods. For all these reasons, it's a worthy addition to the toolkit of any PHP developer. Try it out the next time you need a interface between your PHP application and your XML configuration file, and see for yourself!
Learn
- The XJConf for PHP Web site: Find more information on the XJConf package.
-
The Stubbles
project: See XJConf for PHP in action. With this PHP 5 framework, combine your favorite features from other programming languages and frameworks. Use only the packages you like and combine it with PEAR, the Zend Framework or any other PHP-based framework.
-
XJConf
Timeline: Track development and news of XJConf for PHP.
-
XMLReader functions: Obtain instructions for to activate PHP's xmlreader extension.
-
More PEAR packages related to PHP and XML development: Find other PEAR packages related to PHP and XML development.
-
developerWorks XML zone: Get the resources you need to advance your skills in the XML arena with articles, tutorials, forums, and more.
-
The technology
bookstore: Browse for books on these and other technical topics.
-
IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
-
XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
-
developerWorks technical events and webcasts: Stay current with technology in these sessions.
Get products and technologies
-
XJConf for PHP package: Download this API.
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
- Participate in the discussion forum.
-
XML zone discussion forums: Participate in any of several XML-centered forums.
-
developerWorks
blogs: Check out these blogs and get involved in the developerWorks community.
-
XML zone discussion forums: Participate in any of several XML-centered forums.
Vikram Vaswani is the founder and CEO of Melonfire, a consulting services firm with special expertise in open-source tools and technologies. He is also the author of the books PHP Programming Solutions and How to do Everything with PHP and MySQL.
Comments (Undergoing maintenance)





