added PSR12 coding style to files.

pull/529/head
Austin Collier 1 year ago
parent 0d7b79bab9
commit cc4338a34b

@ -37,7 +37,8 @@
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpstan/extension-installer": "^1.3", "phpstan/extension-installer": "^1.3",
"rregeer/phpunit-coverage-check": "^0.3.1" "rregeer/phpunit-coverage-check": "^0.3.1",
"squizlabs/php_codesniffer": "^3.8"
}, },
"config": { "config": {
"allow-plugins": { "allow-plugins": {
@ -47,7 +48,9 @@
"scripts": { "scripts": {
"test": "phpunit", "test": "phpunit",
"test-coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage --coverage-clover=clover.xml && vendor/bin/coverage-check clover.xml 100", "test-coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html=coverage --coverage-clover=clover.xml && vendor/bin/coverage-check clover.xml 100",
"lint": "phpstan --no-progress -cphpstan.neon" "lint": "phpstan --no-progress -cphpstan.neon",
"beautify": "phpcbf --standard=phpcs.xml",
"phpcs": "phpcs --standard=phpcs.xml"
}, },
"suggest": { "suggest": {
"latte/latte": "Latte template engine", "latte/latte": "Latte template engine",

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -78,12 +79,12 @@ class Engine
*/ */
protected Dispatcher $dispatcher; protected Dispatcher $dispatcher;
/** /**
* If the framework has been initialized or not * If the framework has been initialized or not
* *
* @var boolean * @var boolean
*/ */
protected bool $initialized = false; protected bool $initialized = false;
/** /**
* Constructor. * Constructor.
@ -377,7 +378,7 @@ class Engine
ob_start(); ob_start();
// Route the request // Route the request
$failed_middleware_check = false; $failed_middleware_check = false;
while ($route = $router->route($request)) { while ($route = $router->route($request)) {
$params = array_values($route->params); $params = array_values($route->params);
@ -386,25 +387,24 @@ class Engine
$params[] = $route; $params[] = $route;
} }
// Run any before middlewares // Run any before middlewares
if(count($route->middleware) > 0) { if (count($route->middleware) > 0) {
foreach($route->middleware as $middleware) { foreach ($route->middleware as $middleware) {
$middleware_object = (is_callable($middleware) === true ? $middleware : (method_exists($middleware, 'before') === true ? [ $middleware, 'before' ] : false));
$middleware_object = (is_callable($middleware) === true ? $middleware : (method_exists($middleware, 'before') === true ? [ $middleware, 'before' ]: false));
if($middleware_object === false) { if ($middleware_object === false) {
continue; continue;
} }
// It's assumed if you don't declare before, that it will be assumed as the before method // It's assumed if you don't declare before, that it will be assumed as the before method
$middleware_result = $middleware_object($route->params); $middleware_result = $middleware_object($route->params);
if ($middleware_result === false) { if ($middleware_result === false) {
$failed_middleware_check = true; $failed_middleware_check = true;
break 2; break 2;
} }
} }
} }
// Call route handler // Call route handler
$continue = $this->dispatcher->execute( $continue = $this->dispatcher->execute(
@ -413,28 +413,26 @@ class Engine
); );
// Run any before middlewares // Run any before middlewares
if(count($route->middleware) > 0) { if (count($route->middleware) > 0) {
// process the middleware in reverse order now
// process the middleware in reverse order now foreach (array_reverse($route->middleware) as $middleware) {
foreach(array_reverse($route->middleware) as $middleware) { // must be an object. No functions allowed here
$middleware_object = is_object($middleware) === true && !($middleware instanceof Closure) && method_exists($middleware, 'after') === true ? [ $middleware, 'after' ] : false;
// must be an object. No functions allowed here // has to have the after method, otherwise just skip it
$middleware_object = is_object($middleware) === true && !($middleware instanceof Closure) && method_exists($middleware, 'after') === true ? [ $middleware, 'after' ] : false; if ($middleware_object === false) {
continue;
}
// has to have the after method, otherwise just skip it $middleware_result = $middleware_object($route->params);
if($middleware_object === false) {
continue;
}
$middleware_result = $middleware_object($route->params); if ($middleware_result === false) {
$failed_middleware_check = true;
if ($middleware_result === false) { break 2;
$failed_middleware_check = true; }
break 2; }
} }
}
}
$dispatched = true; $dispatched = true;
@ -447,9 +445,9 @@ class Engine
$dispatched = false; $dispatched = false;
} }
if($failed_middleware_check === true) { if ($failed_middleware_check === true) {
$this->halt(403, 'Forbidden'); $this->halt(403, 'Forbidden');
} else if($dispatched === false) { } elseif ($dispatched === false) {
$this->notFound(); $this->notFound();
} }
} }
@ -476,11 +474,11 @@ class Engine
->status(500) ->status(500)
->write($msg) ->write($msg)
->send(); ->send();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} catch (Throwable $t) { } catch (Throwable $t) {
exit($msg); exit($msg);
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
/** /**
@ -512,20 +510,20 @@ class Engine
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callable $callback Callback function * @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback * @param bool $pass_route Pass the matching route object to the callback
* @param string $alias the alias for the route * @param string $alias the alias for the route
* @return Route * @return Route
*/ */
public function _route(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route public function _route(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{ {
return $this->router()->map($pattern, $callback, $pass_route, $alias); return $this->router()->map($pattern, $callback, $pass_route, $alias);
} }
/** /**
* Routes a URL to a callback function. * Routes a URL to a callback function.
* *
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callable $callback Callback function that includes the Router class as first parameter * @param callable $callback Callback function that includes the Router class as first parameter
* @param array<callable> $group_middlewares The middleware to be applied to the route * @param array<callable> $group_middlewares The middleware to be applied to the route
*/ */
public function _group(string $pattern, callable $callback, array $group_middlewares = []): void public function _group(string $pattern, callable $callback, array $group_middlewares = []): void
{ {
@ -585,7 +583,7 @@ class Engine
* *
* @param int $code HTTP status code * @param int $code HTTP status code
* @param string $message Response message * @param string $message Response message
* *
*/ */
public function _halt(int $code = 200, string $message = ''): void public function _halt(int $code = 200, string $message = ''): void
{ {
@ -594,10 +592,10 @@ class Engine
->status($code) ->status($code)
->write($message) ->write($message)
->send(); ->send();
// apologies for the crappy hack here... // apologies for the crappy hack here...
if($message !== 'skip---exit') { if ($message !== 'skip---exit') {
exit(); // @codeCoverageIgnore exit(); // @codeCoverageIgnore
} }
} }
/** /**
@ -728,7 +726,7 @@ class Engine
{ {
$id = (('weak' === $type) ? 'W/' : '') . $id; $id = (('weak' === $type) ? 'W/' : '') . $id;
$this->response()->header('ETag', '"'.str_replace('"', '\"', $id).'"'); $this->response()->header('ETag', '"' . str_replace('"', '\"', $id) . '"');
if ( if (
isset($_SERVER['HTTP_IF_NONE_MATCH']) && isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
@ -755,14 +753,14 @@ class Engine
} }
} }
/** /**
* Gets a url from an alias that's supplied. * Gets a url from an alias that's supplied.
* *
* @param string $alias the route alias. * @param string $alias the route alias.
* @param array<string, mixed> $params The params for the route if applicable. * @param array<string, mixed> $params The params for the route if applicable.
*/ */
public function _getUrl(string $alias, array $params = []): string public function _getUrl(string $alias, array $params = []): string
{ {
return $this->router()->getUrlByAlias($alias, $params); return $this->router()->getUrlByAlias($alias, $params);
} }
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -61,34 +62,34 @@ class Flight
{ {
/** /**
* Framework engine. * Framework engine.
* *
* @var Engine $engine * @var Engine $engine
*/ */
private static Engine $engine; private static Engine $engine;
/** /**
* Whether or not the app has been initialized * Whether or not the app has been initialized
* *
* @var boolean * @var boolean
*/ */
private static bool $initialized = false; private static bool $initialized = false;
/** /**
* Don't allow object instantiation * Don't allow object instantiation
* *
* @codeCoverageIgnore * @codeCoverageIgnore
* @return void * @return void
*/ */
private function __construct() private function __construct()
{ {
} }
/** /**
* Forbid cloning the class * Forbid cloning the class
* *
* @codeCoverageIgnore * @codeCoverageIgnore
* @return void * @return void
*/ */
private function __clone() private function __clone()
{ {
} }
@ -145,14 +146,14 @@ class Flight
return self::$engine; return self::$engine;
} }
/** /**
* Set the engine instance * Set the engine instance
* *
* @param Engine $engine Vroom vroom! * @param Engine $engine Vroom vroom!
* @return void * @return void
*/ */
public static function setEngine(Engine $engine): void public static function setEngine(Engine $engine): void
{ {
self::$engine = $engine; self::$engine = $engine;
} }
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -53,7 +54,7 @@ class Dispatcher
} }
// Run requested method // Run requested method
$callback = $this->get($name); $callback = $this->get($name);
$output = $callback(...$params); $output = $callback(...$params);
// Run post-filters // Run post-filters
@ -178,7 +179,7 @@ class Dispatcher
*/ */
public static function callFunction($func, array &$params = []) public static function callFunction($func, array &$params = [])
{ {
return call_user_func_array($func, $params); return call_user_func_array($func, $params);
} }
/** /**
@ -195,9 +196,9 @@ class Dispatcher
$instance = \is_object($class); $instance = \is_object($class);
return ($instance) ? return ($instance) ?
$class->$method(...$params) : $class->$method(...$params) :
$class::$method(); $class::$method();
} }
/** /**

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -134,7 +135,7 @@ class Loader
return \call_user_func_array($class, $params); return \call_user_func_array($class, $params);
} }
return new $class(...$params); return new $class(...$params);
} }
/** /**
@ -179,8 +180,8 @@ class Loader
/** /**
* Autoloads classes. * Autoloads classes.
* *
* Classes are not allowed to have underscores in their names. * Classes are not allowed to have underscores in their names.
* *
* @param string $class Class name * @param string $class Class name
*/ */

@ -5,144 +5,149 @@ namespace flight\database;
use PDO; use PDO;
use PDOStatement; use PDOStatement;
class PdoWrapper extends PDO { class PdoWrapper extends PDO
{
/** /**
* How you create the connection for the database * How you create the connection for the database
* *
* @param string $dsn - Ex: 'mysql:host=localhost;port=3306;dbname=testdb;charset=utf8mb4' * @param string $dsn - Ex: 'mysql:host=localhost;port=3306;dbname=testdb;charset=utf8mb4'
* @param string $username - Ex: 'root' * @param string $username - Ex: 'root'
* @param string $password - Ex: 'password' * @param string $password - Ex: 'password'
* @param array<int,mixed> $options - PDO options you can pass in * @param array<int,mixed> $options - PDO options you can pass in
*/ */
public function __construct(string $dsn, ?string $username = null, ?string $password = null, array $options = []) { public function __construct(string $dsn, ?string $username = null, ?string $password = null, array $options = [])
parent::__construct($dsn, $username, $password, $options); {
} parent::__construct($dsn, $username, $password, $options);
}
/**
* Use this for INSERTS, UPDATES, or if you plan on using a SELECT in a while loop /**
* * Use this for INSERTS, UPDATES, or if you plan on using a SELECT in a while loop
* Ex: $statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]); *
* while($row = $statement->fetch()) { * Ex: $statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
* // ... * while($row = $statement->fetch()) {
* } * // ...
* * }
* $db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]); *
* $db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]); * $db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
* * $db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" *
* @param array<int|string,mixed> $params - Ex: [ $something ] * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
* @return PDOStatement * @param array<int|string,mixed> $params - Ex: [ $something ]
*/ * @return PDOStatement
public function runQuery(string $sql, array $params = []): PDOStatement { */
$processed_sql_data = $this->processInStatementSql($sql, $params); public function runQuery(string $sql, array $params = []): PDOStatement
$sql = $processed_sql_data['sql']; {
$params = $processed_sql_data['params']; $processed_sql_data = $this->processInStatementSql($sql, $params);
$statement = $this->prepare($sql); $sql = $processed_sql_data['sql'];
$statement->execute($params); $params = $processed_sql_data['params'];
return $statement; $statement = $this->prepare($sql);
} $statement->execute($params);
return $statement;
/** }
* Pulls one field from the query
* /**
* Ex: $id = $db->fetchField("SELECT id FROM table WHERE something = ?", [ $something ]); * Pulls one field from the query
* *
* @param string $sql - Ex: "SELECT id FROM table WHERE something = ?" * Ex: $id = $db->fetchField("SELECT id FROM table WHERE something = ?", [ $something ]);
* @param array<int|string,mixed> $params - Ex: [ $something ] *
* @return mixed * @param string $sql - Ex: "SELECT id FROM table WHERE something = ?"
*/ * @param array<int|string,mixed> $params - Ex: [ $something ]
public function fetchField(string $sql, array $params = []) { * @return mixed
$data = $this->fetchRow($sql, $params); */
return reset($data); public function fetchField(string $sql, array $params = [])
} {
$data = $this->fetchRow($sql, $params);
/** return reset($data);
* Pulls one row from the query }
*
* Ex: $row = $db->fetchRow("SELECT * FROM table WHERE something = ?", [ $something ]); /**
* * Pulls one row from the query
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" *
* @param array<int|string,mixed> $params - Ex: [ $something ] * Ex: $row = $db->fetchRow("SELECT * FROM table WHERE something = ?", [ $something ]);
* @return array<string,mixed> *
*/ * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
public function fetchRow(string $sql, array $params = []): array { * @param array<int|string,mixed> $params - Ex: [ $something ]
$sql .= stripos($sql, 'LIMIT') === false ? ' LIMIT 1' : ''; * @return array<string,mixed>
$result = $this->fetchAll($sql, $params); */
return count($result) > 0 ? $result[0] : []; public function fetchRow(string $sql, array $params = []): array
} {
$sql .= stripos($sql, 'LIMIT') === false ? ' LIMIT 1' : '';
/** $result = $this->fetchAll($sql, $params);
* Pulls all rows from the query return count($result) > 0 ? $result[0] : [];
* }
* Ex: $rows = $db->fetchAll("SELECT * FROM table WHERE something = ?", [ $something ]);
* foreach($rows as $row) { /**
* // ... * Pulls all rows from the query
* } *
* * Ex: $rows = $db->fetchAll("SELECT * FROM table WHERE something = ?", [ $something ]);
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * foreach($rows as $row) {
* @param array<int|string,mixed> $params - Ex: [ $something ] * // ...
* @return array<int,array<string,mixed>> * }
*/ *
public function fetchAll(string $sql, array $params = []): array { * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
$processed_sql_data = $this->processInStatementSql($sql, $params); * @param array<int|string,mixed> $params - Ex: [ $something ]
$sql = $processed_sql_data['sql']; * @return array<int,array<string,mixed>>
$params = $processed_sql_data['params']; */
$statement = $this->prepare($sql); public function fetchAll(string $sql, array $params = []): array
$statement->execute($params); {
$result = $statement->fetchAll(); $processed_sql_data = $this->processInStatementSql($sql, $params);
return is_array($result) ? $result : []; $sql = $processed_sql_data['sql'];
} $params = $processed_sql_data['params'];
$statement = $this->prepare($sql);
/** $statement->execute($params);
* Don't worry about this guy. Converts stuff for IN statements $result = $statement->fetchAll();
* return is_array($result) ? $result : [];
* Ex: $row = $db->fetchAll("SELECT * FROM table WHERE id = ? AND something IN(?), [ $id, [1,2,3] ]); }
* Converts this to "SELECT * FROM table WHERE id = ? AND something IN(?,?,?)"
* /**
* @param string $sql the sql statement * Don't worry about this guy. Converts stuff for IN statements
* @param array<int|string,mixed> $params the params for the sql statement *
* @return array<string,string|array<int|string,mixed>> * Ex: $row = $db->fetchAll("SELECT * FROM table WHERE id = ? AND something IN(?), [ $id, [1,2,3] ]);
*/ * Converts this to "SELECT * FROM table WHERE id = ? AND something IN(?,?,?)"
protected function processInStatementSql(string $sql, array $params = []): array { *
* @param string $sql the sql statement
// Handle "IN(?)". This is to be used with a comma delimited string, but can also be used with an array. * @param array<int|string,mixed> $params the params for the sql statement
// Remove the spaces in variations of "IN ( ? )" where the space after IN is optional, and any number of spaces before and after the question mark is optional. * @return array<string,string|array<int|string,mixed>>
// Then loop through each "IN(?)" in the query and replace the single question mark with the correct number of question marks. */
$sql = preg_replace('/IN\s*\(\s*\?\s*\)/i', 'IN(?)', $sql); protected function processInStatementSql(string $sql, array $params = []): array
$current_index = 0; {
while(($current_index = strpos($sql, 'IN(?)', $current_index)) !== false) {
$preceeding_count = substr_count($sql, '?', 0, $current_index - 1); // Handle "IN(?)". This is to be used with a comma delimited string, but can also be used with an array.
// Remove the spaces in variations of "IN ( ? )" where the space after IN is optional, and any number of spaces before and after the question mark is optional.
$param = $params[$preceeding_count]; // Then loop through each "IN(?)" in the query and replace the single question mark with the correct number of question marks.
$question_marks = '?'; $sql = preg_replace('/IN\s*\(\s*\?\s*\)/i', 'IN(?)', $sql);
$current_index = 0;
// If param is a string, explode it and replace the question mark with the correct number of question marks while (($current_index = strpos($sql, 'IN(?)', $current_index)) !== false) {
if(is_string($param) || is_array($param)) { $preceeding_count = substr_count($sql, '?', 0, $current_index - 1);
$params_to_use = $param; $param = $params[$preceeding_count];
if(is_string($param)) { $question_marks = '?';
$params_to_use = explode(',', $param);
} // If param is a string, explode it and replace the question mark with the correct number of question marks
if (is_string($param) || is_array($param)) {
foreach($params_to_use as $key => $value) { $params_to_use = $param;
if(is_string($value)) { if (is_string($param)) {
$params_to_use[$key] = trim($value); $params_to_use = explode(',', $param);
} }
}
foreach ($params_to_use as $key => $value) {
// Replace the single question mark with the appropriate number of question marks. if (is_string($value)) {
$question_marks = join(',', array_fill(0, count($params_to_use), '?')); $params_to_use[$key] = trim($value);
$sql = substr_replace($sql, $question_marks, $current_index + 3, 1); }
}
// Insert the new params into the params array.
array_splice($params, $preceeding_count, 1, $params_to_use); // Replace the single question mark with the appropriate number of question marks.
} $question_marks = join(',', array_fill(0, count($params_to_use), '?'));
$sql = substr_replace($sql, $question_marks, $current_index + 3, 1);
// Increment by the length of the question marks and accounting for the length of "IN()"
$current_index += strlen($question_marks) + 4; // Insert the new params into the params array.
} array_splice($params, $preceeding_count, 1, $params_to_use);
}
return [ 'sql' => $sql, 'params' => $params ];
} // Increment by the length of the question marks and accounting for the length of "IN()"
$current_index += strlen($question_marks) + 4;
}
return [ 'sql' => $sql, 'params' => $params ];
}
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -187,9 +188,9 @@ class Request
} }
// Get the requested URL without the base directory // Get the requested URL without the base directory
// This rewrites the url in case the public url and base directories match // This rewrites the url in case the public url and base directories match
// (such as installing on a subdirectory in a web server) // (such as installing on a subdirectory in a web server)
// @see testInitUrlSameAsBaseDirectory // @see testInitUrlSameAsBaseDirectory
if ('/' !== $this->base && '' !== $this->base && 0 === strpos($this->url, $this->base)) { if ('/' !== $this->base && '' !== $this->base && 0 === strpos($this->url, $this->base)) {
$this->url = substr($this->url, \strlen($this->base)); $this->url = substr($this->url, \strlen($this->base));
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -237,7 +238,7 @@ class Response
{ {
// Send status code header // Send status code header
if (false !== strpos(\PHP_SAPI, 'cgi')) { if (false !== strpos(\PHP_SAPI, 'cgi')) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
$this->setRealHeader( $this->setRealHeader(
sprintf( sprintf(
'Status: %d %s', 'Status: %d %s',
@ -246,7 +247,7 @@ class Response
), ),
true true
); );
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} else { } else {
$this->setRealHeader( $this->setRealHeader(
sprintf( sprintf(
@ -283,19 +284,20 @@ class Response
return $this; return $this;
} }
/** /**
* Sets a real header. Mostly used for test mocking. * Sets a real header. Mostly used for test mocking.
* *
* @param string $header_string The header string you would pass to header() * @param string $header_string The header string you would pass to header()
* @param bool $replace The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in false as the second argument you can force multiple headers of the same type. * @param bool $replace The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in false as the second argument you can force multiple headers of the same type.
* @param int $response_code The response code to send * @param int $response_code The response code to send
* @return $this * @return $this
* @codeCoverageIgnore * @codeCoverageIgnore
*/ */
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): self { public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): self
header($header_string, $replace, $response_code); {
return $this; header($header_string, $replace, $response_code);
} return $this;
}
/** /**
* Gets the content length. * Gets the content length.

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -52,15 +53,15 @@ class Route
*/ */
public bool $pass = false; public bool $pass = false;
/** /**
* The alias is a way to identify the route using a simple name ex: 'login' instead of /admin/login * The alias is a way to identify the route using a simple name ex: 'login' instead of /admin/login
*/ */
public string $alias = ''; public string $alias = '';
/** /**
* @var array<int,callable|object> The middleware to be applied to the route * @var array<int,callable|object> The middleware to be applied to the route
*/ */
public array $middleware = []; public array $middleware = [];
/** /**
* Constructor. * Constructor.
@ -76,7 +77,7 @@ class Route
$this->callback = $callback; $this->callback = $callback;
$this->methods = $methods; $this->methods = $methods;
$this->pass = $pass; $this->pass = $pass;
$this->alias = $alias; $this->alias = $alias;
} }
/** /**
@ -165,55 +166,58 @@ class Route
return \count(array_intersect([$method, '*'], $this->methods)) > 0; return \count(array_intersect([$method, '*'], $this->methods)) > 0;
} }
/** /**
* Checks if an alias matches the route alias. * Checks if an alias matches the route alias.
*/ */
public function matchAlias(string $alias): bool public function matchAlias(string $alias): bool
{ {
return $this->alias === $alias; return $this->alias === $alias;
} }
/** /**
* Hydrates the route url with the given parameters * Hydrates the route url with the given parameters
* *
* @param array<string,mixed> $params the parameters to pass to the route * @param array<string,mixed> $params the parameters to pass to the route
*/ */
public function hydrateUrl(array $params = []): string { public function hydrateUrl(array $params = []): string
$url = preg_replace_callback("/(?:@([a-zA-Z0-9]+)(?:\:([^\/]+))?\)*)/i", function($match) use ($params) { {
if(isset($match[1]) && isset($params[$match[1]])) { $url = preg_replace_callback("/(?:@([a-zA-Z0-9]+)(?:\:([^\/]+))?\)*)/i", function ($match) use ($params) {
return $params[$match[1]]; if (isset($match[1]) && isset($params[$match[1]])) {
} return $params[$match[1]];
}, $this->pattern); }
}, $this->pattern);
// catches potential optional parameter
$url = str_replace('(/', '/', $url); // catches potential optional parameter
// trim any trailing slashes $url = str_replace('(/', '/', $url);
$url = rtrim($url, '/'); // trim any trailing slashes
return $url; $url = rtrim($url, '/');
} return $url;
}
/**
* Sets the route alias /**
* * Sets the route alias
* @return $this *
*/ * @return $this
public function setAlias(string $alias): self { */
$this->alias = $alias; public function setAlias(string $alias): self
return $this; {
} $this->alias = $alias;
return $this;
/** }
* Sets the route middleware
* /**
* @param array<int, callable>|callable $middleware * Sets the route middleware
* @return $this *
*/ * @param array<int, callable>|callable $middleware
public function addMiddleware($middleware): self { * @return $this
if(is_array($middleware) === true) { */
$this->middleware = array_merge($this->middleware, $middleware); public function addMiddleware($middleware): self
} else { {
$this->middleware[] = $middleware; if (is_array($middleware) === true) {
} $this->middleware = array_merge($this->middleware, $middleware);
return $this; } else {
} $this->middleware[] = $middleware;
}
return $this;
}
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -34,17 +35,17 @@ class Router
*/ */
protected int $index = 0; protected int $index = 0;
/** /**
* When groups are used, this is mapped against all the routes * When groups are used, this is mapped against all the routes
*/ */
protected string $group_prefix = ''; protected string $group_prefix = '';
/** /**
* Group Middleware * Group Middleware
* *
* @var array<int,mixed> * @var array<int,mixed>
*/ */
protected array $group_middlewares = []; protected array $group_middlewares = [];
/** /**
* Gets mapped routes. * Gets mapped routes.
@ -70,7 +71,7 @@ class Router
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callable $callback Callback function * @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback * @param bool $pass_route Pass the matching route object to the callback
* @param string $route_alias Alias for the route * @param string $route_alias Alias for the route
*/ */
public function map(string $pattern, callable $callback, bool $pass_route = false, string $route_alias = ''): Route public function map(string $pattern, callable $callback, bool $pass_route = false, string $route_alias = ''): Route
{ {
@ -83,94 +84,100 @@ class Router
$methods = explode('|', $method); $methods = explode('|', $method);
} }
$route = new Route($this->group_prefix.$url, $callback, $methods, $pass_route, $route_alias); $route = new Route($this->group_prefix . $url, $callback, $methods, $pass_route, $route_alias);
// to handle group middleware // to handle group middleware
foreach($this->group_middlewares as $gm) { foreach ($this->group_middlewares as $gm) {
$route->addMiddleware($gm); $route->addMiddleware($gm);
} }
$this->routes[] = $route; $this->routes[] = $route;
return $route; return $route;
}
/**
* Creates a GET based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function get(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{
return $this->map('GET ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a POST based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function post(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{
return $this->map('POST ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a PUT based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function put(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{
return $this->map('PUT ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a PATCH based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
{
return $this->map('PATCH ' . $pattern, $callback, $pass_route, $alias);
} }
/** /**
* Creates a GET based route * Creates a DELETE based route
* *
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callable $callback Callback function * @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback * @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route * @param string $alias Alias for the route
*/ */
public function get(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route { public function delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route
return $this->map('GET ' . $pattern, $callback, $pass_route, $alias); {
} return $this->map('DELETE ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a POST based route /**
* * Group together a set of routes
* @param string $pattern URL pattern to match *
* @param callable $callback Callback function * @param string $group_prefix group URL prefix (such as /api/v1)
* @param bool $pass_route Pass the matching route object to the callback * @param callable $callback The necessary calling that holds the Router class
* @param string $alias Alias for the route * @param array<int,callable|object> $group_middlewares The middlewares to be applied to the group Ex: [ $middleware1, $middleware2 ]
*/ */
public function post(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route { public function group(string $group_prefix, callable $callback, array $group_middlewares = []): void
return $this->map('POST ' . $pattern, $callback, $pass_route, $alias); {
} $old_group_prefix = $this->group_prefix;
$old_group_middlewares = $this->group_middlewares;
/** $this->group_prefix .= $group_prefix;
* Creates a PUT based route $this->group_middlewares = array_merge($this->group_middlewares, $group_middlewares);
* $callback($this);
* @param string $pattern URL pattern to match $this->group_prefix = $old_group_prefix;
* @param callable $callback Callback function $this->group_middlewares = $old_group_middlewares;
* @param bool $pass_route Pass the matching route object to the callback }
* @param string $alias Alias for the route
*/
public function put(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('PUT ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a PATCH based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('PATCH ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Creates a DELETE based route
*
* @param string $pattern URL pattern to match
* @param callable $callback Callback function
* @param bool $pass_route Pass the matching route object to the callback
* @param string $alias Alias for the route
*/
public function delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = ''): Route {
return $this->map('DELETE ' . $pattern, $callback, $pass_route, $alias);
}
/**
* Group together a set of routes
*
* @param string $group_prefix group URL prefix (such as /api/v1)
* @param callable $callback The necessary calling that holds the Router class
* @param array<int,callable|object> $group_middlewares The middlewares to be applied to the group Ex: [ $middleware1, $middleware2 ]
*/
public function group(string $group_prefix, callable $callback, array $group_middlewares = []): void {
$old_group_prefix = $this->group_prefix;
$old_group_middlewares = $this->group_middlewares;
$this->group_prefix .= $group_prefix;
$this->group_middlewares = array_merge($this->group_middlewares, $group_middlewares);
$callback($this);
$this->group_prefix = $old_group_prefix;
$this->group_middlewares = $old_group_middlewares;
}
/** /**
* Routes the current request. * Routes the current request.
@ -190,58 +197,58 @@ class Router
return false; return false;
} }
/** /**
* Gets the URL for a given route alias * Gets the URL for a given route alias
* *
* @param string $alias the alias to match * @param string $alias the alias to match
* @param array<string,mixed> $params the parameters to pass to the route * @param array<string,mixed> $params the parameters to pass to the route
*/ */
public function getUrlByAlias(string $alias, array $params = []): string { public function getUrlByAlias(string $alias, array $params = []): string
$potential_aliases = []; {
foreach($this->routes as $route) { $potential_aliases = [];
$potential_aliases[] = $route->alias; foreach ($this->routes as $route) {
if ($route->matchAlias($alias)) { $potential_aliases[] = $route->alias;
return $route->hydrateUrl($params); if ($route->matchAlias($alias)) {
} return $route->hydrateUrl($params);
}
} }
// use a levenshtein to find the closest match and make a recommendation // use a levenshtein to find the closest match and make a recommendation
$closest_match = ''; $closest_match = '';
$closest_match_distance = 0; $closest_match_distance = 0;
foreach($potential_aliases as $potential_alias) { foreach ($potential_aliases as $potential_alias) {
$levenshtein_distance = levenshtein($alias, $potential_alias); $levenshtein_distance = levenshtein($alias, $potential_alias);
if($levenshtein_distance > $closest_match_distance) { if ($levenshtein_distance > $closest_match_distance) {
$closest_match = $potential_alias; $closest_match = $potential_alias;
$closest_match_distance = $levenshtein_distance; $closest_match_distance = $levenshtein_distance;
} }
} }
$exception_message = 'No route found with alias: \'' . $alias . '\'.'; $exception_message = 'No route found with alias: \'' . $alias . '\'.';
if($closest_match !== '') { if ($closest_match !== '') {
$exception_message .= ' Did you mean \'' . $closest_match . '\'?'; $exception_message .= ' Did you mean \'' . $closest_match . '\'?';
} }
throw new Exception($exception_message); throw new Exception($exception_message);
} }
/** /**
* Rewinds the current route index. * Rewinds the current route index.
*/ */
public function rewind(): void public function rewind(): void
{ {
$this->index = 0; $this->index = 0;
} }
/** /**
* Checks if more routes can be iterated. * Checks if more routes can be iterated.
* *
* @return bool More routes * @return bool More routes
*/ */
public function valid(): bool public function valid(): bool
{ {
return isset($this->routes[$this->index]); return isset($this->routes[$this->index]);
} }
/** /**
* Gets the current route. * Gets the current route.

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -165,7 +166,7 @@ class View
$file .= $ext; $file .= $ext;
} }
$is_windows = \strtoupper(\substr(PHP_OS, 0, 3)) === 'WIN'; $is_windows = \strtoupper(\substr(PHP_OS, 0, 3)) === 'WIN';
if (('/' == \substr($file, 0, 1)) || ($is_windows === true && ':' == \substr($file, 1, 1))) { if (('/' == \substr($file, 0, 1)) || ($is_windows === true && ':' == \substr($file, 1, 1))) {
return $file; return $file;
@ -183,7 +184,7 @@ class View
*/ */
public function e(string $str): string public function e(string $str): string
{ {
$value = \htmlentities($str); $value = \htmlentities($str);
echo $value; echo $value;
return $value; return $value;
} }

@ -1,6 +1,7 @@
<?php <?php
declare(strict_types=1); declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,3 +1,5 @@
<?php <?php
class ReturnTypeWillChange {} class ReturnTypeWillChange
{
}

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="pcsg-generated-ruleset">
<description>Created with the PHP Coding Standard Generator. http://edorian.github.io/php-coding-standard-generator/</description>
<arg name="colors"/>
<arg name="tab-width" value="4"/>
<rule ref="PSR12"/>
<!-- <rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/> -->
<file>flight/</file>
<file>tests/</file>
</ruleset>

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -7,92 +8,97 @@
*/ */
class CollectionTest extends PHPUnit\Framework\TestCase class CollectionTest extends PHPUnit\Framework\TestCase
{ {
/** /**
* @var \flight\util\Collection * @var \flight\util\Collection
*/ */
private $collection; private $collection;
protected function setUp(): void protected function setUp(): void
{ {
$this->collection = new \flight\util\Collection(['a' => 1, 'b' => 2]); $this->collection = new \flight\util\Collection(['a' => 1, 'b' => 2]);
} }
// Get an item // Get an item
public function testGet() public function testGet()
{ {
$this->assertEquals(1, $this->collection->a); $this->assertEquals(1, $this->collection->a);
} }
// Set an item // Set an item
public function testSet() public function testSet()
{ {
$this->collection->c = 3; $this->collection->c = 3;
$this->assertEquals(3, $this->collection->c); $this->assertEquals(3, $this->collection->c);
} }
// Check if an item exists // Check if an item exists
public function testExists() public function testExists()
{ {
$this->assertTrue(isset($this->collection->a)); $this->assertTrue(isset($this->collection->a));
} }
// Unset an item // Unset an item
public function testUnset() public function testUnset()
{ {
unset($this->collection->a); unset($this->collection->a);
$this->assertFalse(isset($this->collection->a)); $this->assertFalse(isset($this->collection->a));
} }
// Count items // Count items
public function testCount() public function testCount()
{ {
$this->assertEquals(2, count($this->collection)); $this->assertEquals(2, count($this->collection));
} }
// Iterate through items // Iterate through items
public function testIterate() public function testIterate()
{ {
$items = []; $items = [];
foreach ($this->collection as $key => $value) { foreach ($this->collection as $key => $value) {
$items[$key] = $value; $items[$key] = $value;
} }
$this->assertEquals(['a' => 1, 'b' => 2], $items); $this->assertEquals(['a' => 1, 'b' => 2], $items);
} }
public function testJsonSerialize() public function testJsonSerialize()
{ {
$this->assertEquals(['a' => 1, 'b' => 2], $this->collection->jsonSerialize()); $this->assertEquals(['a' => 1, 'b' => 2], $this->collection->jsonSerialize());
} }
public function testOffsetSetWithNullOffset() { public function testOffsetSetWithNullOffset()
$this->collection->offsetSet(null, 3); {
$this->assertEquals(3, $this->collection->offsetGet(0)); $this->collection->offsetSet(null, 3);
} $this->assertEquals(3, $this->collection->offsetGet(0));
}
public function testOffsetExists() { public function testOffsetExists()
$this->collection->a = 1; {
$this->assertTrue($this->collection->offsetExists('a')); $this->collection->a = 1;
} $this->assertTrue($this->collection->offsetExists('a'));
}
public function testOffsetUnset() { public function testOffsetUnset()
$this->collection->a = 1; {
$this->assertTrue($this->collection->offsetExists('a')); $this->collection->a = 1;
$this->collection->offsetUnset('a'); $this->assertTrue($this->collection->offsetExists('a'));
$this->assertFalse($this->collection->offsetExists('a')); $this->collection->offsetUnset('a');
} $this->assertFalse($this->collection->offsetExists('a'));
}
public function testKeys() { public function testKeys()
$this->collection->a = 1; {
$this->collection->b = 2; $this->collection->a = 1;
$this->assertEquals(['a', 'b'], $this->collection->keys()); $this->collection->b = 2;
} $this->assertEquals(['a', 'b'], $this->collection->keys());
}
public function testClear() { public function testClear()
$this->collection->a = 1; {
$this->collection->b = 2; $this->collection->a = 1;
$this->assertEquals(['a', 'b'], $this->collection->keys()); $this->collection->b = 2;
$this->collection->clear(); $this->assertEquals(['a', 'b'], $this->collection->keys());
$this->assertEquals(0, $this->collection->count()); $this->collection->clear();
} $this->assertEquals(0, $this->collection->count());
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -50,54 +51,56 @@ class DispatcherTest extends PHPUnit\Framework\TestCase
return 'hello'; return 'hello';
}); });
$result = $this->dispatcher->has('map-event'); $result = $this->dispatcher->has('map-event');
$this->assertTrue($result); $this->assertTrue($result);
} }
public function testClearAllRegisteredEvents() { public function testClearAllRegisteredEvents()
$this->dispatcher->set('map-event', function () { {
return 'hello'; $this->dispatcher->set('map-event', function () {
}); return 'hello';
});
$this->dispatcher->set('map-event-2', function () { $this->dispatcher->set('map-event-2', function () {
return 'there'; return 'there';
}); });
$this->dispatcher->clear(); $this->dispatcher->clear();
$result = $this->dispatcher->has('map-event'); $result = $this->dispatcher->has('map-event');
$this->assertFalse($result); $this->assertFalse($result);
$result = $this->dispatcher->has('map-event-2'); $result = $this->dispatcher->has('map-event-2');
$this->assertFalse($result); $this->assertFalse($result);
} }
public function testClearDeclaredRegisteredEvent() { public function testClearDeclaredRegisteredEvent()
$this->dispatcher->set('map-event', function () { {
return 'hello'; $this->dispatcher->set('map-event', function () {
}); return 'hello';
});
$this->dispatcher->set('map-event-2', function () { $this->dispatcher->set('map-event-2', function () {
return 'there'; return 'there';
}); });
$this->dispatcher->clear('map-event'); $this->dispatcher->clear('map-event');
$result = $this->dispatcher->has('map-event'); $result = $this->dispatcher->has('map-event');
$this->assertFalse($result); $this->assertFalse($result);
$result = $this->dispatcher->has('map-event-2'); $result = $this->dispatcher->has('map-event-2');
$this->assertTrue($result); $this->assertTrue($result);
} }
// Map a static function // Map a static function
public function testStaticFunctionMapping() public function testStaticFunctionMapping()
{ {
$this->dispatcher->set('map2', 'Hello::sayHi'); $this->dispatcher->set('map2', 'Hello::sayHi');
$result = $this->dispatcher->run('map2'); $result = $this->dispatcher->run('map2');
self::assertEquals('hello', $result); self::assertEquals('hello', $result);
} }
// Map a class method // Map a class method
public function testClassMethodMapping() public function testClassMethodMapping()
@ -151,30 +154,33 @@ class DispatcherTest extends PHPUnit\Framework\TestCase
$this->dispatcher->execute(['NonExistentClass', 'nonExistentMethod']); $this->dispatcher->execute(['NonExistentClass', 'nonExistentMethod']);
} }
public function testCallFunction4Params() { public function testCallFunction4Params()
$closure = function($param1, $params2, $params3, $param4) { {
return 'hello'.$param1.$params2.$params3.$param4; $closure = function ($param1, $params2, $params3, $param4) {
}; return 'hello' . $param1 . $params2 . $params3 . $param4;
$params = ['param1', 'param2', 'param3', 'param4']; };
$result = $this->dispatcher->callFunction($closure, $params); $params = ['param1', 'param2', 'param3', 'param4'];
$this->assertEquals('helloparam1param2param3param4', $result); $result = $this->dispatcher->callFunction($closure, $params);
} $this->assertEquals('helloparam1param2param3param4', $result);
}
public function testCallFunction5Params() {
$closure = function($param1, $params2, $params3, $param4, $param5) { public function testCallFunction5Params()
return 'hello'.$param1.$params2.$params3.$param4.$param5; {
}; $closure = function ($param1, $params2, $params3, $param4, $param5) {
$params = ['param1', 'param2', 'param3', 'param4', 'param5']; return 'hello' . $param1 . $params2 . $params3 . $param4 . $param5;
$result = $this->dispatcher->callFunction($closure, $params); };
$this->assertEquals('helloparam1param2param3param4param5', $result); $params = ['param1', 'param2', 'param3', 'param4', 'param5'];
} $result = $this->dispatcher->callFunction($closure, $params);
$this->assertEquals('helloparam1param2param3param4param5', $result);
public function testCallFunction6Params() { }
$closure = function($param1, $params2, $params3, $param4, $param5, $param6) {
return 'hello'.$param1.$params2.$params3.$param4.$param5.$param6; public function testCallFunction6Params()
}; {
$params = ['param1', 'param2', 'param3', 'param4', 'param5', 'param6']; $closure = function ($param1, $params2, $params3, $param4, $param5, $param6) {
$result = $this->dispatcher->callFunction($closure, $params); return 'hello' . $param1 . $params2 . $params3 . $param4 . $param5 . $param6;
$this->assertEquals('helloparam1param2param3param4param5param6', $result); };
} $params = ['param1', 'param2', 'param3', 'param4', 'param5', 'param6'];
$result = $this->dispatcher->callFunction($closure, $params);
$this->assertEquals('helloparam1param2param3param4param5param6', $result);
}
} }

@ -13,423 +13,524 @@ use flight\net\Response;
class EngineTest extends PHPUnit\Framework\TestCase class EngineTest extends PHPUnit\Framework\TestCase
{ {
public function setUp(): void { public function setUp(): void
$_SERVER = []; {
} $_SERVER = [];
}
public function tearDown(): void {
$_SERVER = []; public function tearDown(): void
} {
public function testInitBeforeStart() { $_SERVER = [];
$engine = new class extends Engine { }
public function getInitializedVar() { public function testInitBeforeStart()
return $this->initialized; {
} $engine = new class extends Engine {
}; public function getInitializedVar()
$this->assertTrue($engine->getInitializedVar()); {
$engine->start(); return $this->initialized;
}
// this is necessary cause it doesn't actually send the response correctly };
ob_end_clean(); $this->assertTrue($engine->getInitializedVar());
$engine->start();
$this->assertFalse($engine->router()->case_sensitive);
$this->assertTrue($engine->response()->content_length); // this is necessary cause it doesn't actually send the response correctly
} ob_end_clean();
public function testHandleErrorNoErrorNumber() { $this->assertFalse($engine->router()->case_sensitive);
$engine = new Engine(); $this->assertTrue($engine->response()->content_length);
$result = $engine->handleError(0, '', '', 0); }
$this->assertFalse($result);
} public function testHandleErrorNoErrorNumber()
{
public function testHandleErrorWithException() { $engine = new Engine();
$engine = new Engine(); $result = $engine->handleError(0, '', '', 0);
$this->expectException(Exception::class); $this->assertFalse($result);
$this->expectExceptionCode(5); }
$this->expectExceptionMessage('thrown error message');
$engine->handleError(5, 'thrown error message', '', 0); public function testHandleErrorWithException()
} {
$engine = new Engine();
public function testHandleException() { $this->expectException(Exception::class);
$engine = new Engine(); $this->expectExceptionCode(5);
$regex_message = preg_quote('<h1>500 Internal Server Error</h1><h3>thrown exception message (20)</h3>'); $this->expectExceptionMessage('thrown error message');
$this->expectOutputRegex('~'.$regex_message.'~'); $engine->handleError(5, 'thrown error message', '', 0);
$engine->handleException(new Exception('thrown exception message', 20)); }
}
public function testHandleException()
public function testMapExistingMethod() { {
$engine = new Engine(); $engine = new Engine();
$this->expectException(Exception::class); $regex_message = preg_quote('<h1>500 Internal Server Error</h1><h3>thrown exception message (20)</h3>');
$this->expectExceptionMessage('Cannot override an existing framework method.'); $this->expectOutputRegex('~' . $regex_message . '~');
$engine->map('_start', function() {}); $engine->handleException(new Exception('thrown exception message', 20));
} }
public function testRegisterExistingMethod() { public function testMapExistingMethod()
$engine = new Engine(); {
$this->expectException(Exception::class); $engine = new Engine();
$this->expectExceptionMessage('Cannot override an existing framework method.'); $this->expectException(Exception::class);
$engine->register('_error', 'stdClass'); $this->expectExceptionMessage('Cannot override an existing framework method.');
} $engine->map('_start', function () {
});
public function testSetArrayOfValues() { }
$engine = new Engine();
$engine->set([ 'key1' => 'value1', 'key2' => 'value2']); public function testRegisterExistingMethod()
$this->assertEquals('value1', $engine->get('key1')); {
$this->assertEquals('value2', $engine->get('key2')); $engine = new Engine();
} $this->expectException(Exception::class);
$this->expectExceptionMessage('Cannot override an existing framework method.');
public function testStartWithRoute() { $engine->register('_error', 'stdClass');
$_SERVER['REQUEST_METHOD'] = 'GET'; }
$_SERVER['REQUEST_URI'] = '/someRoute';
public function testSetArrayOfValues()
$engine = new class extends Engine { {
public function getInitializedVar() { $engine = new Engine();
return $this->initialized; $engine->set([ 'key1' => 'value1', 'key2' => 'value2']);
} $this->assertEquals('value1', $engine->get('key1'));
}; $this->assertEquals('value2', $engine->get('key2'));
$engine->route('/someRoute', function() { echo 'i ran'; }, true); }
$this->expectOutputString('i ran');
$engine->start(); public function testStartWithRoute()
} {
$_SERVER['REQUEST_METHOD'] = 'GET';
// n0nag0n - I don't know why this does what it does, but it's existing framework functionality 1/1/24 $_SERVER['REQUEST_URI'] = '/someRoute';
public function testStartWithRouteButReturnedValueThrows404() {
$_SERVER['REQUEST_METHOD'] = 'GET'; $engine = new class extends Engine {
$_SERVER['REQUEST_URI'] = '/someRoute'; public function getInitializedVar()
{
$engine = new class extends Engine { return $this->initialized;
public function getInitializedVar() { }
return $this->initialized; };
} $engine->route('/someRoute', function () {
}; echo 'i ran';
$engine->route('/someRoute', function() { echo 'i ran'; return true; }, true); }, true);
$this->expectOutputString('<h1>404 Not Found</h1><h3>The page you have requested could not be found.</h3> '); $this->expectOutputString('i ran');
$engine->start(); $engine->start();
} }
public function testStopWithCode() { // n0nag0n - I don't know why this does what it does, but it's existing framework functionality 1/1/24
$engine = new class extends Engine { public function testStartWithRouteButReturnedValueThrows404()
public function getLoader() { {
return $this->loader; $_SERVER['REQUEST_METHOD'] = 'GET';
} $_SERVER['REQUEST_URI'] = '/someRoute';
};
// doing this so we can overwrite some parts of the response $engine = new class extends Engine {
$engine->getLoader()->register('response', function() { public function getInitializedVar()
return new class extends Response { {
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): self return $this->initialized;
{ }
return $this; };
} $engine->route('/someRoute', function () {
}; echo 'i ran';
}); return true;
// need to add another one of these because _stop() stops and gets clean, but $response->send() does too..... }, true);
ob_start(); $this->expectOutputString('<h1>404 Not Found</h1><h3>The page you have requested could not be found.</h3> ');
$engine->response()->write('I am a teapot'); $engine->start();
$this->expectOutputString('I am a teapot'); }
$engine->stop(500);
$this->assertEquals(500, $engine->response()->status()); public function testStopWithCode()
} {
$engine = new class extends Engine {
public function testPostRoute() { public function getLoader()
$engine = new Engine(); {
$engine->post('/someRoute', function() { echo 'i ran'; }, true); return $this->loader;
$routes = $engine->router()->getRoutes(); }
$this->assertEquals('POST', $routes[0]->methods[0]); };
$this->assertEquals('/someRoute', $routes[0]->pattern); // doing this so we can overwrite some parts of the response
} $engine->getLoader()->register('response', function () {
return new class extends Response {
public function testPutRoute() { public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): self
$engine = new Engine(); {
$engine->put('/someRoute', function() { echo 'i ran'; }, true); return $this;
$routes = $engine->router()->getRoutes(); }
$this->assertEquals('PUT', $routes[0]->methods[0]); };
$this->assertEquals('/someRoute', $routes[0]->pattern); });
} // need to add another one of these because _stop() stops and gets clean, but $response->send() does too.....
ob_start();
public function testPatchRoute() { $engine->response()->write('I am a teapot');
$engine = new Engine(); $this->expectOutputString('I am a teapot');
$engine->patch('/someRoute', function() { echo 'i ran'; }, true); $engine->stop(500);
$routes = $engine->router()->getRoutes(); $this->assertEquals(500, $engine->response()->status());
$this->assertEquals('PATCH', $routes[0]->methods[0]); }
$this->assertEquals('/someRoute', $routes[0]->pattern);
} public function testPostRoute()
{
public function testDeleteRoute() { $engine = new Engine();
$engine = new Engine(); $engine->post('/someRoute', function () {
$engine->delete('/someRoute', function() { echo 'i ran'; }, true); echo 'i ran';
$routes = $engine->router()->getRoutes(); }, true);
$this->assertEquals('DELETE', $routes[0]->methods[0]); $routes = $engine->router()->getRoutes();
$this->assertEquals('/someRoute', $routes[0]->pattern); $this->assertEquals('POST', $routes[0]->methods[0]);
} $this->assertEquals('/someRoute', $routes[0]->pattern);
}
public function testHalt() {
$engine = new class extends Engine { public function testPutRoute()
public function getLoader() { {
return $this->loader; $engine = new Engine();
} $engine->put('/someRoute', function () {
}; echo 'i ran';
// doing this so we can overwrite some parts of the response }, true);
$engine->getLoader()->register('response', function() { $routes = $engine->router()->getRoutes();
return new class extends \flight\net\Response { $this->assertEquals('PUT', $routes[0]->methods[0]);
public function __construct() {} $this->assertEquals('/someRoute', $routes[0]->pattern);
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response }
{
return $this; public function testPatchRoute()
} {
}; $engine = new Engine();
}); $engine->patch('/someRoute', function () {
$this->expectOutputString('skip---exit'); echo 'i ran';
$engine->halt(500, 'skip---exit'); }, true);
$this->assertEquals(500, $engine->response()->status()); $routes = $engine->router()->getRoutes();
} $this->assertEquals('PATCH', $routes[0]->methods[0]);
$this->assertEquals('/someRoute', $routes[0]->pattern);
public function testRedirect() { }
$engine = new Engine();
$engine->redirect('https://github.com', 302); public function testDeleteRoute()
$this->assertEquals('https://github.com', $engine->response()->headers()['Location']); {
$this->assertEquals(302, $engine->response()->status()); $engine = new Engine();
} $engine->delete('/someRoute', function () {
echo 'i ran';
public function testRedirectWithBaseUrl() { }, true);
$engine = new Engine(); $routes = $engine->router()->getRoutes();
$engine->set('flight.base_url', '/subdirectory'); $this->assertEquals('DELETE', $routes[0]->methods[0]);
$engine->redirect('/someRoute', 301); $this->assertEquals('/someRoute', $routes[0]->pattern);
$this->assertEquals('/subdirectory/someRoute', $engine->response()->headers()['Location']); }
$this->assertEquals(301, $engine->response()->status());
} public function testHalt()
{
public function testJson() { $engine = new class extends Engine {
$engine = new Engine(); public function getLoader()
$engine->json(['key1' => 'value1', 'key2' => 'value2']); {
$this->expectOutputString('{"key1":"value1","key2":"value2"}'); return $this->loader;
$this->assertEquals('application/json; charset=utf-8', $engine->response()->headers()['Content-Type']); }
$this->assertEquals(200, $engine->response()->status()); };
} // doing this so we can overwrite some parts of the response
$engine->getLoader()->register('response', function () {
public function testJsonP() { return new class extends \flight\net\Response {
$engine = new Engine(); public function __construct()
$engine->request()->query['jsonp'] = 'whatever'; {
$engine->jsonp(['key1' => 'value1', 'key2' => 'value2']); }
$this->expectOutputString('whatever({"key1":"value1","key2":"value2"});'); public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response
$this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']); {
$this->assertEquals(200, $engine->response()->status()); return $this;
} }
};
public function testJsonPBadParam() { });
$engine = new Engine(); $this->expectOutputString('skip---exit');
$engine->jsonp(['key1' => 'value1', 'key2' => 'value2']); $engine->halt(500, 'skip---exit');
$this->expectOutputString('({"key1":"value1","key2":"value2"});'); $this->assertEquals(500, $engine->response()->status());
$this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']); }
$this->assertEquals(200, $engine->response()->status());
} public function testRedirect()
{
public function testEtagSimple() { $engine = new Engine();
$engine = new Engine(); $engine->redirect('https://github.com', 302);
$engine->etag('etag'); $this->assertEquals('https://github.com', $engine->response()->headers()['Location']);
$this->assertEquals('"etag"', $engine->response()->headers()['ETag']); $this->assertEquals(302, $engine->response()->status());
} }
public function testEtagWithHttpIfNoneMatch() { public function testRedirectWithBaseUrl()
// just need this not to exit... {
$engine = new class extends Engine { $engine = new Engine();
public function _halt(int $code = 200, string $message = ''): void $engine->set('flight.base_url', '/subdirectory');
{ $engine->redirect('/someRoute', 301);
$this->response()->status($code); $this->assertEquals('/subdirectory/someRoute', $engine->response()->headers()['Location']);
$this->response()->write($message); $this->assertEquals(301, $engine->response()->status());
} }
};
$_SERVER['HTTP_IF_NONE_MATCH'] = 'etag'; public function testJson()
$engine->etag('etag'); {
$this->assertEquals('"etag"', $engine->response()->headers()['ETag']); $engine = new Engine();
$this->assertEquals(304, $engine->response()->status()); $engine->json(['key1' => 'value1', 'key2' => 'value2']);
} $this->expectOutputString('{"key1":"value1","key2":"value2"}');
$this->assertEquals('application/json; charset=utf-8', $engine->response()->headers()['Content-Type']);
public function testLastModifiedSimple() { $this->assertEquals(200, $engine->response()->status());
$engine = new Engine(); }
$engine->lastModified(1234567890);
$this->assertEquals('Fri, 13 Feb 2009 23:31:30 GMT', $engine->response()->headers()['Last-Modified']); public function testJsonP()
} {
$engine = new Engine();
public function testLastModifiedWithHttpIfModifiedSince() { $engine->request()->query['jsonp'] = 'whatever';
// just need this not to exit... $engine->jsonp(['key1' => 'value1', 'key2' => 'value2']);
$engine = new class extends Engine { $this->expectOutputString('whatever({"key1":"value1","key2":"value2"});');
public function _halt(int $code = 200, string $message = ''): void $this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']);
{ $this->assertEquals(200, $engine->response()->status());
$this->response()->status($code); }
$this->response()->write($message);
} public function testJsonPBadParam()
}; {
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = 'Fri, 13 Feb 2009 23:31:30 GMT'; $engine = new Engine();
$engine->lastModified(1234567890); $engine->jsonp(['key1' => 'value1', 'key2' => 'value2']);
$this->assertEquals('Fri, 13 Feb 2009 23:31:30 GMT', $engine->response()->headers()['Last-Modified']); $this->expectOutputString('({"key1":"value1","key2":"value2"});');
$this->assertEquals(304, $engine->response()->status()); $this->assertEquals('application/javascript; charset=utf-8', $engine->response()->headers()['Content-Type']);
} $this->assertEquals(200, $engine->response()->status());
}
public function testGetUrl() {
$engine = new Engine; public function testEtagSimple()
$engine->route('/path1/@param:[0-9]{3}', function() { echo 'I win'; }, false, 'path1'); {
$url = $engine->getUrl('path1', [ 'param' => 123 ]); $engine = new Engine();
$this->assertEquals('/path1/123', $url); $engine->etag('etag');
} $this->assertEquals('"etag"', $engine->response()->headers()['ETag']);
}
public function testMiddlewareCallableFunction() {
$engine = new Engine(); public function testEtagWithHttpIfNoneMatch()
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) {
->addMiddleware(function($params) { echo 'before'.$params['id']; }); // just need this not to exit...
$engine->request()->url = '/path1/123'; $engine = new class extends Engine {
$engine->start(); public function _halt(int $code = 200, string $message = ''): void
$this->expectOutputString('before123OK123'); {
} $this->response()->status($code);
$this->response()->write($message);
public function testMiddlewareCallableFunctionReturnFalse() { }
$engine = new class extends Engine { };
public function _halt(int $code = 200, string $message = ''): void $_SERVER['HTTP_IF_NONE_MATCH'] = 'etag';
{ $engine->etag('etag');
$this->response()->status($code); $this->assertEquals('"etag"', $engine->response()->headers()['ETag']);
$this->response()->write($message); $this->assertEquals(304, $engine->response()->status());
} }
};
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) public function testLastModifiedSimple()
->addMiddleware(function($params) { echo 'before'.$params['id']; return false; }); {
$engine->request()->url = '/path1/123'; $engine = new Engine();
$engine->start(); $engine->lastModified(1234567890);
$this->expectOutputString('Forbiddenbefore123'); $this->assertEquals('Fri, 13 Feb 2009 23:31:30 GMT', $engine->response()->headers()['Last-Modified']);
$this->assertEquals(403, $engine->response()->status()); }
}
public function testLastModifiedWithHttpIfModifiedSince()
public function testMiddlewareClassBefore() { {
$middleware = new class { // just need this not to exit...
public function before($params) { $engine = new class extends Engine {
echo 'before'.$params['id']; public function _halt(int $code = 200, string $message = ''): void
} {
}; $this->response()->status($code);
$engine = new Engine(); $this->response()->write($message);
}
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) };
->addMiddleware($middleware); $_SERVER['HTTP_IF_MODIFIED_SINCE'] = 'Fri, 13 Feb 2009 23:31:30 GMT';
$engine->request()->url = '/path1/123'; $engine->lastModified(1234567890);
$engine->start(); $this->assertEquals('Fri, 13 Feb 2009 23:31:30 GMT', $engine->response()->headers()['Last-Modified']);
$this->expectOutputString('before123OK123'); $this->assertEquals(304, $engine->response()->status());
} }
public function testMiddlewareClassBeforeAndAfter() { public function testGetUrl()
$middleware = new class { {
public function before($params) { $engine = new Engine();
echo 'before'.$params['id']; $engine->route('/path1/@param:[0-9]{3}', function () {
} echo 'I win';
public function after($params) { }, false, 'path1');
echo 'after'.$params['id']; $url = $engine->getUrl('path1', [ 'param' => 123 ]);
} $this->assertEquals('/path1/123', $url);
}; }
$engine = new Engine();
public function testMiddlewareCallableFunction()
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) {
->addMiddleware($middleware); $engine = new Engine();
$engine->request()->url = '/path1/123'; $engine->route('/path1/@id', function ($id) {
$engine->start(); echo 'OK' . $id;
$this->expectOutputString('before123OK123after123'); })
} ->addMiddleware(function ($params) {
echo 'before' . $params['id'];
public function testMiddlewareClassAfter() { });
$middleware = new class { $engine->request()->url = '/path1/123';
public function after($params) { $engine->start();
$this->expectOutputString('before123OK123');
echo 'after'.$params['id']; }
}
}; public function testMiddlewareCallableFunctionReturnFalse()
$engine = new Engine(); {
$engine = new class extends Engine {
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) public function _halt(int $code = 200, string $message = ''): void
->addMiddleware($middleware); {
$engine->request()->url = '/path1/123'; $this->response()->status($code);
$engine->start(); $this->response()->write($message);
$this->expectOutputString('OK123after123'); }
} };
$engine->route('/path1/@id', function ($id) {
public function testMiddlewareClassAfterFailedCheck() { echo 'OK' . $id;
$middleware = new class { })
public function after($params) { ->addMiddleware(function ($params) {
echo 'after'.$params['id']; echo 'before' . $params['id'];
return false; return false;
} });
}; $engine->request()->url = '/path1/123';
$engine = new class extends Engine { $engine->start();
public function _halt(int $code = 200, string $message = ''): void $this->expectOutputString('Forbiddenbefore123');
{ $this->assertEquals(403, $engine->response()->status());
$this->response()->status($code); }
$this->response()->write($message);
} public function testMiddlewareClassBefore()
}; {
$middleware = new class {
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) public function before($params)
->addMiddleware($middleware); {
$engine->request()->url = '/path1/123'; echo 'before' . $params['id'];
$engine->start(); }
$this->assertEquals(403, $engine->response()->status()); };
$this->expectOutputString('ForbiddenOK123after123'); $engine = new Engine();
}
$engine->route('/path1/@id', function ($id) {
public function testMiddlewareCallableFunctionMultiple() { echo 'OK' . $id;
$engine = new Engine(); })
$engine->route('/path1/@id', function($id) { echo 'OK'.$id; }) ->addMiddleware($middleware);
->addMiddleware(function($params) { echo 'before1'.$params['id']; }) $engine->request()->url = '/path1/123';
->addMiddleware(function($params) { echo 'before2'.$params['id']; }); $engine->start();
$engine->request()->url = '/path1/123'; $this->expectOutputString('before123OK123');
$engine->start(); }
$this->expectOutputString('before1123before2123OK123');
} public function testMiddlewareClassBeforeAndAfter()
{
// Pay attention to the order on how the middleware is executed in this test. $middleware = new class {
public function testMiddlewareClassCallableRouteMultiple() { public function before($params)
$middleware = new class { {
public function before($params) { echo 'before' . $params['id'];
echo 'before'.$params['another_id']; }
} public function after($params)
public function after($params) { {
echo 'after'.$params['id']; echo 'after' . $params['id'];
} }
}; };
$middleware2 = new class { $engine = new Engine();
public function before($params) {
echo 'before'.$params['id']; $engine->route('/path1/@id', function ($id) {
} echo 'OK' . $id;
public function after($params) { })
echo 'after'.$params['id'].$params['another_id']; ->addMiddleware($middleware);
} $engine->request()->url = '/path1/123';
}; $engine->start();
$engine = new Engine(); $this->expectOutputString('before123OK123after123');
$engine->route('/path1/@id/subpath1/@another_id', function() { echo 'OK'; })->addMiddleware([ $middleware, $middleware2 ]); }
$engine->request()->url = '/path1/123/subpath1/456'; public function testMiddlewareClassAfter()
$engine->start(); {
$this->expectOutputString('before456before123OKafter123456after123'); $middleware = new class {
} public function after($params)
{
public function testMiddlewareClassGroupRouteMultipleBooyah() {
$middleware = new class { echo 'after' . $params['id'];
public function before($params) { }
echo 'before'.$params['another_id']; };
} $engine = new Engine();
public function after($params) {
echo 'after'.$params['id']; $engine->route('/path1/@id', function ($id) {
} echo 'OK' . $id;
}; })
$middleware2 = new class { ->addMiddleware($middleware);
public function before($params) { $engine->request()->url = '/path1/123';
echo 'before'.$params['id']; $engine->start();
} $this->expectOutputString('OK123after123');
public function after($params) { }
echo 'after'.$params['id'].$params['another_id'];
} public function testMiddlewareClassAfterFailedCheck()
}; {
$engine = new Engine(); $middleware = new class {
$engine->group('/path1/@id', function($router) { public function after($params)
$router->map('/subpath1/@another_id', function() { echo 'OK'; }); {
$router->map('/@cool_id', function() { echo 'OK'; }); echo 'after' . $params['id'];
}, [ $middleware, $middleware2 ]); return false;
}
$engine->request()->url = '/path1/123/subpath1/456'; };
$engine->start(); $engine = new class extends Engine {
$this->expectOutputString('before456before123OKafter123456after123'); public function _halt(int $code = 200, string $message = ''): void
} {
$this->response()->status($code);
$this->response()->write($message);
}
};
$engine->route('/path1/@id', function ($id) {
echo 'OK' . $id;
})
->addMiddleware($middleware);
$engine->request()->url = '/path1/123';
$engine->start();
$this->assertEquals(403, $engine->response()->status());
$this->expectOutputString('ForbiddenOK123after123');
}
public function testMiddlewareCallableFunctionMultiple()
{
$engine = new Engine();
$engine->route('/path1/@id', function ($id) {
echo 'OK' . $id;
})
->addMiddleware(function ($params) {
echo 'before1' . $params['id'];
})
->addMiddleware(function ($params) {
echo 'before2' . $params['id'];
});
$engine->request()->url = '/path1/123';
$engine->start();
$this->expectOutputString('before1123before2123OK123');
}
// Pay attention to the order on how the middleware is executed in this test.
public function testMiddlewareClassCallableRouteMultiple()
{
$middleware = new class {
public function before($params)
{
echo 'before' . $params['another_id'];
}
public function after($params)
{
echo 'after' . $params['id'];
}
};
$middleware2 = new class {
public function before($params)
{
echo 'before' . $params['id'];
}
public function after($params)
{
echo 'after' . $params['id'] . $params['another_id'];
}
};
$engine = new Engine();
$engine->route('/path1/@id/subpath1/@another_id', function () {
echo 'OK';
})->addMiddleware([ $middleware, $middleware2 ]);
$engine->request()->url = '/path1/123/subpath1/456';
$engine->start();
$this->expectOutputString('before456before123OKafter123456after123');
}
public function testMiddlewareClassGroupRouteMultipleBooyah()
{
$middleware = new class {
public function before($params)
{
echo 'before' . $params['another_id'];
}
public function after($params)
{
echo 'after' . $params['id'];
}
};
$middleware2 = new class {
public function before($params)
{
echo 'before' . $params['id'];
}
public function after($params)
{
echo 'after' . $params['id'] . $params['another_id'];
}
};
$engine = new Engine();
$engine->group('/path1/@id', function ($router) {
$router->map('/subpath1/@another_id', function () {
echo 'OK';
});
$router->map('/@cool_id', function () {
echo 'OK';
});
}, [ $middleware, $middleware2 ]);
$engine->request()->url = '/path1/123/subpath1/456';
$engine->start();
$this->expectOutputString('before456before123OKafter123456after123');
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -16,17 +16,18 @@ class FlightTest extends PHPUnit\Framework\TestCase
{ {
protected function setUp(): void protected function setUp(): void
{ {
$_SERVER = []; $_SERVER = [];
$_REQUEST = []; $_REQUEST = [];
Flight::init(); Flight::init();
Flight::setEngine(new Engine()); Flight::setEngine(new Engine());
} }
protected function tearDown(): void { protected function tearDown(): void
unset($_REQUEST); {
unset($_SERVER); unset($_REQUEST);
Flight::clear(); unset($_SERVER);
} Flight::clear();
}
// Checks that default components are loaded // Checks that default components are loaded
public function testDefaultComponents() public function testDefaultComponents()
@ -100,124 +101,138 @@ class FlightTest extends PHPUnit\Framework\TestCase
Flight::doesNotExist(); Flight::doesNotExist();
} }
public function testStaticRoute() { public function testStaticRoute()
Flight::route('/test', function() { {
echo 'test'; Flight::route('/test', function () {
}); echo 'test';
Flight::request()->url = '/test'; });
Flight::request()->url = '/test';
$this->expectOutputString('test'); $this->expectOutputString('test');
Flight::start(); Flight::start();
} }
public function testStaticRouteGroup() { public function testStaticRouteGroup()
Flight::group('/group', function() { {
Flight::route('/test', function() { Flight::group('/group', function () {
echo 'test'; Flight::route('/test', function () {
}); echo 'test';
}); });
Flight::request()->url = '/group/test'; });
Flight::request()->url = '/group/test';
$this->expectOutputString('test'); $this->expectOutputString('test');
Flight::start(); Flight::start();
} }
public function testStaticRouteGet() { public function testStaticRouteGet()
{
// can't actually get "get" because that gets a variable // can't actually get "get" because that gets a variable
Flight::route('GET /test', function() { Flight::route('GET /test', function () {
echo 'test get'; echo 'test get';
}); });
$_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_METHOD'] = 'GET';
Flight::request()->url = '/test'; Flight::request()->url = '/test';
$this->expectOutputString('test get'); $this->expectOutputString('test get');
Flight::start(); Flight::start();
} }
public function testStaticRoutePost() { public function testStaticRoutePost()
{
Flight::post('/test', function() { Flight::post('/test', function () {
echo 'test post'; echo 'test post';
}); });
$_SERVER['REQUEST_METHOD'] = 'POST'; $_SERVER['REQUEST_METHOD'] = 'POST';
Flight::request()->url = '/test'; Flight::request()->url = '/test';
$this->expectOutputString('test post'); $this->expectOutputString('test post');
Flight::start(); Flight::start();
} }
public function testStaticRoutePut() { public function testStaticRoutePut()
{
Flight::put('/test', function() { Flight::put('/test', function () {
echo 'test put'; echo 'test put';
}); });
$_SERVER['REQUEST_METHOD'] = 'PUT'; $_SERVER['REQUEST_METHOD'] = 'PUT';
Flight::request()->url = '/test'; Flight::request()->url = '/test';
$this->expectOutputString('test put'); $this->expectOutputString('test put');
Flight::start(); Flight::start();
} }
public function testStaticRoutePatch() { public function testStaticRoutePatch()
{
Flight::patch('/test', function() { Flight::patch('/test', function () {
echo 'test patch'; echo 'test patch';
}); });
$_SERVER['REQUEST_METHOD'] = 'PATCH'; $_SERVER['REQUEST_METHOD'] = 'PATCH';
Flight::request()->url = '/test'; Flight::request()->url = '/test';
$this->expectOutputString('test patch'); $this->expectOutputString('test patch');
Flight::start(); Flight::start();
} }
public function testStaticRouteDelete() { public function testStaticRouteDelete()
{
Flight::delete('/test', function() { Flight::delete('/test', function () {
echo 'test delete'; echo 'test delete';
}); });
$_SERVER['REQUEST_METHOD'] = 'DELETE'; $_SERVER['REQUEST_METHOD'] = 'DELETE';
Flight::request()->url = '/test'; Flight::request()->url = '/test';
$this->expectOutputString('test delete'); $this->expectOutputString('test delete');
Flight::start(); Flight::start();
} }
public function testGetUrl() { public function testGetUrl()
Flight::route('/path1/@param:[a-zA-Z0-9]{2,3}', function() { echo 'I win'; }, false, 'path1'); {
$url = Flight::getUrl('path1', [ 'param' => 123 ]); Flight::route('/path1/@param:[a-zA-Z0-9]{2,3}', function () {
$this->assertEquals('/path1/123', $url); echo 'I win';
} }, false, 'path1');
$url = Flight::getUrl('path1', [ 'param' => 123 ]);
$this->assertEquals('/path1/123', $url);
}
public function testRouteGetUrlWithGroupSimpleParams() { public function testRouteGetUrlWithGroupSimpleParams()
Flight::group('/path1/@id', function() { {
Flight::route('/@name', function() { echo 'whatever'; }, false, 'path1'); Flight::group('/path1/@id', function () {
}); Flight::route('/@name', function () {
$url = Flight::getUrl('path1', ['id' => 123, 'name' => 'abc']); echo 'whatever';
}, false, 'path1');
});
$url = Flight::getUrl('path1', ['id' => 123, 'name' => 'abc']);
$this->assertEquals('/path1/123/abc', $url); $this->assertEquals('/path1/123/abc', $url);
} }
public function testRouteGetUrlNestedGroups() { public function testRouteGetUrlNestedGroups()
Flight::group('/user', function () { {
Flight::group('/all_users', function () { Flight::group('/user', function () {
Flight::group('/check_user', function () { Flight::group('/all_users', function () {
Flight::group('/check_one', function () { Flight::group('/check_user', function () {
Flight::route("/normalpath", function () { Flight::group('/check_one', function () {
echo "normalpath"; Flight::route("/normalpath", function () {
},false,"normalpathalias"); echo "normalpath";
}); }, false, "normalpathalias");
}); });
}); });
}); });
});
$url = Flight::getUrl('normalpathalias'); $url = Flight::getUrl('normalpathalias');
$this->assertEquals('/user/all_users/check_user/check_one/normalpath', $url); $this->assertEquals('/user/all_users/check_user/check_one/normalpath', $url);
} }
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -119,35 +120,39 @@ class LoaderTest extends PHPUnit\Framework\TestCase
self::assertInstanceOf(Factory::class, $obj); self::assertInstanceOf(Factory::class, $obj);
} }
public function testUnregisterClass() { public function testUnregisterClass()
$this->loader->register('g', 'User'); {
$current_class = $this->loader->get('g'); $this->loader->register('g', 'User');
$this->assertEquals([ 'User', [], null ], $current_class); $current_class = $this->loader->get('g');
$this->loader->unregister('g'); $this->assertEquals([ 'User', [], null ], $current_class);
$unregistered_class_result = $this->loader->get('g'); $this->loader->unregister('g');
$this->assertNull($unregistered_class_result); $unregistered_class_result = $this->loader->get('g');
} $this->assertNull($unregistered_class_result);
}
public function testNewInstance6Params() {
$TesterClass = $this->loader->newInstance('TesterClass', ['Bob','Fred', 'Joe', 'Jane', 'Sally', 'Suzie']); public function testNewInstance6Params()
$this->assertEquals('Bob', $TesterClass->param1); {
$this->assertEquals('Fred', $TesterClass->param2); $TesterClass = $this->loader->newInstance('TesterClass', ['Bob','Fred', 'Joe', 'Jane', 'Sally', 'Suzie']);
$this->assertEquals('Joe', $TesterClass->param3); $this->assertEquals('Bob', $TesterClass->param1);
$this->assertEquals('Jane', $TesterClass->param4); $this->assertEquals('Fred', $TesterClass->param2);
$this->assertEquals('Sally', $TesterClass->param5); $this->assertEquals('Joe', $TesterClass->param3);
$this->assertEquals('Suzie', $TesterClass->param6); $this->assertEquals('Jane', $TesterClass->param4);
} $this->assertEquals('Sally', $TesterClass->param5);
$this->assertEquals('Suzie', $TesterClass->param6);
public function testAddDirectoryAsArray() { }
$loader = new class extends Loader {
public function getDirectories() { public function testAddDirectoryAsArray()
return self::$dirs; {
} $loader = new class extends Loader {
}; public function getDirectories()
$loader->addDirectory([__DIR__ . '/classes']); {
self::assertEquals([ return self::$dirs;
dirname(__DIR__), }
__DIR__ . '/classes' };
], $loader->getDirectories()); $loader->addDirectory([__DIR__ . '/classes']);
} self::assertEquals([
dirname(__DIR__),
__DIR__ . '/classes'
], $loader->getDirectories());
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -21,91 +21,102 @@ class PdoWrapperTest extends PHPUnit\Framework\TestCase
protected function setUp(): void protected function setUp(): void
{ {
$this->pdo_wrapper = new PdoWrapper('sqlite::memory:'); $this->pdo_wrapper = new PdoWrapper('sqlite::memory:');
// create a test table and insert 3 rows of data // create a test table and insert 3 rows of data
$this->pdo_wrapper->exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)'); $this->pdo_wrapper->exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
$this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("one")'); $this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("one")');
$this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("two")'); $this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("two")');
$this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("three")'); $this->pdo_wrapper->exec('INSERT INTO test (name) VALUES ("three")');
} }
protected function tearDown(): void protected function tearDown(): void
{ {
// delete the test table // delete the test table
$this->pdo_wrapper->exec('DROP TABLE test'); $this->pdo_wrapper->exec('DROP TABLE test');
} }
public function testRunQuerySelectAllStatement() { public function testRunQuerySelectAllStatement()
$statement = $this->pdo_wrapper->runQuery('SELECT * FROM test'); {
$this->assertInstanceOf(PDOStatement::class, $statement); $statement = $this->pdo_wrapper->runQuery('SELECT * FROM test');
$this->assertCount(3, $statement->fetchAll()); $this->assertInstanceOf(PDOStatement::class, $statement);
} $this->assertCount(3, $statement->fetchAll());
}
public function testRunQuerySelectOneStatement() {
$statement = $this->pdo_wrapper->runQuery('SELECT * FROM test WHERE id = 1');
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertCount(1, $statement->fetchAll());
}
public function testRunQueryInsertStatement() {
$statement = $this->pdo_wrapper->runQuery('INSERT INTO test (name) VALUES ("four")');
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(1, $statement->rowCount());
}
public function testRunQueryUpdateStatement() {
$statement = $this->pdo_wrapper->runQuery('UPDATE test SET name = "something" WHERE name LIKE ?', ['%t%']);
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(2, $statement->rowCount());
}
public function testRunQueryDeleteStatement() {
$statement = $this->pdo_wrapper->runQuery('DELETE FROM test WHERE name LIKE ?', ['%t%']);
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(2, $statement->rowCount());
}
public function testFetchField() {
$id = $this->pdo_wrapper->fetchField('SELECT id FROM test WHERE name = ?', ['two']);
$this->assertEquals(2, $id);
}
public function testFetchRow() {
$row = $this->pdo_wrapper->fetchRow('SELECT * FROM test WHERE name = ?', ['two']);
$this->assertEquals(2, $row['id']);
$this->assertEquals('two', $row['name']);
}
public function testFetchAll() {
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test');
$this->assertCount(3, $rows);
$this->assertEquals(1, $rows[0]['id']);
$this->assertEquals('one', $rows[0]['name']);
$this->assertEquals(2, $rows[1]['id']);
$this->assertEquals('two', $rows[1]['name']);
$this->assertEquals(3, $rows[2]['id']);
$this->assertEquals('three', $rows[2]['name']);
}
public function testFetchAllWithNamedParams() {
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE name = :name', [ 'name' => 'two']);
$this->assertCount(1, $rows);
$this->assertEquals(2, $rows[0]['id']);
$this->assertEquals('two', $rows[0]['name']);
}
public function testFetchAllWithInInt() {
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id IN(?)', [ [1,2 ]]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInString() {
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE name IN(?)', [ ['one','two' ]]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInStringCommas() {
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id > ? AND name IN(?)', [ 0, 'one,two' ]);
$this->assertEquals(2, count($rows));
}
public function testRunQuerySelectOneStatement()
{
$statement = $this->pdo_wrapper->runQuery('SELECT * FROM test WHERE id = 1');
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertCount(1, $statement->fetchAll());
}
public function testRunQueryInsertStatement()
{
$statement = $this->pdo_wrapper->runQuery('INSERT INTO test (name) VALUES ("four")');
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(1, $statement->rowCount());
}
public function testRunQueryUpdateStatement()
{
$statement = $this->pdo_wrapper->runQuery('UPDATE test SET name = "something" WHERE name LIKE ?', ['%t%']);
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(2, $statement->rowCount());
}
public function testRunQueryDeleteStatement()
{
$statement = $this->pdo_wrapper->runQuery('DELETE FROM test WHERE name LIKE ?', ['%t%']);
$this->assertInstanceOf(PDOStatement::class, $statement);
$this->assertEquals(2, $statement->rowCount());
}
public function testFetchField()
{
$id = $this->pdo_wrapper->fetchField('SELECT id FROM test WHERE name = ?', ['two']);
$this->assertEquals(2, $id);
}
public function testFetchRow()
{
$row = $this->pdo_wrapper->fetchRow('SELECT * FROM test WHERE name = ?', ['two']);
$this->assertEquals(2, $row['id']);
$this->assertEquals('two', $row['name']);
}
public function testFetchAll()
{
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test');
$this->assertCount(3, $rows);
$this->assertEquals(1, $rows[0]['id']);
$this->assertEquals('one', $rows[0]['name']);
$this->assertEquals(2, $rows[1]['id']);
$this->assertEquals('two', $rows[1]['name']);
$this->assertEquals(3, $rows[2]['id']);
$this->assertEquals('three', $rows[2]['name']);
}
public function testFetchAllWithNamedParams()
{
$rows = $this->pdo_wrapper->fetchAll('SELECT * FROM test WHERE name = :name', [ 'name' => 'two']);
$this->assertCount(1, $rows);
$this->assertEquals(2, $rows[0]['id']);
$this->assertEquals('two', $rows[0]['name']);
}
public function testFetchAllWithInInt()
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id IN(?)', [ [1,2 ]]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInString()
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE name IN(?)', [ ['one','two' ]]);
$this->assertEquals(2, count($rows));
}
public function testFetchAllWithInStringCommas()
{
$rows = $this->pdo_wrapper->fetchAll('SELECT id FROM test WHERE id > ? AND name IN(?)', [ 0, 'one,two' ]);
$this->assertEquals(2, count($rows));
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -15,8 +16,8 @@ class RequestTest extends PHPUnit\Framework\TestCase
protected function setUp(): void protected function setUp(): void
{ {
$_SERVER = []; $_SERVER = [];
$_REQUEST = []; $_REQUEST = [];
$_SERVER['REQUEST_URI'] = '/'; $_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/index.php'; $_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_METHOD'] = 'GET';
@ -34,10 +35,11 @@ class RequestTest extends PHPUnit\Framework\TestCase
$this->request = new Request(); $this->request = new Request();
} }
protected function tearDown(): void { protected function tearDown(): void
unset($_REQUEST); {
unset($_SERVER); unset($_REQUEST);
} unset($_SERVER);
}
public function testDefaults() public function testDefaults()
{ {
@ -156,41 +158,44 @@ class RequestTest extends PHPUnit\Framework\TestCase
self::assertEquals('http', $request->scheme); self::assertEquals('http', $request->scheme);
} }
public function testInitUrlSameAsBaseDirectory() { public function testInitUrlSameAsBaseDirectory()
$request = new Request([ {
'url' => '/vagrant/public/flightphp', $request = new Request([
'base' => '/vagrant/public', 'url' => '/vagrant/public/flightphp',
'query' => new Collection(), 'base' => '/vagrant/public',
'type' => '' 'query' => new Collection(),
]); 'type' => ''
$this->assertEquals('/flightphp', $request->url); ]);
} $this->assertEquals('/flightphp', $request->url);
}
public function testInitNoUrl() {
$request = new Request([ public function testInitNoUrl()
'url' => '', {
'base' => '/vagrant/public', $request = new Request([
'type' => '' 'url' => '',
]); 'base' => '/vagrant/public',
$this->assertEquals('/', $request->url); 'type' => ''
} ]);
$this->assertEquals('/', $request->url);
public function testInitWithJsonBody() { }
// create dummy file to pull request body from
$tmpfile = tmpfile(); public function testInitWithJsonBody()
$stream_path = stream_get_meta_data($tmpfile)['uri']; {
file_put_contents($stream_path, '{"foo":"bar"}'); // create dummy file to pull request body from
$_SERVER['REQUEST_METHOD'] = 'POST'; $tmpfile = tmpfile();
$request = new Request([ $stream_path = stream_get_meta_data($tmpfile)['uri'];
'url' => '/something/fancy', file_put_contents($stream_path, '{"foo":"bar"}');
'base' => '/vagrant/public', $_SERVER['REQUEST_METHOD'] = 'POST';
'type' => 'application/json', $request = new Request([
'length' => 13, 'url' => '/something/fancy',
'data' => new Collection(), 'base' => '/vagrant/public',
'query' => new Collection(), 'type' => 'application/json',
'stream_path' => $stream_path 'length' => 13,
]); 'data' => new Collection(),
$this->assertEquals([ 'foo' => 'bar' ], $request->data->getData()); 'query' => new Collection(),
$this->assertEquals('{"foo":"bar"}', $request->getBody()); 'stream_path' => $stream_path
} ]);
$this->assertEquals([ 'foo' => 'bar' ], $request->data->getData());
$this->assertEquals('{"foo":"bar"}', $request->getBody());
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -12,216 +13,236 @@ use flight\util\Collection;
class ResponseTest extends PHPUnit\Framework\TestCase class ResponseTest extends PHPUnit\Framework\TestCase
{ {
protected function setUp(): void protected function setUp(): void
{ {
$_SERVER = []; $_SERVER = [];
$_REQUEST = []; $_REQUEST = [];
$_GET = []; $_GET = [];
$_POST = []; $_POST = [];
$_COOKIE = []; $_COOKIE = [];
$_FILES = []; $_FILES = [];
} }
protected function tearDown(): void { protected function tearDown(): void
unset($_REQUEST); {
unset($_SERVER); unset($_REQUEST);
} unset($_SERVER);
}
public function testStatusDefault() {
$response = new Response(); public function testStatusDefault()
$this->assertSame(200, $response->status()); {
} $response = new Response();
$this->assertSame(200, $response->status());
public function testStatusValidCode() { }
$response = new Response();
$response->status(200); public function testStatusValidCode()
$this->assertEquals(200, $response->status()); {
} $response = new Response();
$response->status(200);
public function testStatusInvalidCode() { $this->assertEquals(200, $response->status());
$response = new Response(); }
$this->expectException(Exception::class);
$this->expectExceptionMessage('Invalid status code.'); public function testStatusInvalidCode()
$response->status(999); {
} $response = new Response();
$this->expectException(Exception::class);
public function testStatusReturnObject() { $this->expectExceptionMessage('Invalid status code.');
$response = new Response(); $response->status(999);
$this->assertEquals($response, $response->status(200)); }
}
public function testStatusReturnObject()
public function testHeaderSingle() { {
$response = new Response(); $response = new Response();
$response->header('Content-Type', 'text/html'); $this->assertEquals($response, $response->status(200));
$this->assertEquals(['Content-Type' => 'text/html'], $response->headers()); }
}
public function testHeaderSingle()
public function testHeaderSingleKeepCaseSensitive() { {
$response = new Response(); $response = new Response();
$response->header('content-type', 'text/html'); $response->header('Content-Type', 'text/html');
$response->header('x-test', 'test'); $this->assertEquals(['Content-Type' => 'text/html'], $response->headers());
$this->assertEquals(['content-type' => 'text/html', 'x-test' => 'test'], $response->headers()); }
}
public function testHeaderSingleKeepCaseSensitive()
public function testHeaderArray() { {
$response = new Response(); $response = new Response();
$response->header(['Content-Type' => 'text/html', 'X-Test' => 'test']); $response->header('content-type', 'text/html');
$this->assertEquals(['Content-Type' => 'text/html', 'X-Test' => 'test'], $response->headers()); $response->header('x-test', 'test');
} $this->assertEquals(['content-type' => 'text/html', 'x-test' => 'test'], $response->headers());
}
public function testHeaderReturnObject() {
$response = new Response(); public function testHeaderArray()
$this->assertEquals($response, $response->header('Content-Type', 'text/html')); {
} $response = new Response();
$response->header(['Content-Type' => 'text/html', 'X-Test' => 'test']);
public function testWrite() { $this->assertEquals(['Content-Type' => 'text/html', 'X-Test' => 'test'], $response->headers());
$response = new class extends Response { }
public function getBody() {
return $this->body; public function testHeaderReturnObject()
} {
}; $response = new Response();
$response->write('test'); $this->assertEquals($response, $response->header('Content-Type', 'text/html'));
$this->assertEquals('test', $response->getBody()); }
}
public function testWrite()
public function testWriteEmptyString() { {
$response = new class extends Response { $response = new class extends Response {
public function getBody() { public function getBody()
return $this->body; {
} return $this->body;
}; }
$response->write(''); };
$this->assertEquals('', $response->getBody()); $response->write('test');
} $this->assertEquals('test', $response->getBody());
}
public function testWriteReturnObject() {
$response = new Response(); public function testWriteEmptyString()
$this->assertEquals($response, $response->write('test')); {
} $response = new class extends Response {
public function getBody()
public function testClear() { {
$response = new class extends Response { return $this->body;
public function getBody() { }
return $this->body; };
} $response->write('');
}; $this->assertEquals('', $response->getBody());
$response->write('test'); }
$response->status(404);
$response->header('Content-Type', 'text/html'); public function testWriteReturnObject()
$response->clear(); {
$this->assertEquals('', $response->getBody()); $response = new Response();
$this->assertEquals(200, $response->status()); $this->assertEquals($response, $response->write('test'));
$this->assertEquals([], $response->headers()); }
}
public function testClear()
public function testCacheSimple() { {
$response = new Response(); $response = new class extends Response {
$cache_time = time() + 60; public function getBody()
$response->cache($cache_time); {
$this->assertEquals([ return $this->body;
'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT', }
'Cache-Control' => 'max-age=60' };
], $response->headers()); $response->write('test');
} $response->status(404);
$response->header('Content-Type', 'text/html');
public function testCacheSimpleWithString() { $response->clear();
$response = new Response(); $this->assertEquals('', $response->getBody());
$cache_time = time() + 60; $this->assertEquals(200, $response->status());
$response->cache('now +60 seconds'); $this->assertEquals([], $response->headers());
$this->assertEquals([ }
'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT',
'Cache-Control' => 'max-age=60' public function testCacheSimple()
], $response->headers()); {
} $response = new Response();
$cache_time = time() + 60;
public function testCacheSimpleWithPragma() { $response->cache($cache_time);
$response = new Response(); $this->assertEquals([
$cache_time = time() + 60; 'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT',
$response->header('Pragma', 'no-cache'); 'Cache-Control' => 'max-age=60'
$response->cache($cache_time); ], $response->headers());
$this->assertEquals([ }
'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT',
'Cache-Control' => 'max-age=60' public function testCacheSimpleWithString()
], $response->headers()); {
} $response = new Response();
$cache_time = time() + 60;
public function testCacheFalseExpiresValue() { $response->cache('now +60 seconds');
$response = new Response(); $this->assertEquals([
$response->cache(false); 'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT',
$this->assertEquals([ 'Cache-Control' => 'max-age=60'
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT', ], $response->headers());
'Cache-Control' => [ }
public function testCacheSimpleWithPragma()
{
$response = new Response();
$cache_time = time() + 60;
$response->header('Pragma', 'no-cache');
$response->cache($cache_time);
$this->assertEquals([
'Expires' => gmdate('D, d M Y H:i:s', $cache_time) . ' GMT',
'Cache-Control' => 'max-age=60'
], $response->headers());
}
public function testCacheFalseExpiresValue()
{
$response = new Response();
$response->cache(false);
$this->assertEquals([
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Cache-Control' => [
'no-store, no-cache, must-revalidate', 'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0', 'post-check=0, pre-check=0',
'max-age=0', 'max-age=0',
], ],
'Pragma' => 'no-cache' 'Pragma' => 'no-cache'
], $response->headers()); ], $response->headers());
} }
public function testSendHeadersRegular() {
$response = new class extends Response {
protected $test_sent_headers = [];
protected array $headers = [
'Cache-Control' => [
'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0',
'max-age=0',
]
];
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response
{
$this->test_sent_headers[] = $header_string;
return $this;
}
public function getSentHeaders(): array
{
return $this->test_sent_headers;
}
};
$response->header('Content-Type', 'text/html');
$response->header('X-Test', 'test');
$response->write('Something');
$response->sendHeaders();
$sent_headers = $response->getSentHeaders();
$this->assertEquals([
'HTTP/1.1 200 OK',
'Cache-Control: no-store, no-cache, must-revalidate',
'Cache-Control: post-check=0, pre-check=0',
'Cache-Control: max-age=0',
'Content-Type: text/html',
'X-Test: test',
'Content-Length: 9'
], $sent_headers);
}
public function testSentDefault() {
$response = new Response();
$this->assertFalse($response->sent());
}
public function testSentTrue() {
$response = new class extends Response {
protected $test_sent_headers = [];
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response
{
$this->test_sent_headers[] = $header_string;
return $this;
}
};
$response->header('Content-Type', 'text/html');
$response->header('X-Test', 'test');
$response->write('Something');
$this->expectOutputString('Something');
$response->send();
$this->assertTrue($response->sent());
}
public function testSendHeadersRegular()
{
$response = new class extends Response {
protected $test_sent_headers = [];
protected array $headers = [
'Cache-Control' => [
'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0',
'max-age=0',
]
];
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response
{
$this->test_sent_headers[] = $header_string;
return $this;
}
public function getSentHeaders(): array
{
return $this->test_sent_headers;
}
};
$response->header('Content-Type', 'text/html');
$response->header('X-Test', 'test');
$response->write('Something');
$response->sendHeaders();
$sent_headers = $response->getSentHeaders();
$this->assertEquals([
'HTTP/1.1 200 OK',
'Cache-Control: no-store, no-cache, must-revalidate',
'Cache-Control: post-check=0, pre-check=0',
'Cache-Control: max-age=0',
'Content-Type: text/html',
'X-Test: test',
'Content-Length: 9'
], $sent_headers);
}
public function testSentDefault()
{
$response = new Response();
$this->assertFalse($response->sent());
}
public function testSentTrue()
{
$response = new class extends Response {
protected $test_sent_headers = [];
public function setRealHeader(string $header_string, bool $replace = true, int $response_code = 0): Response
{
$this->test_sent_headers[] = $header_string;
return $this;
}
};
$response->header('Content-Type', 'text/html');
$response->header('X-Test', 'test');
$response->write('Something');
$this->expectOutputString('Something');
$response->send();
$this->assertTrue($response->sent());
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -20,17 +21,18 @@ class RouterTest extends PHPUnit\Framework\TestCase
protected function setUp(): void protected function setUp(): void
{ {
$_SERVER = []; $_SERVER = [];
$_REQUEST = []; $_REQUEST = [];
$this->router = new Router(); $this->router = new Router();
$this->request = new Request(); $this->request = new Request();
$this->dispatcher = new Dispatcher(); $this->dispatcher = new Dispatcher();
} }
protected function tearDown(): void { protected function tearDown(): void
unset($_REQUEST); {
unset($_SERVER); unset($_REQUEST);
} unset($_SERVER);
}
// Simple output // Simple output
public function ok() public function ok()
@ -105,7 +107,7 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check('OK'); $this->check('OK');
} }
// Simple path with trailing slash // Simple path with trailing slash
public function testPathRouteTrailingSlash() public function testPathRouteTrailingSlash()
{ {
$this->router->map('/path/', [$this, 'ok']); $this->router->map('/path/', [$this, 'ok']);
@ -114,7 +116,7 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check('OK'); $this->check('OK');
} }
public function testGetRouteShortcut() public function testGetRouteShortcut()
{ {
$this->router->get('/path', [$this, 'ok']); $this->router->get('/path', [$this, 'ok']);
$this->request->url = '/path'; $this->request->url = '/path';
@ -152,29 +154,32 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check('OK'); $this->check('OK');
} }
public function testPutRouteShortcut() { public function testPutRouteShortcut()
$this->router->put('/path', [$this, 'ok']); {
$this->request->url = '/path'; $this->router->put('/path', [$this, 'ok']);
$this->request->method = 'PUT'; $this->request->url = '/path';
$this->request->method = 'PUT';
$this->check('OK'); $this->check('OK');
} }
public function testPatchRouteShortcut() { public function testPatchRouteShortcut()
$this->router->patch('/path', [$this, 'ok']); {
$this->request->url = '/path'; $this->router->patch('/path', [$this, 'ok']);
$this->request->method = 'PATCH'; $this->request->url = '/path';
$this->request->method = 'PATCH';
$this->check('OK'); $this->check('OK');
} }
public function testDeleteRouteShortcut() { public function testDeleteRouteShortcut()
$this->router->delete('/path', [$this, 'ok']); {
$this->request->url = '/path'; $this->router->delete('/path', [$this, 'ok']);
$this->request->method = 'DELETE'; $this->request->url = '/path';
$this->request->method = 'DELETE';
$this->check('OK'); $this->check('OK');
} }
// Test regular expression matching // Test regular expression matching
public function testRegEx() public function testRegEx()
@ -249,18 +254,19 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check('OK'); $this->check('OK');
} }
public function testWildcardDuplicate() { public function testWildcardDuplicate()
$this->router->map('/account/*' , [$this, 'ok']); {
$this->request->url = '/account/account/account'; $this->router->map('/account/*', [$this, 'ok']);
$this->check('OK'); $this->request->url = '/account/account/account';
} $this->check('OK');
}
public function testRouteWithLongQueryParamWithMultilineEncoded() public function testRouteWithLongQueryParamWithMultilineEncoded()
{ {
$this->router->map('GET /api/intune/hey', [$this, 'ok']); $this->router->map('GET /api/intune/hey', [$this, 'ok']);
$this->request->url = '/api/intune/hey?error=access_denied&error_description=AADSTS65004%3a+User+declined+to+consent+to+access+the+app.%0d%0aTrace+ID%3a+747c0cc1-ccbd-4e53-8e2f-48812eb24100%0d%0aCorrelation+ID%3a+362e3cb3-20ef-400b-904e-9983bd989184%0d%0aTimestamp%3a+2022-09-08+09%3a58%3a12Z&error_uri=https%3a%2f%2flogin.microsoftonline.com%2ferror%3fcode%3d65004&admin_consent=True&state=x2EUE0fcSj#'; $this->request->url = '/api/intune/hey?error=access_denied&error_description=AADSTS65004%3a+User+declined+to+consent+to+access+the+app.%0d%0aTrace+ID%3a+747c0cc1-ccbd-4e53-8e2f-48812eb24100%0d%0aCorrelation+ID%3a+362e3cb3-20ef-400b-904e-9983bd989184%0d%0aTimestamp%3a+2022-09-08+09%3a58%3a12Z&error_uri=https%3a%2f%2flogin.microsoftonline.com%2ferror%3fcode%3d65004&admin_consent=True&state=x2EUE0fcSj#';
$this->check('OK'); $this->check('OK');
} }
// Check if route object was passed // Check if route object was passed
public function testRouteObjectPassing() public function testRouteObjectPassing()
@ -298,17 +304,21 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check(); $this->check();
} }
public function testRouteBeingReturned() { public function testRouteBeingReturned()
$route = $this->router->map('/hi', function() {}); {
$route_in_router = $this->router->getRoutes()[0]; $route = $this->router->map('/hi', function () {
$this->assertSame($route, $route_in_router); });
} $route_in_router = $this->router->getRoutes()[0];
$this->assertSame($route, $route_in_router);
}
public function testRouteSetAlias() { public function testRouteSetAlias()
$route = $this->router->map('/hi', function() {}); {
$route->setAlias('hello'); $route = $this->router->map('/hi', function () {
$this->assertEquals('hello', $route->alias); });
} $route->setAlias('hello');
$this->assertEquals('hello', $route->alias);
}
// Test splat // Test splat
public function testSplatWildcard() public function testSplatWildcard()
@ -374,230 +384,249 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->check('цветя'); $this->check('цветя');
} }
public function testGetAndClearRoutes() { public function testGetAndClearRoutes()
$this->router->map('/path1', [$this, 'ok']); {
$this->router->map('/path2', [$this, 'ok']); $this->router->map('/path1', [$this, 'ok']);
$this->router->map('/path3', [$this, 'ok']); $this->router->map('/path2', [$this, 'ok']);
$this->router->map('/path4', [$this, 'ok']); $this->router->map('/path3', [$this, 'ok']);
$this->router->map('/path5', [$this, 'ok']); $this->router->map('/path4', [$this, 'ok']);
$this->router->map('/path6', [$this, 'ok']); $this->router->map('/path5', [$this, 'ok']);
$this->router->map('/path7', [$this, 'ok']); $this->router->map('/path6', [$this, 'ok']);
$this->router->map('/path8', [$this, 'ok']); $this->router->map('/path7', [$this, 'ok']);
$this->router->map('/path9', [$this, 'ok']); $this->router->map('/path8', [$this, 'ok']);
$this->router->map('/path9', [$this, 'ok']);
$routes = $this->router->getRoutes();
$this->assertEquals(9, count($routes)); $routes = $this->router->getRoutes();
$this->assertEquals(9, count($routes));
$this->router->clear();
$this->router->clear();
$this->assertEquals(0, count($this->router->getRoutes()));
} $this->assertEquals(0, count($this->router->getRoutes()));
}
public function testResetRoutes() {
$router = new class extends Router { public function testResetRoutes()
public function getIndex() { {
return $this->index; $router = new class extends Router {
} public function getIndex()
}; {
return $this->index;
$router->map('/path1', [$this, 'ok']); }
$router->map('/path2', [$this, 'ok']); };
$router->map('/path3', [$this, 'ok']);
$router->map('/path4', [$this, 'ok']); $router->map('/path1', [$this, 'ok']);
$router->map('/path5', [$this, 'ok']); $router->map('/path2', [$this, 'ok']);
$router->map('/path6', [$this, 'ok']); $router->map('/path3', [$this, 'ok']);
$router->map('/path7', [$this, 'ok']); $router->map('/path4', [$this, 'ok']);
$router->map('/path8', [$this, 'ok']); $router->map('/path5', [$this, 'ok']);
$router->map('/path9', [$this, 'ok']); $router->map('/path6', [$this, 'ok']);
$router->map('/path7', [$this, 'ok']);
$router->next(); $router->map('/path8', [$this, 'ok']);
$router->next(); $router->map('/path9', [$this, 'ok']);
$router->next();
$router->next();
$this->assertEquals(3, $router->getIndex()); $router->next();
$router->reset(); $router->next();
$this->assertEquals(0, $router->getIndex());
} $this->assertEquals(3, $router->getIndex());
$router->reset();
// Passing URL parameters $this->assertEquals(0, $router->getIndex());
}
// Passing URL parameters
public function testGroupRoutes() public function testGroupRoutes()
{ {
$this->router->group('/user', function(Router $router) { $this->router->group('/user', function (Router $router) {
$router->map('/@id', function ($id) { $router->map('/@id', function ($id) {
echo $id; echo $id;
}); });
$router->map('/@id/@name', function ($id, $name) { $router->map('/@id/@name', function ($id, $name) {
echo $id . $name; echo $id . $name;
}); });
}); });
$this->request->url = '/user/123'; $this->request->url = '/user/123';
$this->check('123'); $this->check('123');
} }
public function testGroupRoutesMultiParams() public function testGroupRoutesMultiParams()
{ {
$this->router->group('/user', function(Router $router) { $this->router->group('/user', function (Router $router) {
$router->map('/@id', function ($id) { $router->map('/@id', function ($id) {
echo $id; echo $id;
}); });
$router->map('/@id/@name', function ($id, $name) { $router->map('/@id/@name', function ($id, $name) {
echo $id . $name; echo $id . $name;
}); });
}); });
$this->request->url = '/user/123/abc'; $this->request->url = '/user/123/abc';
$this->check('123abc'); $this->check('123abc');
} }
public function testGroupNestedRoutes() public function testGroupNestedRoutes()
{ {
$this->router->group('/client', function(Router $router) { $this->router->group('/client', function (Router $router) {
$router->group('/user', function(Router $router) { $router->group('/user', function (Router $router) {
$router->map('/@id', function ($id) { $router->map('/@id', function ($id) {
echo $id; echo $id;
}); });
$router->map('/@id/@name', function ($id, $name) { $router->map('/@id/@name', function ($id, $name) {
echo $id . $name; echo $id . $name;
}); });
}); });
}); });
$this->request->url = '/client/user/123/abc'; $this->request->url = '/client/user/123/abc';
$this->check('123abc'); $this->check('123abc');
} }
public function testGroupNestedRoutesWithCustomMethods() public function testGroupNestedRoutesWithCustomMethods()
{ {
$this->router->group('/client', function(Router $router) { $this->router->group('/client', function (Router $router) {
$router->group('/user', function(Router $router) { $router->group('/user', function (Router $router) {
$router->get('/@id', function ($id) { $router->get('/@id', function ($id) {
echo $id; echo $id;
}); });
$router->post('/@id/@name', function ($id, $name) { $router->post('/@id/@name', function ($id, $name) {
echo $id . $name; echo $id . $name;
}); });
}); });
}); });
$this->request->url = '/client/user/123/abc'; $this->request->url = '/client/user/123/abc';
$this->request->method = 'POST'; $this->request->method = 'POST';
$this->check('123abc'); $this->check('123abc');
} }
public function testGetUrlByAliasBadReferenceButCatchRecommendation() { public function testGetUrlByAliasBadReferenceButCatchRecommendation()
$this->router->map('/path1', [$this, 'ok'], false, 'path1'); {
$this->expectException(\Exception::class); $this->router->map('/path1', [$this, 'ok'], false, 'path1');
$this->expectExceptionMessage('No route found with alias: \'path2\'. Did you mean \'path1\'?'); $this->expectException(\Exception::class);
$this->router->getUrlByAlias('path2'); $this->expectExceptionMessage('No route found with alias: \'path2\'. Did you mean \'path1\'?');
} $this->router->getUrlByAlias('path2');
}
public function testRewindAndValid() {
$this->router->map('/path1', [$this, 'ok']); public function testRewindAndValid()
$this->router->map('/path2', [$this, 'ok']); {
$this->router->map('/path3', [$this, 'ok']); $this->router->map('/path1', [$this, 'ok']);
$this->router->map('/path2', [$this, 'ok']);
$this->router->next(); $this->router->map('/path3', [$this, 'ok']);
$this->router->next();
$result = $this->router->valid(); $this->router->next();
$this->assertTrue($result); $this->router->next();
$this->router->next(); $result = $this->router->valid();
$result = $this->router->valid(); $this->assertTrue($result);
$this->assertFalse($result); $this->router->next();
$result = $this->router->valid();
$this->router->rewind(); $this->assertFalse($result);
$result = $this->router->valid();
$this->assertTrue($result); $this->router->rewind();
$result = $this->router->valid();
} $this->assertTrue($result);
}
public function testGetUrlByAliasNoMatches() {
$this->router->map('/path1', [$this, 'ok'], false, 'path1'); public function testGetUrlByAliasNoMatches()
$this->expectException(\Exception::class); {
$this->expectExceptionMessage('No route found with alias: \'path2\''); $this->router->map('/path1', [$this, 'ok'], false, 'path1');
$this->router->getUrlByAlias('path2'); $this->expectException(\Exception::class);
} $this->expectExceptionMessage('No route found with alias: \'path2\'');
$this->router->getUrlByAlias('path2');
public function testGetUrlByAliasNoParams() { }
$this->router->map('/path1', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1'); public function testGetUrlByAliasNoParams()
$this->assertEquals('/path1', $url); {
} $this->router->map('/path1', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1');
public function testGetUrlByAliasSimpleParams() { $this->assertEquals('/path1', $url);
$this->router->map('/path1/@id', [$this, 'ok'], false, 'path1'); }
$url = $this->router->getUrlByAlias('path1', ['id' => 123]);
$this->assertEquals('/path1/123', $url); public function testGetUrlByAliasSimpleParams()
} {
$this->router->map('/path1/@id', [$this, 'ok'], false, 'path1');
public function testGetUrlByAliasSimpleParamsWithNumber() { $url = $this->router->getUrlByAlias('path1', ['id' => 123]);
$this->router->map('/path1/@id1', [$this, 'ok'], false, 'path1'); $this->assertEquals('/path1/123', $url);
$url = $this->router->getUrlByAlias('path1', ['id1' => 123]); }
$this->assertEquals('/path1/123', $url);
} public function testGetUrlByAliasSimpleParamsWithNumber()
{
public function testGetUrlByAliasSimpleOptionalParamsWithParam() { $this->router->map('/path1/@id1', [$this, 'ok'], false, 'path1');
$this->router->map('/path1(/@id)', [$this, 'ok'], false, 'path1'); $url = $this->router->getUrlByAlias('path1', ['id1' => 123]);
$url = $this->router->getUrlByAlias('path1', ['id' => 123]); $this->assertEquals('/path1/123', $url);
$this->assertEquals('/path1/123', $url); }
}
public function testGetUrlByAliasSimpleOptionalParamsWithParam()
public function testGetUrlByAliasSimpleOptionalParamsWithNumberWithParam() { {
$this->router->map('/path1(/@id1)', [$this, 'ok'], false, 'path1'); $this->router->map('/path1(/@id)', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id1' => 123]); $url = $this->router->getUrlByAlias('path1', ['id' => 123]);
$this->assertEquals('/path1/123', $url); $this->assertEquals('/path1/123', $url);
} }
public function testGetUrlByAliasSimpleOptionalParamsNoParam() { public function testGetUrlByAliasSimpleOptionalParamsWithNumberWithParam()
$this->router->map('/path1(/@id)', [$this, 'ok'], false, 'path1'); {
$url = $this->router->getUrlByAlias('path1'); $this->router->map('/path1(/@id1)', [$this, 'ok'], false, 'path1');
$this->assertEquals('/path1', $url); $url = $this->router->getUrlByAlias('path1', ['id1' => 123]);
} $this->assertEquals('/path1/123', $url);
}
public function testGetUrlByAliasSimpleOptionalParamsWithNumberNoParam() {
$this->router->map('/path1(/@id1)', [$this, 'ok'], false, 'path1'); public function testGetUrlByAliasSimpleOptionalParamsNoParam()
$url = $this->router->getUrlByAlias('path1'); {
$this->assertEquals('/path1', $url); $this->router->map('/path1(/@id)', [$this, 'ok'], false, 'path1');
} $url = $this->router->getUrlByAlias('path1');
$this->assertEquals('/path1', $url);
public function testGetUrlByAliasMultipleParams() { }
$this->router->map('/path1/@id/@name', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id' => 123, 'name' => 'abc']); public function testGetUrlByAliasSimpleOptionalParamsWithNumberNoParam()
$this->assertEquals('/path1/123/abc', $url); {
} $this->router->map('/path1(/@id1)', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1');
public function testGetUrlByAliasMultipleComplexParams() { $this->assertEquals('/path1', $url);
$this->router->map('/path1/@id:[0-9]+/@name:[a-zA-Z0-9]{5}', [$this, 'ok'], false, 'path1'); }
$url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc']);
$this->assertEquals('/path1/123/abc', $url); public function testGetUrlByAliasMultipleParams()
} {
$this->router->map('/path1/@id/@name', [$this, 'ok'], false, 'path1');
public function testGetUrlByAliasMultipleComplexParamsWithNumbers() { $url = $this->router->getUrlByAlias('path1', ['id' => 123, 'name' => 'abc']);
$this->router->map('/path1/@5id:[0-9]+/@n1ame:[a-zA-Z0-9]{5}', [$this, 'ok'], false, 'path1'); $this->assertEquals('/path1/123/abc', $url);
$url = $this->router->getUrlByAlias('path1', ['5id' => '123', 'n1ame' => 'abc']); }
$this->assertEquals('/path1/123/abc', $url);
} public function testGetUrlByAliasMultipleComplexParams()
{
public function testGetUrlByAliasMultipleComplexOptionalParamsMissingOne() { $this->router->map('/path1/@id:[0-9]+/@name:[a-zA-Z0-9]{5}', [$this, 'ok'], false, 'path1');
$this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1'); $url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc']);
$url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc']); $this->assertEquals('/path1/123/abc', $url);
$this->assertEquals('/path1/123/abc', $url); }
}
public function testGetUrlByAliasMultipleComplexParamsWithNumbers()
public function testGetUrlByAliasMultipleComplexOptionalParamsAllParams() { {
$this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1'); $this->router->map('/path1/@5id:[0-9]+/@n1ame:[a-zA-Z0-9]{5}', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc', 'crazy' => 'xyz']); $url = $this->router->getUrlByAlias('path1', ['5id' => '123', 'n1ame' => 'abc']);
$this->assertEquals('/path1/123/abc/xyz', $url); $this->assertEquals('/path1/123/abc', $url);
} }
public function testGetUrlByAliasMultipleComplexOptionalParamsNoParams() { public function testGetUrlByAliasMultipleComplexOptionalParamsMissingOne()
$this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1'); {
$url = $this->router->getUrlByAlias('path1'); $this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1');
$this->assertEquals('/path1', $url); $url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc']);
} $this->assertEquals('/path1/123/abc', $url);
}
public function testGetUrlByAliasWithGroupSimpleParams() {
$this->router->group('/path1/@id', function($router) { public function testGetUrlByAliasMultipleComplexOptionalParamsAllParams()
$router->get('/@name', [$this, 'ok'], false, 'path1'); {
}); $this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id' => 123, 'name' => 'abc']); $url = $this->router->getUrlByAlias('path1', ['id' => '123', 'name' => 'abc', 'crazy' => 'xyz']);
$this->assertEquals('/path1/123/abc/xyz', $url);
$this->assertEquals('/path1/123/abc', $url); }
}
public function testGetUrlByAliasMultipleComplexOptionalParamsNoParams()
{
$this->router->map('/path1(/@id:[0-9]+(/@name(/@crazy:[a-z]{5})))', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1');
$this->assertEquals('/path1', $url);
}
public function testGetUrlByAliasWithGroupSimpleParams()
{
$this->router->group('/path1/@id', function ($router) {
$router->get('/@name', [$this, 'ok'], false, 'path1');
});
$url = $this->router->getUrlByAlias('path1', ['id' => 123, 'name' => 'abc']);
$this->assertEquals('/path1/123/abc', $url);
}
} }

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *

@ -36,20 +36,21 @@ class ViewTest extends PHPUnit\Framework\TestCase
$this->assertNull($this->view->get('test')); $this->assertNull($this->view->get('test'));
} }
public function testMultipleVariables() { public function testMultipleVariables()
$this->view->set([ {
'test' => 123, $this->view->set([
'foo' => 'bar' 'test' => 123,
]); 'foo' => 'bar'
]);
$this->assertEquals(123, $this->view->get('test')); $this->assertEquals(123, $this->view->get('test'));
$this->assertEquals('bar', $this->view->get('foo')); $this->assertEquals('bar', $this->view->get('foo'));
$this->view->clear(); $this->view->clear();
$this->assertNull($this->view->get('test')); $this->assertNull($this->view->get('test'));
$this->assertNull($this->view->get('foo')); $this->assertNull($this->view->get('foo'));
} }
// Check if template files exist // Check if template files exist
public function testTemplateExists() public function testTemplateExists()
@ -66,12 +67,13 @@ class ViewTest extends PHPUnit\Framework\TestCase
$this->expectOutputString('Hello, Bob!'); $this->expectOutputString('Hello, Bob!');
} }
public function testRenderBadFilePath() { public function testRenderBadFilePath()
$this->expectException(Exception::class); {
$this->expectExceptionMessage('Template file not found: ' . __DIR__ . DIRECTORY_SEPARATOR . 'views'. DIRECTORY_SEPARATOR . 'badfile.php'); $this->expectException(Exception::class);
$this->expectExceptionMessage('Template file not found: ' . __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'badfile.php');
$this->view->render('badfile'); $this->view->render('badfile');
} }
// Fetch template output // Fetch template output
public function testFetch() public function testFetch()
@ -102,24 +104,27 @@ class ViewTest extends PHPUnit\Framework\TestCase
$this->expectOutputString('Hello world, Bob!'); $this->expectOutputString('Hello world, Bob!');
} }
public function testGetTemplateAbsolutePath() { public function testGetTemplateAbsolutePath()
$tmpfile = tmpfile(); {
$this->view->extension = ''; $tmpfile = tmpfile();
$file_path = stream_get_meta_data($tmpfile)['uri']; $this->view->extension = '';
$this->assertEquals($file_path, $this->view->getTemplate($file_path)); $file_path = stream_get_meta_data($tmpfile)['uri'];
} $this->assertEquals($file_path, $this->view->getTemplate($file_path));
}
public function testE() {
$this->expectOutputString('&lt;script&gt;'); public function testE()
$result = $this->view->e('<script>'); {
$this->assertEquals('&lt;script&gt;', $result); $this->expectOutputString('&lt;script&gt;');
} $result = $this->view->e('<script>');
$this->assertEquals('&lt;script&gt;', $result);
public function testENoNeedToEscape() { }
$this->expectOutputString('script');
$result = $this->view->e('script'); public function testENoNeedToEscape()
$this->assertEquals('script', $result); {
} $this->expectOutputString('script');
$result = $this->view->e('script');
$this->assertEquals('script', $result);
}
public function testNormalizePath(): void public function testNormalizePath(): void
{ {

@ -1,17 +1,20 @@
<?php <?php
class TesterClass {
public $param1; class TesterClass
public $param2; {
public $param3; public $param1;
public $param4; public $param2;
public $param5; public $param3;
public $param6; public $param4;
public function __construct($param1, $param2, $param3, $param4, $param5, $param6) { public $param5;
$this->param1 = $param1; public $param6;
$this->param2 = $param2; public function __construct($param1, $param2, $param3, $param4, $param5, $param6)
$this->param3 = $param3; {
$this->param4 = $param4; $this->param1 = $param1;
$this->param5 = $param5; $this->param2 = $param2;
$this->param6 = $param6; $this->param3 = $param3;
} $this->param4 = $param4;
$this->param5 = $param5;
$this->param6 = $param6;
}
}; };

@ -1,6 +1,6 @@
<?php <?php
$path = file_exists(__DIR__ . '/../vendor/autoload.php') $path = file_exists(__DIR__ . '/../vendor/autoload.php')
? __DIR__ . '/../vendor/autoload.php' ? __DIR__ . '/../vendor/autoload.php'
: __DIR__ . '/../flight/autoload.php'; : __DIR__ . '/../flight/autoload.php';
require_once($path); require_once($path);
Loading…
Cancel
Save