235 lines
5.9 KiB
Markdown
235 lines
5.9 KiB
Markdown
# Coding Guidelines
|
|
|
|
## Allgemeine Prinzipien
|
|
|
|
Diese Guidelines definieren die Standards für die Entwicklung und Wartung des Projekts. Sie sorgen für Konsistenz, Lesbarkeit und Wartbarkeit des Codes.
|
|
|
|
## PHP-Version
|
|
|
|
- **PHP 8.4**: Die Codebase nutzt stets die neueste stabile PHP-Version (aktuell PHP 8.4)
|
|
- Alle neuen PHP-Sprachfeatures sollten, wo sinnvoll, genutzt werden
|
|
|
|
## Abhängigkeiten
|
|
|
|
- **Externe Abhängigkeiten vermeiden**: Das Projekt soll möglichst ohne externe Bibliotheken auskommen
|
|
- **Eigene Implementierungen bevorzugen**: Anstatt externe Pakete einzubinden, sollten eigene Lösungen entwickelt werden
|
|
- **Erlaubte Abhängigkeiten**: Nur die bereits in composer.json definierten Pakete dürfen verwendet werden
|
|
- **Neue Abhängigkeiten**: Müssen explizit genehmigt werden und sollten nur in Ausnahmefällen hinzugefügt werden
|
|
|
|
## Klassenstruktur
|
|
|
|
### Klassen
|
|
|
|
- **Alle Klassen müssen `final` sein**, es sei denn, es gibt einen zwingenden Grund für Vererbung
|
|
- **Keine abstrakten Klassen** - bevorzuge Interfaces und Kompositionen
|
|
- **Klassen sollten `readonly` sein**, wann immer möglich
|
|
- Interfaces verwenden, um Verträge zwischen Komponenten zu definieren
|
|
|
|
```php
|
|
// Gut
|
|
final readonly class AnalyticsManager
|
|
{
|
|
// ...
|
|
}
|
|
|
|
// Vermeiden
|
|
abstract class BaseManager
|
|
{
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Properties
|
|
|
|
- **Properties sollten `readonly` sein**, wann immer möglich
|
|
- Private Visibility für alle Properties, es sei denn, es gibt einen zwingenden Grund
|
|
- Typisierung ist obligatorisch für alle Properties
|
|
|
|
```php
|
|
// Gut
|
|
private readonly StorageInterface $storage;
|
|
|
|
// Vermeiden
|
|
public array $config;
|
|
```
|
|
|
|
## Methoden und Funktionen
|
|
|
|
- Typisierung ist obligatorisch für alle Parameter und Rückgabewerte
|
|
- Methoden sollten klein und fokussiert sein (Single Responsibility)
|
|
- Verwende named arguments für bessere Lesbarkeit
|
|
|
|
```php
|
|
// Gut
|
|
public function track(string $event, array $properties = [], ?string $userId = null): void
|
|
{
|
|
// ...
|
|
}
|
|
|
|
// Vermeiden
|
|
public function process($data)
|
|
{
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Dependency Injection
|
|
|
|
- Constructor Injection für alle Abhängigkeiten
|
|
- Keine Service Locator oder globale Zustände
|
|
- Verwende das `#[Initializer]`-Attribut für Service-Registrierung
|
|
|
|
```php
|
|
// Gut
|
|
public function __construct(
|
|
private readonly Configuration $config,
|
|
private readonly StorageInterface $storage
|
|
) {}
|
|
|
|
// Vermeiden
|
|
public function __construct()
|
|
{
|
|
$this->config = Container::get(Configuration::class);
|
|
}
|
|
```
|
|
|
|
## Moderne PHP-Features
|
|
|
|
### Nullable Types und Union Types
|
|
|
|
```php
|
|
public function findUser(?int $id): ?User
|
|
public function process(int|string $identifier): void
|
|
```
|
|
|
|
### Match Expression statt Switch
|
|
|
|
```php
|
|
// Gut
|
|
return match($storageType) {
|
|
'file' => new FileStorage($path),
|
|
'redis' => new RedisStorage($connection),
|
|
default => throw new \InvalidArgumentException("Nicht unterstützter Storage-Typ: {$storageType}")
|
|
};
|
|
|
|
// Vermeiden
|
|
switch ($storageType) {
|
|
case 'file':
|
|
return new FileStorage($path);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Named Arguments
|
|
|
|
```php
|
|
$this->createUser(
|
|
email: 'user@example.com',
|
|
role: 'admin',
|
|
sendWelcomeEmail: true
|
|
);
|
|
```
|
|
|
|
### Constructor Property Promotion
|
|
|
|
```php
|
|
public function __construct(
|
|
private readonly Configuration $config,
|
|
private readonly PathProvider $pathProvider
|
|
) {}
|
|
```
|
|
|
|
## Fehlerbehandlung
|
|
|
|
- Spezifische Exceptions werfen
|
|
- Typed Exceptions verwenden
|
|
- Early Return Pattern bevorzugen
|
|
|
|
```php
|
|
// Gut
|
|
if (!$this->isValid()) {
|
|
throw new ValidationException('Ungültige Daten');
|
|
}
|
|
|
|
// Vermeiden
|
|
if ($this->isValid()) {
|
|
// Lange Verarbeitung...
|
|
} else {
|
|
throw new Exception('Fehler');
|
|
}
|
|
```
|
|
|
|
## Testing
|
|
|
|
- Tests mit Pest-Framework schreiben
|
|
- Für jede öffentliche Methode sollte mindestens ein Test existieren
|
|
- Mocking nur für externe Abhängigkeiten verwenden
|
|
|
|
```php
|
|
test('track method records events correctly', function () {
|
|
$analytics = new Analytics($manager, $dispatcher);
|
|
$analytics->track('test_event', ['key' => 'value']);
|
|
|
|
expect($manager->getEvents())->toHaveCount(1);
|
|
});
|
|
```
|
|
|
|
## Dokumentation
|
|
|
|
- PHPDoc für alle öffentlichen Methoden und Klassen
|
|
- Typen in PHPDoc sollten den tatsächlichen Typen entsprechen
|
|
- Klare, beschreibende Kommentare für komplexe Logik
|
|
|
|
```php
|
|
/**
|
|
* Zeichnet ein Event auf und wendet Middleware an
|
|
*
|
|
* @param string $event Event-Name
|
|
* @param array $properties Event-Eigenschaften
|
|
* @param string|null $userId Benutzer-ID (optional)
|
|
*/
|
|
public function track(string $event, array $properties = [], ?string $userId = null): void
|
|
```
|
|
|
|
## Namenskonventionen
|
|
|
|
- **Klassen**: PascalCase (`AnalyticsManager`)
|
|
- **Methoden/Funktionen**: camelCase (`getEventStats()`)
|
|
- **Variablen**: camelCase (`$eventData`)
|
|
- **Konstanten**: SNAKE_CASE (`MAX_BATCH_SIZE`)
|
|
- **Dateien**: Klassenname.php (`AnalyticsManager.php`)
|
|
|
|
## Architektur
|
|
|
|
- Dependency Inversion Principle befolgen
|
|
- Interfaces für alle externen Abhängigkeiten
|
|
- Vermeide zirkuläre Abhängigkeiten zwischen Modulen
|
|
- Services sollten eine klare, fokussierte Verantwortung haben
|
|
|
|
## Performance
|
|
|
|
- Lazy Loading für ressourcenintensive Operationen
|
|
- Caching wo sinnvoll
|
|
- Datenstrukturen sorgfältig auswählen
|
|
|
|
## Security
|
|
|
|
- Alle Benutzereingaben validieren und bereinigen
|
|
- Prepared Statements für Datenbankabfragen
|
|
- Vermeidung von `eval()` und ähnlichen unsicheren Funktionen
|
|
- Sensible Daten niemals in Logs schreiben
|
|
|
|
## Best Practices
|
|
|
|
- **Immutability**: Bevorzuge unveränderliche Objekte
|
|
- **Pure Functions**: Bevorzuge reine Funktionen ohne Seiteneffekte
|
|
- **Enums**: Verwende Enums statt String-Konstanten für feste Optionssätze
|
|
- **DTOs**: Verwende Data Transfer Objects für Datentransport
|
|
- **Value Objects**: Verwende Value Objects für semantisch reiche Datentypen
|
|
|
|
## Refactoring
|
|
|
|
- Code, der diese Guidelines nicht erfüllt, sollte beim Bearbeiten aktualisiert werden
|
|
- Tests müssen vor und nach dem Refactoring bestehen
|
|
- Große Refactorings sollten in kleinen, separaten Commits erfolgen
|