PHP ob_start вызывается дважды, теперь не будет сброшен правильно

#php #flush #ob-start

#php #сброс #ob-start

Вопрос:

Я написал 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;

}
  

Опять же, это отлично работает и отлично справляется со всеми ошибками! Я не использую никакого сброса или чего-либо внутри класса ошибок.

Теперь у меня есть контроллер, который использует ob_start, поскольку это довольно длительное время выполнения определенного метода, и я хочу, чтобы пользователь получал обратную связь о том, что происходит и что делает скрипт. Для этого я использую ob_flush() .

Однако после реализации этого ob_start(‘error_logging’); ob_flush, похоже, не работает. Они ждут, пока весь скрипт не будет завершен (что занимает около 8 минут).

Почему?

Пример того, как я сбрасываю, выглядит так: в Controller-method:

 $this->login->execute();
  

В 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;

}
  

Each $this->debug->log() call flushes the output like so:

 public function log( $string )
{

    if( $this->active() || $this->logging() )
    {

        echo str_repeat("<!-- AGENT SMITH -->", 500);
        echo $string;

        ob_flush();
        flush();

    }

}
  

Any idea why it’s not flushing correctly??
Tell me if you need to view some more code and I’ll provide it!!

Thanks

::::::::SOLUTION::::::::::

Мне пришлось сохранить первый буфер (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' );
  

Login::execute():

 public function execute( $site = 'mobile' )
{

    Buffer::getInstance()->start_separate();

    // Everything in between

    Buffer::getInstance()->end_separate();

    // Return $this
    return $this;

}
  

Работает отлично! Не будет поддерживать несколько вложенных ob_start, поскольку он сохраняет только 1 предыдущий вывод. Может быть исправлено, но не обязательно прямо сейчас!

Ответ №1:

Я предполагаю, что это как-то связано с вложенными 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;
}
  

Комментарии:

1. Ха-ха! Это сработало так хорошо: D обновит main-post решением! Большое вам спасибо!