1212use Gt \Routing \Path \DynamicPath ;
1313use Gt \WebEngine \View \BaseView ;
1414use Gt \WebEngine \View \HTMLView ;
15+ use Gt \WebEngine \View \NullView ;
1516
1617class DefaultRouter extends BaseRouter {
1718 #[Any(name: "page-route " , accept: "text/html,application/xhtml+xml " )]
1819 public function page (Request $ request ):void {
1920 $ pathMatcher = new PathMatcher ("page " );
2021 $ this ->setViewClass (HTMLView::class);
21- $ pathMatcher ->addFilter (function (string $ filePath , string $ uriPath , string $ baseDir ):bool {
22- // There are three types of matching files: Basic, Magic and Dynamic.
23- // Basic is where a URI matches directly to a file on disk.
24- // Magic is where a URI matches a PHP.Gt-specific file, like _common or _header.
25- // Dynamic is where a URI matches a file/directory marked as dynamic with "@".
26- $ basicFileMatch = new BasicFileMatch ($ filePath , $ baseDir );
27- if ($ basicFileMatch ->matches ($ uriPath )) {
28- return true ;
22+ $ this ->pathMatcherFilter ($ pathMatcher );
23+
24+ // This sort function allow multiple headers and footers to be in nested
25+ // directories, so the highest level header is at the start of the list,
26+ // with the reverse logic applied to footers.
27+ // TODO: Extract into own function. Should this be maintained within PHP.Gt/Routing ?
28+ $ headerFooterSort = function (string $ a , string $ b ):int {
29+ $ fileNameA = pathinfo ($ a , PATHINFO_FILENAME );
30+ $ fileNameB = pathinfo ($ b , PATHINFO_FILENAME );
31+
32+ if ($ fileNameA === "_header " ) {
33+ if ($ fileNameB === "_header " ) {
34+ $ aDepth = substr_count ($ a , "/ " );
35+ $ bDepth = substr_count ($ b , "/ " );
36+ if ($ aDepth > $ bDepth ) {
37+ return 1 ;
38+ }
39+ elseif ($ aDepth < $ bDepth ) {
40+ return -1 ;
41+ }
42+ else {
43+ return 0 ;
44+ }
45+ }
46+
47+
48+ return -1 ;
2949 }
3050
31- $ magicFileMatch = new MagicFileMatch ($ filePath , $ baseDir );
32- if ($ magicFileMatch ->matches ($ uriPath )) {
33- return true ;
51+ if ($ fileNameA === "_footer " ) {
52+ if ($ fileNameB === "_footer " ) {
53+ $ aDepth = substr_count ($ a , "/ " );
54+ $ bDepth = substr_count ($ b , "/ " );
55+ if ($ aDepth < $ bDepth ) {
56+ return 1 ;
57+ }
58+ elseif ($ aDepth > $ bDepth ) {
59+ return -1 ;
60+ }
61+ else {
62+ return 0 ;
63+ }
64+ }
65+
66+ return 1 ;
3467 }
3568
36- return false ;
37- });
38- // TODO: add logic and view assembly in the api directory
39- // (configured from $this->routerConfig)
69+ if ($ fileNameB === "_header " ) {
70+ return 1 ;
71+ }
72+
73+ if ($ fileNameB === "_footer " ) {
74+ return -1 ;
75+ }
76+
77+ return 0 ;
78+ };
4079
4180 $ sortNestLevelCallback = fn (string $ a , string $ b ) =>
42- substr_count ($ a , "/ " ) > substr_count ($ b , "/ " );
43- $ headerSort = fn ( string $ a , string $ b ) =>
44- strtok ( basename ($ a) , ". " ) === " _header " ? - 1 : 1 ;
45- $ footerSort = fn ( string $ a , string $ b ) =>
46- strtok ( basename ( $ a ), " . " ) === " _footer " ? 1 : - 1 ;
81+ substr_count ($ a , "/ " ) > substr_count ($ b , "/ " )
82+ ? 1
83+ : ( substr_count ($ a , "/ " ) < substr_count ( $ b , " / " )
84+ ? - 1
85+ : 0 ) ;
4786
4887 $ matchingLogics = $ pathMatcher ->findForUriPath (
4988 $ request ->getUri ()->getPath (),
@@ -60,12 +99,74 @@ public function page(Request $request):void {
6099 "page " ,
61100 "html "
62101 );
63- usort ($ matchingViews , $ sortNestLevelCallback );
64- usort ($ matchingViews , $ headerSort );
65- usort ($ matchingViews , $ footerSort );
102+ usort ($ matchingViews , $ headerFooterSort );
103+ foreach ($ matchingViews as $ path ) {
104+ $ this ->addToViewAssembly ($ path );
105+ }
106+ }
107+
108+ #[Any(name: "api-route " , accept: "application/xml,application/json " )]
109+ public function api (
110+ Request $ request
111+ ):void {
112+ $ pathMatcher = new PathMatcher ("api " );
113+ $ this ->pathMatcherFilter ($ pathMatcher );
114+ $ this ->setViewClass (NullView::class);
115+ $ sortNestLevelCallback = fn (string $ a , string $ b ) =>
116+ substr_count ($ a , "/ " ) > substr_count ($ b , "/ " )
117+ ? 1
118+ : (substr_count ($ a , "/ " ) < substr_count ($ b , "/ " )
119+ ? -1
120+ : 0 );
66121
122+ $ matchingLogics = $ pathMatcher ->findForUriPath (
123+ $ request ->getUri ()->getPath (),
124+ "api " ,
125+ "php "
126+ );
127+ usort ($ matchingLogics , $ sortNestLevelCallback );
128+ foreach ($ matchingLogics as $ path ) {
129+ $ this ->addToLogicAssembly ($ path );
130+ }
131+
132+ $ matchingViews = $ pathMatcher ->findForUriPath (
133+ $ request ->getUri ()->getPath (),
134+ "api " ,
135+ "xml "
136+ );
67137 foreach ($ matchingViews as $ path ) {
68138 $ this ->addToViewAssembly ($ path );
69139 }
70140 }
141+
142+ public function pathMatcherFilter (PathMatcher $ pathMatcher ):void {
143+ $ pathMatcher ->addFilter (function (string $ filePath , string $ uriPath , string $ baseDir ):bool {
144+ foreach (glob ($ baseDir . $ uriPath . ".* " ) as $ globMatch ) {
145+ $ URI_CONTAINER = pathinfo ($ uriPath , PATHINFO_DIRNAME );
146+ $ TRIM_THIS = $ baseDir . $ URI_CONTAINER ;
147+ if (str_starts_with ($ globMatch , $ TRIM_THIS )) {
148+ $ trimmed = substr ($ filePath , strlen ($ TRIM_THIS ));
149+ if (str_contains ($ trimmed , "@ " )) {
150+ return false ;
151+ }
152+ }
153+ }
154+
155+ // There are three types of matching files: Basic, Magic and Dynamic.
156+ // Basic is where a URI matches directly to a file on disk.
157+ // Magic is where a URI matches a PHP.Gt-specific file, like _common or _header.
158+ // Dynamic is where a URI matches a file/directory marked as dynamic with "@".
159+ $ basicFileMatch = new BasicFileMatch ($ filePath , $ baseDir );
160+ if ($ basicFileMatch ->matches ($ uriPath )) {
161+ return true ;
162+ }
163+
164+ $ magicFileMatch = new MagicFileMatch ($ filePath , $ baseDir );
165+ if ($ magicFileMatch ->matches ($ uriPath )) {
166+ return true ;
167+ }
168+
169+ return false ;
170+ });
171+ }
71172}
0 commit comments