diff --git a/src/SegmentRouterTrait.php b/src/SegmentRouterTrait.php index 40162a8..3445639 100644 --- a/src/SegmentRouterTrait.php +++ b/src/SegmentRouterTrait.php @@ -25,6 +25,19 @@ trait SegmentRouterTrait ); } + /** + * Adds a handler to the routing node based on the provided segments and method. + * + * This method recursively traverses the segments array, creating nested nodes as needed, + * and assigns the handler to the final node corresponding to the method. + * + * @param array $node The current routing node. + * @param array $segments The segments of the route to add. + * @param string $method The HTTP method (e.g., 'GET', 'POST') for which the handler is being added. + * @param callable $handler The handler to be executed for the given route and method. + * + * @return void + */ private static function addNode(array &$node, array $segments, string $method, callable $handler): void { // Base case: if no more segments, add the handler @@ -63,32 +76,79 @@ trait SegmentRouterTrait // node is a reference to our current location in the node tree $node = $routes; - // if the URI is the root, and the method is defined, return the handler - if ($uri === '/' && isset($node[$method]) && array_key_exists($method, $node)) { + // if the URI is the root, and the method is defined, return the handler + if (self::isRootUri($uri, $node, $method)) { return ['code' => 200, 'handler' => $node[$method], 'params' => null]; } // params will hold any dynamic segments we find $params = []; - foreach (explode('/', trim($uri, '/')) as $segment) { - if (isset($node[$segment])) { - $node = $node[$segment]; - } elseif (isset($node[':x'])) { - $params[] = $segment; - $node = $node[':x']; - } else { - return ['code' => 404, 'handler' => null, 'params' => []]; - } + $segments = explode('/', trim($uri, '/')); + if (!self::traverseSegments($node, $segments, $params)) { + return ['code' => 404, 'handler' => null, 'params' => []]; } - // if we found a handler for the method, return it and any params. if not, return a 405 - if (array_key_exists($method, $node)) { - return ['code' => 200, 'handler' => $node[$method], 'params' => $params]; - } - - return ['code' => 405, 'handler' => null, 'params' => []]; + return self::getHandler($node, $method, $params) ?? ['code' => 405, 'handler' => null, 'params' => []]; } + /** + * Traverses the given segments and updates the node and params accordingly. + * + * This method iterates over the segments and attempts to match each segment + * with the corresponding node. If a segment matches, it continues to the next + * segment. If a segment does not match but a wildcard ':x' is found, the segment + * is added to the params array and traversal continues. If no match is found, + * the traversal stops and returns false. + * + * @param array $node The current node to traverse. + * @param array $segments The segments to traverse through the node. + * @param array $params The parameters collected during traversal. + * @return bool Returns true if traversal is successful, otherwise false. + */ + private static function traverseSegments(array &$node, array $segments, array &$params): bool + { + return ($node = array_reduce($segments, function ($carry, $segment) use (&$params) { + if (isset($carry[$segment])) { + return $carry[$segment]; + } + if (isset($carry[':x'])) { + $params[] = $segment; + return $carry[':x']; + } + return false; + }, $node)) !== false; + } + + /** + * Checks if the given URI is the root URI and if the specified method exists in the node array. + * + * @param string $uri The URI to check. + * @param array &$node The node array to check against. + * @param string $method The HTTP method to check for in the node array. + * @return bool Returns true if the URI is the root URI and the method exists in the node array, false otherwise. + */ + public static function isRootUri(string $uri, array &$node, string $method): bool + { + return ($uri === '/' && isset($node[$method]) && array_key_exists($method, $node)); + } + + // if we found a handler for the method, return it and any params. if not, return a 405 + + /** + * Retrieves the handler for a given node and method. + * + * @param array $node The node containing possible handlers. + * @param string $method The method to look up in the node. + * @param array $params The parameters to pass to the handler. + * @return array|null An array containing the handler information if found, or null if not found. + */ + public static function getHandler($node, $method, $params): ?array + { + return array_key_exists($method, $node) + ? ['code' => 200, 'handler' => $node[$method], 'params' => $params] + : null; + } + /** * Clear all routes from the router. */