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
This commit is contained in:
598
docs/contributing/code-style.md
Normal file
598
docs/contributing/code-style.md
Normal file
@@ -0,0 +1,598 @@
|
||||
# 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)](https://www.php-fig.org/psr/) des PHP Framework Interop Group (PHP-FIG):
|
||||
|
||||
- [PSR-1: Basic Coding Standard](https://www.php-fig.org/psr/psr-1/)
|
||||
- [PSR-2: Coding Style Guide](https://www.php-fig.org/psr/psr-2/) und [PSR-12: Extended Coding Style](https://www.php-fig.org/psr/psr-12/) (ersetzt PSR-2)
|
||||
- [PSR-4: Autoloading Standard](https://www.php-fig.org/psr/psr-4/)
|
||||
|
||||
### 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`.
|
||||
|
||||
```php
|
||||
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.
|
||||
|
||||
```php
|
||||
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.
|
||||
|
||||
```php
|
||||
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.
|
||||
|
||||
```php
|
||||
private string $firstName;
|
||||
protected int $itemCount;
|
||||
public bool $isActive;
|
||||
```
|
||||
|
||||
#### Konstanten
|
||||
|
||||
- Klassenkonstanten MÜSSEN in `UPPER_CASE` mit Unterstrichen als Trennzeichen definiert werden.
|
||||
|
||||
```php
|
||||
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.
|
||||
|
||||
```php
|
||||
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
|
||||
<?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.
|
||||
|
||||
```php
|
||||
// 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
|
||||
<?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.
|
||||
|
||||
```php
|
||||
/**
|
||||
* 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.
|
||||
|
||||
```php
|
||||
// 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](https://eslint.org/) 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.
|
||||
|
||||
```javascript
|
||||
// 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.
|
||||
|
||||
```javascript
|
||||
// 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](https://stylelint.io/) 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`
|
||||
|
||||
```css
|
||||
/* 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.
|
||||
|
||||
```css
|
||||
/* 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:
|
||||
|
||||
- [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) für PHP
|
||||
- [ESLint](https://eslint.org/) für JavaScript
|
||||
- [Stylelint](https://stylelint.io/) für CSS
|
||||
|
||||
### Automatische Formatierung
|
||||
|
||||
Das Projekt unterstützt automatische Codeformatierung mit:
|
||||
|
||||
- [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) für PHP
|
||||
- [Prettier](https://prettier.io/) für JavaScript und CSS
|
||||
|
||||
### Verwendung der Tools
|
||||
|
||||
#### PHP_CodeSniffer
|
||||
|
||||
```bash
|
||||
# Überprüfen des Codes
|
||||
vendor/bin/phpcs src tests
|
||||
|
||||
# Automatisches Beheben von Problemen
|
||||
vendor/bin/phpcbf src tests
|
||||
```
|
||||
|
||||
#### PHP-CS-Fixer
|
||||
|
||||
```bash
|
||||
# Ü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
|
||||
|
||||
```bash
|
||||
# Überprüfen des Codes
|
||||
npm run lint:js
|
||||
|
||||
# Automatisches Beheben von Problemen
|
||||
npm run lint:js:fix
|
||||
```
|
||||
|
||||
#### Stylelint
|
||||
|
||||
```bash
|
||||
# Überprüfen des Codes
|
||||
npm run lint:css
|
||||
|
||||
# Automatisches Beheben von Problemen
|
||||
npm run lint:css:fix
|
||||
```
|
||||
|
||||
#### Prettier
|
||||
|
||||
```bash
|
||||
# Formatieren von JavaScript und CSS
|
||||
npm run format
|
||||
```
|
||||
|
||||
## Weitere Informationen
|
||||
|
||||
- [Pull Request-Anleitung](pull-requests.md): Erfahren Sie, wie Sie Pull Requests erstellen und überprüfen.
|
||||
- [Dokumentations-Anleitung](documentation.md): Erfahren Sie, wie Sie zur Dokumentation beitragen können.
|
||||
- [Architekturübersicht](../architecture/overview.md): Überblick über die Architektur des Frameworks.
|
||||
Reference in New Issue
Block a user