Skip to content

Commit

Permalink
enhance: ini string parse, add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Nov 4, 2021
1 parent cfe192b commit 1e4e068
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 28 deletions.
90 changes: 63 additions & 27 deletions app/Lib/Parser/IniParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@

use Toolkit\Stdlib\Str;
use function explode;
use function is_array;
use function is_numeric;
use function is_string;
use function ltrim;
use function preg_match;
use function rtrim;
use function str_ends_with;
use function str_starts_with;
use function strlen;
use function substr;
use function trim;
use function vdump;

/**
* class IniParser
Expand All @@ -34,7 +38,7 @@ public static function parseString(string $str): array
* - ignores commented lines that start with ";" or "#"
* - ignores broken lines that do not have "="
* - supports array values and array value keys
* - supports inline array value
* - enhance: supports inline array value
*
* @param string $str
*
Expand All @@ -52,16 +56,19 @@ public function parse(string $str): array

$sectionName = '';
foreach ($lines as $line) {
$line = trim($line);
// empty line
if (!$line = trim($line)) {
continue;
}

// empty or comments line
if (!$line || $line[0] === "#" || $line[0] === ";") {
// comments line
if ($line[0] === "#" || $line[0] === ";" || str_starts_with($line, '//')) {
continue;
}

// section line. eg: [arrayName]
if ($line[0] === '[' && $endIdx = strpos($line, ']')) {
$sectionName = substr($line, 1, $endIdx - 1);
if (strlen($line) > 3 && $line[0] === '[' && str_ends_with($line, ']')) {
$sectionName = substr($line, 1, -1);
continue;
}

Expand All @@ -76,45 +83,68 @@ public function parse(string $str): array

// inline array value. eg: tags=[abc, 234]
if ($val && $val[0] === '[' && str_ends_with($val, ']')) {
$val = Str::splitTrimmed(substr($val, 1, - 1));
$val = Str::toTypedArray(substr($val, 1, - 1));
}

// top field
if (!$sectionName) {
$ret[$key] = $val;
continue;
}

// in section. eg: [arrayName] -> $sectionName='arrayName'
if ($sectionName) {
// remove quote chars
if (preg_match("/^\".*\"$/", $val) || preg_match("/^'.*'$/", $val)) {
$val = mb_substr($val, 1, -1);
}

// is array.
// eg: val_arr[] = "arr_elem_one"
// eg: val_arr_two[some_key] = "some_key_value"
// $t = preg_match("^\[(.*?)\]^", $key, $matches);
$ok = preg_match("^\[(.*?)]^", $key, $matches);
if ($ok === 1 && isset($matches[0])) {
// $arr_name = preg_replace('#\[(.*?)\]#is', '', $key);
[$arrName, $subKey] = explode('[', trim($key, ']'));
// remove quote chars
if (
is_string($val) &&
(preg_match("/^\".*\"$/", $val) || preg_match("/^'.*'$/", $val))
) {
$val = mb_substr($val, 1, -1);
}

// is array sub key.
// eg:
// [] = "arr_elem_one"
// val_arr[] = "arr_elem_one"
// val_arr_two[some_key] = "some_key_value"
$ok = preg_match("/[\w-]{0,64}\[(.*?)]$/", $key, $matches);
if ($ok === 1 && isset($matches[0])) {
[$arrName, $subKey] = explode('[', trim($key, ']'));

if ($arrName !== '') {
if (!isset($ret[$sectionName][$arrName]) || !is_array($ret[$sectionName][$arrName])) {
$ret[$sectionName][$arrName] = [];
}

if ($subKey !== '') {
if ($subKey !== '') { // eg: val_arr[subKey] = "arr_elem_one"
$ret[$sectionName][$arrName][$subKey] = $val;
} else { // eg: val_arr[] = "arr_elem_one"
$ret[$sectionName][$arrName][] = $val;
}
} else {
$ret[$sectionName][$key] = $val;
if (!isset($ret[$sectionName]) || !is_array($ret[$sectionName])) {
$ret[$sectionName] = [];
}

if ($subKey !== '') { // eg: [subKey] = "arr_elem_one"
$ret[$sectionName][$subKey] = $val;
} else { // eg: [] = "arr_elem_one"
$ret[$sectionName][] = $val;
}
}
} else {
$ret[$key] = $val;
$ret[$sectionName][$key] = $val;
}
}

return $ret;
}

/**
* @param string $str
*
* @return string
*/
protected function removeQuotes(string $str): string
{
if (preg_match("/^\".*\"$/", $str) || preg_match("/^'.*'$/", $str)) {
Expand All @@ -124,16 +154,22 @@ protected function removeQuotes(string $str): string
return $str;
}

protected function str2array(string $str): array
/**
* @param string $str
*
* @return array
*/
protected function str2typedList(string $str): array
{
$str = substr($str, 1, - 1);
if (!$str) {
return [];
}

$arr = Str::splitTrimmed(substr($str, 1, - 1));
foreach ($arr as $val) {
$arr = Str::splitTrimFiltered($str);
foreach ($arr as &$val) {
if (is_numeric($val) && strlen($val) < 11) {

$val = (int)$val;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
* @package Inhere\KiteTest
*/
abstract class BaseTestCase extends TestCase
abstract class BaseKiteTestCase extends TestCase
{

}
74 changes: 74 additions & 0 deletions test/unittest/Lib/Parser/IniParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php declare(strict_types=1);

namespace Inhere\KiteTest\Lib\Parser;

use Inhere\Kite\Lib\Parser\IniParser;
use Inhere\KiteTest\BaseKiteTestCase;
use function vdump;

class IniParserTest extends BaseKiteTestCase
{
public function testParseIni_full(): void
{
$ini = <<<INI
; comments line
// comments line
# comments line
# simple inline array
inlineArr = [ab, 23, 34.5]
# simple list array
[simpleList]
[] = 567
[] = "some value"
# k-v map
[simpleMap]
[val_one] = 567
[val_two] = 'some value'
# k-v map, equals to the 'simpleMap'
[simpleMap2]
[val_one] = 567
[val_two] = "some value"
# multi level list array
[array]
arr_sub_key[] = "arr_elem_one"
arr_sub_key[] = "arr_elem_two"
arr_sub_key[] = "arr_elem_three"
# multi level k-v map sub array
[array_keys]
val_arr_two[6] = "key_6"
val_arr_two[some_key] = "some_key_value"
INI;
$data = IniParser::parseString($ini);
vdump($data);
$this->assertNotEmpty($data);
$this->assertArrayHasKey('inlineArr', $data);
$this->assertEquals(['ab', 23, 34.5], $data['inlineArr']);

$this->assertArrayHasKey('simpleList', $data);
$this->assertEquals([567, 'some value'], $data['simpleList']);

$this->assertArrayHasKey('simpleMap', $data);
$this->assertEquals(['val_one' => 567, 'val_two' => 'some value'], $data['simpleMap']);
}

public function testParseIni_1levelList(): void
{
$ini = '
# simple list array
[simpleList]
[] = 567
[] = "some value"
';
$data = IniParser::parseString($ini);
vdump($data);
$this->assertNotEmpty($data);
$this->assertArrayHasKey('simpleList', $data);
$this->assertEquals([567, 'some value'], $data['simpleList']);
}
}

0 comments on commit 1e4e068

Please sign in to comment.