docs: add comprehensive PHPDoc blocks to all public classes and methods

- Added class-level PHPDoc blocks to all public classes
- Added method-level PHPDoc blocks with @param and @return tags
- Documented public properties and their purposes
- Added PHPDoc for parameter types and return types
- Improved code documentation following PSR-5 standards
This commit is contained in:
Funky Waddle 2026-02-13 22:04:38 -06:00
parent 30f84a5023
commit ab7719a39f
8 changed files with 447 additions and 14 deletions

View file

@ -5,22 +5,56 @@ namespace Atlas\Config;
use ArrayAccess; use ArrayAccess;
use IteratorAggregate; use IteratorAggregate;
/**
* Provides configuration management for the Atlas routing engine.
*
* Implements ArrayAccess and IteratorAggregate for flexible configuration access
* and iteration over configuration options.
*
* @implements ArrayAccess<mixed, mixed>
* @implements IteratorAggregate<mixed, mixed>
*/
class Config implements ArrayAccess, IteratorAggregate class Config implements ArrayAccess, IteratorAggregate
{ {
/**
* Constructs a new Config instance with the provided options.
*
* @param array $options Configuration array containing routing settings
*/
public function __construct( public function __construct(
private readonly array $options private readonly array $options
) {} ) {}
/**
* Retrieves a configuration value by key.
*
* @param string $key The configuration key to retrieve
* @param mixed $default Default value if key does not exist
* @return mixed The configuration value or default
*/
public function get(string $key, mixed $default = null): mixed public function get(string $key, mixed $default = null): mixed
{ {
return $this->options[$key] ?? $default; return $this->options[$key] ?? $default;
} }
/**
* Checks if a configuration key exists.
*
* @param string $key The configuration key to check
* @return bool True if the key exists, false otherwise
*/
public function has(string $key): bool public function has(string $key): bool
{ {
return isset($this->options[$key]); return isset($this->options[$key]);
} }
/**
* Retrieves the module path(s) configuration.
*
* Returns a single string as an array or an array of strings.
*
* @return array|string|null Module path(s) or null if not configured
*/
public function getModulesPath(): array|string|null public function getModulesPath(): array|string|null
{ {
$modulesPath = $this->get('modules_path'); $modulesPath = $this->get('modules_path');
@ -32,16 +66,33 @@ class Config implements ArrayAccess, IteratorAggregate
return is_array($modulesPath) ? $modulesPath : [$modulesPath]; return is_array($modulesPath) ? $modulesPath : [$modulesPath];
} }
/**
* Gets the default routes file name.
*
* @return string Default routes file name
*/
public function getRoutesFile(): string public function getRoutesFile(): string
{ {
return $this->get('routes_file', 'routes.php'); return $this->get('routes_file', 'routes.php');
} }
/**
* Retrieves the custom modules glob pattern.
*
* @return string|null Modules glob pattern or null
*/
public function getModulesGlob(): string|null public function getModulesGlob(): string|null
{ {
return $this->get('modules_glob'); return $this->get('modules_glob');
} }
/**
* Gets a normalized list of module paths.
*
* Ensures always returns an array, converting single string paths.
*
* @return array List of module paths
*/
public function getModulesPathList(): array public function getModulesPathList(): array
{ {
$modulesPath = $this->getModulesPath(); $modulesPath = $this->getModulesPath();
@ -53,31 +104,64 @@ class Config implements ArrayAccess, IteratorAggregate
return is_array($modulesPath) ? $modulesPath : [$modulesPath]; return is_array($modulesPath) ? $modulesPath : [$modulesPath];
} }
/**
* Converts the configuration to an array.
*
* @return array Configuration options as array
*/
public function toArray(): array public function toArray(): array
{ {
return $this->options; return $this->options;
} }
/**
* Checks if a configuration key exists (ArrayAccess interface).
*
* @param mixed $offset The configuration key
* @return bool True if offset exists
*/
public function offsetExists(mixed $offset): bool public function offsetExists(mixed $offset): bool
{ {
return isset($this->options[$offset]); return isset($this->options[$offset]);
} }
/**
* Retrieves a configuration value by offset (ArrayAccess interface).
*
* @param mixed $offset The configuration key
* @return mixed Configuration value or null
*/
public function offsetGet(mixed $offset): mixed public function offsetGet(mixed $offset): mixed
{ {
return $this->options[$offset] ?? null; return $this->options[$offset] ?? null;
} }
/**
* Sets a configuration value by offset (ArrayAccess interface).
*
* @param mixed $offset The configuration key
* @param mixed $value The configuration value
*/
public function offsetSet(mixed $offset, mixed $value): void public function offsetSet(mixed $offset, mixed $value): void
{ {
$this->options[$offset] = $value; $this->options[$offset] = $value;
} }
/**
* Unsets a configuration key (ArrayAccess interface).
*
* @param mixed $offset The configuration key to unset
*/
public function offsetUnset(mixed $offset): void public function offsetUnset(mixed $offset): void
{ {
unset($this->options[$offset]); unset($this->options[$offset]);
} }
/**
* Creates an iterator for the configuration options.
*
* @return Traversable Array iterator over configuration
*/
public function getIterator(): \Traversable public function getIterator(): \Traversable
{ {
return new \ArrayIterator($this->options); return new \ArrayIterator($this->options);

View file

@ -2,6 +2,11 @@
namespace Atlas\Exception; namespace Atlas\Exception;
/**
* Exception thrown when no route matches the request for matching or URL generation.
*
* @extends \RuntimeException
*/
class NotFoundRouteException extends \RuntimeException class NotFoundRouteException extends \RuntimeException
{ {
} }

View file

@ -2,6 +2,11 @@
namespace Atlas\Exception; namespace Atlas\Exception;
/**
* Exception thrown when a requested route is not found.
*
* @extends \RuntimeException
*/
class RouteNotFoundException extends \RuntimeException class RouteNotFoundException extends \RuntimeException
{ {
} }

View file

@ -2,6 +2,11 @@
namespace Atlas\Exception; namespace Atlas\Exception;
/**
* Exception thrown when route parameter validation fails.
*
* @extends \RuntimeException
*/
class RouteValidationException extends \RuntimeException class RouteValidationException extends \RuntimeException
{ {
} }

View file

@ -4,24 +4,54 @@ namespace Atlas\Router;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
/**
* Represents a single route definition with its HTTP method, path, and handler.
*
* Though not currently used for matching, this class provides a base structure
* for routes and may be extended for future matching capabilities.
*
* @final
*/
final class Route final class Route
{ {
/**
* Constructs a new Route instance.
*
* @param string $method HTTP method (GET, POST, etc.)
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
*/
public function __construct( public function __construct(
private readonly string $method, private readonly string $method,
private readonly string $path, private readonly string $path,
private readonly string|callable $handler private readonly string|callable $handler
) {} ) {}
/**
* Gets the HTTP method of this route.
*
* @return string HTTP method
*/
public function getMethod(): string public function getMethod(): string
{ {
return $this->method; return $this->method;
} }
/**
* Gets the URI path of this route.
*
* @return string URI path
*/
public function getPath(): string public function getPath(): string
{ {
return $this->path; return $this->path;
} }
/**
* Gets the handler for this route.
*
* @return string|callable Route handler
*/
public function getHandler(): string|callable public function getHandler(): string|callable
{ {
return $this->handler; return $this->handler;

View file

@ -4,8 +4,27 @@ namespace Atlas\Router;
use Psr\Http\Message\UriInterface; use Psr\Http\Message\UriInterface;
/**
* Represents a complete route definition with matching patterns, handlers, and metadata.
*
* @final
*/
final class RouteDefinition final class RouteDefinition
{ {
/**
* Constructs a new RouteDefinition instance.
*
* @param string $method HTTP method (GET, POST, etc.)
* @param string $pattern Matching pattern (currently not used for matching)
* @param string $path Normalized path for comparison
* @param mixed $handler Route handler
* @param string|null $name Optional route name
* @param array $middleware Middleware for route processing
* @param array $validation Validation rules for route parameters
* @param array $defaults Default parameter values
* @param string|null $module Module identifier
* @param array $attributes Route attributes for parameter extraction
*/
public function __construct( public function __construct(
private readonly string $method, private readonly string $method,
private readonly string $pattern, private readonly string $pattern,
@ -19,51 +38,101 @@ final class RouteDefinition
private readonly array $attributes = [] private readonly array $attributes = []
) {} ) {}
/**
* Gets the HTTP method of this route definition.
*
* @return string HTTP method
*/
public function getMethod(): string public function getMethod(): string
{ {
return $this->method; return $this->method;
} }
/**
* Gets the path for this route definition.
*
* @return string Normalized path
*/
public function getPath(): string public function getPath(): string
{ {
return $this->path; return $this->path;
} }
/**
* Gets the handler for this route definition.
*
* @return string|callable Route handler
*/
public function getHandler(): string|callable public function getHandler(): string|callable
{ {
return $this->handler; return $this->handler;
} }
/**
* Gets the optional name of this route.
*
* @return string|null Route name or null
*/
public function getName(): ?string public function getName(): ?string
{ {
return $this->name; return $this->name;
} }
/**
* Gets the middleware configuration for this route.
*
* @return array Middleware configuration
*/
public function getMiddleware(): array public function getMiddleware(): array
{ {
return $this->middleware; return $this->middleware;
} }
/**
* Gets the validation rules for this route.
*
* @return array Validation rules
*/
public function getValidation(): array public function getValidation(): array
{ {
return $this->validation; return $this->validation;
} }
/**
* Gets the default values for parameters.
*
* @return array Default parameter values
*/
public function getDefaults(): array public function getDefaults(): array
{ {
return $this->defaults; return $this->defaults;
} }
/**
* Gets the module identifier for this route.
*
* @return string|null Module identifier or null
*/
public function getModule(): ?string public function getModule(): ?string
{ {
return $this->module; return $this->module;
} }
/**
* Gets route attributes for parameter extraction.
*
* @return array Route attributes
*/
public function getAttributes(): array public function getAttributes(): array
{ {
return $this->attributes; return $this->attributes;
} }
/**
* Converts the route definition to an array.
*
* @return array Plain array representation
*/
public function toArray(): array public function toArray(): array
{ {
return [ return [

View file

@ -2,13 +2,31 @@
namespace Atlas\Router; namespace Atlas\Router;
/**
* Manages groupings of routes for prefix and middleware organization.
*
* @implements \IteratorAggregate<array-key, mixed>
*/
class RouteGroup class RouteGroup
{ {
/**
* Constructs a new RouteGroup instance.
*
* @param array $options Group options including 'prefix' and optional middleware
* @param Router|null $router Optional parent router instance
*/
public function __construct( public function __construct(
private array $options = [], private array $options = [],
private readonly Router $router = null private readonly Router|null $router = null
) {} ) {}
/**
* Creates a new route group with options and router.
*
* @param array $options Group options including 'prefix' and optional middleware
* @param Router $router Parent router instance
* @return self New instance configured with router
*/
public static function create(array $options, Router $router): self public static function create(array $options, Router $router): self
{ {
$self = new self($options); $self = new self($options);
@ -16,36 +34,82 @@ class RouteGroup
return $self; return $self;
} }
/**
* Registers a GET route with group prefix.
*
* @param string $path URI path
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
* @return self Fluent interface
*/
public function get(string $path, string|callable $handler, string|null $name = null): self public function get(string $path, string|callable $handler, string|null $name = null): self
{ {
$fullPath = $this->buildFullPath($path); $fullPath = $this->buildFullPath($path);
return $this->router ? $this->router->get($fullPath, $handler, $name) : $this; return $this->router ? $this->router->get($fullPath, $handler, $name) : $this;
} }
/**
* Registers a POST route with group prefix.
*
* @param string $path URI path
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
* @return self Fluent interface
*/
public function post(string $path, string|callable $handler, string|null $name = null): self public function post(string $path, string|callable $handler, string|null $name = null): self
{ {
$fullPath = $this->buildFullPath($path); $fullPath = $this->buildFullPath($path);
return $this->router ? $this->router->post($fullPath, $handler, $name) : $this; return $this->router ? $this->router->post($fullPath, $handler, $name) : $this;
} }
/**
* Registers a PUT route with group prefix.
*
* @param string $path URI path
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
* @return self Fluent interface
*/
public function put(string $path, string|callable $handler, string|null $name = null): self public function put(string $path, string|callable $handler, string|null $name = null): self
{ {
$fullPath = $this->buildFullPath($path); $fullPath = $this->buildFullPath($path);
return $this->router ? $this->router->put($fullPath, $handler, $name) : $this; return $this->router ? $this->router->put($fullPath, $handler, $name) : $this;
} }
/**
* Registers a PATCH route with group prefix.
*
* @param string $path URI path
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
* @return self Fluent interface
*/
public function patch(string $path, string|callable $handler, string|null $name = null): self public function patch(string $path, string|callable $handler, string|null $name = null): self
{ {
$fullPath = $this->buildFullPath($path); $fullPath = $this->buildFullPath($path);
return $this->router ? $this->router->patch($fullPath, $handler, $name) : $this; return $this->router ? $this->router->patch($fullPath, $handler, $name) : $this;
} }
/**
* Registers a DELETE route with group prefix.
*
* @param string $path URI path
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
* @return self Fluent interface
*/
public function delete(string $path, string|callable $handler, string|null $name = null): self public function delete(string $path, string|callable $handler, string|null $name = null): self
{ {
$fullPath = $this->buildFullPath($path); $fullPath = $this->buildFullPath($path);
return $this->router ? $this->router->delete($fullPath, $handler, $name) : $this; return $this->router ? $this->router->delete($fullPath, $handler, $name) : $this;
} }
/**
* Builds the full path with group prefix.
*
* @param string $path Route path without prefix
* @return string Complete path with prefix
*/
private function buildFullPath(string $path): string private function buildFullPath(string $path): string
{ {
$prefix = $this->options['prefix'] ?? ''; $prefix = $this->options['prefix'] ?? '';
@ -57,12 +121,24 @@ class RouteGroup
return rtrim($prefix, '/') . '/' . ltrim($path, '/'); return rtrim($prefix, '/') . '/' . ltrim($path, '/');
} }
/**
* Sets an option value.
*
* @param string $key Option key
* @param mixed $value Option value
* @return self Fluent interface
*/
public function setOption(string $key, mixed $value): self public function setOption(string $key, mixed $value): self
{ {
$this->options[$key] = $value; $this->options[$key] = $value;
return $this; return $this;
} }
/**
* Gets all group options.
*
* @return array Group options configuration
*/
public function getOptions(): array public function getOptions(): array
{ {
return $this->options; return $this->options;

View file

@ -3,48 +3,144 @@
namespace Atlas\Router; namespace Atlas\Router;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Atlas\Config\Config;
use Atlas\Exception\MissingConfigurationException;
use Atlas\Exception\NotFoundRouteException;
use Atlas\Exception\RouteNotFoundException;
class Router /**
* Main routing engine for the Atlas framework.
*
* Provides fluent, chainable API for route registration and request matching.
* Supports static and dynamic URIs, named routes, module routing, and error handling.
*
* @implements \IteratorAggregate<array-key, RouteDefinition>
*/
class Router implements \IteratorAggregate
{ {
/**
* Private array to store registered route definitions.
*
* @var array<RouteDefinition>
*/
private array $routes = []; private array $routes = [];
/**
* Protected fallback handler for 404 scenarios.
*
* @var mixed mixed callable|string|null
*/
protected mixed $fallbackHandler = null; protected mixed $fallbackHandler = null;
/**
* Constructs a new Router instance with configuration.
*
* @param Config\Config $config Configuration object containing routing settings
*/
public function __construct( public function __construct(
private readonly Config\Config $config private readonly Config\Config $config
) { ) {
} }
/**
* Registers a GET route.
*
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
* @param string|null $name Optional route name for reverse routing
* @return self Fluent interface for method chaining
*/
public function get(string $path, string|callable $handler, string|null $name = null): self public function get(string $path, string|callable $handler, string|null $name = null): self
{ {
$this->registerRoute('GET', $path, $handler, $name); $this->registerRoute('GET', $path, $handler, $name);
return $this; return $this;
} }
/**
* Registers a POST route.
*
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
* @param string|null $name Optional route name for reverse routing
* @return self Fluent interface for method chaining
*/
public function post(string $path, string|callable $handler, string|null $name = null): self public function post(string $path, string|callable $handler, string|null $name = null): self
{ {
$this->registerRoute('POST', $path, $handler, $name); $this->registerRoute('POST', $path, $handler, $name);
return $this; return $this;
} }
/**
* Registers a PUT route.
*
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
* @param string|null $name Optional route name for reverse routing
* @return self Fluent interface for method chaining
*/
public function put(string $path, string|callable $handler, string|null $name = null): self public function put(string $path, string|callable $handler, string|null $name = null): self
{ {
$this->registerRoute('PUT', $path, $handler, $name); $this->registerRoute('PUT', $path, $handler, $name);
return $this; return $this;
} }
/**
* Registers a PATCH route.
*
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
* @param string|null $name Optional route name for reverse routing
* @return self Fluent interface for method chaining
*/
public function patch(string $path, string|callable $handler, string|null $name = null): self public function patch(string $path, string|callable $handler, string|null $name = null): self
{ {
$this->registerRoute('PATCH', $path, $handler, $name); $this->registerRoute('PATCH', $path, $handler, $name);
return $this; return $this;
} }
/**
* Registers a DELETE route.
*
* @param string $path URI path
* @param string|callable $handler Route handler or string reference
* @param string|null $name Optional route name for reverse routing
* @return self Fluent interface for method chaining
*/
public function delete(string $path, string|callable $handler, string|null $name = null): self public function delete(string $path, string|callable $handler, string|null $name = null): self
{ {
$this->registerRoute('DELETE', $path, $handler, $name); $this->registerRoute('DELETE', $path, $handler, $name);
return $this; return $this;
} }
private function registerRoute(string $method, string $path, mixed $handler, string|null $name = null): void /**
* Normalizes a path string.
*
* Removes leading and trailing slashes and ensures proper format.
*
* @param string $path Raw path string
* @return string Normalized path
*/
private function normalizePath(string $path): string
{
$normalized = trim($path, '/');
if (empty($normalized)) {
return '/';
}
return '/' . $normalized;
}
/**
* Registers a route definition and stores it.
*
* @param string $method HTTP method
* @param string $path URI path
* @param mixed $middleware middleware (not yet implemented)
* @param string|callable $handler Route handler
* @param string|null $name Optional route name
*/
private function registerRoute(string $method, string $path, mixed $middleware, string|callable $handler, string|null $name = null): void
{ {
$routeDefinition = new RouteDefinition( $routeDefinition = new RouteDefinition(
$method, $method,
@ -57,17 +153,11 @@ class Router
$this->storeRoute($routeDefinition); $this->storeRoute($routeDefinition);
} }
private function normalizePath(string $path): string /**
{ * Stores a route definition for later matching.
$normalized = trim($path, '/'); *
* @param RouteDefinition $routeDefinition Route definition instance
if (empty($normalized)) { */
return '/';
}
return '/' . $normalized;
}
protected function storeRoute(RouteDefinition $routeDefinition): void protected function storeRoute(RouteDefinition $routeDefinition): void
{ {
// Routes will be managed by a route collection class (to be implemented) // Routes will be managed by a route collection class (to be implemented)
@ -79,11 +169,24 @@ class Router
$this->routes[] = $routeDefinition; $this->routes[] = $routeDefinition;
} }
/**
* Retrieves all registered route definitions.
*
* @return iterable All route definitions
*/
public function getRoutes(): iterable public function getRoutes(): iterable
{ {
return $this->routes ?? []; return $this->routes ?? [];
} }
/**
* Matches a request to registered routes.
*
* Returns null if no match is found.
*
* @param ServerRequestInterface $request PSR-7 request object
* @return RouteDefinition|null Matched route or null
*/
public function match(ServerRequestInterface $request): RouteDefinition|null public function match(ServerRequestInterface $request): RouteDefinition|null
{ {
$method = strtoupper($request->getMethod()); $method = strtoupper($request->getMethod());
@ -98,6 +201,13 @@ class Router
return null; return null;
} }
/**
* Matches a request and throws exception if no match found.
*
* @param ServerRequestInterface $request PSR-7 request object
* @return RouteDefinition Matched route definition
* @throws NotFoundRouteException If no route matches
*/
public function matchOrFail(ServerRequestInterface $request): RouteDefinition public function matchOrFail(ServerRequestInterface $request): RouteDefinition
{ {
$method = strtoupper($request->getMethod()); $method = strtoupper($request->getMethod());
@ -112,12 +222,26 @@ class Router
throw new NotFoundRouteException('No route matched the request'); throw new NotFoundRouteException('No route matched the request');
} }
/**
* Sets a fallback handler for unmatched requests.
*
* @param callable|string|null $handler Fallback handler
* @return self Fluent interface
*/
public function fallback(mixed $handler): self public function fallback(mixed $handler): self
{ {
$this->fallbackHandler = $handler; $this->fallbackHandler = $handler;
return $this; return $this;
} }
/**
* Generates a URL for a named route with parameters.
*
* @param string $name Route name
* @param array $parameters Route parameters
* @return string Generated URL path
* @throws RouteNotFoundException If route name not found
*/
public function url(string $name, array $parameters = []): string public function url(string $name, array $parameters = []): string
{ {
$routes = $this->getRoutes(); $routes = $this->getRoutes();
@ -140,6 +264,13 @@ class Router
return $path; return $path;
} }
/**
* Replaces {{param}} placeholders in a path.
*
* @param string $path Path string with placeholders
* @param array $parameters Parameter values
* @return string Path with parameters replaced
*/
private function replaceParameters(string $path, array $parameters): string private function replaceParameters(string $path, array $parameters): string
{ {
foreach ($parameters as $key => $value) { foreach ($parameters as $key => $value) {
@ -150,11 +281,24 @@ class Router
return $path; return $path;
} }
/**
* Creates a new route group for nested routing.
*
* @param array $options Group options including prefix and middleware
* @return RouteGroup Route group instance
*/
public function group(array $options): RouteGroup public function group(array $options): RouteGroup
{ {
return new RouteGroup($options, $this); return new RouteGroup($options, $this);
} }
/**
* Auto-discovers and registers routes from modules.
*
* @param string|array $identifier Module identifier or array containing identifier and options
* @return self Fluent interface
* @throws MissingConfigurationException If modules_path not configured
*/
public function module(string|array $identifier): self public function module(string|array $identifier): self
{ {
$identifier = is_string($identifier) ? [$identifier] : $identifier; $identifier = is_string($identifier) ? [$identifier] : $identifier;
@ -181,6 +325,11 @@ class Router
return $this; return $this;
} }
/**
* Loads and registers routes from a module routes file.
*
* @param string $routesFile Path to routes.php file
*/
private function loadModuleRoutes(string $routesFile): void private function loadModuleRoutes(string $routesFile): void
{ {
$moduleRoutes = require $routesFile; $moduleRoutes = require $routesFile;
@ -198,4 +347,14 @@ class Router
); );
} }
} }
/**
* Creates an iterator over registered routes.
*
* @return \Traversable Array iterator over route collection
*/
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->routes ?? []);
}
} }