@@ -45,6 +45,10 @@ class Lifecycle implements MiddlewareInterface {
4545 private Throwable $ throwable ;
4646
4747 public function start ():void {
48+ // Before we start, we check if the current URI should be redirected. If it
49+ // should, we won't go any further into the lifecycle.
50+ $ this ->handleRedirects ();
51+
4852// The first thing that's done within the WebEngine lifecycle is start a timer.
4953// This timer is only used again at the end of the call, when finish() is
5054// called - at which point the entire duration of the request is logged out (and
@@ -237,4 +241,65 @@ public function finish(
237241
238242 exit ;
239243 }
244+
245+ private function handleRedirects ():void {
246+ $ redirectFiles = [
247+ "\t" => "redirects.tsv " ,
248+ ", " => "redirects.csv " ,
249+ ];
250+ foreach ($ redirectFiles as $ separatorCharacter => $ fileName ) {
251+ if (!is_file ($ fileName )) {
252+ continue ;
253+ }
254+
255+ Log::debug ("Checking redirect file: $ fileName " );
256+ $ currentUri = $ _SERVER ["REQUEST_URI " ];
257+
258+ $ lines = file ($ fileName );
259+ usort ($ lines , function (string $ lineA , string $ lineB ):int {
260+ $ lineARegex = str_starts_with ($ lineA , "~ " );
261+ $ lineBRegex = str_starts_with ($ lineB , "~ " );
262+ if ($ lineARegex && !$ lineBRegex ) {
263+ return -1 ;
264+ }
265+
266+ if (!$ lineARegex && $ lineBRegex ) {
267+ return 1 ;
268+ }
269+
270+ return 0 ;
271+ });
272+
273+ foreach ($ lines as $ line ) {
274+ $ row = str_getcsv ($ line , $ separatorCharacter );
275+ if (!$ row || !$ row [0 ]) {
276+ continue ;
277+ }
278+
279+ $ matchingUri = $ row [0 ];
280+ $ redirectUri = $ row [1 ];
281+ $ responseCode = $ row [2 ] ?? 302 ;
282+
283+ $ match = $ currentUri === $ matchingUri ;
284+ if ($ matchingUri [0 ] === "~ " ) {
285+ $ matchingUri = substr ($ matchingUri , 1 );
286+ if (preg_match ("~ $ matchingUri~ " , $ currentUri , $ matches )) {
287+ $ match = true ;
288+ $ matchIndex = 1 ;
289+ while (str_contains ($ redirectUri , '$ ' . $ matchIndex )) {
290+ $ redirectUri = str_replace ('$ ' . $ matchIndex , $ matches [$ matchIndex ], $ redirectUri );
291+ }
292+ }
293+ }
294+
295+ if ($ match ) {
296+ Log::notice ("Redirecting: $ currentUri -> $ redirectUri ( $ responseCode) " );
297+ header ("Location: $ redirectUri " , true , $ responseCode );
298+ exit ;
299+ }
300+ }
301+ return ;
302+ }
303+ }
304+
240305}
0 commit comments