我使用array_replace_recursive作为创建级联配置系统的一种方式,但我有一个问题:它将数字索引的数组视为要递归的数组,而不是值:
$a = [
'tournaments' => [
'modes' => ['single', 'double', 'round-robin']
]
];
$b = [
'tournaments' => [
'modes' => ['single']
]
];
$c = [
'tournaments' => [
'modes' => ['double']
]
];
$result = array_replace_recursive($a, $b, $c);返回
[
'tournaments' => [
0 => 'double',
1 => 'double',
2 => 'round-robin'
]
]我真正想要的是:
[
'tournaments' => [
0 => 'double'
]
]我可以编写自己的array_replace_recursive替代品,但我也想保持它的多样性,这会变得更加复杂。有没有办法将'modes'设置作为一个普通的“列表”值,而不是array_replace_recusive递归到的数组?
编辑:我觉得在这种情况下,PHP数组的一体化灵活性实际上是有害的,而不是有益的。如果PHP有一个简单的列表作为一个简单的值(但仍然可以迭代),那就太好了。
发布于 2014-12-31 03:47:56
让我给你一个例子,说明如何使用适当的对象和数组的混合将使这一点更加简单。在这里,我向您展示了一组配置的JSON表示,因为从文件中读取JSON配置或已设置为某个变量的字符串值并使用json_decode()转换为PHP对象是很容易的。
{
"tournaments": {
"modes": [
"single",
"double",
"round-robin"]
],
"second_level_property": {
"third_level_property": "foo"
}
},
"first_level_property": "bar"
}假设还有两个本地配置覆盖,如下所示,并打算按如下所示的顺序应用:
{
"tournaments": {
"modes": ["single"]
}
}和
{
"tournaments": {
"modes": [
"single",
"round-robin"
],
"second_level_property": null
},
"first_level_property": "baz"
}最终配置应如下所示:
{
"tournaments": {
"modes": [
"single",
"round-robin"
],
"second_level_property": null
},
"first_level_property": "baz"
}让我们看看如何在PHP中做到这一点。首先,我们创建一个简单的函数来合并stdClass对象(在json_decode上创建的object类)。
function stdClass_object_merge(stdClass $a, stdClass $b) {
foreach ($b as $k => $v) {
if ($v instanceof stdClass && isset($a->$k) && $a->$k instanceof stdClass) {
// both reference and mergin objects have stdClass objects for this property
// so we want to recursively merge these objects at this property
$a->$k = stdClass_object_merge($a->$k, $v);
} else {
// this property may or may not be present on reference object
// but in either case, we want to overwrite the value in reference object
// with value from merging object
$a->$k = $v;
}
}
return $a;
}请注意,此函数仅在嵌套对象存在于同一属性键的情况下递归。它还将始终将合并对象(参数$b)中的值视为权威,覆盖引用数组中相同属性的值,或者如果引用数组中缺少属性,则添加属性。这意味着只有stdClass对象可以在配置中充当“节点”,所有其他数据类型都被视为“叶子”,如果您在合并对象中传递了一个非节点值,其中节点值存在于引用对象中(就像上面合并示例中的锦标赛->second_level_property值一样),那么就有可能切断引用配置中的分支。
这样用法就会变得非常简单:
// read default config into stdClass object from file
$config = json_decode(file_get_contents('/path/to/default_config.json'));
// specify local override to configuration and merge that config to existing
$local_config_1 = json_decode(file_get_contents('/path/to/local_config_1.json'));
$config = stdClass_object_merge($config, $local_config_1);
// merge another config
$local_config_2 = json_decode(file_get_contents('/path/to/local_config_2.json'));
$config = stdClass_object_merge($config, $local_config_2);尽管我对基于JSON的灵活简洁的语法稍有偏爱,但实际上您基本上可以在基于PHP的定义中做您现在正在做的事情。他们只是变得有点冗长。在PHP中完成上述默认配置的示例如下:
$config = new stdClass;
$config->tournaments->modes = [
"single",
"double",
"round-robin"
]
$config->tournaments->second_level_property->third_level_property = 'foo';
$config->first_level_property = 'bar';对我来说,这似乎并不能很好地传达配置的整体结构。我也反对让配置文件本身需要代码来设置配置,并反对将配置放在单独的非可执行文件中,这在JSON方法中很容易做到。
https://stackoverflow.com/questions/27709206
复制相似问题