如何将PHP字符串转换为函数名称并调用该函数

时间:2019-04-10 15:15:08

标签: php

这是我的PHP代码:

metrics.png

我当然会收到警告

  

为foreach()提供了无效的参数...

因为<?php function getItems($arg) { $products = array(); $products[] = (object) array('id' => '0', 'title' => 'Product1'); $products[] = (object) array('id' => '1', 'title' => 'Product2'); return $products; } $jsonContent = '{"myKey":["loop", "getItems(\"Arg\")", "<div><h3>{$item->title}</h3></div>"]}'; $jsonObj = json_decode($jsonContent); $options = $jsonObj->myKey; if($options[0] == 'loop') { $html = []; foreach($options[1] as $item) { $html[] = $options[2]; } echo implode('', $html); } ?> 的参数不是数组而是字符串。

那么如何将字符串转换为函数名称并调用该函数呢?

以及如何将foreach()数组的第三个元素用作循环中动态数据的包装器(希望您能通过代码理解我)

我需要得到以下结果:

$options

6 个答案:

答案 0 :(得分:1)

它的基本思想是跳过foreach并仅“运行它”并输入options [2]作为参数,但是由于字符串具有(),因此必须用substr删除。

$html = [];


$html[] =substr($options[1],0,-2)($options[2]);

var_dump($html);

https://3v4l.org/sE2bT


我看到你问过一种没有评估的不同方法。

您可以在函数的循环中使用str_replace。
因为创建字符串时未定义产品,所以“”不会使其与函数中的产品值一致。

但这会(我想)会为您提供所需的输出。

function getItems($item) {
    $products = array();
    $products[] = (object) array('id' => '0', 'title' => 'Product1');
    $products[] = (object) array('id' => '1', 'title' => 'Product2');
    $return = "";
    foreach($products as $prod){
        $return  .= str_replace('$products->title',$prod->title, $item);
    }
    return $return;
}

$jsonContent = '{"myKey":["loop", "getItems()", "<div><h3>{$products->title}</h3></div>", "<div><h1>{$products->title}</h1></div>"]}';

$jsonObj = json_decode($jsonContent);

$options = $jsonObj->myKey;

if($options[0] == 'loop') {

    $html = [];
    $func = substr($options[1],0,-2);
    foreach(array_slice($options,2) as $arg){
        $html[] = $func($arg);
    }

    var_dump($html);
}

/*
array(2) {
  [0]=>
  string(60) "<div><h3>{Product1}</h3></div><div><h3>{Product2}</h3></div>"
  [1]=>
  string(60) "<div><h1>{Product1}</h1></div><div><h1>{Product2}</h1></div>"
}
*/

https://3v4l.org/66Kv7


如果您无法更改函数中的代码,请在“前端”进行相同的处理。

function getItems() {
    $products = array();
    $products[] = (object) array('id' => '0', 'title' => 'Product1');
    $products[] = (object) array('id' => '1', 'title' => 'Product2');
    return $products;
}

$jsonContent = '{"myKey":["loop", "getItems()", "<div><h3>{$products->title}</h3></div>", "<div><h1>{$products->title}</h1></div>"]}';

$jsonObj = json_decode($jsonContent);

$options = $jsonObj->myKey;

if($options[0] == 'loop') {

    $html = [];
    $func = substr($options[1],0,-2);
    $products = $func();

    foreach(array_slice($options,2) as $arg){ 
        foreach($products as $prod){
            $html[] = str_replace('$products->title',$prod->title, $arg);
        }       
    }

    var_dump($html);
}

/*
array(4) {
  [0]=>
  string(30) "<div><h3>{Product1}</h3></div>"
  [1]=>
  string(30) "<div><h3>{Product2}</h3></div>"
  [2]=>
  string(30) "<div><h1>{Product1}</h1></div>"
  [3]=>
  string(30) "<div><h1>{Product2}</h1></div>"
}
*/

https://3v4l.org/1jT48

答案 1 :(得分:1)

由于在HTML中添加值的额外复杂性,我只能想到邪恶的eval。但是对于其余部分,只需将函数名称与参数分开,然后将它们创建为JSON中的数组。然后可以使用Argument unpacking ...运算符来传递它们。我修改了该函数以显示传入的参数如何工作。

在JSON中,我有

["Product", 3]

映射到

function getItems( $prefix, $start) {

所以把它们放在一起...

function getItems( $prefix, $start) {
    $products = array();
    $products[] = (object) array('id' => '0', 'title' => $prefix.$start);
    $products[] = (object) array('id' => '1', 'title' => $prefix.($start+1));
    return $products;
}

$jsonContent = '{"myKey":["loop", "getItems", ["Product", 3], 
            "<div><h3>{$item->title}</h3></div>"]}';

$jsonObj = json_decode($jsonContent);

$options = $jsonObj->myKey;

if($options[0] == 'loop') {

    $html = [];

    foreach($options[1](...$options[2]) as $item) {
        $html[] = eval("return \"{$options[3]}\";");
    }

    echo implode('', $html);
}

返回

<div><h3>Product3</h3></div><div><h3>Product4</h3></div>

答案 2 :(得分:0)

要调用字符串作为函数,您需要从字符串中删除()

$jsonContent = '{"myKey":["loop", "getItems", "<div><h3>{$item->title}</h3></div>"]}';

然后,在您的foreach()循环上,像这样在$options[0]之后再次添加它们:

 foreach( $options[1]() as $item) {
    $html[] = $options[2];
 }

答案 3 :(得分:0)

这很黑,使用eval,对不可信的数据可能很危险,但是很有趣:

$arg = 'something';

if($options[0] == 'loop') {
    $func = rtrim($options[1], '()');

    foreach($func($arg) as $item) {
        eval("echo \"{$options[2]}\";");
    }
}

或者没有参数的稍微不同的方法:

if($options[0] == 'loop') {
    eval("\$items = {$options[1]};");

    foreach($items as $item) {
        eval("echo \"{$options[2]}\";");
    }
}

答案 4 :(得分:0)

使用call_user_func从字符串中调用函数,并且正如Andreas指出的那样,对函数和参数使用单独的数组位置

但是我想指出的是,这非常复杂,也许没有必要……还有其他方法可以使它变得不复杂吗?

编辑

为什么您需要传递一个甚至不使用的arg?

function getItems($arg) {
    $products = array();
    $products[] = (object) array('id' => '0', 'title' => 'Product1');
    $products[] = (object) array('id' => '1', 'title' => 'Product2');
    return $products;
}

$jsonContent = '{"myKey":["loop", "getItems", "Arg1,Arg2", "<div><h3>{$item->title}</h3></div>"]}';

$jsonObj = json_decode($jsonContent);

$options = $jsonObj->myKey;

if($options[0] == 'loop') {

    $html = [];

    $args = explode(',', $options[2]);

    foreach($args as $item) {
        $html[] = call_user_func ($options[1], $item);
    }

    echo implode('', $html);
}
?>````

答案 5 :(得分:0)

我认为通过对JSON进行一些更改,这将更加可行。 首先,我认为如果options是一个对象而不是一个数组,它将使代码更具可读性,但是如果您愿意,同样的方法也应该适用于数组。其次,我认为模板中使用的函数参数和任何对象属性应该是单独的选项,以避免使用eval()

{
    "myKey": {
        "type": "loop",
        "function": "getItems",
        "args": [
            "Arg"
        ],
        "template": "<div><h3>%s<\/h3><\/div>",
        "properties": [
            "title"
        ]
    }
}

解码JSON后用于处理选项的代码:

if ($options->type == 'loop') {
    $html = [];
    foreach (($options->function)(...$options->args) as $item) {
        $html[] = vsprintf($options->template, array_map(function ($property) use ($item) {
            return $item->$property;
        }, $options->properties));
    }
    echo implode('', $html);
}

如果JSON可能来自任何外部来源,我不建议使用此方法。即使没有eval,这似乎也是让人们在您的系统上执行任意代码的好方法。即使您只是自己使用它,我认为还是要至少根据允许的函数列表来验证函数名称,这还是谨慎的做法。


选项数组而不是对象的代码:

JSON:

{"myKey":["loop","getItems",["Arg"],"<div><h3>%s<\/h3><\/div>",["title"]]}

PHP:

if ($options[0] == 'loop') {
    $html = [];
    foreach (($options[1])(...$options[2]) as $item) {
        $html[] = vsprintf($options[3], array_map(function ($property) use ($item) {
            return $item->$property;
        }, $options[4]));
    }
    echo implode('', $html);
}