diff --git a/flight/Engine.php b/flight/Engine.php index d6e5490..1826445 100644 --- a/flight/Engine.php +++ b/flight/Engine.php @@ -66,15 +66,14 @@ use flight\net\Route; */ class Engine { - - /** - * @var array List of methods that can be extended in the Engine class. - */ - private const MAPPABLE_METHODS = [ - 'start', 'stop', 'route', 'halt', 'error', 'notFound', - 'render', 'redirect', 'etag', 'lastModified', 'json', 'jsonp', - 'post', 'put', 'patch', 'delete', 'group', 'getUrl' - ]; + /** + * @var array List of methods that can be extended in the Engine class. + */ + private const MAPPABLE_METHODS = [ + 'start', 'stop', 'route', 'halt', 'error', 'notFound', + 'render', 'redirect', 'etag', 'lastModified', 'json', 'jsonp', + 'post', 'put', 'patch', 'delete', 'group', 'getUrl' + ]; /** @var array Stored variables. */ protected array $vars = []; diff --git a/flight/net/Request.php b/flight/net/Request.php index 7fffd75..eb932c0 100644 --- a/flight/net/Request.php +++ b/flight/net/Request.php @@ -331,6 +331,49 @@ class Request return $headers; } + /** + * Alias of Request->getHeader(). Gets a single header. + * + * @param string $header Header name. Can be caps, lowercase, or mixed. + * @param string $default Default value if the header does not exist + * + * @return string + */ + public static function header(string $header, $default = '') + { + return self::getHeader($header, $default); + } + + /** + * Alias of Request->getHeaders(). Gets all the request headers + * + * @return array + */ + public static function headers(): array + { + return self::getHeaders(); + } + + /** + * Gets the full request URL. + * + * @return string URL + */ + public function getFullUrl(): string + { + return $this->scheme . '://' . $this->host . $this->url; + } + + /** + * Grabs the scheme and host. Does not end with a / + * + * @return string + */ + public function getBaseUrl(): string + { + return $this->scheme . '://' . $this->host; + } + /** * Parse query parameters from a URL. * diff --git a/flight/net/Response.php b/flight/net/Response.php index 61df314..761d1a6 100644 --- a/flight/net/Response.php +++ b/flight/net/Response.php @@ -173,6 +173,34 @@ class Response return $this; } + /** + * Gets a single header from the response. + * + * @param string $name the name of the header + * + * @return string|null + */ + public function getHeader(string $name): ?string + { + $headers = $this->headers; + // lowercase all the header keys + $headers = array_change_key_case($headers, CASE_LOWER); + return $headers[strtolower($name)] ?? null; + } + + /** + * Alias of Response->header(). Adds a header to the response. + * + * @param array|string $name Header name or array of names and values + * @param ?string $value Header value + * + * @return $this + */ + public function setHeader($name, ?string $value): self + { + return $this->header($name, $value); + } + /** * Returns the headers from the response. * @@ -183,6 +211,16 @@ class Response return $this->headers; } + /** + * Alias for Response->headers(). Returns the headers from the response. + * + * @return array> + */ + public function getHeaders(): array + { + return $this->headers(); + } + /** * Writes content to the response body. * diff --git a/tests/DocExamplesTest.php b/tests/DocExamplesTest.php index dd5d6c6..1518363 100644 --- a/tests/DocExamplesTest.php +++ b/tests/DocExamplesTest.php @@ -45,7 +45,7 @@ class DocExamplesTest extends TestCase $this->assertEquals('[]', Flight::response()->getBody()); } - public function testMapNotFoundMethodV2OutputBuffering() + public function testMapNotFoundMethodV2OutputBuffering() { Flight::map('notFound', function () { Flight::json([], 404); @@ -57,9 +57,9 @@ class DocExamplesTest extends TestCase echo 'hello world!'; }); - Flight::set('flight.v2.output_buffering', true); + Flight::set('flight.v2.output_buffering', true); Flight::start(); - ob_get_clean(); + ob_get_clean(); $this->assertEquals(404, Flight::response()->status()); $this->assertEquals('[]', Flight::response()->getBody()); } diff --git a/tests/FlightTest.php b/tests/FlightTest.php index c82caff..c068dbe 100644 --- a/tests/FlightTest.php +++ b/tests/FlightTest.php @@ -243,37 +243,39 @@ class FlightTest extends TestCase $this->assertEquals('/user/all_users/check_user/check_one/normalpath', $url); } - public function testHookOutputBuffering() { - Flight::route('/test', function () { - echo 'test'; - }); - - Flight::before('start', function ($output) { - echo 'hooked before start'; - }); - - Flight::request()->url = '/test'; - - $this->expectOutputString('hooked before starttest'); - Flight::start(); - $this->assertEquals('test', Flight::response()->getBody()); - } - - public function testHookOutputBufferingV2OutputBuffering() { - Flight::route('/test', function () { - echo 'test'; - }); - - Flight::before('start', function ($output) { - echo 'hooked before start'; - }); - - Flight::set('flight.v2.output_buffering', true); - Flight::request()->url = '/test'; - - $this->expectOutputString('hooked before starttest'); - ob_start(); - Flight::start(); - $this->assertEquals('hooked before starttest', Flight::response()->getBody()); - } + public function testHookOutputBuffering() + { + Flight::route('/test', function () { + echo 'test'; + }); + + Flight::before('start', function ($output) { + echo 'hooked before start'; + }); + + Flight::request()->url = '/test'; + + $this->expectOutputString('hooked before starttest'); + Flight::start(); + $this->assertEquals('test', Flight::response()->getBody()); + } + + public function testHookOutputBufferingV2OutputBuffering() + { + Flight::route('/test', function () { + echo 'test'; + }); + + Flight::before('start', function ($output) { + echo 'hooked before start'; + }); + + Flight::set('flight.v2.output_buffering', true); + Flight::request()->url = '/test'; + + $this->expectOutputString('hooked before starttest'); + ob_start(); + Flight::start(); + $this->assertEquals('hooked before starttest', Flight::response()->getBody()); + } } diff --git a/tests/RequestTest.php b/tests/RequestTest.php index e3cdfb5..a8b4310 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -205,10 +205,10 @@ class RequestTest extends TestCase // or the headers that are already in $_SERVER $this->assertEquals('XMLHttpRequest', $request->getHeader('X-REqUesTed-WiTH')); - $this->assertEquals('32.32.32.32', $request->getHeader('X-Forwarded-For')); + $this->assertEquals('32.32.32.32', $request->header('X-Forwarded-For')); // default values - $this->assertEquals('default value', $request->getHeader('X-Non-Existent-Header', 'default value')); + $this->assertEquals('default value', $request->header('X-Non-Existent-Header', 'default value')); } public function testGetHeaders() @@ -231,7 +231,7 @@ class RequestTest extends TestCase $_SERVER = []; $_SERVER['HTTP_X_CUSTOM_HEADER'] = ''; $request = new Request(); - $this->assertEquals(['X-Custom-Header' => ''], $request->getHeaders()); + $this->assertEquals(['X-Custom-Header' => ''], $request->headers()); } public function testGetHeadersWithMultipleHeaders() @@ -245,4 +245,38 @@ class RequestTest extends TestCase 'X-Custom-Header2' => 'custom header value 2' ], $request->getHeaders()); } + + public function testGetFullUrlNoHttps() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/page?id=1'; + $request = new Request(); + $this->assertEquals('http://example.com/page?id=1', $request->getFullUrl()); + } + + public function testGetFullUrlWithHttps() + { + $_SERVER['HTTP_HOST'] = 'localhost:8000'; + $_SERVER['REQUEST_URI'] = '/page?id=1'; + $_SERVER['HTTPS'] = 'on'; + $request = new Request(); + $this->assertEquals('https://localhost:8000/page?id=1', $request->getFullUrl()); + } + + public function testGetBaseUrlNoHttps() + { + $_SERVER['HTTP_HOST'] = 'example.com'; + $_SERVER['REQUEST_URI'] = '/page?id=1'; + $request = new Request(); + $this->assertEquals('http://example.com', $request->getBaseUrl()); + } + + public function testGetBaseUrlWithHttps() + { + $_SERVER['HTTP_HOST'] = 'localhost:8000'; + $_SERVER['REQUEST_URI'] = '/page?id=1'; + $_SERVER['HTTPS'] = 'on'; + $request = new Request(); + $this->assertEquals('https://localhost:8000', $request->getBaseUrl()); + } } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index 1b9c1da..d7155fc 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -65,7 +65,7 @@ class ResponseTest extends TestCase $response = new Response(); $response->header('content-type', 'text/html'); $response->header('x-test', 'test'); - $this->assertEquals(['content-type' => 'text/html', 'x-test' => 'test'], $response->headers()); + $this->assertEquals(['content-type' => 'text/html', 'x-test' => 'test'], $response->getHeaders()); } public function testHeaderArray() @@ -81,6 +81,13 @@ class ResponseTest extends TestCase $this->assertEquals($response, $response->header('Content-Type', 'text/html')); } + public function testGetHeaderCrazyCase() + { + $response = new Response(); + $response->setHeader('CoNtEnT-tYpE', 'text/html'); + $this->assertEquals('text/html', $response->getHeader('content-type')); + } + public function testWrite() { $response = new Response(); @@ -104,6 +111,9 @@ class ResponseTest extends TestCase public function testClear() { $response = new Response(); + + // Should clear this echo out + echo 'hi'; $response->write('test'); $response->status(404); $response->header('Content-Type', 'text/html'); @@ -111,6 +121,7 @@ class ResponseTest extends TestCase $this->assertEquals('', $response->getBody()); $this->assertEquals(200, $response->status()); $this->assertEquals([], $response->headers()); + $this->assertEquals(0, ob_get_length()); } public function testCacheSimple() @@ -219,8 +230,8 @@ class ResponseTest extends TestCase return $this; } }; - $response->header('Content-Type', 'text/html'); - $response->header('X-Test', 'test'); + $response->setHeader('Content-Type', 'text/html'); + $response->setHeader('X-Test', 'test'); $response->write('Something'); $this->expectOutputString('Something'); diff --git a/tests/server-v2/index.php b/tests/server-v2/index.php index 240313e..2c6d129 100644 --- a/tests/server-v2/index.php +++ b/tests/server-v2/index.php @@ -1,9 +1,11 @@ Route text: Root route works!'; }); -Flight::route('/querytestpath', function(){ +Flight::route('/querytestpath', function () { echo 'Route text: This ir query route
'; echo "I got such query parameters:
";
     print_r(Flight::request()->query);
     echo "
"; -},false,"querytestpath"); +}, false, "querytestpath"); // Test 2: Simple route -Flight::route('/test', function(){ +Flight::route('/test', function () { echo 'Route text: Test route works!'; }); // Test 3: Route with parameter -Flight::route('/user/@name', function($name){ +Flight::route('/user/@name', function ($name) { echo "Route text: Hello, $name!"; }); Flight::route('POST /postpage', function () { echo 'Route text: THIS IS POST METHOD PAGE'; -},false,"postpage"); +}, false, "postpage"); // Test 4: Grouped routes -Flight::group('/group', function(){ - Flight::route('/test', function(){ +Flight::group('/group', function () { + Flight::route('/test', function () { echo 'Route text: Group test route works!'; }); - Flight::route('/user/@name', function($name){ + Flight::route('/user/@name', function ($name) { echo "Route text: There is variable called name and it is $name"; }); - Flight::group('/group1', function(){ - Flight::group('/group2', function(){ - Flight::group('/group3', function(){ - Flight::group('/group4', function(){ - Flight::group('/group5', function(){ - Flight::group('/group6', function(){ - Flight::group('/group7', function(){ - Flight::group('/group8', function(){ - Flight::route('/final_group', function(){ + Flight::group('/group1', function () { + Flight::group('/group2', function () { + Flight::group('/group3', function () { + Flight::group('/group4', function () { + Flight::group('/group5', function () { + Flight::group('/group6', function () { + Flight::group('/group7', function () { + Flight::group('/group8', function () { + Flight::route('/final_group', function () { echo 'Mega Group test route works!'; - },false,"final_group"); + }, false, "final_group"); }); }); }); @@ -64,39 +66,41 @@ Flight::group('/group', function(){ }); }); }); - }); + }); }); // Test 5: Route alias -Flight::route('/alias', function(){ +Flight::route('/alias', function () { echo 'Route text: Alias route works!'; }, false, 'aliasroute'); -class authCheck { - public function before(){ - if(!isset($_COOKIE['user'])){ +class AuthCheck +{ + public function before() + { + if (!isset($_COOKIE['user'])) { echo 'Middleware text: You are not authorized to access this route!'; } } } -$middle = new authCheck(); +$middle = new AuthCheck(); // Test 6: Route with middleware -Flight::route('/protected', function(){ +Flight::route('/protected', function () { echo 'Route text: Protected route works!'; })->addMiddleware([$middle]); // Test 7: Route with template -Flight::route('/template/@name', function($name){ - Flight::render('template.php', ['name' => $name]); +Flight::route('/template/@name', function ($name) { + Flight::render('template.phtml', ['name' => $name]); }); Flight::set('flight.views.path', './'); Flight::map('error', function (Throwable $error) { echo "

An error occurred, mapped error method worked, error bellow

"; echo '
';
-    echo str_replace(getenv('PWD'),"***CLASSIFIED*****",$error->getTraceAsString());
+    echo str_replace(getenv('PWD'), "***CLASSIFIED*****", $error->getTraceAsString());
     echo "
"; echo "Go back"; }); -Flight::map('notFound', function() { +Flight::map('notFound', function () { echo 'Route text: The requested URL was not found'; echo "Go back"; }); @@ -158,7 +162,7 @@ echo '
  • Template path
  • Query path
  • Post method test page - should be 404
  • -
  • Mega group
  • +
  • Mega group
  • '; Flight::before('start', function ($params) { echo '
    '; @@ -172,5 +176,3 @@ Flight::after('start', function ($params) { echo "
    "; }); Flight::start(); - - \ No newline at end of file diff --git a/tests/server-v2/template.php b/tests/server-v2/template.phtml similarity index 96% rename from tests/server-v2/template.php rename to tests/server-v2/template.phtml index 7cb97b4..d2ab1fa 100644 --- a/tests/server-v2/template.php +++ b/tests/server-v2/template.phtml @@ -1 +1 @@ -Route text: Template works! \ No newline at end of file +Route text: Template works! diff --git a/tests/server/AuthCheck.php b/tests/server/AuthCheck.php new file mode 100644 index 0000000..58526b2 --- /dev/null +++ b/tests/server/AuthCheck.php @@ -0,0 +1,18 @@ +Middleware text: You are not authorized to access this route!'; + } + } +} diff --git a/tests/server/LayoutMiddleware.php b/tests/server/LayoutMiddleware.php new file mode 100644 index 0000000..bb30bda --- /dev/null +++ b/tests/server/LayoutMiddleware.php @@ -0,0 +1,93 @@ + + ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #333; + } + + li { + float: left; + } + #infotext { + font-weight: bold; + color: blueviolet; + } + li a { + display: block; + color: white; + text-align: center; + padding: 14px 16px; + text-decoration: none; + } + + li a:hover { + background-color: #111; + } + #container { + color: #333; + font-size: 16px; + line-height: 1.5; + margin: 20px 0; + padding: 10px; + border: 1px solid #ddd; + background-color: #f9f9f9; + } + #debugrequest { + color: #333; + font-size: 16px; + line-height: 1.5; + margin: 20px 0; + padding: 10px; + border: 1px solid #ddd; + background-color: #f9f9f9; + } + + +HTML; + echo '
    '; + } + + public function after() + { + echo '
    '; + echo '
    '; + echo "

    Request Information

    ";
    +        print_r(Flight::request());
    +        echo '

    Raw Request Information

    '; + print_r($_SERVER); + echo "

    Response Information

    ";
    +        print_r(Flight::response());
    +        echo "
    "; + echo "
    "; + } +} diff --git a/tests/server/index.php b/tests/server/index.php index 15de575..d70eabf 100644 --- a/tests/server/index.php +++ b/tests/server/index.php @@ -1,199 +1,115 @@ - ul { - list-style-type: none; - margin: 0; - padding: 0; - overflow: hidden; - background-color: #333; - } - - li { - float: left; - } - #infotext { - font-weight: bold; - color: blueviolet; - } - li a { - display: block; - color: white; - text-align: center; - padding: 14px 16px; - text-decoration: none; - } - - li a:hover { - background-color: #111; - } - #container { - color: #333; - font-size: 16px; - line-height: 1.5; - margin: 20px 0; - padding: 10px; - border: 1px solid #ddd; - background-color: #f9f9f9; - } - #debugrequest { - color: #333; - font-size: 16px; - line-height: 1.5; - margin: 20px 0; - padding: 10px; - border: 1px solid #ddd; - background-color: #f9f9f9; - } - - - HTML; - echo '
    '; - } - - public function after() { - echo '
    '; - echo '
    '; - echo "

    Request Information

    ";
    -		print_r(Flight::request());
    -		echo '

    Raw Request Information

    '; - print_r($_SERVER); - echo "

    Response Information

    ";
    -		print_r(Flight::response());
    -		echo "
    "; - echo "
    "; - } -} - -Flight::group('', function() { - - // Test 1: Root route - Flight::route('/', function(){ - echo 'Route text: Root route works!'; - }); - Flight::route('/querytestpath', function(){ - echo 'Route text: This ir query route
    '; - echo "I got such query parameters:
    ";
    -		print_r(Flight::request()->query);
    -		echo "
    "; - },false,"querytestpath"); - - // Test 2: Simple route - Flight::route('/test', function(){ - echo 'Route text: Test route works!'; - }); - - // Test 3: Route with parameter - Flight::route('/user/@name', function($name){ - echo "Route text: Hello, $name!"; - }); - Flight::route('POST /postpage', function () { - echo 'Route text: THIS IS POST METHOD PAGE'; - },false,"postpage"); - - // Test 4: Grouped routes - Flight::group('/group', function(){ - Flight::route('/test', function(){ - echo 'Route text: Group test route works!'; - }); - Flight::route('/user/@name', function($name){ - echo "Route text: There is variable called name and it is $name"; - }); - Flight::group('/group1', function(){ - Flight::group('/group2', function(){ - Flight::group('/group3', function(){ - Flight::group('/group4', function(){ - Flight::group('/group5', function(){ - Flight::group('/group6', function(){ - Flight::group('/group7', function(){ - Flight::group('/group8', function(){ - Flight::route('/final_group', function(){ - echo 'Mega Group test route works!'; - },false,"final_group"); - }); - }); - }); - }); - }); - }); - }); - }); - }); - - // Test 5: Route alias - Flight::route('/alias', function(){ - echo 'Route text: Alias route works!'; - }, false, 'aliasroute'); - - /** middleware test */ - class authCheck { - public function before(){ - if(!isset($_COOKIE['user'])){ - echo 'Middleware text: You are not authorized to access this route!'; - } - } - } - $middle = new authCheck(); - // Test 6: Route with middleware - Flight::route('/protected', function(){ - echo 'Route text: Protected route works!'; - })->addMiddleware([$middle]); - - // Test 7: Route with template - Flight::route('/template/@name', function($name){ - Flight::render('template.php', ['name' => $name]); - }); - - // Test 8: Throw an error - Flight::route('/error', function() { - trigger_error('This is a successful error'); - }); -}, [ new LayoutMiddleware ]); +require_once 'LayoutMiddleware.php'; + +Flight::group('', function () { + + // Test 1: Root route + Flight::route('/', function () { + echo 'Route text: Root route works!'; + }); + Flight::route('/querytestpath', function () { + echo 'Route text: This ir query route
    '; + echo "I got such query parameters:
    ";
    +        print_r(Flight::request()->query);
    +        echo "
    "; + }, false, "querytestpath"); + + // Test 2: Simple route + Flight::route('/test', function () { + echo 'Route text: Test route works!'; + }); + + // Test 3: Route with parameter + Flight::route('/user/@name', function ($name) { + echo "Route text: Hello, $name!"; + }); + Flight::route('POST /postpage', function () { + echo 'Route text: THIS IS POST METHOD PAGE'; + }, false, "postpage"); + + // Test 4: Grouped routes + Flight::group('/group', function () { + Flight::route('/test', function () { + echo 'Route text: Group test route works!'; + }); + Flight::route('/user/@name', function ($name) { + echo "Route text: There is variable called name and it is $name"; + }); + Flight::group('/group1', function () { + Flight::group('/group2', function () { + Flight::group('/group3', function () { + Flight::group('/group4', function () { + Flight::group('/group5', function () { + Flight::group('/group6', function () { + Flight::group('/group7', function () { + Flight::group('/group8', function () { + Flight::route('/final_group', function () { + echo 'Mega Group test route works!'; + }, false, "final_group"); + }); + }); + }); + }); + }); + }); + }); + }); + }); + + // Test 5: Route alias + Flight::route('/alias', function () { + echo 'Route text: Alias route works!'; + }, false, 'aliasroute'); + + /** Middleware test */ + include_once 'AuthCheck.php'; + $middle = new AuthCheck(); + // Test 6: Route with middleware + Flight::route('/protected', function () { + echo 'Route text: Protected route works!'; + })->addMiddleware([$middle]); + + // Test 7: Route with template + Flight::route('/template/@name', function ($name) { + Flight::render('template.phtml', ['name' => $name]); + }); + + // Test 8: Throw an error + Flight::route('/error', function () { + trigger_error('This is a successful error'); + }); +}, [ new LayoutMiddleware() ]); Flight::map('error', function (Throwable $e) { - echo sprintf( - '

    500 Internal Server Error

    ' . - '

    %s (%s)

    ' . - '
    %s
    ', - $e->getMessage(), - $e->getCode(), - str_replace(getenv('PWD'), '***CONFIDENTIAL***', $e->getTraceAsString()) - ); - echo "
    Go back"; + echo sprintf( + '

    500 Internal Server Error

    ' . + '

    %s (%s)

    ' . + '
    %s
    ', + $e->getMessage(), + $e->getCode(), + str_replace(getenv('PWD'), '***CONFIDENTIAL***', $e->getTraceAsString()) + ); + echo "
    Go back"; }); -Flight::map('notFound', function() { - echo 'Route text: The requested URL was not found
    '; - echo "Go back"; +Flight::map('notFound', function () { + echo 'Route text: The requested URL was not found
    '; + echo "Go back"; }); Flight::start(); diff --git a/tests/server/template.php b/tests/server/template.phtml similarity index 96% rename from tests/server/template.php rename to tests/server/template.phtml index 7cb97b4..d2ab1fa 100644 --- a/tests/server/template.php +++ b/tests/server/template.phtml @@ -1 +1 @@ -Route text: Template works! \ No newline at end of file +Route text: Template works!