Merge pull request #571 from flightphp/response-body-callback

Added ability to convert the response body with callbacks
pull/572/head v3.8.0
n0nag0n 10 months ago committed by GitHub
commit d92c65f8ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -314,7 +314,7 @@ class Engine
*/
public function get(?string $key = null)
{
if (null === $key) {
if ($key === null) {
return $this->vars;
}
@ -360,7 +360,7 @@ class Engine
*/
public function clear(?string $key = null): void
{
if (null === $key) {
if ($key === null) {
$this->vars = [];
return;
}
@ -570,9 +570,11 @@ class Engine
public function _error(Throwable $e): void
{
$msg = sprintf(
'<h1>500 Internal Server Error</h1>' .
'<h3>%s (%s)</h3>' .
'<pre>%s</pre>',
<<<HTML
<h1>500 Internal Server Error</h1>
<h3>%s (%s)</h3>
<pre>%s</pre>
HTML,
$e->getMessage(),
$e->getCode(),
$e->getTraceAsString()
@ -603,8 +605,8 @@ class Engine
{
$response = $this->response();
if (!$response->sent()) {
if (null !== $code) {
if ($response->sent() === false) {
if ($code !== null) {
$response->status($code);
}
@ -729,12 +731,12 @@ class Engine
{
$base = $this->get('flight.base_url');
if (null === $base) {
if ($base === null) {
$base = $this->request()->base;
}
// Append base url to redirect url
if ('/' !== $base && false === strpos($url, '://')) {
if ($base !== '/' && strpos($url, '://') === false) {
$url = $base . preg_replace('#/+#', '/', '/' . $url);
}
@ -756,7 +758,7 @@ class Engine
*/
public function _render(string $file, ?array $data = null, ?string $key = null): void
{
if (null !== $key) {
if ($key !== null) {
$this->view()->set($key, $this->view()->fetch($file, $data));
return;
}
@ -833,7 +835,7 @@ class Engine
*/
public function _etag(string $id, string $type = 'strong'): void
{
$id = (('weak' === $type) ? 'W/' : '') . $id;
$id = (($type === 'weak') ? 'W/' : '') . $id;
$this->response()->header('ETag', '"' . str_replace('"', '\"', $id) . '"');

@ -151,7 +151,7 @@ class Request
'method' => self::getMethod(),
'referrer' => self::getVar('HTTP_REFERER'),
'ip' => self::getVar('REMOTE_ADDR'),
'ajax' => 'XMLHttpRequest' === self::getVar('HTTP_X_REQUESTED_WITH'),
'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest',
'scheme' => self::getScheme(),
'user_agent' => self::getVar('HTTP_USER_AGENT'),
'type' => self::getVar('CONTENT_TYPE'),
@ -160,7 +160,7 @@ class Request
'data' => new Collection($_POST),
'cookies' => new Collection($_COOKIE),
'files' => new Collection($_FILES),
'secure' => 'https' === self::getScheme(),
'secure' => self::getScheme() === 'https',
'accept' => self::getVar('HTTP_ACCEPT'),
'proxy_ip' => self::getProxyIpAddress(),
'host' => self::getVar('HTTP_HOST'),
@ -188,12 +188,12 @@ class Request
// This rewrites the url in case the public url and base directories match
// (such as installing on a subdirectory in a web server)
// @see testInitUrlSameAsBaseDirectory
if ('/' !== $this->base && '' !== $this->base && 0 === strpos($this->url, $this->base)) {
if ($this->base !== '/' && $this->base !== '' && strpos($this->url, $this->base) === 0) {
$this->url = substr($this->url, \strlen($this->base));
}
// Default url
if (empty($this->url)) {
if (empty($this->url) === true) {
$this->url = '/';
} else {
// Merge URL query parameters with $_GET
@ -203,11 +203,11 @@ class Request
}
// Check for JSON input
if (0 === strpos($this->type, 'application/json')) {
if (strpos($this->type, 'application/json') === 0) {
$body = $this->getBody();
if ('' !== $body) {
if ($body !== '') {
$data = json_decode($body, true);
if (is_array($data)) {
if (is_array($data) === true) {
$this->data->setData($data);
}
}
@ -225,13 +225,13 @@ class Request
{
$body = $this->body;
if ('' !== $body) {
if ($body !== '') {
return $body;
}
$method = $this->method ?? self::getMethod();
if ('POST' === $method || 'PUT' === $method || 'DELETE' === $method || 'PATCH' === $method) {
if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE' || $method === 'PATCH') {
$body = file_get_contents($this->stream_path);
}
@ -247,9 +247,9 @@ class Request
{
$method = self::getVar('REQUEST_METHOD', 'GET');
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) === true) {
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
} elseif (isset($_REQUEST['_method'])) {
} elseif (isset($_REQUEST['_method']) === true) {
$method = $_REQUEST['_method'];
}
@ -275,9 +275,9 @@ class Request
$flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE;
foreach ($forwarded as $key) {
if (\array_key_exists($key, $_SERVER)) {
if (\array_key_exists($key, $_SERVER) === true) {
sscanf($_SERVER[$key], '%[^,]', $ip);
if (false !== filter_var($ip, \FILTER_VALIDATE_IP, $flags)) {
if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) {
return $ip;
}
}
@ -322,7 +322,7 @@ class Request
{
$headers = [];
foreach ($_SERVER as $key => $value) {
if (0 === strpos($key, 'HTTP_')) {
if (strpos($key, 'HTTP_') === 0) {
// converts headers like HTTP_CUSTOM_HEADER to Custom-Header
$key = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$key] = $value;
@ -386,7 +386,7 @@ class Request
$params = [];
$args = parse_url($url);
if (isset($args['query'])) {
if (isset($args['query']) === true) {
parse_str($args['query'], $params);
}
@ -401,13 +401,13 @@ class Request
public static function getScheme(): string
{
if (
(isset($_SERVER['HTTPS']) && 'on' === strtolower($_SERVER['HTTPS']))
(isset($_SERVER['HTTPS']) === true && strtolower($_SERVER['HTTPS']) === 'on')
||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'])
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) === true && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
||
(isset($_SERVER['HTTP_FRONT_END_HTTPS']) && 'on' === $_SERVER['HTTP_FRONT_END_HTTPS'])
(isset($_SERVER['HTTP_FRONT_END_HTTPS']) === true && $_SERVER['HTTP_FRONT_END_HTTPS'] === 'on')
||
(isset($_SERVER['REQUEST_SCHEME']) && 'https' === $_SERVER['REQUEST_SCHEME'])
(isset($_SERVER['REQUEST_SCHEME']) === true && $_SERVER['REQUEST_SCHEME'] === 'https')
) {
return 'https';
}

@ -128,6 +128,13 @@ class Response
*/
protected bool $sent = false;
/**
* These are callbacks that can process the response body before it's sent
*
* @var array<int, callable> $responseBodyCallbacks
*/
protected array $responseBodyCallbacks = [];
/**
* Sets the HTTP status of the response.
*
@ -429,8 +436,38 @@ class Response
$this->sendHeaders(); // @codeCoverageIgnore
}
// Only for the v3 output buffering.
if ($this->v2_output_buffering === false) {
$this->processResponseCallbacks();
}
echo $this->body;
$this->sent = true;
}
/**
* Adds a callback to process the response body before it's sent. These are processed in the order
* they are added
*
* @param callable $callback The callback to process the response body
*
* @return void
*/
public function addResponseBodyCallback(callable $callback): void
{
$this->responseBodyCallbacks[] = $callback;
}
/**
* Cycles through the response body callbacks and processes them in order
*
* @return void
*/
protected function processResponseCallbacks(): void
{
foreach ($this->responseBodyCallbacks as $callback) {
$this->body = $callback($this->body);
}
}
}

@ -101,7 +101,7 @@ class Router
$methods = ['*'];
if (false !== strpos($url, ' ')) {
if (strpos($url, ' ') !== false) {
[$method, $url] = explode(' ', $url, 2);
$url = trim($url);
$methods = explode('|', $method);

@ -6,6 +6,7 @@ parameters:
level: 6
excludePaths:
- vendor
- flight/util/ReturnTypeWillChange.php
paths:
- flight
- index.php

@ -87,8 +87,7 @@ class EngineTest extends TestCase
public function testHandleException()
{
$engine = new Engine();
$regex_message = preg_quote('<h1>500 Internal Server Error</h1><h3>thrown exception message (20)</h3>');
$this->expectOutputRegex('~' . $regex_message . '~');
$this->expectOutputRegex('~\<h1\>500 Internal Server Error\</h1\>[\s\S]*\<h3\>thrown exception message \(20\)\</h3\>~');
$engine->handleException(new Exception('thrown exception message', 20));
}

@ -255,4 +255,37 @@ class ResponseTest extends TestCase
$response->write('new', true);
$this->assertEquals('new', $response->getBody());
}
public function testResponseBodyCallback()
{
$response = new Response();
$response->write('test');
$str_rot13 = function ($body) {
return str_rot13($body);
};
$response->addResponseBodyCallback($str_rot13);
ob_start();
$response->send();
$rot13_body = ob_get_clean();
$this->assertEquals('grfg', $rot13_body);
}
public function testResponseBodyCallbackMultiple()
{
$response = new Response();
$response->write('test');
$str_rot13 = function ($body) {
return str_rot13($body);
};
$str_replace = function ($body) {
return str_replace('g', 'G', $body);
};
$response->addResponseBodyCallback($str_rot13);
$response->addResponseBodyCallback($str_replace);
$response->addResponseBodyCallback($str_rot13);
ob_start();
$response->send();
$rot13_body = ob_get_clean();
$this->assertEquals('TesT', $rot13_body);
}
}

@ -161,9 +161,11 @@ Flight::route('/jsonp', function () {
Flight::map('error', function (Throwable $e) {
echo sprintf(
'<h1>500 Internal Server Error</h1>' .
'<h3>%s (%s)</h3>' .
'<pre style="border: 2px solid red; padding: 21px; background: lightgray; font-weight: bold;">%s</pre>',
<<<HTML
<h1>500 Internal Server Error</h1>
<h3>%s (%s)</h3>
<pre style="border: 2px solid red; padding: 21px; background: lightgray; font-weight: bold;">%s</pre>
HTML,
$e->getMessage(),
$e->getCode(),
str_replace(getenv('PWD'), '***CONFIDENTIAL***', $e->getTraceAsString())

Loading…
Cancel
Save