Previously, the Router loaded configuration from raw PHP array files:
// OLD WAY - NOT PSR-4 compliant ❌
$this->apiConfig = require __DIR__ . '/../config/api.php';
$this->authEnabled = $this->apiConfig['auth_enabled'] ?? true;Issues:
- ❌ Not object-oriented
- ❌ No type safety
- ❌ No IDE autocomplete
- ❌ Difficult to test
- ❌ Inconsistent with PSR-4 architecture
We now use PSR-4 compliant Config classes that provide:
- ✅ Object-oriented design
- ✅ Type safety with return types
- ✅ Full IDE autocomplete
- ✅ Easy testing and mocking
- ✅ Getter/setter methods
- ✅ Validation logic
- ✅ Backward compatibility
Location: src/Config/ApiConfig.php
Purpose: Manages all API settings (authentication, RBAC, rate limiting, logging, monitoring)
Usage:
use App\Config\ApiConfig;
// Load from config/api.php (backward compatible)
$config = ApiConfig::fromFile(__DIR__ . '/../config/api.php');
// Or create programmatically
$config = new ApiConfig([
'auth_enabled' => true,
'auth_method' => 'jwt',
'jwt_secret' => 'my-secret-key',
]);
// Type-safe getters
$isAuthEnabled = $config->isAuthEnabled(); // bool
$authMethod = $config->getAuthMethod(); // string
$roles = $config->getRoles(); // array
// Dynamic setters
$config->setAuthMethod('apikey');
$config->addApiKey('new-key-123');
$config->assignUserRole('john', 'admin');Key Methods:
// Authentication
isAuthEnabled(): bool
getAuthMethod(): string
getApiKeys(): array
getJwtSecret(): string
useDatabaseAuth(): bool
// RBAC
getRoles(): array
getUserRoles(): array
getUserRole(string $username): ?string
assignUserRole(string $username, string $role): void
// Rate Limiting
getRateLimitConfig(): array
// Logging & Monitoring
getLoggingConfig(): array
getMonitoringConfig(): array
isMonitoringEnabled(): bool
// Backward compatibility
toArray(): arrayLocation: src/Config/CacheConfig.php
Purpose: Manages cache settings (driver, TTL, excluded tables)
Usage:
use App\Config\CacheConfig;
// Load from config/cache.php (backward compatible)
$config = CacheConfig::fromFile(__DIR__ . '/../config/cache.php');
// Or create programmatically
$config = new CacheConfig([
'enabled' => true,
'driver' => 'file',
'ttl' => 300,
'table_ttl' => [
'users' => 300,
'products' => 600,
],
]);
// Type-safe getters
$isEnabled = $config->isEnabled(); // bool
$driver = $config->getDriver(); // string
$ttl = $config->getTableTtl('users'); // int
$shouldCache = $config->shouldCacheTable('logs'); // bool
// Dynamic configuration
$config->enable();
$config->setDriver('redis');
$config->setTableTtl('products', 1800);
$config->excludeTable('sessions');Key Methods:
// Core settings
isEnabled(): bool
getDriver(): string
getDefaultTtl(): int
getCachePath(): string
// Table-specific
getTableTtl(string $table): int
shouldCacheTable(string $table): bool
setTableTtl(string $table, int $ttl): void
excludeTable(string $table): void
includeTable(string $table): void
// Vary by parameters
getVaryBy(): array
// Backward compatibility
toArray(): arrayOLD WAY:
// ❌ Array-based, no type safety
$this->apiConfig = require __DIR__ . '/../config/api.php';
$this->authEnabled = $this->apiConfig['auth_enabled'] ?? true;
$this->rbac = new Rbac(
$this->apiConfig['roles'] ?? [],
$this->apiConfig['user_roles'] ?? []
);NEW WAY:
// ✅ OOP, type-safe, PSR-4 compliant
$this->apiConfig = ApiConfig::fromFile(__DIR__ . '/../config/api.php');
$this->authEnabled = $this->apiConfig->isAuthEnabled();
$this->rbac = new Rbac(
$this->apiConfig->getRoles(),
$this->apiConfig->getUserRoles()
);Config classes support both approaches:
// config/api.php
return [
'auth_enabled' => true,
'auth_method' => 'jwt',
'jwt_secret' => 'secret',
];$config = ApiConfig::fromFile(__DIR__ . '/config/api.php');$array = $config->toArray();
// Same structure as config/api.php| Feature | Old (Arrays) | New (Config Classes) |
|---|---|---|
| Type safety | ❌ No | ✅ Yes |
| IDE autocomplete | ❌ No | ✅ Yes |
| PSR-4 compliant | ❌ No | ✅ Yes |
| Testable | ❌ Difficult | ✅ Easy |
| Validation | ❌ Manual | ✅ Built-in |
| Documentation | ❌ Comments | ✅ PHPDoc |
| OOP | ❌ No | ✅ Yes |
| Backward compatible | ✅ Yes | ✅ Yes |
use App\Config\ApiConfig;
use App\Config\CacheConfig;
// Test ApiConfig
$config = new ApiConfig(['auth_enabled' => false]);
assert($config->isAuthEnabled() === false);
$config->enableAuth();
assert($config->isAuthEnabled() === true);
$config->setAuthMethod('jwt');
assert($config->getAuthMethod() === 'jwt');
// Test CacheConfig
$config = new CacheConfig(['enabled' => true, 'driver' => 'file']);
assert($config->isEnabled() === true);
assert($config->getDriver() === 'file');
$config->setTableTtl('users', 600);
assert($config->getTableTtl('users') === 600);
$config->excludeTable('sessions');
assert($config->shouldCacheTable('sessions') === false);Before:
$this->apiConfig = require __DIR__ . '/../config/api.php';After:
$this->apiConfig = ApiConfig::fromFile(__DIR__ . '/../config/api.php');Before:
$isEnabled = $this->apiConfig['auth_enabled'] ?? true;
$roles = $this->apiConfig['roles'] ?? [];After:
$isEnabled = $this->apiConfig->isAuthEnabled();
$roles = $this->apiConfig->getRoles();use App\Config\ApiConfig;
use App\Config\CacheConfig;Planned config classes:
- DatabaseConfig - Database connection settings
- SecurityConfig - CORS, CSP, rate limiting
- ValidationConfig - Input validation rules
- OpenApiConfig - OpenAPI specification settings
- WebhookConfig - Webhook delivery settings
- Always use Config classes in new code
- Keep config/*.php files for user configuration
- Use
fromFile()to load from existing config files - Use
toArray()for backward compatibility with old code - Add type hints to all getter methods
- Validate values in setter methods
- Document all config options with PHPDoc
use App\Router;
use App\Database;
use App\Authenticator;
use App\Config\ApiConfig;
use App\Config\CacheConfig;
// Initialize database
$db = new Database([
'dsn' => 'mysql:host=localhost;dbname=mydb',
'username' => 'root',
'password' => 'secret',
]);
// Load API configuration (PSR-4 way)
$apiConfig = ApiConfig::fromFile(__DIR__ . '/config/api.php');
// Initialize authenticator with config
$auth = new Authenticator([
'auth_method' => $apiConfig->getAuthMethod(),
'jwt_secret' => $apiConfig->getJwtSecret(),
'api_keys' => $apiConfig->getApiKeys(),
]);
// Initialize router (automatically loads configs)
$router = new Router($db, $auth);
// Route the request
$router->route($_GET);The new PSR-4 Config classes provide:
- 🎯 Type safety - No more array key typos
- 🚀 Better DX - IDE autocomplete everywhere
- 🧪 Testability - Easy to mock and test
- 📚 Documentation - PHPDoc for every method
- 🔒 Validation - Built-in value checking
- ♻️ Backward compatible - Works with existing config files
This is the correct way to handle configuration in modern PHP applications.