【问题标题】:What is the benefit of passing a callback to ob_start compared to just processing the result of ob_get_clean()?与仅处理 ob_get_clean() 的结果相比,将回调传递给 ob_start 有什么好处?
【发布时间】:2013-02-08 05:00:12
【问题描述】:

我想知道使用它是否有任何真正的好处......

function getSomeContent() {
    ob_start(function($content) {
        // ... modify content ...
        return $content;
    }
    // ... output stuff ...
    return ob_get_clean();
}

...与此相反...

function getSomeContent() {
    ob_start();
    // ... output stuff ...
    $result = ob_get_clean();
    // ... modify content ...
    return $result;
}

...?

假设“输出内容”和“修改内容”部分在每种情况下都是相同的。关键是“修改内容”的位置发生了变化,第一种情况是在回调中,第二种情况是“内联”。

两者之间是否存在性能优势?例如,当第一种形式只使用一个时,第二种形式是否会复制两个缓冲区内容?还是纯粹是编码风格决定?为什么会选择一种形式而不是另一种形式?

我可以看到作用域访问存在差异,因为封闭作用域中的任何变量都将在第二个示例的“修改内容”部分中可用,它们必须使用 use “传入”第一个例子中的子句。事实上,这正是我通常会选择第二种形式的原因。

【问题讨论】:

  • 在第一种情况下,你不需要做$result = ob_get_clean();,你会做ob_end_clean();(在清理之前调用回调)。否则第二种情况是你的。如果您想以这种方式使用第一种情况(修改 2 次结果),您只会增加应用程序的复杂性。
  • 谢谢@Ninsuo,但我想你可能没有正确理解我的代码示例(我的问题不够清楚),所以我通过将它们添加到方法中添加了更多上下文具有相同签名的调用。第一种情况不是两次修改结果,我不明白你的意思,你能澄清一下是怎么回事吗?当然,我想使用ob_get_clean() 而不是ob_end_clean(),因为这只会丢弃我想要保留的缓冲区内容。我问的是在ob_start() 中使用回调与处理结果“内联”可以这么说。
  • 虽然我认为所有这些都只是编码风格的问题(Ninsuo 正确地指出回调风格可以让你的代码更简洁),但你应该重新评估它是否真的有必要回退到使用输出缓冲来捕获输出。你在ob_startob_get_clean 之间在做什么?您不能首先将输出附加到变量吗?
  • @akirk 老实说,这个问题主要是理论上的,我倾向于同意,但是我确实发现在 ob_start()ob_get_clean() 中包装视图脚本很有用(没有回调!)然后视图脚本可以使用标准输出函数(echo 等),我以后仍然可以操作输出。至于回调,我很难看到我会使用它们。

标签: php performance coding-style output-buffering


【解决方案1】:

现在您的代码很清楚了,是的,在您的第一个示例中,您给出了一个使用 $result 两次的案例(这不是一个好主意)。

我的主要想法是:只有当您不需要需要在当前范围内使用您的$result 时,您才可以通过回调调用ob_start。你的第一个例子变成了:

ob_start(function($content) {
    // ... modify content ...
    return $content;
}
// ... output stuff ...
ob_end_clean();

在这种情况下,$result 的工作是在一个新的范围内完成的,这可以使您的代码更清晰(例如:您调用 ob_start(array($this, 'method'));),并且您不需要 unset 您的 $result在工作结束时将其从您的主要范围中释放出来(我假设您当然正在做其他事情)。

【讨论】:

  • 谢谢@Ninsuo。如果我理解正确,您是说如果您直接输出缓冲区内容,那么将回调传递给ob_start() 会更干净。但是,如果您仍然对内容做其他事情,那么使用回调并没有真正的好处。这就说得通了。我喜欢使用方法引用作为回调的想法,除了它使我的 OO 方面感到烦恼,因为该方法不应该真正公开,从技术上讲,它是一个应该保持私有的实现细节。 (也许可以使用ReflectionMethod::setAccessible)。
  • 我唯一不明白的是为什么您调用ob_end_clean() 只是丢弃缓冲区内容。我确定它应该是 echo ob_end_clean() 还是我还遗漏了什么?
  • 我正在使用ob_end_clean();,因为当你清理缓冲区时,你的回调仍然被调用。因此,如果您使用回调,我假设您不需要在主范围内获得结果,因此您不需要返回 $result。如果您需要echo ob_get_clean();,请致电ob_start,无需回调。否则,使用回调。如果你从一个类调用ob_start,你可以使用array($this, 'method')作为回调,即使method是私有的(因为你从同一个类调用它)。
  • "如果你需要回显 ob_get_clean();,调用 ob_start 而不回调。"我的朋友,智慧的话,谢谢。我将为像我这样的慢速人发布一个答案,但是在我又读了几次 RTFM 之后,我将你的答案标记为正确,因为它让我到达那里:-)
【解决方案2】:

只是为了澄清一下宁索的正确答案。

我的两个代码示例实际上不会产生相同的结果。其实回调和ob_get_clean()结合使用是完全没用的。

这是因为回调是在清理刷新缓冲区时应用的。

但是ob_get_clean() 首先检索内容,然后然后 清理缓冲区。也就是说返回的内容不是回调返回的结果,而是传入回调的输入。

我写了这两个简单(和hacky)的脚本来演示。

这个使用ob_get_clean()并没有产生正确的结果:

// Tests the use of callbacks with ob_get_clean().
class TestWithGetClean {
    // This variable is set when obcallback is called.
    public $foo = null;

    // The output buffer callback.
    public function obcallback($value) { 
        $this->foo = 'set';
        return $value . '(modified)'; 
    }

    // Main method.
    public function run() {
        ob_start(array($this, 'obcallback'));
        echo 'this is the output', PHP_EOL;
        return ob_get_clean();
    }
}

// Run the test with ob_get_clean().
$t = new TestWithGetClean();
echo $t->run(); // This method returns a value in this test. (But not the correct value!)
echo $t->foo, PHP_EOL;

运行的输出是:

这是输出

设置

文本'(modified)' 没有出现在任何地方。但是请注意,实例变量$foo 已设置,因此肯定会调用回调,但是输出与我最初预期的不一样。

与使用ob_end_flush()的这个比较:

// Tests the use of callbacks with ob_end_flush().
class TestWithEndFlush {
    // This variable is set when obcallback is called.
    public $foo = null;
    
    // The output buffer callback.
    public function obcallback($value) { 
        $this->foo = 'set';
        return $value . '(modified)' . PHP_EOL; 
    }

    // Main method.
    public function run() {
        ob_start(array($this, 'obcallback'));
        echo 'this is the output', PHP_EOL;
        ob_end_flush();
    }
}

// Run the test with ob_end_flush().
$t2 = new TestWithEndFlush();
$t2->run(); // This method produces output in this test.
echo $t2->foo, PHP_EOL;

这会产生以下输出:

这是输出

(修改)

设置

但是,这当然没有那么有用,因为输出直接发送到客户端,因此我们无法进一步操纵结果。 (例如,将文本包装在 Symfony HttpFoundation 组件请求对象中)。

【讨论】:

  • 谢谢,我想问为什么保存 ob_get_clean 返回的字符串似乎忽略了回调函数:-)
猜你喜欢
  • 2019-06-10
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 2010-12-22
  • 1970-01-01
  • 2020-01-01
  • 2018-12-22
  • 2014-12-06
相关资源
最近更新 更多