Salem Ouerdani
Salem Ouerdani

Reputation: 7886

Yii2 headers sent twice when running console from web app

Hi I'm trying to deploy a Yii2 app to Google App Engine Standard (Not Flex).

Standard environment doesn't allow background tasks or popen() or even writing to php://stdout. Also GAE Cron as Queued tasks seems to use http to communicate with other services. So I'm trying to figure out a way to wrap my console apps by a web service to which I'll restrict access to intern use only. My attempt is the following action:

public function actionMigrateTest()
{
    /**
     * GAE use
     * https://cloud.google.com/appengine/docs/standard/php/googlestorage/
     */
    // defined('STDOUT') or define('STDOUT', fopen('gs://stdout0/stdout', 'w'));
    // defined('STDERR') or define('STDERR', fopen('gs://stdout0/stderr', 'w'));

    /**
     * local use
     */
    defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
    defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
    defined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));

    $migration = new \yii\console\controllers\MigrateController('migrate', Yii::$app);
    $status = $migration->runAction('up', ['migrationPath' => '@app/migrations/', 'interactive' => false]);

    fclose(\STDOUT);
    fclose(\STDERR);
}

That works fine and the script is executed after uncommenting the top lines to write to GAE storage instead of php://stdout. And there is no problems when the response I find written into that file is something similar to this:

Yii Migration Tool (based on Yii v2.0.15.1)

No new migrations found. Your system is up-to-date.

The problem is when the console task is executed and the response is something like:

...
...
*** applying m180206_052910_create_variant_value_table
*** applied m180206_052910_create_variant_value_table (time: 0.129s)


15 migrations were applied.

Migrated up successfully.

Just after that successful script the following error is raised (in GAE as in my local environment):

An Error occurred while handling another error:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/db/Migration.php on line 591.' in /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/web/Response.php:366
Stack trace:
#0 /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/web/Response.php(339): yii\web\Response->sendHeaders()
#1 /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/web/ErrorHandler.php(135): yii\web\Response->send()
#2 /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/base/ErrorHandler.php(111): yii\web\ErrorHandler->renderException(Object(yii\web\HeadersAlreadySentException))
#3 [internal function]: yii\base\ErrorHandler->handleException(Object(yii\web\HeadersAlreadySentException))
#4 {main}
Previous exception:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/db/Migration.php on line 591.' in /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/web/Response.php:366
Stack trace:
#0 /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/web/Response.php(339): yii\web\Response->sendHeaders()
#1 /Users/salem/Sites/kt/shell55/vendor/yiisoft/yii2/base/Application.php(392): yii\web\Response->send()
#2 /Users/salem/Sites/kt/shell55/web/index.php(28): yii\base\Application->run()
#3 {main}

Any idea on why headers are being sent twice with that specific response or how to avoid it?

Upvotes: 2

Views: 1567

Answers (1)

rob006
rob006

Reputation: 22174

Try wrap migration run with ob_start() and ob_get_clean() or put exit at the end of this method to prevent further processing.

public function actionMigrateTest()
{
    ob_start();

    defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
    defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
    defined('STDERR') or define('STDERR', fopen('php://stderr', 'w'));

    $migration = new \yii\console\controllers\MigrateController('migrate', Yii::$app);
    $status = $migration->runAction('up', ['migrationPath' => '@app/migrations/', 'interactive' => false]);

    fclose(\STDOUT);
    fclose(\STDERR);

    return ob_get_clean();
}

Since 2.0.14 Yii does not allow to echo in the controller, you need to return response. There are several similar issues on GitHub.

Upvotes: 2

Related Questions