我想创建一个映射函数,它接受一个数组、一个键字符串和一个值字符串。这两个字符串包含PHP代码,我希望对数组中的每个元素进行评估。
这两个调用的返回值应该用作将要返回的关联数组中的键和值。
例如
$assoc_list = my_mapper_function($list_of_people, 'id', 'full_name()');
假设列表中包含两个人,Alice和Bob,他们的id分别为4和5,那么返回值应该类似于:
[
'4' => 'Alice Foo',
'5' => 'Bob Bar'
]
有什么关于如何四处做这件事的想法吗?
对于属性,它很简单,因为您可以使用括号表示法,但我希望它也能用于(链式)函数调用。
这是我想出的代码,不幸的是,它只适用于属性:
public static function assoc_mapper($array, $key, $value) {
$results = array();
foreach ($array as $element) {
$results[$element[$key]] = $element[$value];
}
return $results;
}
到目前为止,我能够做到这一点的唯一方法是使用官方文档警告不要使用的eval
。然而,由于我没有使用用户输入,所以这不是安全风险。
public static function assoc_mapper($array, $key_expression, $value_expression) {
$results = array();
foreach ($array as $element) {
$key = eval("return '$element->$key_expression;");
$value = eval("return '$element->$value_expression;");
$results[$key] = $value;
}
return $results;
}
如果有人有一个更巧妙的想法,仍然可以缩短方法调用,我很乐意接受这个答案。
未经测试,很快就被破解了,但我希望你能明白。与其使用字面上的foo()->bar()
表示法,不如使用简化的foo.bar
表示法,您可以用各种方式处理它。
function getPropertyFromString($obj, $string) {
if (is_array($obj) && array_key_exists($string, $obj)) {
return $obj[$string];
} else if (is_object($obj)) {
if (isset($obj->$string)) {
return $obj->$string;
} else if (method_exists($obj, $string)) {
return call_user_func(array($obj, $string));
} else if (method_exists($obj, 'get' . ucfirst($string))) {
return call_user_func(array($obj, 'get' . ucfirst($string)));
}
}
return null;
}
function getValue($obj, $getter) {
if (is_string($getter)) {
return array_reduce(explode('.', $getter), 'getPropertyFromString', $obj);
} else if (is_callable($getter)) {
return call_user_func($getter, $obj);
}
throw new InvalidArgumentException('Invalid getter');
}
function mapper(array $array, $keyGetter, $valueGetter) {
$result = array();
foreach ($array as $value) {
$result[getValue($value, $keyGetter)] = getValue($value, $valueGetter);
}
return $result;
}
示例:
mapper($array, 'id', 'name'); // simple properties
mapper($array, 'foo.bar', 'baz'); // nested properties
// super complex values
mapper($array, 'id', function ($obj) { return strtoupper($obj->foo(42)->bar()->baz); });