【问题标题】:Section-inheritance with Config::IniFilesConfig::IniFiles 的部分继承
【发布时间】:2013-09-16 08:52:18
【问题描述】:

我有一个 php-ini 文件,我需要使用 Config::IniFiles 对其进行解析和读取。在这个 ini 文件中是从其他部分继承其设置的部分。结构如下:

[section1]
...
...
[section2:section1]
...
...
[section3:section2]
...
...

等等。

现在,如果我想从section3 中读取键的值,该对象将返回undef,因为它只知道section3:section2 部分。正如您可能已经预料到的那样,这不是继承的期望行为。在我的情况下,我希望对象首先尝试从section3 读取值,如果在那里找不到,请尝试section2,然后尝试section1。有没有办法用这个模块来实现这一点,还是我必须编写一个包装模块并自己实现这个功能?

【问题讨论】:

    标签: perl ini


    【解决方案1】:

    我找不到实现配置继承的 Perl 模块。

    但是编写一个简单的过滤器来解决继承并不是那么复杂。给定一个 HoH,如果我们将配置中的每个条目都视为一个 delta,我们可以像下面这样解析继承:

    use Algorithm::C3;  # must be installed from CPAN
    
    sub resolve_inheritance {
      my $input = shift;
      my %output;
    
      # hash of arrays where the arrays contain all parents
      my %child_parent_relations =
        map { my ($c, @p) = split /\s*:\s*/; $c => [\@p, $input->{$_}] } keys %$input;
      my $get_parents = sub { @{ $child_parent_relations{shift()}[0] } };
      my $get_data    = sub {    $child_parent_relations{shift()}[1] };
    
      # prepare stuff for C3 resolution
      my $resolution_cache = {};
      my $resolve = sub {
        my $child = shift;
        Algorithm::C3::merge($child, $get_parents, $resolution_cache);
      };
    
      # now we go through all childs, and build temporary hashes from the C3 linearization.
      for my $child (keys %child_parent_relations) {
        my @linearization = $resolve->($child);
        my %temp;
        for my $delta_name (reverse @linearization) {
          my $delta = $get_data->($delta_name);
          @temp{keys %$delta} = values %$delta;
        }
        # save the data in the output:
        $output{$child} = \%temp;
      }
    
      return \%output;
    }
    

    测试:

    my $input = {
      's1'       => { A => 1 },
      's2:s1'    => { A => 2, B => 2 },
      's3:s1'    => { B => 3, C => 3 },
      's4:s3:s2' => { }
    };
    my $expected = {
      's1' => { A => 1 },
      's2' => { A => 2, B => 2 },
      's3' => { A => 1, B => 3, C => 3 },
      's4' => { A => 2, B => 3, C => 3 },
    };
    use Test::More tests => 1;
    is_deeply resolve_inheritance($input), $expected, 'C3 resolution';
    

    如您所见,这使:-operator 具有右关联性(从右到左组合)。

    如果你想要深度优先分辨率,即:

    my $expected = {
      's1' => { A => 1 },
      's2' => { A => 2, B => 2 },
      's3' => { A => 1, B => 3, C => 3 },
      's4' => { A => 1, B => 3, C => 3 },  # s3 completely overwrites s2
    };
    

    然后你会得到不同的结果。这可以通过首先继承每个父级,然后仅合并直接父级而不是整个层次结构来完成。在单继承的情况下,深度优先线性化和C3分辨率的结果是等价的。

    为了支持深度优先分辨率,我们把上面代码中的$resolve函数换掉,改成

    my $resolve; $resolve = sub {
      my $child = shift;
      return $child, map { $resolve->($_) } $get_parents->($child);
    };
    

    这是最小的更改,但当然可以通过仅保留每个父级的最左侧出现来提高效率:

    use List::MoreUtils 'uniq';
    
    my $resolve; $resolve = sub {
      my $child = shift;
      return uniq $child, map { $resolve->($_) } $get_parents->($child);
    };
    

    如果用于输入的数据结构可以记住部分的顺序,那么这种深度优先的解决方案会变得更加容易。但是,哈希是无序的。

    【讨论】:

      猜你喜欢
      • 2017-01-21
      • 2021-12-09
      • 2013-09-06
      • 2020-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-13
      相关资源
      最近更新 更多