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 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
{
/**
* Constructs a new Config instance with the provided options.
*
* @param array $options Configuration array containing routing settings
*/
public function __construct(
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
{
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
{
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
{
$modulesPath = $this->get('modules_path');
@ -32,16 +66,33 @@ class Config implements ArrayAccess, IteratorAggregate
return is_array($modulesPath) ? $modulesPath : [$modulesPath];
}
/**
* Gets the default routes file name.
*
* @return string Default routes file name
*/
public function getRoutesFile(): string
{
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
{
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
{
$modulesPath = $this->getModulesPath();
@ -53,31 +104,64 @@ class Config implements ArrayAccess, IteratorAggregate
return is_array($modulesPath) ? $modulesPath : [$modulesPath];
}
/**
* Converts the configuration to an array.
*
* @return array Configuration options as array
*/
public function toArray(): array
{
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
{
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
{
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
{
$this->options[$offset] = $value;
}
/**
* Unsets a configuration key (ArrayAccess interface).
*
* @param mixed $offset The configuration key to unset
*/
public function offsetUnset(mixed $offset): void
{
unset($this->options[$offset]);
}
/**
* Creates an iterator for the configuration options.
*
* @return Traversable Array iterator over configuration
*/
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->options);

View file

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

View file

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

View file

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

View file

@ -4,24 +4,54 @@ namespace Atlas\Router;
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
{
/**
* 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(
private readonly string $method,
private readonly string $path,
private readonly string|callable $handler
) {}
/**
* Gets the HTTP method of this route.
*
* @return string HTTP method
*/
public function getMethod(): string
{
return $this->method;
}
/**
* Gets the URI path of this route.
*
* @return string URI path
*/
public function getPath(): string
{
return $this->path;
}
/**
* Gets the handler for this route.
*
* @return string|callable Route handler
*/
public function getHandler(): string|callable
{
return $this->handler;

View file

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

View file

@ -2,13 +2,31 @@
namespace Atlas\Router;
/**
* Manages groupings of routes for prefix and middleware organization.
*
* @implements \IteratorAggregate<array-key, mixed>
*/
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(
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
{
$self = new self($options);
@ -16,36 +34,82 @@ class RouteGroup
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
{
$fullPath = $this->buildFullPath($path);
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
{
$fullPath = $this->buildFullPath($path);
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
{
$fullPath = $this->buildFullPath($path);
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
{
$fullPath = $this->buildFullPath($path);
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
{
$fullPath = $this->buildFullPath($path);
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
{
$prefix = $this->options['prefix'] ?? '';
@ -57,12 +121,24 @@ class RouteGroup
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
{
$this->options[$key] = $value;
return $this;
}
/**
* Gets all group options.
*
* @return array Group options configuration
*/
public function getOptions(): array
{
return $this->options;

View file

@ -3,48 +3,144 @@
namespace Atlas\Router;
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 = [];
/**
* Protected fallback handler for 404 scenarios.
*
* @var mixed mixed callable|string|null
*/
protected mixed $fallbackHandler = null;
/**
* Constructs a new Router instance with configuration.
*
* @param Config\Config $config Configuration object containing routing settings
*/
public function __construct(
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
{
$this->registerRoute('GET', $path, $handler, $name);
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
{
$this->registerRoute('POST', $path, $handler, $name);
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
{
$this->registerRoute('PUT', $path, $handler, $name);
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
{
$this->registerRoute('PATCH', $path, $handler, $name);
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
{
$this->registerRoute('DELETE', $path, $handler, $name);
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(
$method,
@ -57,17 +153,11 @@ class Router
$this->storeRoute($routeDefinition);
}
private function normalizePath(string $path): string
{
$normalized = trim($path, '/');
if (empty($normalized)) {
return '/';
}
return '/' . $normalized;
}
/**
* Stores a route definition for later matching.
*
* @param RouteDefinition $routeDefinition Route definition instance
*/
protected function storeRoute(RouteDefinition $routeDefinition): void
{
// Routes will be managed by a route collection class (to be implemented)
@ -79,11 +169,24 @@ class Router
$this->routes[] = $routeDefinition;
}
/**
* Retrieves all registered route definitions.
*
* @return iterable All route definitions
*/
public function getRoutes(): iterable
{
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
{
$method = strtoupper($request->getMethod());
@ -98,6 +201,13 @@ class Router
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
{
$method = strtoupper($request->getMethod());
@ -112,12 +222,26 @@ class Router
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
{
$this->fallbackHandler = $handler;
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
{
$routes = $this->getRoutes();
@ -140,6 +264,13 @@ class Router
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
{
foreach ($parameters as $key => $value) {
@ -150,11 +281,24 @@ class Router
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
{
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
{
$identifier = is_string($identifier) ? [$identifier] : $identifier;
@ -181,6 +325,11 @@ class Router
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
{
$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 ?? []);
}
}