Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capability to set / remove comment lines, parse mode normal / numeric only / raw #2

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ composer require "jelix/inifile"
# Usage

The ```\Jelix\IniFile\IniModifier``` class allows to read an ini file, to modify its
content, and save it by preserving its comments and empty lines.
content including comments, and save it by preserving empty lines.

Don't use this class to just read content. Use instead ```\Jelix\IniFile\Util``` or
```parse_ini_file()``` for this purpose, it's more efficient and performant.
Expand All @@ -32,9 +32,17 @@ $val = $ini->getValue('parameter_name', 'section_name');
// remove a parameter
$ini->removeValue('parameter_name', 'section_name');

// setting a comment (in the line preceding the parameter) - the leading ';'
// can be omitted or included in the comment line, if missing will be added
$ini->setComments('parameter_name', '; single-line comment text', 'section_name');

// save into file
// setting a multi-line comment (in the lines preceding the parameter)
$ini->setComments('parameter_name', [ 'first line', 'second line' ], 'section_name');

// remove all comment lines preceding a parameter
$ini->removeComments('parameter_name', 'section_name');

// save into file
$ini->save();
$ini->saveAs('otherfile.ini');

Expand All @@ -49,6 +57,8 @@ $ini->mergeSection('sectionSource', 'sectionTarget');

```

It support parsing of parameter values into PHP types (string, numeric, boolean) as well as

It supports also array values (indexed or associative) like :

```
Expand All @@ -60,7 +70,7 @@ assoc[otherkey]=bus

Then in PHP:

```
```php
$ini = new \Jelix\IniFile\IniModifier('myfile.ini');

$val = $ini->getValue('foo'); // array('bar', 'baz');
Expand All @@ -72,14 +82,13 @@ $val = $ini->getValue('foo'); // array('bar', 'baz', 'other value');
$ini->setValue('foo', 'five', 0, 5);
$val = $ini->getValue('foo'); // array('bar', 'baz', 'other value', 5 => 'five');


$ini->setValue('assoc', 'other value', 0, 'ov');
$val = $ini->getValue('assoc'); // array('key1'=>'car', 'otherkey'=>'bus', 'ov'=>'other value');
```

After saving, the ini content is:

```
```ini
foo[]=bar
foo[]=baz
assoc[key1]=car
Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "library",
"description": "classes to read and modify ini files by preserving comments and empty lines",
"keywords": ["ini", "files"],
"homepage": "http://jelix.org",
"homepage": "https://github.com/jelix/inifile",
"license": "LGPL-2.1",
"authors": [
{
Expand All @@ -12,6 +12,9 @@
},
{
"name": "Loic Mathaud"
},
{
"name": "Riccardo Mazzei"
}
],
"require": {
Expand Down
188 changes: 175 additions & 13 deletions lib/IniModifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ class IniModifier extends IniReader implements IniModifierInterface
* @param string $initialContent if the file does not exists, it takes the given content
* as initial content.
*/
public function __construct($filename, $initialContent = '')
public function __construct($filename, $initialContent = '', $parsemode = self::PR_NORMAL)
{
$this->parsemode = $parsemode;
if (!$filename) {
throw new IniInvalidArgumentException('Filename should not be empty');
}
Expand Down Expand Up @@ -132,7 +133,16 @@ protected function _setValue($name, $value, $section = 0, $key = null)
}
if (!$foundValue) {
if ($key === null) {
$this->content[$section][] = array(self::TK_VALUE, $name, $value);
if ($this->content[$section]) {
for ($pos = count($this->content[$section]) - 1; $pos >= 0; $pos--) {
if ($this->content[$section][$pos][0] !== self::TK_WS && $this->content[$section][$pos][0] !== self::TK_COMMENT) {
array_splice($this->content[$section], $pos + 1, 0, array(array(self::TK_VALUE, $name, $value)));
break;
}
}
} else {
$this->content[$section][] = array(self::TK_VALUE, $name, $value);
}
} else {
if ($key === '') {
if ($lastKey != -1) {
Expand All @@ -141,7 +151,16 @@ protected function _setValue($name, $value, $section = 0, $key = null)
$key = 0;
}
}
$this->content[$section][] = array(self::TK_ARR_VALUE, $name, $value, $key);
if ($this->content[$section]) {
for ($pos = count($this->content[$section]) - 1; $pos >= 0; $pos--) {
if ($this->content[$section][$pos][0] !== self::TK_WS && $this->content[$section][$pos][0] !== self::TK_COMMENT) {
array_splice($this->content[$section], $pos + 1, 0, array(array(self::TK_ARR_VALUE, $name, $value, $key)));
break;
}
}
} else {
$this->content[$section][] = array(self::TK_ARR_VALUE, $name, $value, $key);
}
}
$this->modified = true;
}
Expand Down Expand Up @@ -185,7 +204,16 @@ protected function _setArrayValue($name, $value, $section = 0)

foreach ($value as $k => $v) {
if (!$foundKeys[$k]) {
$this->content[$section][] = array(self::TK_ARR_VALUE, $name, $v, $k);
if ($this->content[$section]) {
for ($pos = count($this->content[$section]) - 1; $pos >= 0; $pos--) {
if ($this->content[$section][$pos][0] !== self::TK_WS && $this->content[$section][$pos][0] !== self::TK_COMMENT) {
array_splice($this->content[$section], $pos + 1, 0, array(array(self::TK_ARR_VALUE, $name, $v, $k)));
break;
}
}
} else {
$this->content[$section][] = array(self::TK_ARR_VALUE, $name, $v, $k);
}
}
}
$this->modified = true;
Expand All @@ -194,7 +222,7 @@ protected function _setArrayValue($name, $value, $section = 0)
/**
* modify several options in the ini file.
*
* @param array $value associated array with key=>value
* @param array $values associated array with key=>value
* @param string $section the section where to set the item. 0 is the global section
*/
public function setValues($values, $section = 0)
Expand Down Expand Up @@ -297,6 +325,129 @@ public function removeValue($name, $section = 0, $key = null, $removePreviousCom
}
}

/**
* create or replace comment lines preceding an option.
*
* @param string $name the name of the option to find
* @param mixed $comments comment line (if string) or array of lines.
* @param string $section the section where to find the item to add/set the comments to. 0 is the global section
* @param int $key for option which is an item of array, the key in the array.
*/
public function setComments($name, $comments, $section = 0, $key = null)
{
$this->_setOrRemoveComments($name, $section, $key, $comments);
}

/**
* remove comment lines preceding an option.
*
* @param string $name the name of the option to find
* @param string $section the section where to set the item. 0 is the global section
* @param int $key for option which is an item of array, the key in the array.
*/
public function removeComments($name, $section = 0, $key = null)
{
$this->_setOrRemoveComments($name, $section, $key, $comments = null);
}

protected function _setOrRemoveComments($name, $section = 0, $key = null, $comments = null)
{
if (is_string($key) && !preg_match('/^[^\\[\\]]*$/', $key)) {
throw new IniInvalidArgumentException("Invalid key $key for the value $name");
}

if ($comments) {
if (!is_array($comments)) {
$comments = array($comments);
}

foreach ($comments as $i => $comment) {
if (substr($comment, 0, 1) !== ';') {
$comments[$i] = ';' . $comments[$i];
}
$comments[$i] = array(self::TK_COMMENT, $comments[$i]);
}
}

if ($name == '') {
// retrieve the previous section
$previousSection = -1;
foreach ($this->content as $s => $c) {
if ($s === $section) {
break;
} else {
$previousSection = $s;
}
}

if ($previousSection != -1) {
//retrieve the last comment
$s = $this->content[$previousSection];
end($s);
$tok = current($s);
while ($tok !== false) {
if ($tok[0] != self::TK_COMMENT) {
break;
}
if ($tok[0] == self::TK_COMMENT && strpos($tok[1], '<?') === false) {
$this->content[$previousSection][key($s)] = array(self::TK_WS, '--');
}
$tok = prev($s);
}
if ($comments) {
foreach ($comments as $comment) {
$this->content[$previousSection][] = $comment;
}
}
}
return;
}

if (isset($this->content[$section])) {
// boolean to erase array values if the option to remove is an array
$previousComment = array();
foreach ($this->content[$section] as $k => $item) {
if ($item[0] == self::TK_COMMENT) {
$previousComment[] = $k;
continue;
}

if ($item[0] == self::TK_WS) {
continue;
}

// if the item is not a value or an array value, or not the same name
if ($item[1] != $name) {
$previousComment = array();
continue;
}

// if it is an array value, and if the key doesn't correspond
if ($item[0] == self::TK_ARR_VALUE && $key !== null) {
if ($item[3] != $key) {
$previousComment = array();
continue;
}
}
$this->modified = true;
if (count($previousComment)) {
$kc = array_pop($previousComment);

$lastKc = -1;
while ($kc !== null) {
if (strpos($this->content[$section][$kc][1], '<?') === false) {
$this->content[$section][$kc] = array(self::TK_WS, '--');
}
$kc = array_pop($previousComment);
}
}
if ($comments) {
array_splice($this->content[$section], $k, 0, $comments);
}
}
}
}

/**
* remove a section from the ini file.
*
Expand Down Expand Up @@ -431,14 +582,25 @@ protected function getIniValue($value)
return "on";
}
}
if ($value === '' ||
is_numeric(trim($value)) ||
(is_string($value) && preg_match('/^[\\w\\-\\.]*$/u', $value) &&
strpos("\n", $value) === false)
) {
return $value;
} else {
$value = '"'.$value.'"';
if ($this->parsemode === self::PR_NORMAL) {
if ($value === '' ||
is_numeric(trim($value)) ||
(is_string($value) && preg_match('/^[\\w\\-\\.]*$/u', $value) &&
strpos("\n", $value) === false)
) {
return trim($value);
} else {
$value = '"'.$value.'"';
}
}
else {
if ($value === '' ||
is_numeric(trim($value))
) {
return trim($value);
} else {
$value = '"'.$value.'"';
}
}

return $value;
Expand Down
36 changes: 35 additions & 1 deletion lib/IniModifierArray.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function setValue($name, $value, $section = 0, $key = null)
/**
* modify several options in the latest ini file.
*
* @param array $value associated array with key=>value
* @param array $values associated array with key=>value
* @param string $section the section where to set the item. 0 is the global section
*/
public function setValues($values, $section = 0)
Expand Down Expand Up @@ -201,6 +201,40 @@ public function removeValue($name, $section = 0, $key = null, $removePreviousCom
}
}

/**
* create or replace comment lines preceding an option from all ini file.
*
* @param string $name the name of the option to find
* @param mixed $comments comment line content (if string) or array of lines contents. Each line
* will be prepended with ";" if needed
* @param string $section the section where to set the item. 0 is the global section
* @param int $key for option which is an item of array, the key in the array.
*/
public function setComments($name, $comments, $section = 0, $key = null)
{
foreach ($this->modifiers as $mod) {
if ($mod instanceof IniModifierInterface) {
$mod->setComments($name, $comments, $section, $key);
}
}
}

/**
* remove comment lines preceding an option from all ini file.
*
* @param string $name the name of the option to find
* @param string $section the section where to set the item. 0 is the global section
* @param int $key for option which is an item of array, the key in the array.
*/
public function removeComments($name, $section = 0, $key = null)
{
foreach ($this->modifiers as $mod) {
if ($mod instanceof IniModifierInterface) {
$mod->removeComments($name, $section, $key);
}
}
}

/**
* remove a section from all ini file.
* @param int $section
Expand Down
Loading