بهترین شیوه های توسعه ماژول
بررسی اجمالی
Section titled “بررسی اجمالی”این سند بهترین شیوه ها را برای توسعه ماژول های XOOPS با کیفیت بالا ادغام می کند. پیروی از این دستورالعمل ها ماژول های قابل نگهداری، ایمن و عملکردی را تضمین می کند.
معماری
Section titled “معماری”معماری پاک را دنبال کنید
Section titled “معماری پاک را دنبال کنید”کدها را در لایه ها سازماندهی کنید:
src/├── Domain/ # Business logic, entities├── Application/ # Use cases, services├── Infrastructure/ # Database, external services└── Presentation/ # Controllers, templatesمسئولیت مجرد
Section titled “مسئولیت مجرد”هر کلاس باید یک دلیل برای تغییر داشته باشد:
// 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() { }}تزریق وابستگی
Section titled “تزریق وابستگی”وابستگی ها را تزریق کنید، آنها را ایجاد نکنید:
// Goodpublic function __construct( private readonly ArticleRepositoryInterface $repository) {}
// Badpublic function __construct() { $this->repository = new ArticleRepository();}کیفیت کد
Section titled “کیفیت کد”ایمنی نوع
Section titled “ایمنی نوع”از انواع دقیق و اعلانهای نوع استفاده کنید:
<?php
declare(strict_types=1);
final class ArticleService{ public function findById(int $id): ?Article { // ... }
public function create(CreateArticleDTO $dto): Article { // ... }}رسیدگی به خطا
Section titled “رسیدگی به خطا”از استثناها به درستی استفاده کنید:
// 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();}ایمنی پوچ
Section titled “ایمنی پوچ”در صورت امکان از null اجتناب کنید:
// Use null object patternpublic function getAuthor(): UserInterface{ return $this->author ?? new AnonymousUser();}
// Use Optional/Maybe patternpublic function findById(int $id): ?Article{ // Explicitly nullable return}پایگاه داده
Section titled “پایگاه داده”از معیارها برای کوئری ها استفاده کنید
Section titled “از معیارها برای کوئری ها استفاده کنید”$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 ورودی
Section titled “Escape User ورودی”$sql = sprintf( "SELECT * FROM %s WHERE id = %d AND title = %s", $db->prefix('mymodule_items'), intval($id), $db->quoteString($title));از تراکنش ها استفاده کنید
Section titled “از تراکنش ها استفاده کنید”$db->query('START TRANSACTION');
try { $handler->insert($article); $handler->insert($metadata); $db->query('COMMIT');} catch (\Exception $e) { $db->query('ROLLBACK'); throw $e;}ورودی را همیشه اعتبارسنجی کنید
Section titled “ورودی را همیشه اعتبارسنجی کنید”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');}از توکن های CSRF استفاده کنید
Section titled “از توکن های CSRF استفاده کنید”// In form$form->addElement(new XoopsFormHiddenToken());
// On submitif (!$GLOBALS['xoopsSecurity']->check()) { redirect_header('index.php', 3, 'Invalid token');}مجوزها را بررسی کنید
Section titled “مجوزها را بررسی کنید”if (!$helper->isUserAdmin()) { redirect_header('index.php', 3, _NOPERM);}
if (!$permHandler->isGranted('edit', $categoryId)) { throw new UnauthorizedException();}عملکرد
Section titled “عملکرد”از حافظه پنهان استفاده کنید
Section titled “از حافظه پنهان استفاده کنید”$cache = $helper->getCache();$cacheKey = "articles_{$categoryId}_{$limit}";
$articles = $cache->read($cacheKey);if ($articles === false) { $articles = $handler->getArticles($categoryId, $limit); $cache->write($cacheKey, $articles, 3600);}کوئری ها را بهینه کنید
Section titled “کوئری ها را بهینه کنید”// 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);تست های واحد را بنویسید
Section titled “تست های واحد را بنویسید”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);}مستندات مرتبط
Section titled “مستندات مرتبط”- Clean-Code - اصول کد پاک
- Code-Organization - ساختار پروژه
- تست - راهنمای تست
- ../02-Core-Concepts/Security/Security-Best-Practices - راهنمای امنیتی