Najboljše prakse za razvoj modulov
Pregled
Section titled “Pregled”Ta dokument združuje najboljše prakse za razvoj visokokakovostnih XOOPS modulov. Upoštevanje teh smernic zagotavlja vzdrževanje, varnost in učinkovitost modulov.
Arhitektura
Section titled “Arhitektura”Sledite čisti arhitekturi
Section titled “Sledite čisti arhitekturi”Organizirajte kodo v plasti:
src/├── Domain/ # Business logic, entities├── Application/ # Use cases, services├── Infrastructure/ # Database, external services└── Presentation/ # Controllers, templatesEna odgovornost
Section titled “Ena odgovornost”Vsak razred mora imeti en razlog za spremembo:
// Good: Focused classesclass ArticleRepository { /* persistence only */ }class ArticleValidator { /* validation only */ }class ArticleNotifier { /* notifications only */ }
// Bad: God classclass Article { public function save() { } public function validate() { } public function notify() { } public function generatePDF() { }}Injekcija odvisnosti
Section titled “Injekcija odvisnosti”Vnesite odvisnosti, ne ustvarite jih:
// Goodpublic function __construct( private readonly ArticleRepositoryInterface $repository) {}
// Badpublic function __construct() { $this->repository = new ArticleRepository();}Kakovost kode
Section titled “Kakovost kode”Vrsta varnosti
Section titled “Vrsta varnosti”Uporabite stroge tipe in deklaracije tipov:
<?php
declare(strict_types=1);
final class ArticleService{ public function findById(int $id): ?Article { // ... }
public function create(CreateArticleDTO $dto): Article { // ... }}Obravnava napak
Section titled “Obravnava napak”Ustrezno uporabite izjeme:
// Throw specific exceptionsthrow new ArticleNotFoundException($id);throw new ValidationException($errors);throw new UnauthorizedException('Cannot edit this article');
// Catch at appropriate leveltry { $article = $service->create($dto);} catch (ValidationException $e) { return $this->renderForm($e->getErrors());} catch (UnauthorizedException $e) { return $this->redirectToLogin();}Null Safety
Section titled “Null Safety”Izogibajte se ničelni vrednosti, kjer je to mogoče:
// Use null object patternpublic function getAuthor(): UserInterface{ return $this->author ?? new AnonymousUser();}
// Use Optional/Maybe patternpublic function findById(int $id): ?Article{ // Explicitly nullable return}Baza podatkov
Section titled “Baza podatkov”Uporabite merila za poizvedbe
Section titled “Uporabite merila za poizvedbe”$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);Escape User Input
Section titled “Escape User Input”$sql = sprintf( "SELECT * FROM %s WHERE id = %d AND title = %s", $db->prefix('mymodule_items'), intval($id), $db->quoteString($title));Uporabite Transakcije
Section titled “Uporabite Transakcije”$db->query('START TRANSACTION');
try { $handler->insert($article); $handler->insert($metadata); $db->query('COMMIT');} catch (\Exception $e) { $db->query('ROLLBACK'); throw $e;}Varnost
Section titled “Varnost”Vedno potrdi vnos
Section titled “Vedno potrdi vnos”use Xmf\Request;
$id = Request::getInt('id', 0);$title = Request::getString('title', '');$data = Request::getArray('data', []);
// Additional validationif (strlen($title) < 5) { throw new ValidationException('Title too short');}Uporabite CSRF žetonov
Section titled “Uporabite CSRF žetonov”// In form$form->addElement(new XoopsFormHiddenToken());
// On submitif (!$GLOBALS['xoopsSecurity']->check()) { redirect_header('index.php', 3, 'Invalid token');}Preverite dovoljenja
Section titled “Preverite dovoljenja”if (!$helper->isUserAdmin()) { redirect_header('index.php', 3, _NOPERM);}
if (!$permHandler->isGranted('edit', $categoryId)) { throw new UnauthorizedException();}Zmogljivost
Section titled “Zmogljivost”Uporabite predpomnjenje
Section titled “Uporabite predpomnjenje”$cache = $helper->getCache();$cacheKey = "articles_{$categoryId}_{$limit}";
$articles = $cache->read($cacheKey);if ($articles === false) { $articles = $handler->getArticles($categoryId, $limit); $cache->write($cacheKey, $articles, 3600);}Optimizirajte poizvedbe
Section titled “Optimizirajte poizvedbe”// Use indexes// Add to sql/mysql.sql:// INDEX `idx_status_date` (`status`, `created_at`)
// Select only needed columns$handler->getObjects($criteria, false, true); // asArray = true
// Use pagination$criteria->setLimit($perPage);$criteria->setStart($offset);Testiranje
Section titled “Testiranje”Napišite teste enot
Section titled “Napišite teste enot”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);}Povezana dokumentacija
Section titled “Povezana dokumentacija”- Clean-Code - Načela čiste kode
- Organizacija kode - Struktura projekta
- Testiranje - Vodnik za testiranje
- ../02-Core-Concepts/Security/Security-Best-Practices - Varnostni vodnik