The previous 'always sync' approach failed because it tried to copy files from /workspace/repo/ which doesn't exist on the production server. The SSH heredoc (<<EOF) executes commands ON the production server, not in the Gitea Actions workspace. File paths inside heredoc are relative to the production server's filesystem. This commit adds an scp step BEFORE the SSH heredoc to transfer docker-compose.base.yml and docker-compose.production.yml from the Actions workspace to the production server. This ensures the build: null overrides (commit2e539ed) reach production and services can restart without build context errors. Changes: - Added scp command to deploy docker-compose files before SSH deployment - Changed file sync check from 'cp' to file existence validation - Updated comments to clarify rsync-based deployment architecture Related commits: -0b342c6: Sequential push strategy -08f6f64: Stable git-SHA IMAGE_TAG -2e539ed: build: null overrides -0db73df: Always-sync docker-compose (incorrect implementation) -3091205: Trigger pipeline with source file change
Custom PHP Framework
Ein modulares und erweiterbares PHP-Framework mit API-Client-Integration, Attribut-basiertem Routing und Production-Ready Infrastructure.
🚀 Quick Start
Development Setup
# Repository klonen
git clone https://github.com/username/framework.git
cd framework
# Mit Docker starten
make up
# Oder: Manuelle Installation
composer install
npm install
# Neue Base+Override Struktur: .env.base + .env.local
# Siehe ENV_SETUP.md für Details
# Für Backward Compatibility: cp .env.example .env (wird als Fallback geladen)
Production Deployment
Neu im Projekt? Starte hier:
📖 Quick Start Guide - Deployment in 30 Minuten
Vollständige Deployment-Dokumentation:
- Deployment README - Navigation und Übersicht
- Deployment Checklist - Printable Checkliste
- Deployment Workflow - Schritt-für-Schritt Anleitung
- Production Guide - Umfassende Referenz
- Ansible Deployment - Infrastructure as Code
Anwendung starten
Das Framework kann entweder mit dem eingebauten PHP-Webserver oder mit einem Webserver wie Apache oder Nginx ausgeführt werden.
Eingebauter PHP-Webserver
php -S localhost:8000 -t public/
Nginx-Konfiguration
server {
listen 80;
server_name yourdomain.com;
root /path/to/framework/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
Production-Ready Features
Monitoring & Health Checks
- 🏥 Multiple Health Endpoints -
/health/summary,/health/detailed,/health/category/{category} - 📊 Prometheus Metrics -
/metricsendpoint for monitoring integration - 🔍 Auto-Discovery - Automatic health check registration via attributes
- ⚡ Real-time Monitoring - Grafana dashboards for production visibility
Security
- 🔒 SSL/TLS - Automatic Let's Encrypt certificate management
- 🔐 Vault Integration - Encrypted secrets management
- 🛡️ WAF - Web Application Firewall with OWASP protection
- 🚨 Security Headers - CSP, HSTS, X-Frame-Options, etc.
- 🔑 CSRF Protection - Automatic token-based protection
Logging
- 📝 Structured Logging - JSON-formatted logs with context
- 🎯 Multiple Strategies - Production, High-Performance, Debug, Staging
- 📈 Log Aggregation - Reduce log volume by 70-90%
- 🔄 Log Rotation - Automatic rotation with configurable retention
- 📊 Performance Metrics - Built-in performance tracking
Deployment
- 🚀 Zero-Downtime - Blue-green deployment support
- 🔄 Rollback Support - Safe migration rollback architecture
- 📦 Docker Compose - Production-ready container orchestration
- 🤖 Ansible - Optional infrastructure as code
- ✅ Health-Verified - Automated health checks during deployment
Architektur
Das Framework folgt einer modularen Architektur mit den folgenden Hauptkomponenten:
Core
- Application: Steuert den Anwendungslebenszyklus
- Container: Dependency Injection Container
- Router: Attribute-basiertes Routing
- Middleware: HTTP-Middleware-Chain
HTTP
DateTime-Modul für das Framework
Dieses Modul bietet eine umfassende Unterstützung für DateTime-Operationen im Framework mit einem Fokus auf Testbarkeit, Immutabilität und Zeitzonen-Unterstützung.
Hauptkomponenten
Clock-Interface
Das Clock-Interface abstrahiert den Zugriff auf die aktuelle Zeit und stellt sicher, dass Anwendungscode testbar bleibt:
// Produktionscode verwendet SystemClock
$clock = new SystemClock();
$now = $clock->now();
// Testcode kann FrozenClock verwenden
$clock = new FrozenClock('2021-01-01 00:00:00');
$frozenTime = $clock->now(); // Gibt immer das gleiche Datum zurück
FrozenClock für Tests
Die FrozenClock-Klasse ist besonders nützlich für Tests, bei denen Sie die Zeit kontrollieren müssen:
$clock = new FrozenClock('2021-01-01 00:00:00');
// Zeit vorstellen
$clock->moveForward('PT1H'); // Eine Stunde vorstellen
// Zeit zurückstellen
$clock->moveBackward('P1D'); // Einen Tag zurückstellen
// Zeit direkt setzen
$clock->setTo('2022-01-01 00:00:00');
DateTimeFormatter
Der DateTimeFormatter bietet bequeme Methoden zum konsistenten Formatieren von Datums- und Zeitwerten:
$formatter = new DateTimeFormatter();
// ISO8601-Format (für APIs und JSON)
$iso = $formatter->formatIso8601($date); // 2021-01-01T12:34:56+00:00
// SQL-Format
$sql = $formatter->formatSql($date); // 2021-01-01 12:34:56
// Nur Datum
$dateOnly = $formatter->formatDate($date); // 2021-01-01
// Benutzerdefiniertes Format
$custom = $formatter->format($date, 'd.m.Y H:i'); // 01.01.2021 12:34
DateRange
Die DateRange-Klasse ermöglicht die einfache Arbeit mit Zeiträumen:
// Zeitraum erstellen
$range = DateRange::fromStrings('2021-01-01', '2021-01-31');
// Prüfen, ob ein Datum im Bereich liegt
$isInRange = $range->contains($someDate);
// Prüfen, ob sich Bereiche überschneiden
$doOverlap = $range->overlaps($otherRange);
// Dauer berechnen
$seconds = $range->getDurationInSeconds();
DI-Container Integration
Das Modul lässt sich nahtlos in den DI-Container des Frameworks integrieren:
// In Ihrer Bootstrap-Datei oder Service-Provider
$container->singleton(Clock::class, SystemClock::class);
$container->singleton(DateTimeFormatter::class, DateTimeFormatter::class);
// Oder den bereitgestellten ServiceProvider verwenden
$dateTimeServiceProvider = new DateTimeServiceProvider();
$dateTimeServiceProvider->register($container);
Testunterstützung
Für Tests kann die FrozenClock einfach registriert werden:
// In Ihrem Test-Setup
$frozenClock = new FrozenClock('2021-01-01 00:00:00');
$container->instance(Clock::class, $frozenClock);
// Zeit während des Tests manipulieren
$frozenClock->moveForward('PT1H');
- Request/Response: HTTP-Nachrichten
- HttpClient: HTTP-Client für API-Anfragen
API
- ApiRequestTrait: Hilfsklasse für API-Clients
- Integrierte Clients: RapidMail, Shopify
Features
- Attribut-basiertes Routing: Deklaratives Routing mit PHP 8 Attributen
- Dependency Injection: Automatische Auflösung von Abhängigkeiten
- Middleware-System: Erweiterbare HTTP-Middleware-Chain
- API-Clients: Integrierte Clients für gängige APIs
- Konfigurationsmanagement: Flexible Konfiguration mit Umgebungsvariablen
Beispiele
Controller mit Routing
<?php
namespace App\Application\Example;
use App\Framework\Attributes\Route;
use App\Framework\Router\Result\JsonResult;
final class ExampleController
{
#[Route(path: '/api/example', method: 'GET')]
public function getExample(): JsonResult
{
return new JsonResult([
'success' => true,
'message' => 'Hello World!'
]);
}
}
API-Client verwenden
<?php
namespace App\Application\Newsletter;
use App\Framework\Attributes\Route;use App\Framework\Http\Status;use App\Framework\Router\Result\JsonResult;use App\Infrastructure\Api\RapidMailClient;use Archive\Config\ApiConfig;
final class NewsletterController
{
private RapidMailClient $client;
public function __construct()
{
$this->client = new RapidMailClient(
ApiConfig::RAPIDMAIL_USERNAME->value,
ApiConfig::RAPIDMAIL_PASSWORD->value
);
}
#[Route(path: '/newsletter/register', method: 'POST')]
public function register(NewsletterRequest $request): JsonResult
{
$result = $this->client->addRecipient(
$request->name,
$request->email,
ApiConfig::getRapidmailListId()
);
return new JsonResult(
['success' => true, 'data' => $result],
Status::CREATED
);
}
}
Erweiterung
Das Framework kann durch eigene Komponenten und Module erweitert werden:
- Eigene Middleware: Erstellen Sie benutzerdefinierte Middleware-Klassen
- Attribute: Definieren Sie eigene Attribute und Mapper
- API-Clients: Integrieren Sie zusätzliche APIs mit dem ApiRequestTrait
Lizenz
MIT
# Starten
make up
# Logs anzeigen
make logs
# Setup-Playbook (Server einmalig vorbereiten)
make setup
# Deployment (Code + Compose auf Server bringen)
make deploy