【问题标题】:statement inside php eval()php eval() 中的语句
【发布时间】:2019-11-01 07:36:38
【问题描述】:

在提供使用 eval() 实现的钩点的 PHP 框架中,我试图从 eval() 调用中中断或继续循环。 这就是我正在尝试的

框架循环像

...
for( $i = 0; $i < 10; $i++ ) {
    // framweork code here ...
    eval( $plugin_code );
    // framweork code here ...
}
...

$plugin_code 包含 PHP 代码 - 在此示例中

if( $i == 5 ) {
    continue;
}

导致这个错误

PHP Fatal error:  'continue' not in the 'loop' or 'switch' context

如果 eval() 确实只评估 expressions 而不能评估 statements - 那么如何在 eval() 中实现 continue / break 语句?

【问题讨论】:

  • eval 可以执行语句。阅读错误消息,它清楚地说明了问题所在。
  • continue; 必须在词法上在循环内,不能在 eval 或函数中完成。
  • 我很惊讶没有人这么说:eval 是在框架中创建“挂钩”的错误工具;运行时评估的代码很脆弱,难以调试,而且很容易变得不安全。如果这是您正在编写的框架,而不是您正在使用的框架,我强烈建议您修改此设计并查看其他框架如何为此目的使用回调和插件对象。
  • 感谢您的回复。 @Barmar 我不明白为什么 continue 不应该在循环内。
  • 您不能在eval() 中使用它,原因与您不能将continue 语句放在循环中调用的函数中的原因相同。它必须在循环语句中以文本形式

标签: php eval


【解决方案1】:

暂时搁置evalcontinue 的机制,我认为有一个更基本的观点:当编写“插入”到另一个系统的代码时,你只能做该系统允许您这样做

如果钩子系统只是简单地执行你给它的代码(无论是通过eval,还是一个回调函数,或者任何其他机制),你就不能用它来控制主框架代码中的循环流等。

如果框架希望你这样做,它必须为你的插件提供一种机制来向框架发回你想要它做的事情 - 你可以以特定的方式注册插件,返回一个特定的值回调,设置特定变量等。

如果您不想直接修改框架,您唯一的选择是向框架的作者请求这样的功能。

【讨论】:

  • 是的 - 我认为你是对的。这似乎是正确的答案。没有办法操纵那种控制流——我现在明白了。我通过解决它和使用其他钩点解决了我的特定任务。只是“继续”会更好:-)谢谢!
【解决方案2】:

您不能在eval() 中使用continuebreak 之类的语句来影响外部循环。

我建议使用变量。

$plugin_code = 'if( $i == 5 ) {
    $continue = true;
}';

$continue = false;
for( $i = 0; $i < 10; $i++ ) {
    eval( $plugin_code );
    if ($continue) {
        continue;
    }
    // other code
}

如果您还想跳过插件的其余代码,则应将该代码放在 else 块中。

$plugin_code = 'if( $i == 5 ) {
    $continue = true;
} else {
    // other code
}';

恕我直言,这整件事闻起来很腥。该插件依赖于用于循环的特定变量,这非常脆弱。

【讨论】:

  • 这行不通,因为分配变量$continue = true 不会改变被评估代码内部的实际控制流。在 eval 完成后执行 continue 是不等价的,并且在循环体结束时执行 continue 无论如何都不会执行任何操作。
  • 这显然是一种简化,我假设eval() 后面还有其他代码应该被continue 跳过。
  • 完全正确 - 这是一种简化,您应该假设。问题是,被评估的其余代码被评估,因为设置$continue = true 不会阻止它被评估。在 eval 完成后执行 continue 为时已晚,无法获得所需的控制流效果。
  • 我看到您已经编辑了您的解决方案,建议将其余代码(在 continue 本来所在的位置之后)放在属于框架代码的 for 循环中。您是在建议编辑框架代码本身!
  • @kaya3 我现在也对其进行了编辑,以展示如何构建插件以跳过其余部分。
【解决方案3】:

continuebreakreturn等影响程序控制流的关键字不能直接在eval内部使用来达到你想要的结果。 eval 是一个函数,函数不能改变调用它的代码的控制流,除非抛出异常。

由于continue 仅表示“跳到循环的下一次迭代”,这相当于“不要执行此块中的其余代码”,因此您可以重写代码以使其余部分以if 语句为条件的块。如果代码看起来像

for( $i = 0; $i < 10; $i++ ) {
    if( $i == 5 ) {
        continue;
    }
    // do more things
}

那么这可以简单地重写为

for( $i = 0; $i < 10; $i++ ) {
    if( $i != 5 ) {
        // do more things
    }
}

现在的形式可以是eval'd:

// your code
$plugin_code = 'if( $i = 5 ) {
    // do more things
}';

// framework code
for( $i = 0; $i < 10; $i++ ) {
    eval($plugin_code);
}

但是,这仍然只会影响您传递到框架中的代码的控制流。无法更改框架代码本身的控制流。

【讨论】:

  • 你会如何把它放在$plugin_code 钩子里?整个想法是这个变量可以用来动态改变循环。
  • $plugin_code 块是循环体,for 循环的 {...} 内的所有内容。这里的重点是,如果循环体在传递给 eval 的字符串中,那么您不能使用像 continue 这样的控制流结构,因此您必须通过以下方式实现您想要的控制流效果其他方式,例如if 声明。
  • 谢谢。不幸的是,我无法在框架代码中写入任何内容...
  • 您不需要编辑框架代码。我将进行编辑以使其更清晰。
  • @kaya3 那行不通。您题为“//做更多事情”的部分在框架中。我无法将该代码拉入我的插件中。
猜你喜欢
  • 1970-01-01
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
  • 2011-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多