PHP调用了两次ob_start,现在不能正确刷新


PHP ob_start called twice, won't flush correctly now

我已经写了一个mvc框架,我正在使用ob_start('error_handler')来捕捉致命的错误。这很有效!

在boot.php

// Start the error-logging
ob_start( 'error_logging' );
// Run the request
Request::run( URL::getInstance()->init()->formatURL() );
// Flush and disable output-buffering
ob_end_flush();

error_logging功能:

function error_logging( $output )
{
    // Get the last error
    $trace = error_get_last();
    // Initialize our final array of fields explaining the error
    $final = array();
    // Check if the error does not exist, return the original output unedited
    if( !isset( $trace ) ) return $output;
    // Loop through or error and build our array
    foreach( $trace as $info => $value ) $final[$info] = $value;
    // Initialize our Error-class
    $error = Error::factory();
    // Handle the error with the fields and build our error-message
    $newOutput = $error->handle( $final['type'] , $final['message'], $final['file'], $final['line'] );
    // Check if the new output is null, if yes set output to the original output, otherwise set it to the new output
    $output = ( $newOutput === null ) ? $output : $newOutput;
    // Return the output
    return $output;
}

同样,这很好地处理了所有的错误!我没有在Error-class中使用任何flush或任何东西。

现在,我有一个使用ob_start的控制器,因为它是一个相当长的某个方法的运行时间,我想向用户反馈发生了什么以及脚本做了什么。我使用ob_flush()。

然而,在实现了这个ob_start('error_logging');ob_flush似乎不起作用。他们等待直到整个脚本完成(大约需要8分钟)。

为什么?

我如何刷新的一个例子是这样的:在控制器方法:

$this->login->execute();
在登录

::执行:

public function execute( $site = 'mobile' )
{
    ob_start();
    $this->debug->log('--------------------------------------------<br />');
    $this->debug->log( 'Logging into site: ' . ucfirst( $site ) . '<br />' );
    // Initiate cookies
    if( $site == 'full' )
        $this->_initCookies();
    // Set up the URL
    $url = Config::get( $site ) . Config::get( 'url_login' );
    curl_setopt( $this->curl->get(), CURLOPT_URL, $url );
    // CURL-values
    curl_setopt( $this->curl->get(), CURLOPT_FOLLOWLOCATION, 1  );
    curl_setopt( $this->curl->get(), CURLOPT_RETURNTRANSFER, 1  );
    curl_setopt( $this->curl->get(), CURLOPT_POST,           1  );
    curl_setopt( $this->curl->get(), CURLOPT_POSTFIELDS,     Config::get( 'postfields' ) );
    curl_setopt( $this->curl->get(), CURLOPT_COOKIEFILE,     'resources/tmp/cookies.txt');
    curl_setopt( $this->curl->get(), CURLOPT_CONNECTTIMEOUT, 10 );
    curl_setopt( $this->curl->get(), CURLOPT_TIMEOUT,        40 );
    curl_setopt( $this->curl->get(), CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0.1) Gecko/20100101 Firefox/6.0.1');
    // Get the page returned after we've logged in
    $this->login_html = str_get_html( curl_exec( $this->curl->get() ) );
    // Validate the page returned to see if we successfully logged in
    $this->_validateLogin( $site );
    $this->debug->log('--------------------------------------------<br />');
    ob_end_flush();
    // Return $this
    return $this;
}

每个$this->debug->log()调用刷新输出如下:

public function log( $string )
{
    if( $this->active() || $this->logging() )
    {
        echo str_repeat("<!-- AGENT SMITH -->", 500);
        echo $string;
        ob_flush();
        flush();
    }
}

知道为什么不能正确冲洗吗??告诉我,如果你需要查看一些更多的代码,我会提供它!!

感谢

::::::::解决方案 ::::::::::

我必须保存第一个缓冲区(error_logging),结束它,开始新的,做我的事情,然后结束那个,再次启动第一个缓冲区并返回保存的输出。

为它创建了一个类:

class Tanaxia_Buffer
{
    public static $instance = null;
    private $main = null;
    private $previousOutput = "";
    public static function getInstance()
    {
        if( Buffer::$instance === null )
            Buffer::$instance = new Buffer();
        return Buffer::$instance;
    }
    public function set_main( $ob )
    {
        $this->main = $ob;
    }
    public function start( $main = null )
    {
        if( $main !== null )
            $this->set_main( $main );
        ob_start( $this->main );
    }
    public function end( $type = 'clean' )
    {
        switch( $type )
        {
            case 'flush': ob_end_flush(); break;
            case 'clean': ob_end_clean(); break;
        }
    }
    public function start_separate()
    {
        $this->previousOutput = ob_get_contents();
        ob_end_clean();
        ob_start();
    }
    public function end_separate( $type = 'flush' )
    {
        switch( $type )
        {
            case 'flush': ob_end_flush(); break;
            case 'clean': ob_end_clean(); break;
        }
        ob_start( $this->main );
        echo $this->previousOutput;
        empty( $this->previousOutput );
    }
}

用法:

boot.php

// Start the error-logging
Buffer::getInstance()->start( 'error_logging' );
// Run the request
Request::run( URL::getInstance()->init()->formatURL() );
// Flush and disable output-buffering
Buffer::getInstance()->end( 'flush' );

登录:execute ():

public function execute( $site = 'mobile' )
{
    Buffer::getInstance()->start_separate();
    // Everything in between
    Buffer::getInstance()->end_separate();
    // Return $this
    return $this;
}

作品完美!不支持多个嵌套的ob_start,因为它只保存一个以前的输出。虽然可以修复,但现在没有必要!

我猜这与嵌套的ob_start()调用有关。我假设你想保持它们嵌套,但它是否冲洗你想要的方式,如果在你的execute()函数你调用ob_end_flush()ob_end_clean()之前,你开始下一个输出缓冲区?像这样:

public function execute( $site = 'mobile' )
{
    // save existing contents of output buffer
    $previousOutput = ob_get_contents();
    // wipe the output buffer itself
    ob_end_clean();
    // start a new output buffer
    ob_start();
    /* SNIP all the code that wouldn't change */
    // flush the function's output buffer
    ob_end_flush();
    // restart the old output buffer
    ob_start("error_logging");
    // take whatever was in the old buffer and make sure it shows up in the new one
    echo $previousOutput;
    return $this;
}