7

I need to be able to edit .ini files (which I'm reading with parse_ini_file), but in such a way that comments and formatting (line breaks, indenting) is preserved.

Do you know any good classes that have nice and optimized functions for this kind of stuff?

Alex
  • 66,732
  • 177
  • 439
  • 641
  • 1
    Do you have some sample input and output? – Explosion Pills Mar 07 '12 at 00:58
  • It looks like a standard ini file, like php.ini, 4-5 sections with some key = value entries inside them – Alex Mar 07 '12 at 01:00
  • Doubt something like this exists meanwhile (for PHP). I would have a [`editconfigini.php`](http://apt.include-once.org/php/), but that ignores sections and only cares about a constrained key=value format. – mario Mar 07 '12 at 01:05
  • @mario - something does exist that does this. Symfony uses ini files for example. – Flukey Mar 11 '12 at 16:31
  • @Flukey - which version of Symfony do you have in mind, out of interest? Version 1.x uses ini files predominantly for Propel (db configuration goes in propel.ini) but that's only a config reader, not a writer afaik. I guess it would be using Phing for this - though that might have a writer in it! – halfer Mar 11 '12 at 16:37
  • On using Phing to create template-based .ini files, [this](http://stackoverflow.com/a/6675598/472495) might be of interest. That said, I find Phing to be a huge and rather daunting library for this relatively trivial task. – halfer Mar 11 '12 at 21:56

4 Answers4

5

I've not used the config writer in the Zend component framework, but I've used the config reader, and it was very reliable. Would certainly be worth a go.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • I tried it and it's really cool, but the writer will not preserve comments, it will just generate a new ini structure :( – Alex Mar 11 '12 at 18:27
  • If that's the only issue, you could perhaps hack that feature in? Of course it would be great if such a change could find its way back to the open source version - if only so you can take advantage of their upgrades - but I guess it depends on what Zend's policy is regarding feature/api stability. – halfer Mar 11 '12 at 19:08
  • Or, you could subclass the Zend writer class with your own class, containing the commenting changes. – halfer Mar 11 '12 at 21:54
5

You could try starting from this, it reads the ini file, and preserves the settings on write, you would have to extend it to support adding new entries:

class ini {
    protected $lines;

    public function read($file) {
        $this->lines = array();

        $section = '';

        foreach(file($file) as $line) {
            // comment or whitespace
            if(preg_match('/^\s*(;.*)?$/', $line)) {
                $this->lines[] = array('type' => 'comment', 'data' => $line);
            // section
            } elseif(preg_match('/\[(.*)\]/', $line, $match)) {
                $section = $match[1];
                $this->lines[] = array('type' => 'section', 'data' => $line, 'section' => $section);
            // entry
            } elseif(preg_match('/^\s*(.*?)\s*=\s*(.*?)\s*$/', $line, $match)) {
                $this->lines[] = array('type' => 'entry', 'data' => $line, 'section' => $section, 'key' => $match[1], 'value' => $match[2]);
            }
        }
    }

    public function get($section, $key) {
        foreach($this->lines as $line) {
            if($line['type'] != 'entry') continue;
            if($line['section'] != $section) continue;
            if($line['key'] != $key) continue;
            return $line['value'];
        }

        throw new Exception('Missing Section or Key');
    }

    public function set($section, $key, $value) {
        foreach($this->lines as &$line) {
            if($line['type'] != 'entry') continue;
            if($line['section'] != $section) continue;
            if($line['key'] != $key) continue;
            $line['value'] = $value;
            $line['data'] = $key . " = " . $value . "\r\n";
            return;
        }

        throw new Exception('Missing Section or Key');
    }

    public function write($file) {
        $fp = fopen($file, 'w');

        foreach($this->lines as $line) {
            fwrite($fp, $line['data']);
        }

        fclose($fp);
    }
}

$ini = new ini();
$ini->read("C:\\php.ini");
$ini->set('PHP', 'engine', 'Off');
echo $ini->get('PHP', 'engine');
$ini->write("C:\\php.ini");
Martin
  • 5,945
  • 7
  • 50
  • 77
  • thank you. do you know how can I make the comment/whitespace regex include new lines too (like empty new lines)? – Alex Mar 14 '12 at 20:27
  • ps: I'm using your regex to "record" only extra data like comments, which I then prepend to the configuration generated by the zend config class, which is so cool :) – Alex Mar 14 '12 at 20:30
  • @Alex: I forgot a question mark in my comment regex, which should make the "colon followed by anything" optional. How does that work? – Martin Mar 14 '12 at 20:41
2

The PEAR::Config package has support for comments, so I assume it does preserve them. Probably it is fitting your needs.

hakre
  • 193,403
  • 52
  • 435
  • 836