diff --git a/AGENTS.md b/AGENTS.md index e80ff46b..6cd1f12b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,33 +1,430 @@ -# Repository Guidelines +# AI Agent Guidelines -## Project Structure & Module Organization -- `src/` layers PHP code (`Application`, `Domain`, `Framework`, `Infrastructure`); create new classes under the matching namespace and keep adapters in Infrastructure. -- `resources/` stores Vite-managed frontend code (`resources/js`, `resources/css`), while build artifacts belong in `public/assets` under the `public/` web root. -- `tests/` mirrors the PHP namespaces and houses Playwright suites in `tests/e2e`; share fixtures and stubs through `tests/Support`. -- Operational docs and tooling sit in `docs/`, `docker/`, and `deployment/`; update those paths whenever runtime behaviour changes. +This file provides comprehensive guidance for AI agents (like Claude Code, Cursor AI, etc.) when working with code in this repository. -## Build, Test & Development Commands -- `make up` / `make down` manage the Docker stack; add `make logs` when you need container output. -- `npm run dev` starts Vite locally, and `npm run build` produces optimized bundles into `public/assets`. -- `make test` drives the full Pest suite; use `make test-unit`, `make test-domain`, or `make test-coverage` for focused runs. -- JavaScript and browser checks run via `npm run test`, `npm run test:e2e`, and `npm run test:coverage`. -- Quality gates: `make phpstan`, `make cs` (dry run), `make cs-fix` (auto-fix), plus `npm run lint:js` and `npm run format`. +**See also**: `CLAUDE.md` for Claude-specific guidance and `docs/claude/` for detailed documentation. -## Coding Style & Naming Conventions -- Follow PSR-12 with strict types; prefer constructor injection and suffix integrations with their role (`MailerAdapter`, `RedisCache`). -- Keep PHP objects immutable unless Domain logic requires mutation; colocate interfaces with their consuming layer. -- Use 4-space indents in PHP and Prettier defaults for TypeScript; name frontend components in PascalCase and utilities in camelCase. +## Quick Reference + +- **Framework**: Custom PHP Framework (PHP 8.5+) +- **Local URL**: https://localhost (HTTPS required) +- **Docker**: Use `make up` to start containers +- **MCP Server**: `docker exec -i php php console.php mcp:server` +- **Testing**: Pest Framework (preferred), PHPUnit (legacy) +- **Code Style**: PSR-12 with php-cs-fixer + +## MCP Server Integration ?? + +**IMPORTANT**: This project has a fully functional MCP (Model Context Protocol) server that provides direct access to framework internals. + +### Quick Access Commands +```bash +# Start MCP server +docker exec -i php php console.php mcp:server + +# Test MCP server +echo '{"jsonrpc": "2.0", "method": "initialize", "params": {}}' | docker exec -i php php console.php mcp:server +``` + +### Available MCP Tools + +**Framework Analysis**: +- `analyze_routes`: Get all registered routes in the framework +- `analyze_container_bindings`: Analyze DI container bindings +- `discover_attributes`: Discover attributes by type +- `framework_health_check`: Health check of framework components +- `list_framework_modules`: List all framework modules + +**Codebase Analysis**: +- `analyze_codebase`: Intelligent codebase analysis with semantic search +- `find_controllers`: Find all controller classes +- `find_services`: Find all services, managers, repositories +- `find_value_objects`: Find all Value Object classes +- `find_initializers`: Find all DI container initializers +- `find_mcp_tools`: Find all MCP tool methods +- `find_commands`: Find all console commands +- `search_by_pattern`: Search by class name patterns + +**File System**: +- `list_directory`: List directory contents (project-scoped) +- `read_file`: Read file contents with line limits +- `find_files`: Find files by pattern + +**Git Operations**: +- `git_status`: Git status with staged/unstaged/untracked changes +- `git_diff`: Diff for staged or unstaged changes +- `git_add`: Stage files for commit +- `git_commit`: Create commit with message +- `git_generate_commit_message`: AI-generated commit messages +- `git_log`: Get commit history +- `git_branch_info`: Branch information +- `git_changed_files`: List changed files with types +- `git_stash`: Stash changes +- `git_stash_list`: Show stash list + +### MCP Resources +- `framework://config`: Framework configuration and environment + +### Configuration for Claude Desktop +```json +{ + "mcpServers": { + "custom-php-framework": { + "command": "docker", + "args": ["exec", "-i", "php", "php", "console.php", "mcp:server"], + "cwd": "/home/michael/dev/michaelschiemer" + } + } +} +``` + +## Framework Architecture Principles + +### Core Principles (MANDATORY) + +1. **No Inheritance**: Composition over inheritance - avoid `extends` completely +2. **Immutable by Design**: Objects should be immutable whenever possible +3. **Readonly Everywhere**: Classes and properties `readonly` where possible +4. **Final by Default**: Classes are `final` unless specifically designed for extension +5. **Explicit Dependency Injection**: No global state or service locators +6. **Value Objects over Primitives**: Use Value Objects instead of arrays/primitives +7. **Variadic Parameters**: Use variadic parameters instead of arrays where possible +8. **Event-Driven Architecture**: Loose coupling through domain events + +### Example: Correct Class Design + +```php +// ? CORRECT: Final readonly class with composition +final readonly class OrderProcessor +{ + public function __construct( + private readonly PaymentGateway $paymentGateway, + private readonly InventoryService $inventory, + private readonly EmailService $emailService, + private readonly Logger $logger + ) {} + + public function process(Order $order): void + { + // Business logic with injected dependencies + } +} + +// ? WRONG: Inheritance and mutable state +class OrderProcessor extends BaseProcessor +{ + private $paymentGateway; // Mutable, no DI +} +``` + +### Value Objects Usage + +```php +// ? WRONG: Primitive obsession +function createUser(string $email, array $preferences): array + +// ? CORRECT: Value Objects +function createUser(Email $email, UserPreferences $preferences): User +``` + +## Development Commands + +### PHP Development +```bash +composer install # Install PHP dependencies +composer cs # Run code style checks (dry-run) +composer cs-fix # Fix code style issues +composer reload # Dump autoloader with optimization +./vendor/bin/pest # Run PHP tests (Pest framework) +make phpstan # Run PHPStan (always use make command) +``` + +### Frontend Development +```bash +npm install # Install Node.js dependencies +npm run dev # Start Vite dev server with HTTPS +npm run build # Build production assets +npm run preview # Preview production build +npm run test # Run Jest tests +npm run deploy # Build and deploy assets to public/ +``` + +### Docker & Environment +```bash +make up # Start all Docker containers +make down # Stop all containers +make build # Build Docker images +make logs # View Docker logs +make reload # Dump autoloader and restart PHP container +make console # Run console commands in Docker PHP container +make cs # Run code style checks in Docker +make cs-fix # Fix code style in Docker +make cs-fix-file FILE=path/to/file.php # Fix code style for specific file +make fix-perms # Fix file permissions +make doctor # Check prerequisites and project health +``` + +### Console Commands +```bash +php console.php # Run console application +docker exec php php console.php # Run console in Docker +php console.php mcp:server # Start MCP server for AI integration +``` + +### Database & Migration Commands +```bash +php console.php make:migration CreateUsersTable [Domain] # Generate migration +php console.php db:migrate # Apply migrations +php console.php db:rollback [steps] # Rollback migrations +php console.php db:status # Show migration status +``` + +### Local Development Access +- **Framework URL**: https://localhost +- **HTTPS Required**: HTTPS is mandatory - HTTP requests will be rejected +- **SSL Certificates**: Automatically configured for local development +- **User Agent Required**: Always use a browser User-Agent header to avoid triggering firewall rules + +### HTTP Client Configuration +When making HTTP requests to the framework, always include a browser User-Agent: + +```bash +curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \ + https://localhost/api/endpoint +``` ## Testing Guidelines -- Add Pest specs beside the feature namespace with expressive names (`it_handles_invalid_tokens`); keep builders in `tests/Support`. -- Mock external services through `tests/__mocks__`; rely on Pest datasets for edge cases. -- Sync Playwright specs with UX changes, reuse `tests/e2e/fixtures`, and review reports via `playwright show-report`. + +### Test Organization + +**Test Directory Structure**: +``` +tests/ +??? debug/ # Debug and development scripts +? ??? test-*.php # Debug scripts for testing functionality +??? Unit/ # Unit tests mirroring src/ structure +??? Feature/ # Feature/integration tests +??? tmp/ # Temporary test files (gitignored) +??? pest.php # Pest configuration +``` + +**Important Test Rules**: +- **All test files** must be placed in `tests/` directory +- **PHP test scripts** (including non-Pest tests) belong in `tests/` directory +- **Debug scripts**: Place debug PHP scripts in `tests/debug/` directory +- **Pest preferred**: Always prefer Pest over PHPUnit for new tests +- **No mocking**: Avoid mocking - use real implementations and test data +- **Directory structure**: Tests should mirror source directory structure +- **Temporary files**: All test temporary files must be created in `tests/tmp/` or `tests/` directory +- **Never create files** in the project root directory during tests +- **Clean up**: Tests should clean up their temporary files after execution +- **Isolation**: Each test should use unique filenames to avoid conflicts + +### Test Example (Pest) +```php +it('can calculate order total with tax', function () { + $order = new Order([ + new OrderItem(new Price(1000, Currency::EUR), quantity: 2), + new OrderItem(new Price(500, Currency::EUR), quantity: 1) + ]); + + $calculator = new OrderCalculator(new TaxRate(0.19)); + $total = $calculator->calculateTotal($order); + + expect($total->cents)->toBe(2975); +}); +``` + +## Project Structure & Module Organization + +- **`src/`** layers PHP code (`Application`, `Domain`, `Framework`, `Infrastructure`) +- **`resources/`** stores Vite-managed frontend code (`resources/js`, `resources/css`) +- **`public/assets`** contains build artifacts under the `public/` web root +- **`tests/`** mirrors PHP namespaces and houses Playwright suites in `tests/e2e` +- **`docs/`**, **`docker/`**, **`deployment/`** contain operational docs and tooling + +### Directory Structure +``` +src/ +??? Application/ # Application-specific controllers and logic +??? Domain/ # Domain models and business logic +??? Framework/ # Framework core components +? ??? Mcp/ # MCP server and tools for AI integration +??? Infrastructure/ # External service integrations +``` + +## Code Style & Naming Conventions + +- **PSR-12**: Follow PSR-12 coding standards with strict types +- **Strict Types**: `declare(strict_types=1)` in all PHP files +- **Constructor Injection**: Prefer constructor injection over setters +- **Naming**: Suffix integrations with their role (`MailerAdapter`, `RedisCache`) +- **Immutability**: Keep PHP objects immutable unless Domain logic requires mutation +- **Interfaces**: Colocate interfaces with their consuming layer +- **Indentation**: 4-space indents in PHP, Prettier defaults for TypeScript +- **Frontend**: PascalCase for components, camelCase for utilities + +## Architecture Components + +### Application Bootstrap +- `src/Framework/Core/Application.php` - Main application class orchestrating request lifecycle +- `src/Framework/Core/AppBootstrapper.php` - Bootstraps application and DI container +- Uses event system for application lifecycle (ApplicationBooted, BeforeHandleRequest, etc.) + +### Dependency Injection +- `src/Framework/DI/Container.php` - DI container interface +- `src/Framework/DI/DefaultContainer.php` - Default container implementation +- Supports binding, singletons, and instance registration +- Automatic dependency resolution and method invocation + +### HTTP & Routing +- Attribute-based routing using `#[Route]` attributes +- `src/Framework/Http/Middlewares/RoutingMiddleware.php` - Core routing middleware +- Middleware chain pattern for request processing +- Support for different result types (JsonResult, ViewResult, Redirect, etc.) + +### Database System +- **EntityManager** with UnitOfWork Pattern +- **Schema Builder**: Database-agnostic migrations with fluent API +- **Connection Pooling**: Health monitoring and automatic recovery +- **Value Objects**: Use `TableName`, `ColumnName`, `IndexName`, etc. for database identifiers + +### Event System +- Event Dispatcher for Application Events +- Domain Events for Business Logic +- Extensible Event Handling Architecture + +## Exception Handling + +### Exception Types +- `FrameworkException`: Base framework exception +- `ConfigurationException`: Configuration-related errors +- `DatabaseException`: Database connection and query errors +- `ValidationException`: Input validation failures +- `AuthorizationException`: Access control violations +- `ResourceNotFoundException`: Missing resources +- `DependencyInjectionException`: DI container errors +- `SerializationException`: Serialization/deserialization issues + +### Error Handling Pattern +```php +final class UserNotFoundException extends FrameworkException +{ + public static function byId(UserId $id): self + { + return self::create( + DatabaseErrorCode::ENTITY_NOT_FOUND, + "User with ID '{$id->toString()}' not found" + )->withData([ + 'user_id' => $id->toString(), + 'search_type' => 'by_id' + ]); + } +} +``` + +## Security Guidelines + +### Authentication & Authorization +- IP-based authentication for Admin Routes +- Route protection via `#[Auth]` attributes +- Session-based authentication with roles + +### Input Validation +- Use Request Objects for validation +- Value Objects for type-safe inputs +- Never use superglobals - use `HttpRequest` instead + +### Security Best Practices +- Never expose sensitive error details in production +- Mask internal system information +- Use generic error messages for security-sensitive scenarios +- Log detailed errors server-side +- Implement rate limiting for repeated error conditions + +## Template System + +**Important**: The template system does NOT use PHP. It uses HTML template components with placeholders in curly braces `{}`. + +## Collections and Interfaces + +- Collections implement `IteratorAggregate` + `Countable` + +## Parameter Design + +- **Variadic Parameters**: Use variadic parameters instead of arrays wherever possible ## Commit & Pull Request Guidelines -- Use Conventional Commits (`fix:`, `feat:`, optional scope) to match history. -- PRs must outline the change, list executed checks, and link issues; attach screenshots for UI work or config diffs for ops updates. -- Confirm CI covers Pest, Jest, Playwright, PHPStan, and php-cs-fixer before requesting review. + +- Use **Conventional Commands** (`fix:`, `feat:`, optional scope) for commit messages +- PRs must outline the change, list executed checks, and link issues +- Attach screenshots for UI work or config diffs for ops updates +- Confirm CI covers Pest, Jest, Playwright, PHPStan, and php-cs-fixer before requesting review ## Security & Configuration Tips -- Never commit `.env` or secrets; follow `ENV_SETUP.md` and store deployment credentials in Vault. -- Run `make security-check` ahead of releases and reflect infrastructure changes in `docs/deployment/`. + +- Never commit `.env` or secrets +- Follow `ENV_SETUP.md` for environment setup +- Store deployment credentials in Vault +- Run `make security-check` ahead of releases +- Reflect infrastructure changes in `docs/deployment/` + +## Performance Guidelines + +### Database Optimization +- Use bulk operations with EntityManager +- Prevent N+1 queries with eager loading +- Use transactions for multiple operations + +### Caching Strategy +- Use framework Cache interface with Value Objects (`CacheKey`, `CacheItem`, `Duration`) +- Use `remember()` pattern for caching +- Support for cache tags for grouped invalidation + +## Configuration Management + +### Typed Configuration +Use `Environment` class with `EnvKey` enums: + +```php +final readonly class DatabaseConfig +{ + public static function fromEnvironment(Environment $env): self + { + return new self( + host: $env->get(EnvKey::DB_HOST, 'localhost'), + port: $env->getInt(EnvKey::DB_PORT, 3306), + database: $env->require(EnvKey::DB_NAME), + username: $env->require(EnvKey::DB_USER), + password: $env->require(EnvKey::DB_PASS) + ); + } +} +``` + +## Additional Documentation + +For detailed information, see: +- `docs/claude/guidelines.md` - Detailed coding guidelines +- `docs/claude/architecture.md` - Architecture documentation +- `docs/claude/development-commands.md` - Command reference +- `docs/claude/common-workflows.md` - Common development workflows +- `docs/claude/error-handling.md` - Error handling patterns +- `docs/claude/security-patterns.md` - Security patterns +- `docs/claude/mcp-integration.md` - MCP integration details +- And other files in `docs/claude/` + +## Quick Checklist for AI Agents + +Before making changes: +- [ ] Follow framework principles (no inheritance, readonly, final, immutable) +- [ ] Use Value Objects instead of primitives/arrays +- [ ] Place test files in `tests/` directory +- [ ] Use Pest for new tests (avoid mocking) +- [ ] Include browser User-Agent in HTTP requests +- [ ] Use `make` commands for Docker operations +- [ ] Run code style checks: `make cs` or `composer cs` +- [ ] Ensure HTTPS for local development +- [ ] Clean up temporary files in tests +- [ ] Use variadic parameters instead of arrays where possible +- [ ] Use MCP tools for framework analysis when available diff --git a/src/Framework/LiveComponents/ComponentRegistryInitializer.php b/src/Framework/LiveComponents/ComponentRegistryInitializer.php index 06e3b6a4..0715c87a 100644 --- a/src/Framework/LiveComponents/ComponentRegistryInitializer.php +++ b/src/Framework/LiveComponents/ComponentRegistryInitializer.php @@ -17,19 +17,19 @@ final readonly class ComponentRegistryInitializer { public function __construct( private Container $container, - private DiscoveryRegistry $discoveryRegistry + private DiscoveryRegistry $discoveryRegistry, ) { } #[Initializer] - public function __invoke(): ComponentRegistryInterface + public function __invoke( + LiveComponentRenderer $renderer, + ComponentCacheManager $cacheManager, + LiveComponentHandler $handler, + ComponentMetaDataCache $metadataCache, + NestedPerformanceTracker $performanceTracker + ): ComponentRegistryInterface { - $renderer = $this->container->get(LiveComponentRenderer::class); - $cacheManager = $this->container->get(ComponentCacheManager::class); - $handler = $this->container->get(LiveComponentHandler::class); - $metadataCache = $this->container->get(ComponentMetadataCache::class); - $performanceTracker = $this->container->get(NestedPerformanceTracker::class); - // DebugPanel is optional $debugPanel = null; @@ -39,7 +39,7 @@ final readonly class ComponentRegistryInitializer // DebugPanel not available, that's okay } - $registry = new ComponentRegistry( + return new ComponentRegistry( container: $this->container, discoveryRegistry: $this->discoveryRegistry, renderer: $renderer, @@ -49,10 +49,5 @@ final readonly class ComponentRegistryInitializer performanceTracker: $performanceTracker, debugPanel: $debugPanel ); - - // Register as interface - $this->container->singleton(ComponentRegistryInterface::class, $registry); - - return $registry; } } diff --git a/src/Framework/Logging/LoggerInitializer.php b/src/Framework/Logging/LoggerInitializer.php index 47e578f8..ffdff9ba 100644 --- a/src/Framework/Logging/LoggerInitializer.php +++ b/src/Framework/Logging/LoggerInitializer.php @@ -18,10 +18,8 @@ use App\Framework\Logging\Handlers\DockerJsonHandler; use App\Framework\Logging\Handlers\FileHandler; use App\Framework\Logging\Handlers\MultiFileHandler; use App\Framework\Logging\Handlers\NullHandler; -use App\Framework\Logging\Handlers\QueuedLogHandler; -use App\Framework\Logging\LogHandler; use App\Framework\Queue\Queue; -use App\Framework\Queue\RedisQueue; +use App\Framework\Queue\FileQueue; use App\Framework\Redis\RedisConfig; use App\Framework\Redis\RedisConnection; @@ -46,7 +44,7 @@ final readonly class LoggerInitializer $processorManager = new ProcessorManager(); $minLevel = $this->determineMinLogLevel($config); $logConfig = $this->initializeLogConfig($pathProvider); - $queue = $this->createQueue($env); + $queue = $this->createQueue($pathProvider); $handlers = $this->createHandlers($config, $env, $logConfig, $pathProvider, $minLevel, $queue); $contextManager = $container->get(LogContextManager::class); $clock = $container->get(Clock::class); @@ -118,16 +116,15 @@ final readonly class LoggerInitializer /** * Erstellt die Queue für asynchrones Logging */ - private function createQueue(Environment $env): Queue + private function createQueue(PathProvider $pathProvider): Queue { - $redisConfig = RedisConfig::fromEnvironment($env); - $redisConnection = new RedisConnection($redisConfig, 'queue'); + #$redisConfig = RedisConfig::fromEnvironment($env); + #$redisConnection = new RedisConnection($redisConfig, 'queue'); - return new RedisQueue($redisConnection, 'commands'); + #return new RedisQueue($redisConnection, 'commands'); - // Alternativ: FileQueue mit aufgelöstem Pfad - // $queuePath = $pathProvider->resolvePath('storage/queue'); - // return new FileQueue($queuePath); + $queuePath = $pathProvider->resolvePath('storage/queue'); + return new FileQueue($queuePath); } /**