Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: improve default routing, closes PhpGt/Routing#61 #648

Merged
merged 2 commits into from
May 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 124 additions & 23 deletions router.default.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,77 @@
use Gt\Routing\Path\DynamicPath;
use Gt\WebEngine\View\BaseView;
use Gt\WebEngine\View\HTMLView;
use Gt\WebEngine\View\NullView;

class DefaultRouter extends BaseRouter {
#[Any(name: "page-route", accept: "text/html,application/xhtml+xml")]
public function page(Request $request):void {
$pathMatcher = new PathMatcher("page");
$this->setViewClass(HTMLView::class);
$pathMatcher->addFilter(function(string $filePath, string $uriPath, string $baseDir):bool {
// There are three types of matching files: Basic, Magic and Dynamic.
// Basic is where a URI matches directly to a file on disk.
// Magic is where a URI matches a PHP.Gt-specific file, like _common or _header.
// Dynamic is where a URI matches a file/directory marked as dynamic with "@".
$basicFileMatch = new BasicFileMatch($filePath, $baseDir);
if($basicFileMatch->matches($uriPath)) {
return true;
$this->pathMatcherFilter($pathMatcher);

// This sort function allow multiple headers and footers to be in nested
// directories, so the highest level header is at the start of the list,
// with the reverse logic applied to footers.
// TODO: Extract into own function. Should this be maintained within PHP.Gt/Routing ?
$headerFooterSort = function(string $a, string $b):int {
$fileNameA = pathinfo($a, PATHINFO_FILENAME);
$fileNameB = pathinfo($b, PATHINFO_FILENAME);

if($fileNameA === "_header") {
if($fileNameB === "_header") {
$aDepth = substr_count($a, "/");
$bDepth = substr_count($b, "/");
if($aDepth > $bDepth) {
return 1;
}
elseif($aDepth < $bDepth) {
return -1;
}
else {
return 0;
}
}


return -1;
}

$magicFileMatch = new MagicFileMatch($filePath, $baseDir);
if($magicFileMatch->matches($uriPath)) {
return true;
if($fileNameA === "_footer") {
if($fileNameB === "_footer") {
$aDepth = substr_count($a, "/");
$bDepth = substr_count($b, "/");
if($aDepth < $bDepth) {
return 1;
}
elseif($aDepth > $bDepth) {
return -1;
}
else {
return 0;
}
}

return 1;
}

return false;
});
// TODO: add logic and view assembly in the api directory
// (configured from $this->routerConfig)
if($fileNameB === "_header") {
return 1;
}

if($fileNameB === "_footer") {
return -1;
}

return 0;
};

$sortNestLevelCallback = fn(string $a, string $b) =>
substr_count($a, "/") > substr_count($b, "/");
$headerSort = fn(string $a, string $b) =>
strtok(basename($a), ".") === "_header" ? -1 : 1;
$footerSort = fn(string $a, string $b) =>
strtok(basename($a), ".") === "_footer" ? 1 : -1;
substr_count($a, "/") > substr_count($b, "/")
? 1
: (substr_count($a, "/") < substr_count($b, "/")
? -1
: 0);

$matchingLogics = $pathMatcher->findForUriPath(
$request->getUri()->getPath(),
Expand All @@ -60,12 +99,74 @@ public function page(Request $request):void {
"page",
"html"
);
usort($matchingViews, $sortNestLevelCallback);
usort($matchingViews, $headerSort);
usort($matchingViews, $footerSort);
usort($matchingViews, $headerFooterSort);
foreach($matchingViews as $path) {
$this->addToViewAssembly($path);
}
}

#[Any(name: "api-route", accept: "application/xml,application/json")]
public function api(
Request $request
):void {
$pathMatcher = new PathMatcher("api");
$this->pathMatcherFilter($pathMatcher);
$this->setViewClass(NullView::class);
$sortNestLevelCallback = fn(string $a, string $b) =>
substr_count($a, "/") > substr_count($b, "/")
? 1
: (substr_count($a, "/") < substr_count($b, "/")
? -1
: 0);

$matchingLogics = $pathMatcher->findForUriPath(
$request->getUri()->getPath(),
"api",
"php"
);
usort($matchingLogics, $sortNestLevelCallback);
foreach($matchingLogics as $path) {
$this->addToLogicAssembly($path);
}

$matchingViews = $pathMatcher->findForUriPath(
$request->getUri()->getPath(),
"api",
"xml"
);
foreach($matchingViews as $path) {
$this->addToViewAssembly($path);
}
}

public function pathMatcherFilter(PathMatcher $pathMatcher):void {
$pathMatcher->addFilter(function(string $filePath, string $uriPath, string $baseDir):bool {
foreach(glob($baseDir . $uriPath . ".*") as $globMatch) {
$URI_CONTAINER = pathinfo($uriPath, PATHINFO_DIRNAME);
$TRIM_THIS = $baseDir . $URI_CONTAINER;
if(str_starts_with($globMatch, $TRIM_THIS)) {
$trimmed = substr($filePath, strlen($TRIM_THIS));
if(str_contains($trimmed, "@")) {
return false;
}
}
}

// There are three types of matching files: Basic, Magic and Dynamic.
// Basic is where a URI matches directly to a file on disk.
// Magic is where a URI matches a PHP.Gt-specific file, like _common or _header.
// Dynamic is where a URI matches a file/directory marked as dynamic with "@".
$basicFileMatch = new BasicFileMatch($filePath, $baseDir);
if($basicFileMatch->matches($uriPath)) {
return true;
}

$magicFileMatch = new MagicFileMatch($filePath, $baseDir);
if($magicFileMatch->matches($uriPath)) {
return true;
}

return false;
});
}
}
Loading