Files
michaelschiemer/docs/contributing/code-style.md
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

14 KiB

Coding Standards

Diese Dokumentation beschreibt die Coding Standards, die bei der Entwicklung des Frameworks eingehalten werden sollten. Die Einhaltung dieser Standards gewährleistet einen konsistenten Codestil im gesamten Projekt und erleichtert die Zusammenarbeit zwischen Entwicklern.

PHP-Standards

PSR-Standards

Das Framework folgt den PHP Standards Recommendations (PSR) des PHP Framework Interop Group (PHP-FIG):

Allgemeine Richtlinien

Dateien

  • Dateien MÜSSEN die UTF-8-Kodierung ohne BOM verwenden.
  • Dateien SOLLTEN entweder Deklarationen (Klassen, Funktionen, Konstanten) ODER Nebeneffekte (z.B. Ausgaben, Änderungen an .ini-Einstellungen) enthalten, aber NICHT beides.
  • Dateinamen MÜSSEN PascalCase für Klassen verwenden (z.B. UserController.php).
  • Alle PHP-Dateien MÜSSEN mit einer einzelnen leeren Zeile enden.

PHP-Tags

  • PHP-Code MUSS die langen Tags <?php ?> oder die kurzen Echo-Tags <?= ?> verwenden.
  • Die schließenden Tags ?> SOLLTEN bei Dateien, die nur PHP enthalten, weggelassen werden.

Zeichenkodierung und Zeilenumbrüche

  • Der Code MUSS UTF-8 ohne BOM verwenden.
  • Zeilenumbrüche MÜSSEN im Unix-Format (LF) sein.

Namenskonventionen

Namespaces

  • Namespaces MÜSSEN PascalCase verwenden.
  • Der Basis-Namespace für das Framework ist App\Framework.
  • Der Basis-Namespace für anwendungsspezifischen Code ist App\Application.
namespace App\Framework\Http;
namespace App\Application\Controllers;

Klassen

  • Klassennamen MÜSSEN PascalCase verwenden.
  • Schnittstellen MÜSSEN mit dem Suffix Interface enden.
  • Abstrakte Klassen SOLLTEN mit dem Präfix Abstract beginnen.
  • Traits SOLLTEN mit dem Suffix Trait enden.
class UserController
interface RepositoryInterface
abstract class AbstractController
trait LoggableTrait

Methoden und Funktionen

  • Methoden- und Funktionsnamen MÜSSEN camelCase verwenden.
  • Methoden SOLLTEN Verben sein, die ihre Aktion beschreiben.
public function getUserById(int $id): ?User
public function createUser(array $data): User
public function updateUserProfile(User $user, array $data): bool

Eigenschaften und Variablen

  • Eigenschaften und Variablen MÜSSEN camelCase verwenden.
  • Private und geschützte Eigenschaften SOLLTEN NICHT mit einem Unterstrich beginnen.
private string $firstName;
protected int $itemCount;
public bool $isActive;

Konstanten

  • Klassenkonstanten MÜSSEN in UPPER_CASE mit Unterstrichen als Trennzeichen definiert werden.
public const MAX_ATTEMPTS = 5;
private const DEFAULT_TIMEOUT = 30;

Enums

  • Enum-Namen MÜSSEN PascalCase verwenden.
  • Enum-Werte MÜSSEN UPPER_CASE mit Unterstrichen als Trennzeichen verwenden.
enum HttpMethod
{
    case GET;
    case POST;
    case PUT;
    case DELETE;
}

enum LogLevel: string
{
    case DEBUG = 'debug';
    case INFO = 'info';
    case WARNING = 'warning';
    case ERROR = 'error';
}

Codestruktur

Klassen

  • Jede Klasse MUSS in einer eigenen Datei definiert werden.
  • Die Struktur einer Klasse SOLLTE wie folgt sein:
    1. Namespace-Deklaration
    2. Verwendete Imports (alphabetisch sortiert)
    3. Klassendokumentation (PHPDoc)
    4. Klassendeklaration
    5. Konstanten (öffentlich, geschützt, privat)
    6. Eigenschaften (öffentlich, geschützt, privat)
    7. Konstruktor und Destruktor
    8. Öffentliche Methoden
    9. Geschützte Methoden
    10. Private Methoden
<?php

namespace App\Framework\Database;

use App\Framework\Contracts\ConnectionInterface;
use App\Framework\Exceptions\DatabaseException;

/**
 * Database connection manager.
 */
class Connection implements ConnectionInterface
{
    /**
     * Default connection timeout in seconds.
     */
    public const DEFAULT_TIMEOUT = 30;
    
    /**
     * The active PDO connection.
     */
    private ?\PDO $pdo = null;
    
    /**
     * The database configuration.
     */
    private array $config;
    
    /**
     * Create a new database connection instance.
     */
    public function __construct(array $config)
    {
        $this->config = $config;
    }
    
    /**
     * Get the PDO connection.
     */
    public function getPdo(): \PDO
    {
        if ($this->pdo === null) {
            $this->connect();
        }
        
        return $this->pdo;
    }
    
    /**
     * Establish a database connection.
     */
    protected function connect(): void
    {
        // Implementation...
    }
    
    /**
     * Build the DSN string.
     */
    private function buildDsn(): string
    {
        // Implementation...
    }
}

Methoden

  • Methoden SOLLTEN eine einzige Verantwortung haben (Single Responsibility Principle).
  • Methoden SOLLTEN kurz sein und eine Sache gut machen.
  • Methoden SOLLTEN früh zurückkehren, um die Verschachtelungstiefe zu reduzieren.
// Gut
public function getUserById(int $id): ?User
{
    if ($id <= 0) {
        return null;
    }
    
    return $this->userRepository->find($id);
}

// Schlecht
public function getUserById(int $id): ?User
{
    if ($id > 0) {
        $user = $this->userRepository->find($id);
        if ($user !== null) {
            return $user;
        } else {
            return null;
        }
    } else {
        return null;
    }
}

Typisierung

  • Der Code MUSS strikte Typisierung verwenden.
  • Jede Datei SOLLTE mit declare(strict_types=1); beginnen.
  • Methoden und Funktionen MÜSSEN Typdeklarationen für Parameter und Rückgabewerte verwenden.
  • Eigenschaften MÜSSEN Typdeklarationen verwenden.
<?php

declare(strict_types=1);

namespace App\Framework\Http;

class Request
{
    private array $query;
    private array $request;
    
    public function __construct(array $query, array $request)
    {
        $this->query = $query;
        $this->request = $request;
    }
    
    public function query(string $key, mixed $default = null): mixed
    {
        return $this->query[$key] ?? $default;
    }
    
    public function input(string $key, mixed $default = null): mixed
    {
        return $this->request[$key] ?? $default;
    }
    
    public function has(string $key): bool
    {
        return isset($this->request[$key]);
    }
}

Dokumentation

  • Alle Klassen, Methoden, Eigenschaften und Konstanten MÜSSEN mit PHPDoc-Kommentaren dokumentiert werden.
  • PHPDoc-Kommentare MÜSSEN eine Beschreibung und alle relevanten Tags enthalten.
  • Komplexe Codeabschnitte SOLLTEN mit Inline-Kommentaren erklärt werden.
/**
 * Represents an HTTP request.
 */
class Request
{
    /**
     * The query parameters.
     *
     * @var array<string, mixed>
     */
    private array $query;
    
    /**
     * Get a query parameter.
     *
     * @param string $key     The parameter key
     * @param mixed  $default The default value if the parameter does not exist
     *
     * @return mixed The parameter value or the default value
     */
    public function query(string $key, mixed $default = null): mixed
    {
        return $this->query[$key] ?? $default;
    }
}

Fehlerbehandlung

  • Exceptions SOLLTEN für außergewöhnliche Bedingungen verwendet werden.
  • Benutzerdefinierte Exceptions SOLLTEN von einer Basis-Exception-Klasse erben.
  • Exception-Nachrichten SOLLTEN klar und informativ sein.
// Definieren einer benutzerdefinierten Exception
class DatabaseException extends \RuntimeException
{
    // ...
}

// Werfen einer Exception
if (!$this->isConnected()) {
    throw new DatabaseException('Database connection failed: ' . $this->getLastError());
}

// Fangen einer Exception
try {
    $user = $this->userRepository->find($id);
} catch (DatabaseException $e) {
    $this->logger->error('Database error: ' . $e->getMessage());
    throw new ServiceException('Could not retrieve user data', 0, $e);
}

JavaScript-Standards

Allgemeine Richtlinien

  • JavaScript-Code SOLLTE ESLint mit der Konfiguration des Projekts verwenden.
  • ES6+ Features SOLLTEN verwendet werden.
  • Semicolons MÜSSEN verwendet werden.

Namenskonventionen

  • Variablen und Funktionen MÜSSEN camelCase verwenden.
  • Klassen MÜSSEN PascalCase verwenden.
  • Konstanten MÜSSEN UPPER_CASE mit Unterstrichen als Trennzeichen verwenden.
  • Private Eigenschaften und Methoden SOLLTEN mit einem Unterstrich beginnen.
// Variablen
const maxItems = 10;
let currentIndex = 0;

// Konstanten
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = 'https://api.example.com';

// Funktionen
function getUserData(userId) {
    // ...
}

// Klassen
class UserService {
    constructor(apiClient) {
        this._apiClient = apiClient;
    }
    
    async getUser(id) {
        return this._apiClient.get(`/users/${id}`);
    }
    
    _handleError(error) {
        console.error('API error:', error);
    }
}

Codestruktur

  • Jede Datei SOLLTE einen einzelnen Export haben.
  • Imports SOLLTEN am Anfang der Datei stehen und nach Typ gruppiert werden.
  • Funktionen SOLLTEN kurz sein und eine Sache gut machen.
// Imports
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';

// Eigene Imports
import { UserService } from '../services/UserService';
import { ErrorBoundary } from '../components/ErrorBoundary';

// Komponente
function UserProfile({ userId }) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);
    
    useEffect(() => {
        const fetchUser = async () => {
            try {
                setLoading(true);
                const userService = new UserService();
                const userData = await userService.getUser(userId);
                setUser(userData);
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };
        
        fetchUser();
    }, [userId]);
    
    if (loading) {
        return <div>Loading...</div>;
    }
    
    if (error) {
        return <div>Error: {error}</div>;
    }
    
    return (
        <div>
            <h1>{user.name}</h1>
            <p>{user.email}</p>
        </div>
    );
}

UserProfile.propTypes = {
    userId: PropTypes.string.isRequired
};

export default UserProfile;

CSS-Standards

Allgemeine Richtlinien

  • CSS-Code SOLLTE Stylelint mit der Konfiguration des Projekts verwenden.
  • CSS-Präprozessoren wie SASS oder LESS KÖNNEN verwendet werden.
  • CSS-Klassen SOLLTEN nach dem BEM-Muster (Block, Element, Modifier) benannt werden.

Namenskonventionen

  • CSS-Klassen MÜSSEN kebab-case verwenden.
  • BEM-Notation: .block__element--modifier
/* Block */
.user-card {
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    padding: 16px;
}

/* Element */
.user-card__avatar {
    border-radius: 50%;
    height: 64px;
    width: 64px;
}

/* Element */
.user-card__name {
    font-size: 18px;
    font-weight: bold;
    margin-top: 8px;
}

/* Modifier */
.user-card--premium {
    background-color: #f8f8f8;
    border: 1px solid #ddd;
}

Codestruktur

  • CSS-Regeln SOLLTEN nach Komponenten organisiert werden.
  • Medienabfragen SOLLTEN am Ende jeder Komponente stehen.
  • Vendor-Präfixe SOLLTEN automatisch mit Tools wie Autoprefixer hinzugefügt werden.
/* Komponente */
.button {
    background-color: #007bff;
    border: none;
    border-radius: 4px;
    color: #fff;
    cursor: pointer;
    font-size: 16px;
    padding: 8px 16px;
    transition: background-color 0.2s;
}

.button:hover {
    background-color: #0069d9;
}

.button:focus {
    outline: none;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
}

.button--secondary {
    background-color: #6c757d;
}

.button--secondary:hover {
    background-color: #5a6268;
}

/* Medienabfragen */
@media (max-width: 768px) {
    .button {
        font-size: 14px;
        padding: 6px 12px;
    }
}

Tools und Automatisierung

Code-Linting

Das Projekt verwendet verschiedene Linting-Tools, um die Einhaltung der Coding Standards zu gewährleisten:

Automatische Formatierung

Das Projekt unterstützt automatische Codeformatierung mit:

Verwendung der Tools

PHP_CodeSniffer

# Überprüfen des Codes
vendor/bin/phpcs src tests

# Automatisches Beheben von Problemen
vendor/bin/phpcbf src tests

PHP-CS-Fixer

# Überprüfen des Codes
vendor/bin/php-cs-fixer fix --dry-run --diff src

# Automatisches Beheben von Problemen
vendor/bin/php-cs-fixer fix src

ESLint

# Überprüfen des Codes
npm run lint:js

# Automatisches Beheben von Problemen
npm run lint:js:fix

Stylelint

# Überprüfen des Codes
npm run lint:css

# Automatisches Beheben von Problemen
npm run lint:css:fix

Prettier

# Formatieren von JavaScript und CSS
npm run format

Weitere Informationen