Route alias and phpstan updates

pull/510/head
n0nag0n 1 year ago
parent 802d51075d
commit 7991530761

@ -13,7 +13,7 @@ class PdoWrapper extends PDO {
* @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 $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);
@ -31,7 +31,7 @@ class PdoWrapper extends PDO {
* $db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]); * $db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);
* *
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
* @param array $params - Ex: [ $something ] * @param array<int|string,mixed> $params - Ex: [ $something ]
* @return PDOStatement * @return PDOStatement
*/ */
public function runQuery(string $sql, array $params = []): PDOStatement { public function runQuery(string $sql, array $params = []): PDOStatement {
@ -49,12 +49,12 @@ class PdoWrapper extends PDO {
* Ex: $id = $db->fetchField("SELECT id FROM table WHERE something = ?", [ $something ]); * Ex: $id = $db->fetchField("SELECT id FROM table WHERE something = ?", [ $something ]);
* *
* @param string $sql - Ex: "SELECT id FROM table WHERE something = ?" * @param string $sql - Ex: "SELECT id FROM table WHERE something = ?"
* @param array $params - Ex: [ $something ] * @param array<int|string,mixed> $params - Ex: [ $something ]
* @return mixed * @return mixed
*/ */
public function fetchField(string $sql, array $params = []) { public function fetchField(string $sql, array $params = []) {
$data = $this->fetchRow($sql, $params); $data = $this->fetchRow($sql, $params);
return is_array($data) ? reset($data) : null; return reset($data);
} }
/** /**
@ -63,13 +63,13 @@ class PdoWrapper extends PDO {
* Ex: $row = $db->fetchRow("SELECT * FROM table WHERE something = ?", [ $something ]); * Ex: $row = $db->fetchRow("SELECT * FROM table WHERE something = ?", [ $something ]);
* *
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
* @param array $params - Ex: [ $something ] * @param array<int|string,mixed> $params - Ex: [ $something ]
* @return array * @return array<string,mixed>
*/ */
public function fetchRow(string $sql, array $params = []): array { public function fetchRow(string $sql, array $params = []): array {
$sql .= stripos($sql, 'LIMIT') === false ? ' LIMIT 1' : ''; $sql .= stripos($sql, 'LIMIT') === false ? ' LIMIT 1' : '';
$result = $this->fetchAll($sql, $params); $result = $this->fetchAll($sql, $params);
return is_array($result) && count($result) ? $result[0] : []; return count($result) > 0 ? $result[0] : [];
} }
/** /**
@ -81,8 +81,8 @@ class PdoWrapper extends PDO {
* } * }
* *
* @param string $sql - Ex: "SELECT * FROM table WHERE something = ?" * @param string $sql - Ex: "SELECT * FROM table WHERE something = ?"
* @param array $params - Ex: [ $something ] * @param array<int|string,mixed> $params - Ex: [ $something ]
* @return array<int,array> * @return array<int,array<string,mixed>>
*/ */
public function fetchAll(string $sql, array $params = []): array { public function fetchAll(string $sql, array $params = []): array {
$processed_sql_data = $this->processInStatementSql($sql, $params); $processed_sql_data = $this->processInStatementSql($sql, $params);
@ -101,8 +101,8 @@ class PdoWrapper extends PDO {
* Converts this to "SELECT * FROM table WHERE id = ? AND something IN(?,?,?)" * Converts this to "SELECT * FROM table WHERE id = ? AND something IN(?,?,?)"
* *
* @param string $sql the sql statement * @param string $sql the sql statement
* @param array $params the params for the sql statement * @param array<int|string,mixed> $params the params for the sql statement
* @return array{sql:string,params:array} * @return array<string,string|array<int|string,mixed>>
*/ */
protected function processInStatementSql(string $sql, array $params = []): array { protected function processInStatementSql(string $sql, array $params = []): array {

@ -145,7 +145,6 @@ final class Request
* Constructor. * Constructor.
* *
* @param array<string, mixed> $config Request configuration * @param array<string, mixed> $config Request configuration
* @param string
*/ */
public function __construct($config = array()) public function __construct($config = array())
{ {
@ -210,7 +209,7 @@ final class Request
// Check for JSON input // Check for JSON input
if (0 === strpos($this->type, 'application/json')) { if (0 === strpos($this->type, 'application/json')) {
$body = $this->getBody(); $body = $this->getBody();
if ('' !== $body && null !== $body) { if ('' !== $body) {
$data = json_decode($body, true); $data = json_decode($body, true);
if (is_array($data)) { if (is_array($data)) {
$this->data->setData($data); $this->data->setData($data);
@ -226,7 +225,7 @@ final class Request
* *
* @return string Raw HTTP request body * @return string Raw HTTP request body
*/ */
public function getBody(): ?string public function getBody(): string
{ {
$body = $this->body; $body = $this->body;

@ -52,6 +52,11 @@ final class Route
*/ */
public bool $pass = false; public bool $pass = false;
/**
* @var string The alias is a way to identify the route using a simple name ex: 'login' instead of /admin/login
*/
public string $alias = '';
/** /**
* Constructor. * Constructor.
* *
@ -60,12 +65,13 @@ final class Route
* @param array<int, string> $methods HTTP methods * @param array<int, string> $methods HTTP methods
* @param bool $pass Pass self in callback parameters * @param bool $pass Pass self in callback parameters
*/ */
public function __construct(string $pattern, $callback, array $methods, bool $pass) public function __construct(string $pattern, $callback, array $methods, bool $pass, string $alias = '')
{ {
$this->pattern = $pattern; $this->pattern = $pattern;
$this->callback = $callback; $this->callback = $callback;
$this->methods = $methods; $this->methods = $methods;
$this->pass = $pass; $this->pass = $pass;
$this->alias = $alias;
} }
/** /**
@ -153,4 +159,30 @@ final 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.
*
* @param string $alias [description]
* @return boolean
*/
public function matchAlias(string $alias): bool
{
return $this->alias === $alias;
}
/**
* Hydrates the route url with the given parameters
*
* @param array<string,mixed> $params the parameters to pass to the route
* @return string
*/
public function hydrateUrl(array $params = []): string {
$url = preg_replace_callback("/(?:@([a-zA-Z]+)(?:\:([^\/]+))?)?/i", function($match) use ($params) {
if(isset($match[1]) && isset($params[$match[1]])) {
return $params[$match[1]];
}
}, $this->pattern);
return $url;
}
} }

@ -10,6 +10,9 @@ declare(strict_types=1);
namespace flight\net; namespace flight\net;
use Exception;
use flight\net\Route;
/** /**
* The Router class is responsible for routing an HTTP request to * The Router class is responsible for routing an HTTP request to
* an assigned callback function. The Router tries to match the * an assigned callback function. The Router tries to match the
@ -63,9 +66,10 @@ 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
* @return void * @return void
*/ */
public function map(string $pattern, callable $callback, bool $pass_route = false): void public function map(string $pattern, callable $callback, bool $pass_route = false, string $route_alias = ''): void
{ {
$url = trim($pattern); $url = trim($pattern);
$methods = ['*']; $methods = ['*'];
@ -76,7 +80,7 @@ class Router
$methods = explode('|', $method); $methods = explode('|', $method);
} }
$this->routes[] = new Route($this->group_prefix.$url, $callback, $methods, $pass_route); $this->routes[] = new Route($this->group_prefix.$url, $callback, $methods, $pass_route, $route_alias);
} }
/** /**
@ -173,6 +177,42 @@ class Router
return false; return false;
} }
/**
* Gets the URL for a given route alias
*
* @param string $alias the alias to match
* @param array<string,mixed> $params the parameters to pass to the route
* @return string
*/
public function getUrlByAlias(string $alias, array $params = []): string {
while ($route = $this->current()) {
if ($route->matchAlias($alias)) {
return $route->hydrateUrl($params);
}
$this->next();
}
throw new Exception('No route found with alias: ' . $alias);
}
/**
* Rewinds the current route index.
*/
public function rewind(): void
{
$this->index = 0;
}
/**
* Checks if more routes can be iterated.
*
* @return bool More routes
*/
public function valid(): bool
{
return isset($this->routes[$this->index]);
}
/** /**
* Gets the current route. * Gets the current route.
* *

@ -461,4 +461,54 @@ class RouterTest extends PHPUnit\Framework\TestCase
$this->request->method = 'POST'; $this->request->method = 'POST';
$this->check('123abc'); $this->check('123abc');
} }
public function testRewindAndValid() {
$this->router->map('/path1', [$this, 'ok']);
$this->router->map('/path2', [$this, 'ok']);
$this->router->map('/path3', [$this, 'ok']);
$this->router->next();
$this->router->next();
$result = $this->router->valid();
$this->assertTrue($result);
$this->router->next();
$result = $this->router->valid();
$this->assertFalse($result);
$this->router->rewind();
$result = $this->router->valid();
$this->assertTrue($result);
}
public function testGetUrlByAliasNoMatches() {
$this->router->map('/path1', [$this, 'ok'], false, 'path1');
$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');
$this->assertEquals('/path1', $url);
}
public function testGetUrlByAliasSimpleParams() {
$this->router->map('/path1/@id', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id' => 123]);
$this->assertEquals('/path1/123', $url);
}
public function testGetUrlByAliasMultipleParams() {
$this->router->map('/path1/@id/@name', [$this, 'ok'], false, 'path1');
$url = $this->router->getUrlByAlias('path1', ['id' => 123, 'name' => 'abc']);
$this->assertEquals('/path1/123/abc', $url);
}
public function testGetUrlByAliasMultipleComplexParams() {
$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);
}
} }

Loading…
Cancel
Save