首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何让array_replace_recursive将数字索引数组作为值处理?

如何让array_replace_recursive将数字索引数组作为值处理?
EN

Stack Overflow用户
提问于 2014-12-31 00:43:02
回答 1查看 352关注 0票数 2

我使用array_replace_recursive作为创建级联配置系统的一种方式,但我有一个问题:它将数字索引的数组视为要递归的数组,而不是值:

代码语言:javascript
复制
$a = [
   'tournaments' => [
       'modes' => ['single', 'double', 'round-robin']
   ]
];

$b = [
   'tournaments' => [
       'modes' => ['single']
   ]
];

$c = [
   'tournaments' => [
       'modes' => ['double']
   ]
];

$result = array_replace_recursive($a, $b, $c);

返回

代码语言:javascript
复制
[
   'tournaments' => [
        0 => 'double',
        1 => 'double',
        2 => 'round-robin'
    ]
]

我真正想要的是:

代码语言:javascript
复制
[
   'tournaments' => [
        0 => 'double'
    ]
]

我可以编写自己的array_replace_recursive替代品,但我也想保持它的多样性,这会变得更加复杂。有没有办法将'modes'设置作为一个普通的“列表”值,而不是array_replace_recusive递归到的数组?

编辑:我觉得在这种情况下,PHP数组的一体化灵活性实际上是有害的,而不是有益的。如果PHP有一个简单的列表作为一个简单的值(但仍然可以迭代),那就太好了。

EN

回答 1

Stack Overflow用户

发布于 2014-12-31 03:47:56

让我给你一个例子,说明如何使用适当的对象和数组的混合将使这一点更加简单。在这里,我向您展示了一组配置的JSON表示,因为从文件中读取JSON配置或已设置为某个变量的字符串值并使用json_decode()转换为PHP对象是很容易的。

代码语言:javascript
复制
{
    "tournaments": {
        "modes": [
            "single",
            "double",
            "round-robin"]
        ],
        "second_level_property": {
            "third_level_property": "foo"
        }
    },
    "first_level_property": "bar"
}

假设还有两个本地配置覆盖,如下所示,并打算按如下所示的顺序应用:

代码语言:javascript
复制
{
   "tournaments": {
        "modes": ["single"]
   }
}

代码语言:javascript
复制
{    
   "tournaments": {
        "modes": [
            "single",
            "round-robin"
        ],
        "second_level_property": null
   },
   "first_level_property": "baz"
}

最终配置应如下所示:

代码语言:javascript
复制
{
    "tournaments": {
        "modes": [
            "single",
            "round-robin"
        ],
        "second_level_property": null
    },
    "first_level_property": "baz"
}

让我们看看如何在PHP中做到这一点。首先,我们创建一个简单的函数来合并stdClass对象(在json_decode上创建的object类)。

代码语言:javascript
复制
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值一样),那么就有可能切断引用配置中的分支。

这样用法就会变得非常简单:

代码语言:javascript
复制
// 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中完成上述默认配置的示例如下:

代码语言:javascript
复制
$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方法中很容易做到。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27709206

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档