Best Practices für Modulentwicklung
Überblick
Abschnitt betitelt „Überblick“Dieses Dokument konsolidiert Best Practices für die Entwicklung hochwertiger XOOPS-Module. Die Befolgung dieser Richtlinien stellt sicher, dass Module wartbar, sicher und leistungsfähig sind.
Architektur
Abschnitt betitelt „Architektur“Folgen Sie Clean Architecture
Abschnitt betitelt „Folgen Sie Clean Architecture“Organisieren Sie den Code in Schichten:
src/├── Domain/ # Geschäftslogik, Entitäten├── Application/ # Use Cases, Services├── Infrastructure/ # Datenbank, externe Services└── Presentation/ # Controller, TemplatesSingle Responsibility
Abschnitt betitelt „Single Responsibility“Jede Klasse sollte einen Grund haben, sich zu ändern:
// Gut: Fokussierte Klassenclass ArticleRepository { /* nur Persistenz */ }class ArticleValidator { /* nur Validierung */ }class ArticleNotifier { /* nur Benachrichtigungen */ }
// Schlecht: Gott-Klasseclass Article { public function save() { } public function validate() { } public function notify() { } public function generatePDF() { }}Dependency Injection
Abschnitt betitelt „Dependency Injection“Injizieren Sie Abhängigkeiten, erstellen Sie sie nicht:
// Gutpublic function __construct( private readonly ArticleRepositoryInterface $repository) {}
// Schlechtpublic function __construct() { $this->repository = new ArticleRepository();}Code-Qualität
Abschnitt betitelt „Code-Qualität“Typsicherheit
Abschnitt betitelt „Typsicherheit“Verwenden Sie strikte Typen und Typdeklarationen:
<?php
declare(strict_types=1);
final class ArticleService{ public function findById(int $id): ?Article { // ... }
public function create(CreateArticleDTO $dto): Article { // ... }}Fehlerbehandlung
Abschnitt betitelt „Fehlerbehandlung“Verwenden Sie Exceptions angemessen:
// Werfen Sie spezifische Exceptionsthrow new ArticleNotFoundException($id);throw new ValidationException($errors);throw new UnauthorizedException('Cannot edit this article');
// Fangen Sie auf angemessener Ebenetry { $article = $service->create($dto);} catch (ValidationException $e) { return $this->renderForm($e->getErrors());} catch (UnauthorizedException $e) { return $this->redirectToLogin();}Null-Sicherheit
Abschnitt betitelt „Null-Sicherheit“Vermeiden Sie null, wo möglich:
// Verwenden Sie Null-Objekt-Musterpublic function getAuthor(): UserInterface{ return $this->author ?? new AnonymousUser();}
// Verwenden Sie Optional/Maybe-Musterpublic function findById(int $id): ?Article{ // Explizit nullbar zurück}Datenbank
Abschnitt betitelt „Datenbank“Verwenden Sie Criteria für Queries
Abschnitt betitelt „Verwenden Sie Criteria für Queries“$criteria = new CriteriaCompo();$criteria->add(new Criteria('status', 'published'));$criteria->add(new Criteria('category_id', $categoryId));$criteria->setSort('created_at');$criteria->setOrder('DESC');$criteria->setLimit($limit);
$items = $handler->getObjects($criteria);Entwenden Sie Benutzereingaben
Abschnitt betitelt „Entwenden Sie Benutzereingaben“$sql = sprintf( "SELECT * FROM %s WHERE id = %d AND title = %s", $db->prefix('mymodule_items'), intval($id), $db->quoteString($title));Verwenden Sie Transaktionen
Abschnitt betitelt „Verwenden Sie Transaktionen“$db->query('START TRANSACTION');
try { $handler->insert($article); $handler->insert($metadata); $db->query('COMMIT');} catch (\Exception $e) { $db->query('ROLLBACK'); throw $e;}Sicherheit
Abschnitt betitelt „Sicherheit“Validieren Sie immer Eingaben
Abschnitt betitelt „Validieren Sie immer Eingaben“use Xmf\Request;
$id = Request::getInt('id', 0);$title = Request::getString('title', '');$data = Request::getArray('data', []);
// Zusätzliche Validierungif (strlen($title) < 5) { throw new ValidationException('Title too short');}Verwenden Sie CSRF-Tokens
Abschnitt betitelt „Verwenden Sie CSRF-Tokens“// Im Formular$form->addElement(new XoopsFormHiddenToken());
// Bei Absendungif (!$GLOBALS['xoopsSecurity']->check()) { redirect_header('index.php', 3, 'Invalid token');}Überprüfen Sie Berechtigungen
Abschnitt betitelt „Überprüfen Sie Berechtigungen“if (!$helper->isUserAdmin()) { redirect_header('index.php', 3, _NOPERM);}
if (!$permHandler->isGranted('edit', $categoryId)) { throw new UnauthorizedException();}Leistung
Abschnitt betitelt „Leistung“Verwenden Sie Caching
Abschnitt betitelt „Verwenden Sie Caching“$cache = $helper->getCache();$cacheKey = "articles_{$categoryId}_{$limit}";
$articles = $cache->read($cacheKey);if ($articles === false) { $articles = $handler->getArticles($categoryId, $limit); $cache->write($cacheKey, $articles, 3600);}Optimieren Sie Queries
Abschnitt betitelt „Optimieren Sie Queries“// Verwenden Sie Indizes// Hinzufügen zu sql/mysql.sql:// INDEX `idx_status_date` (`status`, `created_at`)
// Wählen Sie nur benötigte Spalten$handler->getObjects($criteria, false, true); // asArray = true
// Verwenden Sie Pagination$criteria->setLimit($perPage);$criteria->setStart($offset);Schreiben Sie Unit Tests
Abschnitt betitelt „Schreiben Sie Unit Tests“public function testCreateArticle(): void{ $repository = $this->createMock(ArticleRepositoryInterface::class); $repository->expects($this->once())->method('save');
$service = new ArticleService($repository); $dto = new CreateArticleDTO('Title', 'Content');
$article = $service->create($dto);
$this->assertInstanceOf(Article::class, $article);}Verwandte Dokumentation
Abschnitt betitelt „Verwandte Dokumentation“- Clean-Code - Clean Code-Prinzipien
- Code-Organization - Projektstruktur
- Testing - Leitfaden zum Testen
- ../02-Core-Concepts/Security/Security-Best-Practices - Sicherheitsleitfaden