ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

MVC Route Matching with Wildcards

PHP

Goal -- WPM

Ready
Exercise Algorithm Area
1<?php
2
3function matchRoute(string $uri, array $routes): ?array
4{
5$bestMatch = null;
6$params = [];
7
8foreach ($routes as $pattern => $handler) {
9if (is_array($handler)) {
10$handler = $handler['handler'];
11}
12
13$routeSegments = explode('/', trim($pattern, '/'));
14$uriSegments = explode('/', trim($uri, '/'));
15
16if (count($routeSegments) > count($uriSegments) && !str_contains($pattern, '?')) {
17continue; // Route pattern is longer than URI and no optional segments
18}
19
20$currentParams = [];
21$match = true;
22$uriIndex = 0;
23
24foreach ($routeSegments as $segment) {
25if ($uriIndex >= count($uriSegments)) {
26// If we've run out of URI segments but still have route segments, check for optionality
27if (str_ends_with($segment, '?')) {
28continue; // This segment is optional and not present
29} else {
30$match = false;
31break;
32}
33}
34
35if (str_starts_with($segment, ':')) {
36// Dynamic segment
37$paramName = substr($segment, 1);
38if (str_ends_with($paramName, '?')) {
39$paramName = rtrim($paramName, '?');
40if ($uriSegments[$uriIndex] === '') {
41// Optional segment is empty, skip it
42$uriIndex++;
43continue;
44}
45}
46$currentParams[$paramName] = $uriSegments[$uriIndex];
47} elseif (str_ends_with($segment, '?')) {
48// Optional static segment
49$staticSegment = rtrim($segment, '?');
50if ($uriSegments[$uriIndex] !== $staticSegment) {
51$uriIndex++;
52continue;
53}
54} elseif ($uriSegments[$uriIndex] !== $segment) {
55// Static segment mismatch
56$match = false;
57break;
58}
59$uriIndex++;
60}
61
62// Ensure all URI segments were consumed if the route pattern wasn't longer
63if ($match && $uriIndex < count($uriSegments) && !str_contains($pattern, '?')) {
64$match = false;
65}
66
67if ($match) {
68// Prioritize longer matches or more specific matches (fewer wildcards)
69// This is a simplified prioritization; a real engine might be more complex.
70if ($bestMatch === null || strlen($pattern) > strlen($bestMatch)) {
71$bestMatch = $pattern;
72$params = $currentParams;
73}
74}
75}
76
77if ($bestMatch !== null) {
78return ['handler' => $routes[$bestMatch], 'params' => $params];
79}
80
81return null;
82}
Algorithm description viewbox

MVC Route Matching with Wildcards

Algorithm description:

This PHP function implements a basic route matching engine for a web application. It takes a requested URI and a set of defined routes, attempting to find the best matching route. It supports static path segments, dynamic segments (like ':id'), and optional segments (like ':page?'). This is fundamental for MVC frameworks to direct incoming requests to the correct controller action.

Algorithm explanation:

The `matchRoute` function iterates through a list of defined routes, comparing each route pattern against the incoming URI. It splits both the URI and the pattern into segments. For dynamic segments starting with ':', it captures the corresponding URI segment as a parameter. Optional segments, marked with '?', are handled by checking if they are present or if their static part matches. The function prioritizes longer route patterns as a simple heuristic for specificity. If a match is found, it returns the handler and extracted parameters; otherwise, it returns null. The time complexity is O(R * S), where R is the number of routes and S is the average number of segments in a route pattern, as each segment is compared. Space complexity is O(P) for storing parameters, where P is the maximum number of parameters in a route.

Pseudocode:

function matchRoute(uri, routes):
  bestMatch = null
  params = {}

  for each pattern, handler in routes:
    routeSegments = split uri by '/'
    uriSegments = split pattern by '/'

    if length(routeSegments) > length(uriSegments) and pattern does not contain '?':
      continue

    currentParams = {}
    match = true
    uriIndex = 0

    for each segment in routeSegments:
      if uriIndex >= length(uriSegments):
        if segment ends with '?':
          continue
        else:
          match = false
          break

      if segment starts with ':':
        paramName = segment without leading ':'
        if paramName ends with '?':
          paramName = paramName without trailing '?'
          if uriSegments[uriIndex] is empty:
            uriIndex++
            continue
        currentParams[paramName] = uriSegments[uriIndex]
      else if segment ends with '?':
        staticSegment = segment without trailing '?'
        if uriSegments[uriIndex] is not staticSegment:
          uriIndex++
          continue
      else if uriSegments[uriIndex] is not segment:
        match = false
        break
      uriIndex++

    if match and uriIndex < length(uriSegments) and pattern does not contain '?':
      match = false

    if match:
      if bestMatch is null or length(pattern) > length(bestMatch):
        bestMatch = pattern
        params = currentParams

  if bestMatch is not null:
    return { handler: routes[bestMatch], params: params }
  else:
    return null