Skip to content

Commit 55f846c

Browse files
committed
Fixed headers already sent issues with php-cgi.
1 parent 0bb48d2 commit 55f846c

File tree

5 files changed

+86
-60
lines changed

5 files changed

+86
-60
lines changed

Bootstraps/HooksInterface.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace PHPPM\Bootstraps;
4+
5+
interface HooksInterface {
6+
public function preHandle($app);
7+
public function postHandle($app);
8+
}

Bootstraps/Laravel.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
namespace PHPPM\Bootstraps;
44

5-
use Stack\Builder;
6-
75
/**
86
* A default bootstrap for the Laravel framework
97
*/
10-
class Laravel implements BootstrapInterface
8+
class Laravel implements BootstrapInterface, HooksInterface
119
{
1210
/**
1311
* @var string|null The application environment
@@ -73,4 +71,20 @@ public function getApplication()
7371

7472
return $this->app;
7573
}
74+
75+
/**
76+
* @param \Illuminate\Contracts\Foundation\Application $app
77+
*/
78+
public function preHandle($app)
79+
{
80+
//reset const LARAVEL_START, to get the correct timing
81+
}
82+
83+
/**
84+
* @param \Illuminate\Contracts\Foundation\Application $app
85+
*/
86+
public function postHandle($app)
87+
{
88+
//reset debugbar if available
89+
}
7690
}

Bootstraps/Symfony.php

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<?php
22

33
namespace PHPPM\Bootstraps;
4+
45
use Symfony\Component\HttpFoundation\Request;
56

67
/**
78
* A default bootstrap for the Symfony framework
89
*/
9-
class Symfony implements BootstrapInterface
10+
class Symfony implements BootstrapInterface, HooksInterface
1011
{
1112
/**
1213
* @var string|null The application environment
@@ -30,13 +31,15 @@ public function __construct($appenv, $debug)
3031
/**
3132
* @return string
3233
*/
33-
public function getStaticDirectory() {
34+
public function getStaticDirectory()
35+
{
3436
return 'web/';
3537
}
3638

3739
/**
3840
* Create a Symfony application
39-
* @return SymfonyAppKernel
41+
*
42+
* @return \AppKernel
4043
*/
4144
public function getApplication()
4245
{
@@ -48,7 +51,7 @@ public function getApplication()
4851
require './vendor/autoload.php';
4952
}
5053

51-
$app = new SymfonyAppKernel($this->appenv, $this->debug); //which extends \AppKernel
54+
$app = new \AppKernel($this->appenv, $this->debug);
5255
$app->loadClassCache();
5356
$app->boot();
5457

@@ -59,4 +62,40 @@ public function getApplication()
5962

6063
return $app;
6164
}
65+
66+
/**
67+
* Does some necessary preparation before each request.
68+
*
69+
* @param \AppKernel $app
70+
*/
71+
public function preHandle($app)
72+
{
73+
//resets Kernels startTime, so Symfony can correctly calculate the execution time
74+
\Closure::bind(
75+
function () {
76+
$this->startTime = microtime(true);
77+
},
78+
$app,
79+
'AppKernel'
80+
)->call($app);
81+
}
82+
83+
/**
84+
* Does some necessary clean up after each request.
85+
*
86+
* @param \AppKernel $app
87+
*/
88+
public function postHandle($app)
89+
{
90+
//resets stopwatch, so it can correctly calculate the execution time
91+
if ($app->getContainer()->has('debug.stopwatch')) {
92+
$app->getContainer()->get('debug.stopwatch')->__construct();
93+
}
94+
95+
if ($app->getContainer()->has('profiler')) {
96+
// since Symfony does not reset Profiler::disable() calls after each request, we need to do it,
97+
// so the profiler bar is visible after the second request as well.
98+
$app->getContainer()->get('profiler')->enable();
99+
}
100+
}
62101
}

Bootstraps/SymfonyAppKernel.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

Bridges/HttpKernel.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace PHPPM\Bridges;
44

55
use PHPPM\Bootstraps\BootstrapInterface;
6-
use PHPPM\Bootstraps\SymfonyAppKernel;
6+
use PHPPM\Bootstraps\HooksInterface;
77
use PHPPM\React\HttpResponse;
88
use React\Http\Request as ReactRequest;
99
use Symfony\Component\HttpFoundation\Cookie;
@@ -73,9 +73,14 @@ public function onRequest(ReactRequest $request, HttpResponse $response)
7373

7474
$syRequest = self::mapRequest($request);
7575

76+
//start buffering the output, so cgi is not sending any http headers
77+
//this is necessary because it would break session handling since
78+
//headers_sent() returns true if any unbuffered output reaches cgi stdout.
79+
ob_start();
80+
7681
try {
77-
if ($this->application instanceof SymfonyAppKernel) {
78-
$this->application->preHandle();
82+
if ($this->bootstrap instanceof HooksInterface) {
83+
$this->bootstrap->preHandle($this->application);
7984
}
8085

8186
$syResponse = $this->application->handle($syRequest);
@@ -91,8 +96,8 @@ public function onRequest(ReactRequest $request, HttpResponse $response)
9196
$this->application->terminate($syRequest, $syResponse);
9297
}
9398

94-
if ($this->application instanceof SymfonyAppKernel) {
95-
$this->application->postHandle();
99+
if ($this->bootstrap instanceof HooksInterface) {
100+
$this->bootstrap->postHandle($this->application);
96101
}
97102
}
98103

@@ -169,7 +174,14 @@ protected static function mapResponse(HttpResponse $reactResponse, SymfonyRespon
169174
$headers['Set-Cookie'] = $cookies;
170175

171176
$reactResponse->writeHead($syResponse->getStatusCode(), $headers);
172-
$reactResponse->end($content);
177+
178+
$stdOut = '';
179+
while ($buffer = @ob_get_clean()) {
180+
$stdOut .= $buffer;
181+
}
182+
183+
$reactResponse->end($stdOut . $content);
184+
173185
}
174186

175187
/**

0 commit comments

Comments
 (0)