Merge 215564868f into 50d6611e37
This commit is contained in:
commit
0f7fbf5029
8 changed files with 2881 additions and 0 deletions
320
.claude/skills/foundation/create-module.md
Normal file
320
.claude/skills/foundation/create-module.md
Normal file
|
|
@ -0,0 +1,320 @@
|
||||||
|
# Create Module
|
||||||
|
|
||||||
|
Create a new module in the Foundation framework following Domain-Driven Design principles and clean architecture.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "create a new module"
|
||||||
|
- User asks to "add a module"
|
||||||
|
- User asks to "scaffold a module"
|
||||||
|
- User wants to add new business functionality
|
||||||
|
- User mentions "new feature module"
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. **Create Module Directory Structure**
|
||||||
|
|
||||||
|
Create the base directory and three-layer architecture:
|
||||||
|
```bash
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Domain
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Application
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Infrastructure/Api
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Infrastructure/Web
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Infrastructure/Database/Commands
|
||||||
|
mkdir -p src/Modules/{ModuleName}/Infrastructure/Database/Queries
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create Domain Layer** (Pure business logic)
|
||||||
|
|
||||||
|
Create domain entities, value objects, and repository interfaces:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Domain/{Entity}.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Domain;
|
||||||
|
|
||||||
|
final class {Entity}
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private int $id,
|
||||||
|
// Add properties
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getId(): int { return $this->id; }
|
||||||
|
// Add getters and business methods
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Domain/{Repository}Interface.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Domain;
|
||||||
|
|
||||||
|
interface {Repository}Interface
|
||||||
|
{
|
||||||
|
public function findById(int $id): ?{Entity};
|
||||||
|
public function save({Entity} $entity): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create Application Layer** (Use cases)
|
||||||
|
|
||||||
|
Create application services that orchestrate domain logic:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Application/{UseCase}/{UseCase}.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Application\{UseCase};
|
||||||
|
|
||||||
|
use Foundation\Modules\{ModuleName}\Domain\{Repository}Interface;
|
||||||
|
|
||||||
|
final class {UseCase}
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private {Repository}Interface $repository
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function execute(/* parameters */): mixed
|
||||||
|
{
|
||||||
|
// Use case logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Create Infrastructure Layer** (External implementations)
|
||||||
|
|
||||||
|
**a) Create Repository Implementation:**
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Infrastructure/Database/{Repository}.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Infrastructure\Database;
|
||||||
|
|
||||||
|
use Foundation\Modules\{ModuleName}\Domain\{Entity};
|
||||||
|
use Foundation\Modules\{ModuleName}\Domain\{Repository}Interface;
|
||||||
|
|
||||||
|
final class {Repository} implements {Repository}Interface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private FetchByIdQuery $fetchById,
|
||||||
|
private SaveCommand $save
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function findById(int $id): ?{Entity}
|
||||||
|
{
|
||||||
|
return $this->fetchById->execute($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save({Entity} $entity): void
|
||||||
|
{
|
||||||
|
$this->save->execute($entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**b) Create Database Commands/Queries:**
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Infrastructure/Database/Queries/FetchByIdQuery.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Infrastructure\Database\Queries;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
use Foundation\Modules\{ModuleName}\Domain\{Entity};
|
||||||
|
|
||||||
|
final class FetchByIdQuery
|
||||||
|
{
|
||||||
|
public function __construct(private PDO $pdo) {}
|
||||||
|
|
||||||
|
public function execute(int $id): ?{Entity}
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare("SELECT * FROM {table} WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
return $data ? new {Entity}(/* map data */) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**c) Create Controller:**
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/Infrastructure/Api/{Controller}.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName}\Infrastructure\Api;
|
||||||
|
|
||||||
|
use Foundation\Core\Infrastructure\ControllerInterface;
|
||||||
|
use Foundation\Core\Routing\Attributes\Get;
|
||||||
|
use Foundation\Core\Routing\Attributes\Post;
|
||||||
|
use Foundation\Core\Routing\Attributes\Group;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
#[Group('/api/{module-name}')]
|
||||||
|
final class {Controller} implements ControllerInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private {UseCase} $useCase
|
||||||
|
) {}
|
||||||
|
|
||||||
|
#[Get('')]
|
||||||
|
public function index(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseInterface $response
|
||||||
|
): ResponseInterface {
|
||||||
|
// Controller logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Create Service Provider**
|
||||||
|
|
||||||
|
Create the service provider to register all module services:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// src/Modules/{ModuleName}/{ModuleName}ServiceProvider.php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Foundation\Modules\{ModuleName};
|
||||||
|
|
||||||
|
use Foundation\Core\Application\ServiceProvider\ServiceProvider;
|
||||||
|
use Foundation\Core\DependencyInjection\InflectableContainer;
|
||||||
|
use Foundation\Modules\{ModuleName}\Domain\{Repository}Interface;
|
||||||
|
use Foundation\Modules\{ModuleName}\Infrastructure\Database\{Repository};
|
||||||
|
use Foundation\Modules\{ModuleName}\Infrastructure\Database\Commands\*;
|
||||||
|
use Foundation\Modules\{ModuleName}\Infrastructure\Database\Queries\*;
|
||||||
|
use Foundation\Modules\{ModuleName}\Application\{UseCase}\*;
|
||||||
|
use Foundation\Modules\{ModuleName}\Infrastructure\Api\{Controller};
|
||||||
|
|
||||||
|
final class {ModuleName}ServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function register(InflectableContainer $container): void
|
||||||
|
{
|
||||||
|
// Register queries
|
||||||
|
$container->register(FetchByIdQuery::class, [\PDO::class]);
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
$container->register(SaveCommand::class, [\PDO::class]);
|
||||||
|
|
||||||
|
// Register repository
|
||||||
|
$container->bind(
|
||||||
|
{Repository}Interface::class,
|
||||||
|
{Repository}::class,
|
||||||
|
[FetchByIdQuery::class, SaveCommand::class]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register use cases
|
||||||
|
$container->register(
|
||||||
|
{UseCase}::class,
|
||||||
|
[{Repository}Interface::class]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register controllers (routes auto-discovered via attributes)
|
||||||
|
$container->register(
|
||||||
|
{Controller}::class,
|
||||||
|
[{UseCase}::class]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Refresh Autoloader**
|
||||||
|
```bash
|
||||||
|
make dump-autoload
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
1. **Check Module is Auto-Discovered**
|
||||||
|
|
||||||
|
The `ModuleLoader` bootstrapper automatically discovers service providers in `src/Modules/*/`.
|
||||||
|
No additional registration needed.
|
||||||
|
|
||||||
|
2. **Verify Routes are Registered**
|
||||||
|
|
||||||
|
Access the controller endpoint to verify routes are working:
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/api/{module-name}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check Service Container**
|
||||||
|
|
||||||
|
Verify services are properly registered by accessing them in a controller or test.
|
||||||
|
|
||||||
|
## Layer Dependency Rules
|
||||||
|
|
||||||
|
**IMPORTANT**: Always follow these strict dependency rules:
|
||||||
|
|
||||||
|
- **Domain Layer**: NO dependencies on other layers
|
||||||
|
- Only pure PHP and domain concepts
|
||||||
|
- No framework dependencies
|
||||||
|
- No database or HTTP concerns
|
||||||
|
|
||||||
|
- **Application Layer**: Can depend on Domain only
|
||||||
|
- Orchestrates domain logic
|
||||||
|
- Defines use cases
|
||||||
|
- No framework dependencies
|
||||||
|
|
||||||
|
- **Infrastructure Layer**: Can depend on Application and itself
|
||||||
|
- Implements repository interfaces
|
||||||
|
- Contains controllers, commands, queries
|
||||||
|
- Has framework dependencies
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Module Not Auto-Discovered
|
||||||
|
|
||||||
|
**Problem**: Service provider not loaded automatically.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify service provider is in: `src/Modules/{ModuleName}/{ModuleName}ServiceProvider.php`
|
||||||
|
2. Verify it extends `Foundation\Core\Application\ServiceProvider\ServiceProvider`
|
||||||
|
3. Check namespace matches directory structure
|
||||||
|
4. Run `make dump-autoload`
|
||||||
|
|
||||||
|
### Routes Not Working
|
||||||
|
|
||||||
|
**Problem**: Controller routes not accessible.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify controller implements `Foundation\Core\Infrastructure\ControllerInterface`
|
||||||
|
2. Check route attributes are properly imported and used
|
||||||
|
3. Verify controller is registered in service provider
|
||||||
|
4. Check for route conflicts with existing routes
|
||||||
|
|
||||||
|
### Autoloading Issues
|
||||||
|
|
||||||
|
**Problem**: Class not found errors.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
make dump-autoload
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify namespace matches directory structure following PSR-4:
|
||||||
|
- `Foundation\Modules\{ModuleName}` → `src/Modules/{ModuleName}`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See existing modules for reference:
|
||||||
|
- `src/Modules/WelcomeScreen/` - Complete example module
|
||||||
|
|
||||||
|
## Quick Checklist
|
||||||
|
|
||||||
|
- [ ] Created Domain layer (entities, interfaces)
|
||||||
|
- [ ] Created Application layer (use cases)
|
||||||
|
- [ ] Created Infrastructure layer (controllers, repositories, commands, queries)
|
||||||
|
- [ ] Created Service Provider
|
||||||
|
- [ ] Service provider extends `ServiceProvider`
|
||||||
|
- [ ] Service provider in correct location
|
||||||
|
- [ ] Controllers implement `ControllerInterface`
|
||||||
|
- [ ] Routes use attributes (#[Get], #[Post], etc.)
|
||||||
|
- [ ] Repository interfaces in Domain layer
|
||||||
|
- [ ] Repository implementations in Infrastructure layer
|
||||||
|
- [ ] Followed layer dependency rules
|
||||||
|
- [ ] Ran `make dump-autoload`
|
||||||
|
- [ ] Verified routes are accessible
|
||||||
364
.claude/skills/foundation/database-operations.md
Normal file
364
.claude/skills/foundation/database-operations.md
Normal file
|
|
@ -0,0 +1,364 @@
|
||||||
|
# Database Operations
|
||||||
|
|
||||||
|
Perform common database operations in the Foundation framework Docker environment.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "import database schema"
|
||||||
|
- User asks to "backup database"
|
||||||
|
- User asks to "restore database"
|
||||||
|
- User asks to "access database"
|
||||||
|
- User asks to "export database"
|
||||||
|
- User mentions "database migration" or "schema setup"
|
||||||
|
- Setting up project for first time
|
||||||
|
- Need to reset database to clean state
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Import Database Schema
|
||||||
|
|
||||||
|
1. **Import SQL Schema File**
|
||||||
|
```bash
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
This imports the database schema from `database/schema.sql` into the `foundation` database.
|
||||||
|
|
||||||
|
### Access Database CLI
|
||||||
|
|
||||||
|
2. **Access MySQL Command Line**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll be in the MySQL CLI. Common commands:
|
||||||
|
```sql
|
||||||
|
-- Show all tables
|
||||||
|
SHOW TABLES;
|
||||||
|
|
||||||
|
-- Describe table structure
|
||||||
|
DESCRIBE table_name;
|
||||||
|
|
||||||
|
-- Query data
|
||||||
|
SELECT * FROM table_name;
|
||||||
|
|
||||||
|
-- Exit
|
||||||
|
exit;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Database
|
||||||
|
|
||||||
|
3. **Export Database to File**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation > backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates `backup.sql` in current directory with complete database dump.
|
||||||
|
|
||||||
|
4. **Export with Timestamp**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation > backup-$(date +%Y%m%d-%H%M%S).sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates timestamped backup file (e.g., `backup-20240115-143022.sql`).
|
||||||
|
|
||||||
|
### Restore Database
|
||||||
|
|
||||||
|
5. **Restore from Backup**
|
||||||
|
```bash
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Restores database from backup file.
|
||||||
|
|
||||||
|
### Reset Database
|
||||||
|
|
||||||
|
6. **Drop and Recreate Database**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password -e "DROP DATABASE IF EXISTS foundation; CREATE DATABASE foundation;"
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Completely resets database to clean state from schema file.
|
||||||
|
|
||||||
|
### Access via phpMyAdmin
|
||||||
|
|
||||||
|
7. **Use phpMyAdmin GUI**
|
||||||
|
- Open browser: http://localhost:8080
|
||||||
|
- Server: `db`
|
||||||
|
- Username: `foundation_user`
|
||||||
|
- Password: `foundation_password`
|
||||||
|
- Database: `foundation`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
### Verify Import Success
|
||||||
|
|
||||||
|
1. **Check Tables Exist**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "SHOW TABLES;"
|
||||||
|
```
|
||||||
|
|
||||||
|
Should list all tables from the schema.
|
||||||
|
|
||||||
|
2. **Verify Table Structure**
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "DESCRIBE users;"
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows structure of `users` table (adjust table name as needed).
|
||||||
|
|
||||||
|
### Verify Backup Success
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check file was created and has content
|
||||||
|
ls -lh backup.sql
|
||||||
|
head -n 20 backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Should show file with SQL dump content.
|
||||||
|
|
||||||
|
## Common Database Tasks
|
||||||
|
|
||||||
|
### Create New Table
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation << EOF
|
||||||
|
CREATE TABLE example (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run SQL File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < path/to/file.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Execute Single SQL Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "SELECT COUNT(*) FROM users;"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Specific Table
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation table_name > table_backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Copy Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation > temp_backup.sql
|
||||||
|
|
||||||
|
# Restore to new database
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password -e "CREATE DATABASE foundation_copy;"
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation_copy < temp_backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Connection Refused
|
||||||
|
|
||||||
|
**Problem**: Can't connect to database.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Check database container is running:
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
2. Verify database service is healthy:
|
||||||
|
```bash
|
||||||
|
docker compose logs db
|
||||||
|
```
|
||||||
|
3. Restart database:
|
||||||
|
```bash
|
||||||
|
docker compose restart db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access Denied
|
||||||
|
|
||||||
|
**Problem**: Authentication failed with error "Access denied for user".
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify credentials in command match Docker environment:
|
||||||
|
- Username: `foundation_user`
|
||||||
|
- Password: `foundation_password`
|
||||||
|
- Database: `foundation`
|
||||||
|
2. Check environment variables in `docker-compose.yml`
|
||||||
|
3. If changed, rebuild containers:
|
||||||
|
```bash
|
||||||
|
make down
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schema Import Errors
|
||||||
|
|
||||||
|
**Problem**: Import fails with SQL syntax errors.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Check SQL file syntax is valid
|
||||||
|
2. Verify file encoding (should be UTF-8)
|
||||||
|
3. Check for incompatible MySQL version syntax
|
||||||
|
4. Try importing via phpMyAdmin to see detailed error
|
||||||
|
|
||||||
|
### Table Already Exists
|
||||||
|
|
||||||
|
**Problem**: Import fails because tables already exist.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
|
||||||
|
**Option 1**: Drop database first:
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password -e "DROP DATABASE foundation; CREATE DATABASE foundation;"
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2**: Use `IF NOT EXISTS` in schema:
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS users (...);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup File Too Large
|
||||||
|
|
||||||
|
**Problem**: Backup file is very large and slow.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. **Compress during backup**:
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation | gzip > backup.sql.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Restore from compressed**:
|
||||||
|
```bash
|
||||||
|
gunzip < backup.sql.gz | docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Locked
|
||||||
|
|
||||||
|
**Problem**: Operations timeout or fail with "database is locked".
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Check for long-running queries:
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "SHOW PROCESSLIST;"
|
||||||
|
```
|
||||||
|
2. Kill problematic query (get ID from processlist):
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "KILL <process_id>;"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Operations
|
||||||
|
|
||||||
|
### Database Performance Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show database size
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "
|
||||||
|
SELECT
|
||||||
|
table_name AS 'Table',
|
||||||
|
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
|
||||||
|
FROM information_schema.TABLES
|
||||||
|
WHERE table_schema = 'foundation'
|
||||||
|
ORDER BY (data_length + index_length) DESC;
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Structure Only (No Data)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password --no-data foundation > schema-only.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export Data Only (No Structure)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password --no-create-info foundation > data-only.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Migration File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Export current state as migration
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation > database/migrations/$(date +%Y%m%d_%H%M%S)_migration.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Connection Parameters
|
||||||
|
|
||||||
|
**Environment Variables** (in Docker):
|
||||||
|
```env
|
||||||
|
DB_HOST=db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=foundation
|
||||||
|
DB_USERNAME=foundation_user
|
||||||
|
DB_PASSWORD=foundation_password
|
||||||
|
```
|
||||||
|
|
||||||
|
**Connection from Host** (outside Docker):
|
||||||
|
```bash
|
||||||
|
mysql -h 127.0.0.1 -P 3306 -u foundation_user -pfoundation_password foundation
|
||||||
|
```
|
||||||
|
|
||||||
|
**PDO Connection String** (in PHP):
|
||||||
|
```php
|
||||||
|
$dsn = "mysql:host=db;port=3306;dbname=foundation;charset=utf8mb4";
|
||||||
|
$pdo = new PDO($dsn, 'foundation_user', 'foundation_password');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Import schema
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
|
||||||
|
# Backup database
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation > backup.sql
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < backup.sql
|
||||||
|
|
||||||
|
# Access MySQL CLI
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation
|
||||||
|
|
||||||
|
# Execute SQL command
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password foundation -e "SHOW TABLES;"
|
||||||
|
|
||||||
|
# Reset database
|
||||||
|
docker compose exec db mysql -u foundation_user -pfoundation_password -e "DROP DATABASE foundation; CREATE DATABASE foundation;"
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
|
||||||
|
# Compressed backup
|
||||||
|
docker compose exec db mysqldump -u foundation_user -pfoundation_password foundation | gzip > backup.sql.gz
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs db
|
||||||
|
|
||||||
|
# Restart database
|
||||||
|
docker compose restart db
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Regular Backups** - Backup before major changes
|
||||||
|
2. **Timestamped Backups** - Use timestamps in backup filenames
|
||||||
|
3. **Test Restores** - Periodically verify backups can be restored
|
||||||
|
4. **Version Control Schema** - Keep schema files in git
|
||||||
|
5. **Use Migrations** - For production, use migration system
|
||||||
|
6. **Separate Test Data** - Keep test data separate from schema
|
||||||
|
7. **Document Changes** - Comment complex SQL in schema files
|
||||||
|
8. **Review Before Import** - Check schema files before importing
|
||||||
|
9. **Monitor Size** - Watch database growth over time
|
||||||
|
10. **Use Transactions** - Wrap multiple operations in transactions
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
1. **Don't Commit Credentials** - Never commit database passwords
|
||||||
|
2. **Change Default Password** - Use strong passwords in production
|
||||||
|
3. **Limit Privileges** - Use least privilege principle for users
|
||||||
|
4. **Encrypt Backups** - Encrypt backup files if they contain sensitive data
|
||||||
|
5. **Secure phpMyAdmin** - Disable or secure phpMyAdmin in production
|
||||||
433
.claude/skills/foundation/debug-setup.md
Normal file
433
.claude/skills/foundation/debug-setup.md
Normal file
|
|
@ -0,0 +1,433 @@
|
||||||
|
# Debug Setup
|
||||||
|
|
||||||
|
Configure Xdebug for debugging PHP applications in the Foundation framework Docker environment.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "set up debugging"
|
||||||
|
- User asks to "configure xdebug"
|
||||||
|
- User asks "how to debug"
|
||||||
|
- User wants to "use breakpoints"
|
||||||
|
- User mentions "step through code"
|
||||||
|
- Setting up IDE for first time
|
||||||
|
- Debugging not working
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker environment running (`make up`)
|
||||||
|
- IDE installed (PHPStorm or VS Code)
|
||||||
|
- Xdebug already installed in Docker container (included by default)
|
||||||
|
|
||||||
|
## Steps for PHPStorm
|
||||||
|
|
||||||
|
### 1. Configure PHP Interpreter
|
||||||
|
|
||||||
|
**a) Open Interpreter Settings**
|
||||||
|
- Go to `Settings/Preferences > PHP`
|
||||||
|
- Click `...` next to CLI Interpreter
|
||||||
|
|
||||||
|
**b) Add Docker Compose Interpreter**
|
||||||
|
- Click `+` → `From Docker, Vagrant, VM, WSL...`
|
||||||
|
- Select `Docker Compose`
|
||||||
|
- Configuration file: `docker-compose.yml`
|
||||||
|
- Service: `app`
|
||||||
|
- Click `OK`
|
||||||
|
|
||||||
|
**c) Verify Configuration**
|
||||||
|
- PHP version should show: `8.1.x`
|
||||||
|
- Xdebug should be listed under Configuration file
|
||||||
|
|
||||||
|
### 2. Configure Debug Settings
|
||||||
|
|
||||||
|
**a) Open Debug Settings**
|
||||||
|
- Go to `Settings/Preferences > PHP > Debug`
|
||||||
|
|
||||||
|
**b) Configure Xdebug**
|
||||||
|
- Xdebug port: `9003`
|
||||||
|
- Check: `Can accept external connections`
|
||||||
|
- Uncheck: `Break at first line in PHP scripts` (optional)
|
||||||
|
- Max simultaneous connections: `5`
|
||||||
|
|
||||||
|
### 3. Configure Server Mapping
|
||||||
|
|
||||||
|
**a) Open Server Settings**
|
||||||
|
- Go to `Settings/Preferences > PHP > Servers`
|
||||||
|
|
||||||
|
**b) Add New Server**
|
||||||
|
- Click `+` to add new server
|
||||||
|
- Name: `foundation-docker`
|
||||||
|
- Host: `localhost`
|
||||||
|
- Port: `8000`
|
||||||
|
- Debugger: `Xdebug`
|
||||||
|
- Check: `Use path mappings`
|
||||||
|
|
||||||
|
**c) Configure Path Mappings**
|
||||||
|
- Project root: `/var/www/html`
|
||||||
|
- Example:
|
||||||
|
```
|
||||||
|
/home/user/foundation → /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Start Debugging Session
|
||||||
|
|
||||||
|
**a) Set Breakpoint**
|
||||||
|
- Click in the gutter next to line number in any PHP file
|
||||||
|
- Red dot should appear
|
||||||
|
|
||||||
|
**b) Start Listening**
|
||||||
|
- Click `Start Listening for PHP Debug Connections` (phone icon) in toolbar
|
||||||
|
- Icon should turn green
|
||||||
|
|
||||||
|
**c) Trigger Request**
|
||||||
|
- Open browser: http://localhost:8000
|
||||||
|
- PHPStorm should pause at breakpoint
|
||||||
|
|
||||||
|
## Steps for VS Code
|
||||||
|
|
||||||
|
### 1. Install PHP Debug Extension
|
||||||
|
|
||||||
|
**a) Install Extension**
|
||||||
|
- Open Extensions panel (`Ctrl+Shift+X` or `Cmd+Shift+X`)
|
||||||
|
- Search for: `PHP Debug`
|
||||||
|
- Install: `PHP Debug` by Xdebug
|
||||||
|
|
||||||
|
### 2. Configure Launch Configuration
|
||||||
|
|
||||||
|
**a) Create launch.json**
|
||||||
|
- Click `Run > Add Configuration...`
|
||||||
|
- Select `PHP` from dropdown
|
||||||
|
|
||||||
|
**b) Edit launch.json**
|
||||||
|
|
||||||
|
Create `.vscode/launch.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Listen for Xdebug",
|
||||||
|
"type": "php",
|
||||||
|
"request": "launch",
|
||||||
|
"port": 9003,
|
||||||
|
"pathMappings": {
|
||||||
|
"/var/www/html": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"log": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Start Debugging Session
|
||||||
|
|
||||||
|
**a) Set Breakpoint**
|
||||||
|
- Click in the gutter next to line number
|
||||||
|
- Red dot should appear
|
||||||
|
|
||||||
|
**b) Start Debug Session**
|
||||||
|
- Press `F5` or click `Run > Start Debugging`
|
||||||
|
- Select `Listen for Xdebug`
|
||||||
|
|
||||||
|
**c) Trigger Request**
|
||||||
|
- Open browser: http://localhost:8000
|
||||||
|
- VS Code should pause at breakpoint
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
### Test Debug Connection
|
||||||
|
|
||||||
|
1. **Set Breakpoint**
|
||||||
|
```php
|
||||||
|
// In any controller, e.g., src/Modules/WelcomeScreen/Infrastructure/Web/WelcomeController.php
|
||||||
|
public function index(): ResponseInterface
|
||||||
|
{
|
||||||
|
$test = "debug test"; // Set breakpoint on this line
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start Listening** (in IDE)
|
||||||
|
|
||||||
|
3. **Access Application**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000
|
||||||
|
# or open in browser
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Verify Pause**
|
||||||
|
- IDE should pause execution at breakpoint
|
||||||
|
- Variables panel should show local variables
|
||||||
|
- Call stack should show current execution path
|
||||||
|
|
||||||
|
### Debug Controls
|
||||||
|
|
||||||
|
Once paused at breakpoint:
|
||||||
|
|
||||||
|
**PHPStorm**:
|
||||||
|
- `F8` - Step Over
|
||||||
|
- `F7` - Step Into
|
||||||
|
- `Shift+F8` - Step Out
|
||||||
|
- `F9` - Resume Program
|
||||||
|
- `Ctrl+F8` - Toggle Breakpoint
|
||||||
|
|
||||||
|
**VS Code**:
|
||||||
|
- `F10` - Step Over
|
||||||
|
- `F11` - Step Into
|
||||||
|
- `Shift+F11` - Step Out
|
||||||
|
- `F5` - Continue
|
||||||
|
- `F9` - Toggle Breakpoint
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Breakpoint Not Hit
|
||||||
|
|
||||||
|
**Problem**: Debugger doesn't pause at breakpoint.
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
# Check Xdebug is loaded
|
||||||
|
docker compose exec app php -v
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# with Xdebug v3.x.x
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Verify Xdebug Configuration**
|
||||||
|
```bash
|
||||||
|
docker compose exec app php -i | grep xdebug
|
||||||
|
```
|
||||||
|
|
||||||
|
Should show:
|
||||||
|
- `xdebug.mode = debug`
|
||||||
|
- `xdebug.start_with_request = yes`
|
||||||
|
- `xdebug.client_host = host.docker.internal`
|
||||||
|
- `xdebug.client_port = 9003`
|
||||||
|
|
||||||
|
**B) Check IDE is Listening**
|
||||||
|
- Verify debug listener is active (green phone icon in PHPStorm)
|
||||||
|
- Verify debug session started in VS Code
|
||||||
|
|
||||||
|
**C) Verify Path Mappings**
|
||||||
|
- Project root must map to `/var/www/html`
|
||||||
|
- Check for typos in paths
|
||||||
|
|
||||||
|
### Connection Refused
|
||||||
|
|
||||||
|
**Problem**: IDE shows "Connection refused" or timeout.
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Check Port Configuration**
|
||||||
|
- IDE listening on port 9003
|
||||||
|
- Xdebug connecting to port 9003
|
||||||
|
- No firewall blocking port 9003
|
||||||
|
|
||||||
|
**B) Verify Docker Network**
|
||||||
|
```bash
|
||||||
|
# Test connection from container
|
||||||
|
docker compose exec app ping -c 3 host.docker.internal
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Check Xdebug Configuration**
|
||||||
|
```bash
|
||||||
|
docker compose exec app cat /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path Mapping Issues
|
||||||
|
|
||||||
|
**Problem**: Debugger can't find source files.
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Verify Mapping**
|
||||||
|
- Local path: `/home/user/foundation`
|
||||||
|
- Container path: `/var/www/html`
|
||||||
|
- Must match exactly
|
||||||
|
|
||||||
|
**B) Check Working Directory**
|
||||||
|
```bash
|
||||||
|
docker compose exec app pwd
|
||||||
|
# Should output: /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Restart IDE and Docker**
|
||||||
|
```bash
|
||||||
|
# Restart Docker
|
||||||
|
make down
|
||||||
|
make up
|
||||||
|
|
||||||
|
# Restart IDE completely
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Debug Sessions
|
||||||
|
|
||||||
|
**Problem**: Multiple IDE instances or debug sessions conflict.
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Close Other IDEs**
|
||||||
|
- Only one IDE should listen for Xdebug at a time
|
||||||
|
|
||||||
|
**B) Change Port in One IDE**
|
||||||
|
- Use different port (e.g., 9004) in second IDE
|
||||||
|
- Update Xdebug config to match
|
||||||
|
|
||||||
|
### Browser Extension Conflicts
|
||||||
|
|
||||||
|
**Problem**: Debug session starts unexpectedly or not at all.
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Install Xdebug Helper**
|
||||||
|
- Chrome: Install "Xdebug Helper" extension
|
||||||
|
- Firefox: Install "Xdebug Helper" addon
|
||||||
|
|
||||||
|
**B) Configure Extension**
|
||||||
|
- Set IDE key to `PHPSTORM` or `VSCODE`
|
||||||
|
- Enable debug mode in extension icon
|
||||||
|
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
### Xdebug Configuration File
|
||||||
|
|
||||||
|
Location in container: `/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini`
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```ini
|
||||||
|
zend_extension=xdebug.so
|
||||||
|
xdebug.mode=debug
|
||||||
|
xdebug.start_with_request=yes
|
||||||
|
xdebug.client_host=host.docker.internal
|
||||||
|
xdebug.client_port=9003
|
||||||
|
xdebug.log=/tmp/xdebug.log
|
||||||
|
xdebug.log_level=0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable Xdebug Logging
|
||||||
|
|
||||||
|
For troubleshooting:
|
||||||
|
```bash
|
||||||
|
# Edit Xdebug config
|
||||||
|
docker compose exec app bash -c "echo 'xdebug.log=/tmp/xdebug.log' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini"
|
||||||
|
docker compose exec app bash -c "echo 'xdebug.log_level=7' >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini"
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
docker compose restart app
|
||||||
|
|
||||||
|
# View log
|
||||||
|
docker compose exec app tail -f /tmp/xdebug.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional Breakpoints
|
||||||
|
|
||||||
|
**PHPStorm**:
|
||||||
|
- Right-click breakpoint → Add condition
|
||||||
|
- Example: `$userId === 123`
|
||||||
|
|
||||||
|
**VS Code**:
|
||||||
|
- Right-click breakpoint → Edit Breakpoint
|
||||||
|
- Add expression: `$userId === 123`
|
||||||
|
|
||||||
|
### Remote Debugging
|
||||||
|
|
||||||
|
For debugging CLI scripts:
|
||||||
|
```bash
|
||||||
|
# Set environment variable
|
||||||
|
docker compose exec app bash
|
||||||
|
export XDEBUG_SESSION=PHPSTORM
|
||||||
|
|
||||||
|
# Run script
|
||||||
|
php script.php
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Specific Scenarios
|
||||||
|
|
||||||
|
### Debug API Requests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With debug cookie
|
||||||
|
curl -b "XDEBUG_SESSION=PHPSTORM" http://localhost:8000/api/endpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug CLI Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start listening in IDE first
|
||||||
|
docker compose exec app bash
|
||||||
|
export XDEBUG_SESSION=PHPSTORM
|
||||||
|
php artisan command
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start listening in IDE first
|
||||||
|
docker compose exec app bash
|
||||||
|
export XDEBUG_SESSION=PHPSTORM
|
||||||
|
vendor/bin/phpunit tests/Unit/MyTest.php
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Disable Xdebug for Tests** (faster execution)
|
||||||
|
```bash
|
||||||
|
docker compose exec app php -d xdebug.mode=off vendor/bin/phpunit
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use Logging Breakpoints** (don't pause execution)
|
||||||
|
- Evaluate and log instead of stopping
|
||||||
|
|
||||||
|
3. **Conditional Breakpoints** (reduce noise)
|
||||||
|
- Only break when specific conditions met
|
||||||
|
|
||||||
|
4. **Watch Expressions** (monitor variables)
|
||||||
|
- Add variables to watch list for continuous monitoring
|
||||||
|
|
||||||
|
5. **Clear Breakpoints** (avoid clutter)
|
||||||
|
- Remove breakpoints when done debugging
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### PHPStorm
|
||||||
|
```
|
||||||
|
Settings > PHP > CLI Interpreter → Add Docker Compose (app service)
|
||||||
|
Settings > PHP > Debug → Port 9003, Accept external connections
|
||||||
|
Settings > PHP > Servers → Add server, path mapping: /var/www/html
|
||||||
|
Start Listening (phone icon) → Set breakpoint → Access app
|
||||||
|
```
|
||||||
|
|
||||||
|
### VS Code
|
||||||
|
```json
|
||||||
|
// .vscode/launch.json
|
||||||
|
{
|
||||||
|
"type": "php",
|
||||||
|
"request": "launch",
|
||||||
|
"port": 9003,
|
||||||
|
"pathMappings": {
|
||||||
|
"/var/www/html": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
F5 to start → Set breakpoint → Access app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Xdebug Info
|
||||||
|
```bash
|
||||||
|
# Check Xdebug is loaded
|
||||||
|
docker compose exec app php -v
|
||||||
|
|
||||||
|
# View Xdebug configuration
|
||||||
|
docker compose exec app php -i | grep xdebug
|
||||||
|
|
||||||
|
# View config file
|
||||||
|
docker compose exec app cat /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
- Not hitting breakpoint → Check IDE is listening, verify path mappings
|
||||||
|
- Connection refused → Check port 9003, verify Xdebug config
|
||||||
|
- Wrong file shown → Fix path mappings
|
||||||
|
- Multiple sessions → Close other IDEs listening on same port
|
||||||
155
.claude/skills/foundation/docker-setup.md
Normal file
155
.claude/skills/foundation/docker-setup.md
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
# Docker Setup
|
||||||
|
|
||||||
|
Initialize and configure the Docker development environment for Foundation framework.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "set up docker environment"
|
||||||
|
- User asks to "initialize development environment"
|
||||||
|
- User asks to "start the project"
|
||||||
|
- User mentions "docker setup" or "docker configuration"
|
||||||
|
- First time project setup
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. **Verify Prerequisites**
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
```
|
||||||
|
Ensure Docker and Docker Compose are installed on the system.
|
||||||
|
|
||||||
|
2. **Set User Permissions (Linux/macOS)**
|
||||||
|
```bash
|
||||||
|
export USER_ID=$(id -u) && export GROUP_ID=$(id -g)
|
||||||
|
```
|
||||||
|
This ensures files created in containers have correct ownership.
|
||||||
|
|
||||||
|
3. **Start Docker Environment**
|
||||||
|
```bash
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
This command:
|
||||||
|
- Builds Docker images if needed
|
||||||
|
- Starts app, db, and phpmyadmin containers
|
||||||
|
- Creates necessary networks and volumes
|
||||||
|
|
||||||
|
4. **Install Dependencies**
|
||||||
|
```bash
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
Installs all Composer dependencies inside the app container.
|
||||||
|
|
||||||
|
5. **Import Database Schema** (if schema exists)
|
||||||
|
```bash
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Copy Environment Configuration** (if not exists)
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
Edit `.env` if custom configuration is needed (usually Docker defaults work).
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
1. **Check Container Status**
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
All three containers (app, db, phpmyadmin) should be in "running" state.
|
||||||
|
|
||||||
|
2. **Verify Application Access**
|
||||||
|
- Open browser: http://localhost:8000
|
||||||
|
- Should see the application homepage
|
||||||
|
|
||||||
|
3. **Verify Database Access**
|
||||||
|
- Open browser: http://localhost:8080 (phpMyAdmin)
|
||||||
|
- Login with: foundation_user / foundation_password
|
||||||
|
- Should see "foundation" database
|
||||||
|
|
||||||
|
4. **Test Container Shell Access**
|
||||||
|
```bash
|
||||||
|
make shell
|
||||||
|
```
|
||||||
|
Should enter the app container bash shell. Type `exit` to leave.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Port Conflicts
|
||||||
|
|
||||||
|
**Problem**: Error about port 8000, 8080, or 3306 already in use.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check what's using the port (example for port 8000)
|
||||||
|
sudo lsof -i :8000
|
||||||
|
|
||||||
|
# Stop conflicting service or modify docker-compose.yml ports
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Errors
|
||||||
|
|
||||||
|
**Problem**: Files created by Docker have wrong ownership.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Set user/group IDs and rebuild
|
||||||
|
export USER_ID=$(id -u) && export GROUP_ID=$(id -g)
|
||||||
|
make down
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Won't Start
|
||||||
|
|
||||||
|
**Problem**: Containers fail to start or crash immediately.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Check logs for specific container
|
||||||
|
docker compose logs app
|
||||||
|
docker compose logs db
|
||||||
|
|
||||||
|
# Try rebuilding
|
||||||
|
make down
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Failed
|
||||||
|
|
||||||
|
**Problem**: Application can't connect to database.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify database container is running: `docker compose ps`
|
||||||
|
2. Check environment variables in `.env` match Docker defaults:
|
||||||
|
```
|
||||||
|
DB_HOST=db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=foundation
|
||||||
|
DB_USERNAME=foundation_user
|
||||||
|
DB_PASSWORD=foundation_password
|
||||||
|
```
|
||||||
|
3. Restart containers: `make down && make up`
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start environment
|
||||||
|
make up
|
||||||
|
|
||||||
|
# Stop environment
|
||||||
|
make down
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f app
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
docker compose restart app
|
||||||
|
|
||||||
|
# Rebuild containers
|
||||||
|
make down
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# Access container shell
|
||||||
|
make shell
|
||||||
|
```
|
||||||
332
.claude/skills/foundation/run-tests.md
Normal file
332
.claude/skills/foundation/run-tests.md
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
# Run Tests
|
||||||
|
|
||||||
|
Execute the testing suite for Foundation framework using PHPUnit.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "run tests"
|
||||||
|
- User asks to "execute test suite"
|
||||||
|
- User asks to "check if tests pass"
|
||||||
|
- User mentions "test coverage"
|
||||||
|
- User asks to "run unit tests" or "run integration tests"
|
||||||
|
- After making code changes that need verification
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Run All Tests
|
||||||
|
|
||||||
|
1. **Execute Full Test Suite**
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
This runs all unit and integration tests.
|
||||||
|
|
||||||
|
### Run Specific Test Suites
|
||||||
|
|
||||||
|
2. **Run Unit Tests Only**
|
||||||
|
```bash
|
||||||
|
make test-unit
|
||||||
|
```
|
||||||
|
Unit tests focus on testing individual components in isolation.
|
||||||
|
|
||||||
|
3. **Run Integration Tests Only**
|
||||||
|
```bash
|
||||||
|
make test-integration
|
||||||
|
```
|
||||||
|
Integration tests verify components work together correctly.
|
||||||
|
|
||||||
|
### Generate Coverage Report
|
||||||
|
|
||||||
|
4. **Run Tests with Coverage**
|
||||||
|
```bash
|
||||||
|
make test-coverage
|
||||||
|
```
|
||||||
|
Generates an HTML coverage report in `coverage-html/` directory.
|
||||||
|
|
||||||
|
5. **View Coverage Report**
|
||||||
|
```bash
|
||||||
|
# Open in browser (adjust path as needed)
|
||||||
|
open coverage-html/index.html
|
||||||
|
# or
|
||||||
|
xdg-open coverage-html/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Specific Test Files
|
||||||
|
|
||||||
|
6. **Run Single Test File** (if needed)
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpunit tests/Unit/Path/To/TestFile.php
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Run Single Test Method** (if needed)
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpunit --filter testMethodName
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
### Successful Test Run
|
||||||
|
|
||||||
|
```
|
||||||
|
PHPUnit 10.x.x
|
||||||
|
|
||||||
|
................................................... 50 / 100 ( 50%)
|
||||||
|
.................................................. 100 / 100 (100%)
|
||||||
|
|
||||||
|
Time: 00:01.234, Memory: 10.00 MB
|
||||||
|
|
||||||
|
OK (100 tests, 250 assertions)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Failed Test Example
|
||||||
|
|
||||||
|
```
|
||||||
|
F
|
||||||
|
|
||||||
|
Time: 00:00.123, Memory: 8.00 MB
|
||||||
|
|
||||||
|
There was 1 failure:
|
||||||
|
|
||||||
|
1) Foundation\Tests\Unit\ExampleTest::testExample
|
||||||
|
Failed asserting that false is true.
|
||||||
|
|
||||||
|
/var/www/html/tests/Unit/ExampleTest.php:15
|
||||||
|
|
||||||
|
FAILURES!
|
||||||
|
Tests: 1, Assertions: 1, Failures: 1.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Organization
|
||||||
|
|
||||||
|
Tests are organized by type:
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── Unit/ # Unit tests
|
||||||
|
│ ├── Core/
|
||||||
|
│ └── Modules/
|
||||||
|
└── Integration/ # Integration tests
|
||||||
|
├── Api/
|
||||||
|
└── Database/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
- Test individual classes in isolation
|
||||||
|
- Mock dependencies
|
||||||
|
- Fast execution
|
||||||
|
- No database or HTTP calls
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```php
|
||||||
|
namespace Foundation\Tests\Unit\Core;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class ExampleTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testExample(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
- Test components working together
|
||||||
|
- May use real database (test database)
|
||||||
|
- Test full request/response cycle
|
||||||
|
- Slower than unit tests
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```php
|
||||||
|
namespace Foundation\Tests\Integration\Api;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class ApiTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testApiEndpoint(): void
|
||||||
|
{
|
||||||
|
// Test full HTTP request/response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Tests Not Found
|
||||||
|
|
||||||
|
**Problem**: PHPUnit can't find test files.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Check phpunit.xml or phpunit.xml.dist configuration
|
||||||
|
2. Verify test files end with `Test.php`
|
||||||
|
3. Verify test classes extend `PHPUnit\Framework\TestCase`
|
||||||
|
4. Run `make dump-autoload`
|
||||||
|
|
||||||
|
### Database Connection Errors (Integration Tests)
|
||||||
|
|
||||||
|
**Problem**: Tests fail with database connection errors.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Ensure Docker containers are running: `docker compose ps`
|
||||||
|
2. Check test environment configuration
|
||||||
|
3. Verify test database exists and is accessible
|
||||||
|
4. Check `.env` or test configuration for correct database credentials
|
||||||
|
|
||||||
|
### Memory Limit Errors
|
||||||
|
|
||||||
|
**Problem**: Tests fail with "Allowed memory size exhausted" error.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Increase memory limit for tests
|
||||||
|
docker compose exec app php -d memory_limit=512M vendor/bin/phpunit
|
||||||
|
```
|
||||||
|
|
||||||
|
Or modify `phpunit.xml`:
|
||||||
|
```xml
|
||||||
|
<php>
|
||||||
|
<ini name="memory_limit" value="512M"/>
|
||||||
|
</php>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slow Test Execution
|
||||||
|
|
||||||
|
**Problem**: Tests take too long to run.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Run only unit tests during development: `make test-unit`
|
||||||
|
2. Run specific test files instead of full suite
|
||||||
|
3. Use `--stop-on-failure` flag to stop at first failure:
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpunit --stop-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
### Coverage Report Not Generated
|
||||||
|
|
||||||
|
**Problem**: Coverage report directory is empty.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify Xdebug is installed in Docker container
|
||||||
|
2. Check phpunit.xml coverage configuration
|
||||||
|
3. Run coverage command with verbose output:
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpunit --coverage-html=coverage-html --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Parallel Test Execution
|
||||||
|
|
||||||
|
For faster execution (if paratest is installed):
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/paratest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Watch Mode (Continuous Testing)
|
||||||
|
|
||||||
|
Using phpunit-watcher (if installed):
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpunit-watcher watch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Filtering
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests in specific namespace
|
||||||
|
docker compose exec app vendor/bin/phpunit --filter "Foundation\\Tests\\Unit\\Core"
|
||||||
|
|
||||||
|
# Run tests matching pattern
|
||||||
|
docker compose exec app vendor/bin/phpunit --filter testUserCreation
|
||||||
|
|
||||||
|
# Run tests from specific group
|
||||||
|
docker compose exec app vendor/bin/phpunit --group integration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Run tests before committing**
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run unit tests during development** (faster feedback)
|
||||||
|
```bash
|
||||||
|
make test-unit
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run full suite before pushing**
|
||||||
|
```bash
|
||||||
|
make test && make static-analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Check coverage periodically**
|
||||||
|
```bash
|
||||||
|
make test-coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Write tests for new features** - Ensure new code is covered
|
||||||
|
|
||||||
|
6. **Keep tests fast** - Mock external dependencies in unit tests
|
||||||
|
|
||||||
|
7. **Use meaningful test names** - Test method names should describe what they test
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Unit tests only
|
||||||
|
make test-unit
|
||||||
|
|
||||||
|
# Integration tests only
|
||||||
|
make test-integration
|
||||||
|
|
||||||
|
# With coverage report
|
||||||
|
make test-coverage
|
||||||
|
|
||||||
|
# Single file
|
||||||
|
docker compose exec app vendor/bin/phpunit tests/Unit/Path/To/TestFile.php
|
||||||
|
|
||||||
|
# Single method
|
||||||
|
docker compose exec app vendor/bin/phpunit --filter testMethodName
|
||||||
|
|
||||||
|
# Stop on first failure
|
||||||
|
docker compose exec app vendor/bin/phpunit --stop-on-failure
|
||||||
|
|
||||||
|
# Verbose output
|
||||||
|
docker compose exec app vendor/bin/phpunit --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Writing Tips
|
||||||
|
|
||||||
|
1. **Follow AAA Pattern**
|
||||||
|
- Arrange: Set up test data
|
||||||
|
- Act: Execute the code under test
|
||||||
|
- Assert: Verify the results
|
||||||
|
|
||||||
|
2. **One Assertion Per Test** (when possible)
|
||||||
|
- Makes failures easier to diagnose
|
||||||
|
|
||||||
|
3. **Use Descriptive Names**
|
||||||
|
```php
|
||||||
|
// Good
|
||||||
|
public function testUserCanBeCreatedWithValidEmail(): void
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
public function testUser(): void
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test Edge Cases**
|
||||||
|
- Empty inputs
|
||||||
|
- Null values
|
||||||
|
- Boundary conditions
|
||||||
|
- Error cases
|
||||||
|
|
||||||
|
5. **Keep Tests Independent**
|
||||||
|
- Tests should not depend on each other
|
||||||
|
- Each test should set up its own data
|
||||||
402
.claude/skills/foundation/static-analysis.md
Normal file
402
.claude/skills/foundation/static-analysis.md
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
# Static Analysis
|
||||||
|
|
||||||
|
Run static analysis tools to check code quality, type safety, and architectural boundaries in the Foundation framework.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User asks to "run static analysis"
|
||||||
|
- User asks to "check code quality"
|
||||||
|
- User asks to "run phpstan" or "run deptrac"
|
||||||
|
- User asks to "analyze code"
|
||||||
|
- User mentions "type checking" or "architecture validation"
|
||||||
|
- Before committing code
|
||||||
|
- After making significant changes
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Run Complete Static Analysis
|
||||||
|
|
||||||
|
1. **Run All Static Analysis Tools**
|
||||||
|
```bash
|
||||||
|
make static-analysis
|
||||||
|
```
|
||||||
|
This runs both PHPStan and Deptrac in sequence.
|
||||||
|
|
||||||
|
### Run Individual Tools
|
||||||
|
|
||||||
|
2. **Run PHPStan** (Type and Code Analysis)
|
||||||
|
```bash
|
||||||
|
make phpstan
|
||||||
|
```
|
||||||
|
PHPStan checks for:
|
||||||
|
- Type errors
|
||||||
|
- Undefined variables
|
||||||
|
- Invalid method calls
|
||||||
|
- Dead code
|
||||||
|
- Strict rules violations
|
||||||
|
|
||||||
|
3. **Run Deptrac** (Architecture Layer Analysis)
|
||||||
|
```bash
|
||||||
|
make deptrac
|
||||||
|
```
|
||||||
|
Deptrac validates:
|
||||||
|
- Layer dependency rules (Domain → Application → Infrastructure)
|
||||||
|
- Module boundaries
|
||||||
|
- Architectural violations
|
||||||
|
|
||||||
|
### Generate Baselines (When Needed)
|
||||||
|
|
||||||
|
4. **Generate PHPStan Baseline**
|
||||||
|
```bash
|
||||||
|
make phpstan-baseline
|
||||||
|
```
|
||||||
|
Creates `phpstan-baseline.neon` to suppress existing errors while preventing new ones.
|
||||||
|
|
||||||
|
5. **Generate Deptrac Baseline**
|
||||||
|
```bash
|
||||||
|
make deptrac-baseline
|
||||||
|
```
|
||||||
|
Creates `deptrac-baseline.yaml` to track existing architectural violations.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
### PHPStan Success
|
||||||
|
|
||||||
|
```
|
||||||
|
PHPStan - PHP Static Analysis Tool
|
||||||
|
|
||||||
|
[OK] No errors
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deptrac Success
|
||||||
|
|
||||||
|
```
|
||||||
|
Deptrac
|
||||||
|
|
||||||
|
[OK] No violations found
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### PHPStan Errors Example
|
||||||
|
|
||||||
|
```
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
Line src/Modules/Example/Domain/Example.php
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
15 Property Example::$name has no type hint
|
||||||
|
23 Method Example::getData() has no return type specified
|
||||||
|
45 Call to undefined method PDO::executeQuery()
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
|
||||||
|
[ERROR] Found 3 errors
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deptrac Violations Example
|
||||||
|
|
||||||
|
```
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
From To
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
Domain\Example (Domain Layer) Infrastructure\Database (Infrastructure Layer)
|
||||||
|
------ --------------------------------------------------------
|
||||||
|
|
||||||
|
[ERROR] Found 1 violation
|
||||||
|
|
||||||
|
Violation: Domain layer must not depend on Infrastructure layer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Analysis Tools Overview
|
||||||
|
|
||||||
|
### PHPStan
|
||||||
|
|
||||||
|
**Purpose**: Static analysis for type safety and code quality
|
||||||
|
|
||||||
|
**Configuration**: `phpstan.neon` or `phpstan.neon.dist`
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- Level-based strictness (0-9, higher = stricter)
|
||||||
|
- Type inference and checking
|
||||||
|
- Dead code detection
|
||||||
|
- Strict rules for code quality
|
||||||
|
|
||||||
|
**What It Checks**:
|
||||||
|
- Type mismatches
|
||||||
|
- Undefined variables and methods
|
||||||
|
- Invalid array access
|
||||||
|
- Return type compatibility
|
||||||
|
- Parameter type compatibility
|
||||||
|
- Property type hints
|
||||||
|
|
||||||
|
### Deptrac
|
||||||
|
|
||||||
|
**Purpose**: Architecture and dependency rule validation
|
||||||
|
|
||||||
|
**Configuration**: `deptrac.yaml` or `deptrac.yaml.dist`
|
||||||
|
|
||||||
|
**Key Features**:
|
||||||
|
- Layer dependency enforcement
|
||||||
|
- Module boundary validation
|
||||||
|
- Custom rule definition
|
||||||
|
|
||||||
|
**What It Checks**:
|
||||||
|
- Domain layer has no dependencies
|
||||||
|
- Application layer only depends on Domain
|
||||||
|
- Infrastructure layer only depends on Application
|
||||||
|
- No circular dependencies
|
||||||
|
- Custom architectural rules
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### PHPStan Errors After Dependency Updates
|
||||||
|
|
||||||
|
**Problem**: New errors appear after updating dependencies.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Review and fix legitimate errors
|
||||||
|
2. If errors are unavoidable (third-party library issues):
|
||||||
|
```bash
|
||||||
|
make phpstan-baseline
|
||||||
|
```
|
||||||
|
3. Commit the baseline file
|
||||||
|
|
||||||
|
### Deptrac False Positives
|
||||||
|
|
||||||
|
**Problem**: Deptrac reports violations that are intentional or unavoidable.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Review the violation - ensure it's truly acceptable
|
||||||
|
2. If legitimate, update `deptrac.yaml` to allow specific exception
|
||||||
|
3. Or generate baseline:
|
||||||
|
```bash
|
||||||
|
make deptrac-baseline
|
||||||
|
```
|
||||||
|
|
||||||
|
### PHPStan Memory Limit
|
||||||
|
|
||||||
|
**Problem**: PHPStan crashes with memory exhausted error.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
```bash
|
||||||
|
# Run with increased memory
|
||||||
|
docker compose exec app php -d memory_limit=1G vendor/bin/phpstan analyse
|
||||||
|
```
|
||||||
|
|
||||||
|
Or modify `phpstan.neon`:
|
||||||
|
```yaml
|
||||||
|
parameters:
|
||||||
|
memory_limit: 1G
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slow Analysis
|
||||||
|
|
||||||
|
**Problem**: Static analysis takes too long.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. **Enable result cache** in `phpstan.neon`:
|
||||||
|
```yaml
|
||||||
|
parameters:
|
||||||
|
tmpDir: var/cache/phpstan
|
||||||
|
```
|
||||||
|
2. **Analyze specific paths** during development:
|
||||||
|
```bash
|
||||||
|
docker compose exec app vendor/bin/phpstan analyse src/Modules/MyModule
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Not Found
|
||||||
|
|
||||||
|
**Problem**: Tool can't find configuration file.
|
||||||
|
|
||||||
|
**Solution**:
|
||||||
|
1. Verify configuration file exists:
|
||||||
|
- PHPStan: `phpstan.neon` or `phpstan.neon.dist`
|
||||||
|
- Deptrac: `deptrac.yaml` or `deptrac.yaml.dist`
|
||||||
|
2. Check file is in project root
|
||||||
|
3. Verify file syntax is valid YAML/NEON
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### PHPStan Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Analyze specific directory
|
||||||
|
docker compose exec app vendor/bin/phpstan analyse src/Modules/MyModule
|
||||||
|
|
||||||
|
# Different error level (0-9)
|
||||||
|
docker compose exec app vendor/bin/phpstan analyse --level=9
|
||||||
|
|
||||||
|
# Show progress
|
||||||
|
docker compose exec app vendor/bin/phpstan analyse --verbose
|
||||||
|
|
||||||
|
# Clear cache
|
||||||
|
docker compose exec app vendor/bin/phpstan clear-result-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deptrac Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Analyze with specific configuration
|
||||||
|
docker compose exec app vendor/bin/deptrac analyse --config-file=deptrac.yaml
|
||||||
|
|
||||||
|
# Generate dependency graph
|
||||||
|
docker compose exec app vendor/bin/deptrac analyse --formatter=graphviz
|
||||||
|
|
||||||
|
# Different output format
|
||||||
|
docker compose exec app vendor/bin/deptrac analyse --formatter=console
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Layer Violations and Fixes
|
||||||
|
|
||||||
|
### Domain Depending on Infrastructure
|
||||||
|
|
||||||
|
**Violation**:
|
||||||
|
```
|
||||||
|
Domain\UserRepository → Infrastructure\Database\PDO
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix**:
|
||||||
|
Move repository interface to Domain, implementation to Infrastructure:
|
||||||
|
```php
|
||||||
|
// Domain/UserRepositoryInterface.php (in Domain layer)
|
||||||
|
interface UserRepositoryInterface
|
||||||
|
{
|
||||||
|
public function findById(int $id): ?User;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infrastructure/Database/UserRepository.php (in Infrastructure layer)
|
||||||
|
class UserRepository implements UserRepositoryInterface
|
||||||
|
{
|
||||||
|
public function __construct(private PDO $pdo) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Depending on Infrastructure
|
||||||
|
|
||||||
|
**Violation**:
|
||||||
|
```
|
||||||
|
Application\UseCase → Infrastructure\Api\Controller
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix**:
|
||||||
|
Reverse the dependency - Controllers should call use cases, not vice versa:
|
||||||
|
```php
|
||||||
|
// Infrastructure/Api/UserController.php
|
||||||
|
class UserController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private CreateUserUseCase $createUser // Application layer
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Run Before Committing**
|
||||||
|
```bash
|
||||||
|
make test && make static-analysis
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Fix Issues Immediately** - Don't let violations accumulate
|
||||||
|
|
||||||
|
3. **Use Baselines Sparingly** - Fix issues rather than suppressing them
|
||||||
|
|
||||||
|
4. **Review Violations** - Understand why a violation occurs before suppressing
|
||||||
|
|
||||||
|
5. **Keep Configuration Strict** - Higher PHPStan levels catch more issues
|
||||||
|
|
||||||
|
6. **Respect Layer Boundaries** - Domain should never depend on outer layers
|
||||||
|
|
||||||
|
7. **Type Everything** - Add type hints to all methods and properties
|
||||||
|
|
||||||
|
## Integration with CI/CD
|
||||||
|
|
||||||
|
Add to your CI pipeline:
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Running static analysis..."
|
||||||
|
make phpstan
|
||||||
|
make deptrac
|
||||||
|
|
||||||
|
echo "All checks passed!"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all static analysis
|
||||||
|
make static-analysis
|
||||||
|
|
||||||
|
# PHPStan only
|
||||||
|
make phpstan
|
||||||
|
|
||||||
|
# Deptrac only
|
||||||
|
make deptrac
|
||||||
|
|
||||||
|
# Generate baselines
|
||||||
|
make phpstan-baseline
|
||||||
|
make deptrac-baseline
|
||||||
|
|
||||||
|
# PHPStan specific path
|
||||||
|
docker compose exec app vendor/bin/phpstan analyse src/Modules/MyModule
|
||||||
|
|
||||||
|
# Deptrac with graph output
|
||||||
|
docker compose exec app vendor/bin/deptrac analyse --formatter=graphviz
|
||||||
|
|
||||||
|
# Clear PHPStan cache
|
||||||
|
docker compose exec app vendor/bin/phpstan clear-result-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
## Understanding Error Levels
|
||||||
|
|
||||||
|
### PHPStan Levels (0-9)
|
||||||
|
|
||||||
|
- **Level 0**: Basic checks, few false positives
|
||||||
|
- **Level 5**: Reasonable balance, good starting point
|
||||||
|
- **Level 9**: Maximum strictness, requires extensive type hints
|
||||||
|
|
||||||
|
Recommendation: Start at level 5, gradually increase to 9.
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
### PHPStan Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# phpstan.neon
|
||||||
|
parameters:
|
||||||
|
level: 9
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
tmpDir: var/cache/phpstan
|
||||||
|
excludePaths:
|
||||||
|
- src/old-code/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deptrac Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# deptrac.yaml
|
||||||
|
deptrac:
|
||||||
|
paths:
|
||||||
|
- ./src
|
||||||
|
layers:
|
||||||
|
- name: Domain
|
||||||
|
collectors:
|
||||||
|
- type: directory
|
||||||
|
regex: src/.*/Domain/.*
|
||||||
|
- name: Application
|
||||||
|
collectors:
|
||||||
|
- type: directory
|
||||||
|
regex: src/.*/Application/.*
|
||||||
|
- name: Infrastructure
|
||||||
|
collectors:
|
||||||
|
- type: directory
|
||||||
|
regex: src/.*/Infrastructure/.*
|
||||||
|
ruleset:
|
||||||
|
Domain: []
|
||||||
|
Application:
|
||||||
|
- Domain
|
||||||
|
Infrastructure:
|
||||||
|
- Application
|
||||||
|
```
|
||||||
524
.claude/skills/foundation/troubleshoot-docker.md
Normal file
524
.claude/skills/foundation/troubleshoot-docker.md
Normal file
|
|
@ -0,0 +1,524 @@
|
||||||
|
# Troubleshoot Docker
|
||||||
|
|
||||||
|
Diagnostic steps and solutions for common Docker issues in the Foundation framework.
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
|
||||||
|
- User reports "Docker not working"
|
||||||
|
- User mentions "containers won't start"
|
||||||
|
- User reports "connection refused" errors
|
||||||
|
- User asks "why isn't the app working?"
|
||||||
|
- User mentions "port already in use"
|
||||||
|
- User reports "permission denied" errors
|
||||||
|
- Application not accessible via browser
|
||||||
|
- Database connection failures
|
||||||
|
|
||||||
|
## Diagnostic Steps
|
||||||
|
|
||||||
|
### Step 1: Check Container Status
|
||||||
|
|
||||||
|
1. **List All Containers**
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected Output**:
|
||||||
|
```
|
||||||
|
NAME STATUS PORTS
|
||||||
|
foundation-app Up X minutes 0.0.0.0:8000->80/tcp
|
||||||
|
foundation-db Up X minutes 0.0.0.0:3306->3306/tcp
|
||||||
|
foundation-phpmyadmin Up X minutes 0.0.0.0:8080->80/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
All containers should show "Up" status.
|
||||||
|
|
||||||
|
### Step 2: Check Container Logs
|
||||||
|
|
||||||
|
2. **View Container Logs**
|
||||||
|
```bash
|
||||||
|
# All containers
|
||||||
|
docker compose logs
|
||||||
|
|
||||||
|
# Specific container
|
||||||
|
docker compose logs app
|
||||||
|
docker compose logs db
|
||||||
|
docker compose logs phpmyadmin
|
||||||
|
|
||||||
|
# Follow logs in real-time
|
||||||
|
docker compose logs -f app
|
||||||
|
```
|
||||||
|
|
||||||
|
Look for error messages, warnings, or stack traces.
|
||||||
|
|
||||||
|
### Step 3: Check Port Availability
|
||||||
|
|
||||||
|
3. **Check if Ports are Available**
|
||||||
|
```bash
|
||||||
|
# Check port 8000 (app)
|
||||||
|
sudo lsof -i :8000
|
||||||
|
|
||||||
|
# Check port 8080 (phpmyadmin)
|
||||||
|
sudo lsof -i :8080
|
||||||
|
|
||||||
|
# Check port 3306 (mysql)
|
||||||
|
sudo lsof -i :3306
|
||||||
|
```
|
||||||
|
|
||||||
|
If output shows other processes using these ports, you have a port conflict.
|
||||||
|
|
||||||
|
### Step 4: Verify Docker Service
|
||||||
|
|
||||||
|
4. **Check Docker Daemon Status**
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
systemctl status docker # Linux
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure Docker daemon is running.
|
||||||
|
|
||||||
|
### Step 5: Test Application Access
|
||||||
|
|
||||||
|
5. **Test HTTP Endpoints**
|
||||||
|
```bash
|
||||||
|
# Test app
|
||||||
|
curl http://localhost:8000
|
||||||
|
|
||||||
|
# Test phpMyAdmin
|
||||||
|
curl http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
Should return HTML content, not connection errors.
|
||||||
|
|
||||||
|
## Common Issues and Solutions
|
||||||
|
|
||||||
|
### Issue 1: Containers Won't Start
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- `docker compose ps` shows containers with "Exit" status
|
||||||
|
- Containers restart repeatedly
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
docker compose logs app
|
||||||
|
docker compose logs db
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Port Conflict**
|
||||||
|
```bash
|
||||||
|
# Find process using port
|
||||||
|
sudo lsof -i :8000
|
||||||
|
|
||||||
|
# Kill process or change port in docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Configuration Error**
|
||||||
|
```bash
|
||||||
|
# Check docker-compose.yml syntax
|
||||||
|
docker compose config
|
||||||
|
|
||||||
|
# Rebuild containers
|
||||||
|
make down
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Previous Container Data Issues**
|
||||||
|
```bash
|
||||||
|
# Remove containers and volumes
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
# Restart fresh
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 2: Application Returns 500 Error
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Browser shows "Internal Server Error"
|
||||||
|
- HTTP 500 status code
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
docker compose logs -f app
|
||||||
|
docker compose exec app tail -f storage/logs/app.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Check File Permissions**
|
||||||
|
```bash
|
||||||
|
# Fix permissions in container
|
||||||
|
docker compose exec app chown -R www-data:www-data storage/
|
||||||
|
docker compose exec app chmod -R 775 storage/
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Check PHP Errors**
|
||||||
|
```bash
|
||||||
|
# View PHP error log
|
||||||
|
docker compose exec app tail -f /var/log/apache2/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Missing Dependencies**
|
||||||
|
```bash
|
||||||
|
# Reinstall dependencies
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 3: Database Connection Failed
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- "Connection refused" to database
|
||||||
|
- "SQLSTATE[HY000] [2002]" errors
|
||||||
|
- Application can't connect to MySQL
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
# Check database container
|
||||||
|
docker compose ps db
|
||||||
|
|
||||||
|
# Check database logs
|
||||||
|
docker compose logs db
|
||||||
|
|
||||||
|
# Test connection from app container
|
||||||
|
docker compose exec app mysql -h db -u foundation_user -pfoundation_password foundation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Database Container Not Running**
|
||||||
|
```bash
|
||||||
|
# Restart database
|
||||||
|
docker compose restart db
|
||||||
|
|
||||||
|
# Check startup logs
|
||||||
|
docker compose logs db
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Wrong Credentials**
|
||||||
|
```bash
|
||||||
|
# Verify environment variables
|
||||||
|
docker compose exec app env | grep DB_
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# DB_HOST=db
|
||||||
|
# DB_PORT=3306
|
||||||
|
# DB_DATABASE=foundation
|
||||||
|
# DB_USERNAME=foundation_user
|
||||||
|
# DB_PASSWORD=foundation_password
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Database Not Initialized**
|
||||||
|
```bash
|
||||||
|
# Import schema
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 4: Port Already in Use
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Error: "port is already allocated"
|
||||||
|
- Error: "bind: address already in use"
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
sudo lsof -i :8000
|
||||||
|
sudo lsof -i :8080
|
||||||
|
sudo lsof -i :3306
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Stop Conflicting Service**
|
||||||
|
```bash
|
||||||
|
# If Apache/Nginx is running
|
||||||
|
sudo systemctl stop apache2
|
||||||
|
# or
|
||||||
|
sudo systemctl stop nginx
|
||||||
|
|
||||||
|
# If MySQL is running
|
||||||
|
sudo systemctl stop mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Change Docker Ports**
|
||||||
|
|
||||||
|
Edit `docker-compose.yml`:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
ports:
|
||||||
|
- "8001:80" # Change 8000 to 8001
|
||||||
|
```
|
||||||
|
|
||||||
|
Then restart:
|
||||||
|
```bash
|
||||||
|
make down
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 5: Permission Denied Errors
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Can't write to files
|
||||||
|
- Can't create directories
|
||||||
|
- Permission denied in logs
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
# Check file ownership
|
||||||
|
docker compose exec app ls -la /var/www/html/storage
|
||||||
|
|
||||||
|
# Check user in container
|
||||||
|
docker compose exec app whoami
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Fix Ownership (Linux/macOS)**
|
||||||
|
```bash
|
||||||
|
export USER_ID=$(id -u) && export GROUP_ID=$(id -g)
|
||||||
|
make down
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Fix Permissions in Container**
|
||||||
|
```bash
|
||||||
|
docker compose exec app chown -R www-data:www-data /var/www/html
|
||||||
|
docker compose exec app chmod -R 775 /var/www/html/storage
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 6: Changes Not Reflected
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Code changes don't appear in browser
|
||||||
|
- Old version still running
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
# Check if volumes are mounted
|
||||||
|
docker compose exec app ls -la /var/www/html
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Clear Caches**
|
||||||
|
```bash
|
||||||
|
# Clear application cache
|
||||||
|
docker compose exec app rm -rf storage/cache/*
|
||||||
|
|
||||||
|
# Clear browser cache
|
||||||
|
# Use Ctrl+Shift+R or Cmd+Shift+R
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Restart Container**
|
||||||
|
```bash
|
||||||
|
docker compose restart app
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Rebuild Container**
|
||||||
|
```bash
|
||||||
|
make down
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 7: Slow Performance
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- Application very slow to respond
|
||||||
|
- High CPU/memory usage
|
||||||
|
- Timeouts
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
# Check container resource usage
|
||||||
|
docker stats
|
||||||
|
|
||||||
|
# Check logs for errors
|
||||||
|
docker compose logs app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Increase Docker Resources**
|
||||||
|
|
||||||
|
In Docker Desktop: Settings → Resources → Increase CPU/Memory
|
||||||
|
|
||||||
|
**B) Clear Logs**
|
||||||
|
```bash
|
||||||
|
docker compose exec app rm -f storage/logs/*
|
||||||
|
docker compose exec app truncate -s 0 /var/log/apache2/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Optimize Autoloader**
|
||||||
|
```bash
|
||||||
|
make dump-autoload
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue 8: Cannot Access Container Shell
|
||||||
|
|
||||||
|
**Symptoms**:
|
||||||
|
- `make shell` or `docker compose exec app bash` fails
|
||||||
|
|
||||||
|
**Diagnostic**:
|
||||||
|
```bash
|
||||||
|
docker compose ps app
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
**A) Container Not Running**
|
||||||
|
```bash
|
||||||
|
make up
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Use Different Shell**
|
||||||
|
```bash
|
||||||
|
docker compose exec app sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Reset Procedure
|
||||||
|
|
||||||
|
If all else fails, completely reset the environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Stop and remove everything
|
||||||
|
docker compose down -v
|
||||||
|
|
||||||
|
# 2. Remove dangling images
|
||||||
|
docker system prune -f
|
||||||
|
|
||||||
|
# 3. Rebuild from scratch
|
||||||
|
export USER_ID=$(id -u) && export GROUP_ID=$(id -g)
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# 4. Reinstall dependencies
|
||||||
|
make install
|
||||||
|
|
||||||
|
# 5. Import database schema
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
|
||||||
|
# 6. Verify
|
||||||
|
docker compose ps
|
||||||
|
curl http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Diagnostic Command Checklist
|
||||||
|
|
||||||
|
Run these commands to gather diagnostic information:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Container status
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
# 2. Container logs
|
||||||
|
docker compose logs --tail=50
|
||||||
|
|
||||||
|
# 3. Docker version
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
|
||||||
|
# 4. Port availability
|
||||||
|
sudo lsof -i :8000
|
||||||
|
sudo lsof -i :8080
|
||||||
|
sudo lsof -i :3306
|
||||||
|
|
||||||
|
# 5. Disk space
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# 6. Docker disk usage
|
||||||
|
docker system df
|
||||||
|
|
||||||
|
# 7. Network connectivity
|
||||||
|
docker compose exec app ping -c 3 db
|
||||||
|
|
||||||
|
# 8. Environment variables
|
||||||
|
docker compose exec app env | grep DB_
|
||||||
|
|
||||||
|
# 9. File permissions
|
||||||
|
docker compose exec app ls -la /var/www/html/storage
|
||||||
|
|
||||||
|
# 10. Test database connection
|
||||||
|
docker compose exec app mysql -h db -u foundation_user -pfoundation_password foundation -e "SELECT 1;"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prevention Best Practices
|
||||||
|
|
||||||
|
1. **Regular Cleanup**
|
||||||
|
```bash
|
||||||
|
docker system prune -f
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Monitor Logs**
|
||||||
|
```bash
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Use Make Commands**
|
||||||
|
- Prefer `make up` over direct `docker compose` commands
|
||||||
|
- Ensures consistent environment setup
|
||||||
|
|
||||||
|
4. **Keep Docker Updated**
|
||||||
|
```bash
|
||||||
|
# Check for updates
|
||||||
|
docker --version
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Document Environment**
|
||||||
|
- Note any custom ports or configurations
|
||||||
|
- Keep `.env` file up to date
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
If issues persist:
|
||||||
|
|
||||||
|
1. **Collect Information**:
|
||||||
|
```bash
|
||||||
|
docker compose ps > debug-status.txt
|
||||||
|
docker compose logs > debug-logs.txt
|
||||||
|
docker compose config > debug-config.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check Docker Documentation**: https://docs.docker.com
|
||||||
|
|
||||||
|
3. **Review Project Issues**: Check if it's a known issue
|
||||||
|
|
||||||
|
4. **System Requirements**:
|
||||||
|
- Docker Engine 20.10+
|
||||||
|
- Docker Compose 2.0+
|
||||||
|
- At least 4GB RAM allocated to Docker
|
||||||
|
- 10GB free disk space
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status and logs
|
||||||
|
docker compose ps
|
||||||
|
docker compose logs -f app
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
docker compose restart app
|
||||||
|
docker compose restart db
|
||||||
|
|
||||||
|
# Full reset
|
||||||
|
docker compose down -v
|
||||||
|
make up
|
||||||
|
|
||||||
|
# Access shell
|
||||||
|
make shell
|
||||||
|
|
||||||
|
# Check ports
|
||||||
|
sudo lsof -i :8000
|
||||||
|
sudo lsof -i :8080
|
||||||
|
sudo lsof -i :3306
|
||||||
|
|
||||||
|
# View resource usage
|
||||||
|
docker stats
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
docker system prune -f
|
||||||
|
|
||||||
|
# Rebuild
|
||||||
|
make down
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
351
Claude.md
Normal file
351
Claude.md
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
# Foundation Framework - Claude Context
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Foundation is a PHP framework built with Domain-Driven Design (DDD) principles, featuring clean architecture, modular design, and modern PHP 8+ capabilities. The framework emphasizes separation of concerns, testability, and maintainability.
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
- **PHP**: 8.1+
|
||||||
|
- **HTTP Framework**: Slim 4
|
||||||
|
- **Dependency Injection**: PHP-DI 7.0
|
||||||
|
- **Database**: MySQL 8.0 with PDO and Repository pattern
|
||||||
|
- **Logging**: Monolog 3.0
|
||||||
|
- **Environment Config**: phpdotenv
|
||||||
|
- **Development**: Docker + Docker Compose with Xdebug
|
||||||
|
|
||||||
|
## Architecture Principles
|
||||||
|
|
||||||
|
### Clean Architecture Layers
|
||||||
|
|
||||||
|
The framework follows strict layered architecture with clear dependency rules:
|
||||||
|
|
||||||
|
```
|
||||||
|
Domain (Pure business logic - No dependencies)
|
||||||
|
↑
|
||||||
|
Application (Use cases - Depends on Domain only)
|
||||||
|
↑
|
||||||
|
Infrastructure (External adapters - Depends on Application only)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Layer Dependency Rules:**
|
||||||
|
- **Domain Layer**: Completely independent, no external dependencies
|
||||||
|
- **Application Layer**: Can depend on Domain and itself only
|
||||||
|
- **Infrastructure Layer**: Can depend on Application and itself only
|
||||||
|
|
||||||
|
### Module Structure
|
||||||
|
|
||||||
|
Each module follows this structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/Modules/{ModuleName}/
|
||||||
|
├── Domain/ # Pure business logic
|
||||||
|
│ ├── {Entity}.php # Entities with identity
|
||||||
|
│ ├── {ValueObject}.php # Immutable value objects
|
||||||
|
│ └── {Repository}Interface.php # Repository contracts
|
||||||
|
├── Application/ # Use cases and orchestration
|
||||||
|
│ └── {UseCase}/
|
||||||
|
│ └── {UseCase}.php
|
||||||
|
└── Infrastructure/ # External implementations
|
||||||
|
├── Api/ # API controllers
|
||||||
|
├── Web/ # Web controllers
|
||||||
|
└── Database/ # Repository implementations
|
||||||
|
├── {Repository}.php
|
||||||
|
├── Commands/
|
||||||
|
└── Queries/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Framework Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/Core/
|
||||||
|
├── Application/ # Application orchestration
|
||||||
|
│ ├── Application.php # Main application class
|
||||||
|
│ ├── Kernel/HttpKernel.php # HTTP request handling
|
||||||
|
│ ├── DependencyInjection/ # DI container setup
|
||||||
|
│ ├── Bootstrapper/ # Application initialization
|
||||||
|
│ └── ServiceProvider/ # Service registration abstractions
|
||||||
|
├── ErrorHandling/ # Error handling and responses
|
||||||
|
├── Logging/ # Logging infrastructure
|
||||||
|
├── Session/ # Session management
|
||||||
|
└── Cache/ # Caching interfaces
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Concepts
|
||||||
|
|
||||||
|
### 1. Service Providers
|
||||||
|
|
||||||
|
Service Providers register services and bootstrap module-specific functionality:
|
||||||
|
|
||||||
|
- Register services in the DI container
|
||||||
|
- Bootstrap module-specific functionality
|
||||||
|
- Register HTTP routes via attribute scanning
|
||||||
|
- Auto-discovered by `ModuleLoader` from `src/Modules/*/` directories
|
||||||
|
|
||||||
|
### 2. Bootstrappers
|
||||||
|
|
||||||
|
Framework components that set up the application before handling requests:
|
||||||
|
|
||||||
|
- `ConfigInitializer` - Environment configuration
|
||||||
|
- `DatabaseInitializer` - Database connections
|
||||||
|
- `SessionInitializer` - Session management
|
||||||
|
- `ModuleLoader` - Auto-discovers and loads module service providers
|
||||||
|
|
||||||
|
### 3. Dependency Injection Container
|
||||||
|
|
||||||
|
The framework uses `InflectableContainer`, a wrapper around PHP-DI with enhanced features:
|
||||||
|
|
||||||
|
- **Container Inflection**: Automatic method invocation during object resolution
|
||||||
|
- **Smart Parameter Resolution**: Intelligent detection of class names vs static values
|
||||||
|
- **Simplified Registration**: Clean syntax for dependency declaration
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```php
|
||||||
|
// Basic registration
|
||||||
|
$container->register(ServiceClass::class, [DependencyClass::class]);
|
||||||
|
|
||||||
|
// Interface binding
|
||||||
|
$container->bind(InterfaceClass::class, ImplementationClass::class, [Dep1::class]);
|
||||||
|
|
||||||
|
// Container inflection (automatic method calls)
|
||||||
|
$container->inflect(UnitOfWork::class)
|
||||||
|
->invokeMethod('registerPersister', [ActivityPersister::class]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Attribute-Based Routing
|
||||||
|
|
||||||
|
Controllers use PHP 8+ attributes for route definitions:
|
||||||
|
|
||||||
|
- Controllers must implement `ControllerInterface`
|
||||||
|
- Routes automatically discovered during controller registration
|
||||||
|
- Available attributes: `#[Get]`, `#[Post]`, `#[Put]`, `#[Delete]`, `#[Route]`, `#[Group]`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```php
|
||||||
|
#[Group('/api/v1/users')]
|
||||||
|
class UserController implements ControllerInterface
|
||||||
|
{
|
||||||
|
#[Get('')]
|
||||||
|
public function index(): ResponseInterface { }
|
||||||
|
|
||||||
|
#[Get('/{id}')]
|
||||||
|
public function show(string $id): ResponseInterface { }
|
||||||
|
|
||||||
|
#[Post('', middleware: [AuthMiddleware::class])]
|
||||||
|
public function create(): ResponseInterface { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Unit of Work Pattern
|
||||||
|
|
||||||
|
Manages database transactions and entity persistence:
|
||||||
|
|
||||||
|
- **Entity Persisters**: Handle database operations for specific entity types
|
||||||
|
- **Registration**: Via container inflection
|
||||||
|
- **Transactional**: All operations executed in single transaction
|
||||||
|
|
||||||
|
### 6. CQRS Separation
|
||||||
|
|
||||||
|
Commands (write operations) and Queries (read operations) are separated:
|
||||||
|
|
||||||
|
- Commands in `Infrastructure/Database/Commands/`
|
||||||
|
- Queries in `Infrastructure/Database/Queries/`
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Docker Environment
|
||||||
|
|
||||||
|
See skill: `/docker-setup` for detailed instructions.
|
||||||
|
|
||||||
|
All development happens in Docker containers with PHP 8.1, MySQL 8.0, and phpMyAdmin. Use `make up` to start the environment and `make install` to install dependencies.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
See skill: `/run-tests` for detailed instructions.
|
||||||
|
|
||||||
|
Comprehensive testing suite with PHPUnit supporting unit tests, integration tests, and coverage reports. Use `make test` to run all tests.
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
|
||||||
|
See skill: `/static-analysis` for detailed instructions.
|
||||||
|
|
||||||
|
Static analysis with PHPStan and Deptrac to enforce type safety and architectural boundaries. Use `make static-analysis` to run both tools.
|
||||||
|
|
||||||
|
## Important Patterns
|
||||||
|
|
||||||
|
### Adding New Modules
|
||||||
|
|
||||||
|
See skill: `/create-module` for detailed instructions.
|
||||||
|
|
||||||
|
Create modules following DDD principles with Domain, Application, and Infrastructure layers. The ModuleLoader automatically discovers service providers in `src/Modules/*/`.
|
||||||
|
|
||||||
|
### Creating Service Provider
|
||||||
|
|
||||||
|
```php
|
||||||
|
class MyModuleServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function register(InflectableContainer $container): void
|
||||||
|
{
|
||||||
|
// Register services
|
||||||
|
$container->register(MyController::class, [MyService::class]);
|
||||||
|
|
||||||
|
// Bind interfaces
|
||||||
|
$container->bind(MyRepositoryInterface::class, MyRepository::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Repository Implementation
|
||||||
|
|
||||||
|
Repositories use Commands/Queries pattern:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class MyRepository implements MyRepositoryInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private FetchAllQuery $fetchAll,
|
||||||
|
private SaveCommand $save,
|
||||||
|
private UnitOfWorkInterface $unitOfWork
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function save(Entity $entity): void
|
||||||
|
{
|
||||||
|
$this->unitOfWork->registerNew($entity);
|
||||||
|
$this->unitOfWork->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication System
|
||||||
|
|
||||||
|
Two authentication modes available:
|
||||||
|
|
||||||
|
- **Session-based**: Traditional session authentication
|
||||||
|
- **JWT-based**: Token-based authentication with firebase/php-jwt
|
||||||
|
|
||||||
|
Configured via `AUTH_TYPE` environment variable.
|
||||||
|
|
||||||
|
## Internationalization
|
||||||
|
|
||||||
|
Multi-language support using Symfony Translation:
|
||||||
|
|
||||||
|
- Default locale configurable via environment
|
||||||
|
- YAML translation files
|
||||||
|
- Module-specific translations supported
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Key environment variables (see `.env.example`):
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Database
|
||||||
|
DB_HOST=db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=foundation
|
||||||
|
DB_USERNAME=foundation_user
|
||||||
|
DB_PASSWORD=foundation_password
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
AUTH_TYPE=session
|
||||||
|
JWT_SECRET=your-secret-key
|
||||||
|
JWT_ACCESS_TOKEN_TTL=900
|
||||||
|
|
||||||
|
# Locale
|
||||||
|
APP_LOCALE=en
|
||||||
|
APP_SUPPORTED_LOCALES=en,de,fr
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
See skill: `/debug-setup` for detailed instructions.
|
||||||
|
|
||||||
|
Xdebug is pre-configured in the Docker environment for PHPStorm and VS Code. Debug sessions connect on port 9003 with automatic path mapping to `/var/www/html`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Complete documentation available in `./documentation/`:
|
||||||
|
|
||||||
|
- `application-core.md` - Framework architecture overview
|
||||||
|
- `application-layers.md` - Layer dependency rules
|
||||||
|
- `attribute-routing.md` - Route attribute system
|
||||||
|
- `dependency-injection.md` - DI container guide
|
||||||
|
- `docker-setup.md` - Docker environment details
|
||||||
|
- `unit-of-work.md` - Unit of Work pattern
|
||||||
|
- `authentication.md` - Authentication implementation
|
||||||
|
- `internationalization.md` - i18n setup
|
||||||
|
|
||||||
|
## Common Commands Reference
|
||||||
|
|
||||||
|
**Docker:**
|
||||||
|
```bash
|
||||||
|
make up # Start Docker environment
|
||||||
|
make down # Stop Docker environment
|
||||||
|
make shell # Access container shell
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
```bash
|
||||||
|
make install # Install composer dependencies
|
||||||
|
make dump-autoload # Refresh autoload files
|
||||||
|
```
|
||||||
|
|
||||||
|
**Testing:**
|
||||||
|
```bash
|
||||||
|
make test # Run all tests
|
||||||
|
make test-unit # Run unit tests
|
||||||
|
make test-coverage # Generate coverage report
|
||||||
|
```
|
||||||
|
|
||||||
|
**Quality:**
|
||||||
|
```bash
|
||||||
|
make phpstan # Static analysis
|
||||||
|
make deptrac # Layer analysis
|
||||||
|
make rector # Code quality preview
|
||||||
|
```
|
||||||
|
|
||||||
|
**Database:**
|
||||||
|
|
||||||
|
See skill: `/database-operations` for detailed instructions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Import schema
|
||||||
|
docker compose exec -T db mysql -u foundation_user -pfoundation_password foundation < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always respect layer boundaries** - Domain must remain pure
|
||||||
|
2. **Use value objects** for primitive obsession
|
||||||
|
3. **Keep controllers thin** - Delegate to application services
|
||||||
|
4. **Use repository interfaces** in Domain layer
|
||||||
|
5. **Implement CQRS** - Separate read and write operations
|
||||||
|
6. **Write tests** - Unit tests for domain, integration for infrastructure
|
||||||
|
7. **Use attributes for routes** - Don't register routes manually
|
||||||
|
8. **Follow PSR standards** - PSR-4 autoloading, PSR-7 HTTP messages
|
||||||
|
9. **Document public APIs** - Clear docblocks for interfaces
|
||||||
|
10. **Use type hints** - Leverage PHP 8.1+ type system
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Docker Issues
|
||||||
|
|
||||||
|
See skill: `/troubleshoot-docker` for detailed diagnostic steps and solutions.
|
||||||
|
|
||||||
|
Common issues include port conflicts (8000, 8080, 3306), permission errors, and container startup failures. Use `docker compose ps` and `docker compose logs` to diagnose problems.
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
|
||||||
|
- Verify database container is running: `docker compose ps`
|
||||||
|
- Check environment variables in `.env`
|
||||||
|
- Ensure database schema is imported
|
||||||
|
|
||||||
|
### Routing Issues
|
||||||
|
|
||||||
|
- Verify controller implements `ControllerInterface`
|
||||||
|
- Check route attributes syntax
|
||||||
|
- Ensure controller is registered in service provider
|
||||||
|
|
||||||
|
### Autoloading Issues
|
||||||
|
|
||||||
|
- Run `make dump-autoload` after adding new classes
|
||||||
|
- Verify PSR-4 namespace matches directory structure
|
||||||
Loading…
Reference in a new issue