Ini files. The New (Old) Way to Configure Your Web App

NOTE: As usual, my views are somewhat PHP-centric. A lot of this will apply generally. Some of it won’t. Act accordingly.

When it comes to providing web applications (or any application for that matter) with configuration information, I am a fan of putting as little as possible in configuration files.  In general, web applications talk to a database of some kind. If that’s the case, configuration information belongs in the database. The only thing I think should really be in a configuration file is information about how to connect to the database.

This becomes particularly important when your app has to scale to multiple web servers. If you’ve got your configuration params (all of them) in a file, you’re going to have to change that file on every web server every time you make a change. Not fun.

I will concede that there are exceptions. If the application uses a logger (and all good apps should) then it would make sense to provide configuration information for that in a config file. As an aside, I always log to a file. It requires the least number of moving parts to get working. If you log to the database, for example, how do you log that you can’t connect to the database?

Let’s get back to the point, though. This isn’t an article about what should or should not be in configuration files. It’s about what format configuration files should use. Configuration files are in kind of a strange land in software development, in that this is information that must be read and understood by both computers and people.

There are quite a few options out there for config file formats.  Before we look at a few of them, let’s lay out what we’re looking for in a format:

  • It must be simple for a (non-developer) human being to read, understand and modify, using anything that can edit plain text files
  • It must allow some kind of inline comments
  • There should be a way to group similar settings together (database, logger, cache, etc.)
  • Parsing it should be trivial and not require a lot of overhead. (Scripted applications will likely read it on every single execution)

With those criteria in mind, let’s look at some of our options.

XML

Ah, XML. So useful. So adaptable. So utterly misunderstood. XML is useful for lots and lots of things. Configuration files? Probably not. First, it’s pretty easy to accidentally muck up an XML file. It’s also very easy to create a totally unwieldy and unnecessarily verbose file. Because XML is so flexible, there’s many, many ways to represent the same information. Let’s consider database connection information.

This:

  1. <database host="localhost" name="myapp" port="3306" username="jaybill" password="as34Hjhd"/>

…is just as valid as this:

  1. <database>
  2.   <host>localhost</host>
  3.   <name>myapp</name>
  4.   <port>3306</port>
  5.   <username>jaybill</username>
  6.   <password>as34Hjhd</password>
  7. </database>

Which one is better?  Kind of hard to say. The first one is shorter, the second one might be easier to read. The point is that both are totally valid XML and an application developer could easily choose either format. There’s no standard. (As I type that, I’m certain someone is going comment about an XML standard for configration files.)

In the end, the main reason XML fails as a config file format is that XML parsers require a lot of overhead (both in terms of lines of code and computing horsepower). This makes them a poor choice for something you’re going to likely read every time you load a page in the case of a PHP-based web app.

I hear someone out there saying “But Jaybill! In my application, I just read the XML file once and cache the values!” To you I say, “That is dumb for like eight reasons. Do not do that.” Seriously, why do something process-intense, like parsing XML, cache the results and then keep checking to see if the cache is dirty, reread the XML and re-cache, when you could just use a simpler format and skip all that? Is it that you just want to be able to say your application uses XML? What are you, a 1998 dotcom startup?

YAML

I have to say I really challenge the need for YAML’s existence, let alone its use as a configuration format. It’s trying to solve a problem that doesn’t exist. At best, it’s trying to solve a problem that’s been solved by other formats. Perhaps not surprisingly, it’s the format that Ruby on Rails uses for config files.

Let’s look at an example in the form of a Ruby on Rails database.yml file:

  1.  development:
  2.     adapter: mysql
  3.     encoding: utf8
  4.     database: myapp
  5.     username: jaybill
  6.     password: as34Hjhd
  7.     host: localhost

“Wait!” I can hear you say. “That’s actually pretty simple!” Sure it is. That’s because it’s properly indented. What bothers me about YAML is that readability depends entirely on proper indentation. Look at this:

  1. development:
  2. adapter: mysql
  3. encoding: utf8
  4. database: myapp
  5. username: jaybill
  6. password: as34Hjhd
  7. host: localhost

All I did was take out the indentation. It’s now entirely unclear that “development” is a section header and not just an value that hasn’t been set.

YAML can also get bafflingly complicated. Amusingly, in many ways it’s more complex than XML, which the authors argue it can functionally supplant in certain domains. In effect, this means there could be even more overhead in parsing a YAML file there is parsing an XML file.

I should note that even in the Rails community, there’s an element that wants to get away from YAML. Considering that what they’re using it for is about 1% of what YAML actually does, this is not surprising.

There’s also the fact that for whatever reason, YAML support in PHP is abysmal. Personally, this is a deal-breaker. Sure, there are examples floating around on how to do it, but they’re not pretty. There’s a PECL extension that wraps something called “syck” which actually reads the file. I’d tell you more about how it works but I gave up on installing it after an hour or so.

INI

Ini files have been around since at least 1981.  They’re simple and they work. They’re nothing more than a set of key/value pairs (optionally) broken up into sections. They are ideally suited for configuration information because that was and always has been thier primary purpose.

It doesn’t get much simpler:

  1. [database]
  2. host=localhost
  3. name=myapp
  4. port=3306
  5. username=jaybill
  6. password=as34Hjhd

Because they are so simple, they are very computationally cheap to load. Additionally, PHP has a built in function to parse them, called, shockingly, parse_ini_file(). This means you can go from a config.ini file to a multidimensional array in one line of code. Try that with an XML file, even using something like SimpleXML.

In terms of speed, the data in an INI file can be loaded and usable four times faster than an XML file.  To determine this and to demonstrate the code complexity (or lack thereof) in getting data from XML files versus INI files, consider the following script:

  1. <?php
  2.  
  3. function timer_start(){
  4.    $mtime = microtime();
  5.    $mtime = explode(" ",$mtime);
  6.    $mtime = $mtime[1] + $mtime[0];
  7.    return  $mtime;
  8. }
  9.  
  10. function timer_end($starttime){
  11.    $mtime = microtime();
  12.    $mtime = explode(" ",$mtime);
  13.    $mtime = $mtime[1] + $mtime[0];
  14.    $endtime = $mtime;
  15.    return ($endtime$starttime);
  16. }
  17.  
  18. function average_array($a){
  19.     return array_sum($a)/count($a);
  20. }
  21.  
  22. $tests = 1000;
  23. $results = array();
  24. $config = array();
  25.  
  26. // xml with attributes
  27. for($x = 0; $x < $tests; $x++){
  28.     $start = timer_start();
  29.     $xml = simplexml_load_file(‘config.xml’);
  30.  
  31.     foreach($xml->attributes() as $key => $val){
  32.         $config[‘xml_attributes’][$key] = (string)$val;
  33.     }
  34.     $results[‘xml_attributes’][] = timer_end($start);
  35. }
  36.  
  37. // xml with elements
  38. for($x = 0; $x < $tests; $x++){
  39.     $start = timer_start();
  40.     $xml = simplexml_load_file(‘config2.xml’);
  41.  
  42.     foreach($xml as $key => $val){
  43.         $config[‘xml_elements’][$key] = (string)$val;
  44.     }
  45.     $results[‘xml_elements’][] = timer_end($start);
  46. }
  47.  
  48. // ini file
  49. for($x = 0; $x < $tests; $x++){
  50.     $start = timer_start();
  51.  
  52.     // This is the *one line* of code you need to get an multidimensional array from an ini file:
  53.     $ini_file = parse_ini_file("config.ini",true);
  54.  
  55.     $config[‘ini_file’] = $ini_file[‘database’];
  56.     $results[‘ini_file’][] = timer_end($start);
  57. }
  58. ?><html><head><title>Config File Load Timer</title></head><body>
  59. <h1>Config File Load Timer</h1>
  60.  
  61. <p><b>XML with attributes:</b>
  62. <br /><pre><?php echo average_array($results[‘xml_attributes’]); ?></pre></p>
  63.  
  64. <p><b>XML with elements:</b>
  65. <br /><pre><?php echo average_array($results[‘xml_elements’]); ?></pre></p>
  66.  
  67. <p><b>INI File:</b>
  68. <br /><pre><?php echo average_array($results[‘ini_file’]); ?></pre></p>
  69.  
  70. <p><pre><?php print_r($config); ?></pre>
  71. </body></html>

Here’s config.xml:

  1. <database host="localhost" name="myapp" port="3306" username="jaybill" password="as34Hjhd"/>

Here’s config2.xml:

  1. <database>
  2.   <host>localhost</host>
  3.   <name>myapp</name>
  4.   <port>3306</port>
  5.   <username>jaybill</username>
  6.   <password>as34Hjhd</password>
  7. </database>

And here’s config.ini:

  1. [database]
  2. host=localhost
  3. name=myapp
  4. port=3306
  5. username=jaybill
  6. password=as34Hjhd

All we’re doing here is loading each of these files 1000 times, timing each load and averaging the results. On my machine, I get the following results:

  • XML with attributes: 0.0004 seconds
  • XML with elements: 0.0003 seconds
  • INI File: 0.0001 seconds

Granted, these are really small amounts of time. If you’ve got a high-traffic web site, however, every little bit counts. Considering, as we said earlier, that a config file will probably get loaded with each page view, the load times (and CPU usage) can add up pretty quickly. We’re also talking about very simple configurations here, obviously, but you get the idea.

INI files are simple, easy to use, easy to process and have been around for a very, very long time. Newer formats may beckon us with their shiny features and whatnot, but all in all, INI files are hard to beat for simple configuration files.

2 Responses to “Ini files. The New (Old) Way to Configure Your Web Application”

  1. On January 8th, 2009 at 12:55 am Binny V A said:

    IMO, a simpler option is to use PHP itself to create the configuration file…

    config.php
    ‘localhost’,
    ‘db_username’ => ‘root’,

    );
    ?>

    And it can be ‘parsed’ with just…
    require(‘config.php’);


  2. On January 8th, 2009 at 8:36 am Jaybill McCarthy said:

    @ Binny V A – I think that really rides the line of “easily editable by non-developer humans”. ;) That approach also makes it impossible to recover from an error in the configuration. Because you’re making the configuration essentially part of the code, a syntax error is going to stop the interpreter with no way to recover. If I parse an INI file, I can check to see if it’s valid and act accordingly.


Leave a Reply

Note: If you've never commented on Jaybill.com before, your comment will not be visible immediately. Someone will need to approve it first. After we approve one of your comments (which means we think you're not a robot) all future comments will show up immediately. Please do not post your comment twice.