This project includes a complete Docker development environment using Docker Compose. The setup consists of three isolated services communicating via a bridge network, optimized for local PHP development with hot-reload, debugging, and database management.
┌─────────────────────────────────────────┐
│ Docker Bridge Network │
│ (f1_network) │
│ │
│ ┌──────────────────┐ ┌──────────────┐ │
│ │ php-app │ │ mariadb │ │
│ │ (Port: 8080) │ │ (Port: 3306) │ │
│ │ PHP 8.2 + Xdebug │ │ MariaDB 11 │ │
│ │ Slim Framework │ │ f1_db_data │ │
│ │ Hot-reload ready │ │ (volume) │ │
│ └──────────────────┘ └──────────────┘ │
│ │ │ │
│ └───────────────────┘ │
│ (mysql queries) │
│ │
│ ┌──────────────────┐ │
│ │ phpmyadmin │ │
│ │ (Port: 8081) │ │
│ │ Database Admin │ │
│ └──────────────────┘ │
│ │ │
│ └──────────────────────────────┤
│ (http queries) │
└─────────────────────────────────────────┘
Host Machine:
localhost:8080 ──► php-app (API)
localhost:3306 ──► mariadb (if direct access needed)
localhost:8081 ──► phpmyadmin (Web UI)
- Image: Custom built from
Dockerfile - Base: PHP 8.2 CLI
- Key Features:
- Xdebug enabled for IDE debugging
- Composer pre-installed
- MySQL/MariaDB extensions (PDO, mysqli)
- Hot-reload via volume mounts
- Runs PHP development server on startup
- Volumes:
.(project root) →/var/www/html(code, hot-reload)./logs→/var/www/html/logs(application logs, persistent)vendor(named volume to optimize build performance)
- Environment: Database credentials from
.envfile - Health: Waits for mariadb service before starting
- Image:
mariadb:11.4 - Key Features:
- MariaDB 11.4 (compatible with MySQL 8.0 API)
- Auto-initializes database from
f1_db.sqlon first run - Health check ensures container is ready before php-app starts
- Volumes:
f1_db_data(named volume) →/var/lib/mysql(persistent data)./f1_db.sql→/docker-entrypoint-initdb.d/f1_db.sql(schema import)
- Environment:
- MARIADB_DATABASE=f1_db
- MARIADB_USER=f1_user
- MARIADB_PASSWORD=f1_password
- MARIADB_ALLOW_EMPTY_PASSWORD=yes
- Network: Only accessible via bridge network (not exposed to host by default, but port 3306 is mapped for direct access)
- Image:
phpmyadmin:latest - Key Features:
- Web-based database management
- Pre-configured to connect to mariadb service
- Environment:
- PMA_HOST=mariadb (resolves via bridge network)
- PMA_USER=f1_user
- PMA_PASSWORD=f1_password
- Access: http://localhost:8081
- Network Type: Bridge (
f1_network) - DNS Resolution: Service names resolve to container IPs (e.g.,
mariadbresolves to mariadb container IP) - Isolation: Services cannot access the host directly; only expose mapped ports
- Communication: php-app connects to mariadb via hostname
mariadb:3306
- Docker Desktop installed (Download)
- 2GB free disk space (for images and volumes)
-
Clone/Download the project
cd /path/to/PHP-RESTful-API -
Create
.envfrom.env.example(if not already present)cp .env.example .env
The
.envfile is pre-configured with Docker service defaults. -
Build and start services
docker-compose up -d
- First run builds the PHP image (may take 2-3 minutes)
- Subsequent runs are instant
- Services start in order: mariadb → php-app → phpmyadmin
- Database is automatically initialized from
f1_db.sql
-
Verify startup
docker-compose ps
All three services should show "Up" status.
-
Access the application
- API: http://localhost:8080
- phpMyAdmin: http://localhost:8081 (f1_user / f1_password)
# Start services in background
docker-compose up -d
# View running containers
docker-compose ps
# View logs for all services
docker-compose logs -f
# View logs for specific service
docker-compose logs -f php-app
docker-compose logs -f mariadb
# Stop services (preserves data)
docker-compose stop
# Stop and remove containers (keeps volumes)
docker-compose down
# Stop, remove containers, and volumes (full cleanup)
docker-compose down -v
# Rebuild services (if Dockerfile changes)
docker-compose up -d --build# Run Composer commands
docker-compose exec php-app composer install
docker-compose exec php-app composer update
docker-compose exec php-app composer test
docker-compose exec php-app composer fix
docker-compose exec php-app composer lint
# Run PHP directly
docker-compose exec php-app php -v
# Get interactive shell
docker-compose exec php-app bash# Access database via MySQL client
docker-compose exec mariadb mysql -u f1_user -pf1_password -D f1_db
# Dump database
docker-compose exec mariadb mysqldump -u f1_user -pf1_password f1_db > backup.sql
# Import SQL file
docker-compose exec -T mariadb mysql -u f1_user -pf1_password f1_db < f1_db.sql# Check what's in volumes
docker volume ls
# Inspect a volume
docker volume inspect f1_db_data
# View application logs on host
cat logs/app.log
# View Xdebug logs
cat logs/xdebug.log- Edit code in your IDE (on your host machine)
- Automatic reload: Changes instantly reflected in container
- No container rebuild needed (except Dockerfile changes)
# Example: Edit a controller
# → Changes visible at http://localhost:8080 immediately# Run all tests
docker-compose exec php-app composer test
# Run specific test file
docker-compose exec php-app vendor/bin/pest tests/Feature/YourTest.php
# Run tests with coverage
docker-compose exec php-app vendor/bin/pest --coverage# Fix coding standards
docker-compose exec php-app composer fix
# Run static analysis
docker-compose exec php-app composer lint
# Run OpenAPI linting (requires redocly-cli)
redocly lint openapi/openapi.yaml-
Configure PHP Interpreter:
- Settings → Languages & Frameworks → PHP
- Click
...next to PHP language level - Click
+→ From Docker Compose - Select service:
php-app - Click
OK
-
Configure Debug Server:
- Settings → Languages & Frameworks → PHP → Debug → Servers
- Click
+to add server - Name:
localhost - Host:
localhost - Port:
8080 - Debugger:
Xdebug
-
Start Debugging:
- Set breakpoint in code
- Press
Ctrl+Alt+F9(or use Debug menu) to start listening for connections - Navigate to endpoint in browser
- Code execution pauses at breakpoint
-
Install Xdebug extension by Felice Polisky
-
Create
.vscode/launch.json:{ "version": "0.2.0", "configurations": [ { "name": "Listen for Xdebug", "type": "php", "request": "launch", "port": 9003, "pathMapping": { "/var/www/html": "${workspaceFolder}" } } ] } -
Start listening and set breakpoints
All database configuration is managed via .env file:
DB_HOST=mariadb # Service hostname on bridge network
DB_PORT=3306 # MariaDB port
DB_DATABASE=f1_db # Database name
DB_USERNAME=f1_user # Database user
DB_PASSWORD=f1_password # Database passwordFor local development without Docker, update .env with your local credentials:
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=f1_db
DB_USERNAME=root
DB_PASSWORD=The application reads these via config/bootstrap.php → config/dependencies.php.
# Check logs
docker-compose logs mariadb
docker-compose logs php-app
# Verify Docker is running
docker ps
# Check port conflicts
lsof -i :8080 # macOS/Linux
netstat -ano | findstr :8080 # Windows# Test connection from php-app
docker-compose exec php-app mysql -h mariadb -u f1_user -pf1_password -D f1_db -e "SELECT 1;"
# If fails, restart mariadb
docker-compose restart mariadb
# Or force rebuild
docker-compose down -v
docker-compose up -d# Vendor directory is mounted as named volume for performance
# If composer install fails, rebuild
docker-compose up -d --build
# Or manually install
docker-compose exec php-app composer install --prefer-dist- Verify port 9003 is not blocked by firewall
- Check Xdebug logs:
cat logs/xdebug.log - Ensure IDE is listening for connections
- Rebuild container:
docker-compose up -d --build
- Project code (
.): Mounted for hot-reload—changes visible immediately - Logs: Separate mount to avoid rebuilding when logs grow
- vendor: Named volume (not synced) for build performance—survive container restarts
- Multi-stage Dockerfile (future enhancement for production)
.dockerignoreexcludes unnecessary files- Composer optimizations (--prefer-dist, --optimize-autoloader)
- Replace Xdebug with production PHP extensions
- Use Nginx/Apache instead of PHP dev server
- Implement separate production Dockerfile
- Use environment-specific configs
- Add persistent backup strategy for database volume
- Implement proper logging infrastructure
project-root/
├── Dockerfile # Development PHP image
├── docker-compose.yml # Service orchestration
├── docker-entrypoint.sh # Container startup script
├── .dockerignore # Build exclusions
├── .env # Local environment vars (not in git)
├── .env.example # Template for .env
├── config/
│ ├── bootstrap.php # Loads .env file
│ └── dependencies.php # Uses $_ENV for database config
├── logs/ # Mounted volume (persistent)
├── f1_db.sql # Database schema (imported on startup)
└── ... (other project files)
- Start developing:
docker-compose up -d - Test the API: http://localhost:8080
- Check README.md for API documentation
- Run tests:
docker-compose exec php-app composer test - Set up IDE debugging (see Xdebug section above)