Thực tiễn tốt nhất về phát triển mô-đun
Tổng quan
Phần tiêu đề “Tổng quan”Tài liệu này tổng hợp các phương pháp hay nhất để phát triển XOOPS modules chất lượng cao. Việc tuân theo các nguyên tắc này đảm bảo modules có thể bảo trì, an toàn và hoạt động hiệu quả.
Kiến trúc
Phần tiêu đề “Kiến trúc”Theo đuổi Kiến trúc Sạch
Phần tiêu đề “Theo đuổi Kiến trúc Sạch”Sắp xếp mã thành các lớp:
src/├── Domain/ # Business logic, entities├── Application/ # Use cases, services├── Infrastructure/ # Database, external services└── Presentation/ # Controllers, templatesTrách nhiệm duy nhất
Phần tiêu đề “Trách nhiệm duy nhất”Mỗi class phải có một lý do để thay đổi:
// 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() { }}Chèn phụ thuộc
Phần tiêu đề “Chèn phụ thuộc”Tiêm các phần phụ thuộc, đừng tạo chúng:
// Goodpublic function __construct( private readonly ArticleRepositoryInterface $repository) {}
// Badpublic function __construct() { $this->repository = new ArticleRepository();}Chất lượng mã
Phần tiêu đề “Chất lượng mã”Loại an toàn
Phần tiêu đề “Loại an toàn”Sử dụng các loại nghiêm ngặt và khai báo kiểu:
<?php
declare(strict_types=1);
final class ArticleService{ public function findById(int $id): ?Article { // ... }
public function create(CreateArticleDTO $dto): Article { // ... }}Xử lý lỗi
Phần tiêu đề “Xử lý lỗi”Sử dụng ngoại lệ một cách thích hợp:
// 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();}Không an toàn
Phần tiêu đề “Không an toàn”Tránh null nếu có thể:
// Use null object patternpublic function getAuthor(): UserInterface{ return $this->author ?? new AnonymousUser();}
// Use Optional/Maybe patternpublic function findById(int $id): ?Article{ // Explicitly nullable return}Cơ sở dữ liệu
Phần tiêu đề “Cơ sở dữ liệu”Sử dụng Tiêu chí cho Truy vấn
Phần tiêu đề “Sử dụng Tiêu chí cho Truy vấn”$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);Thoát khỏi đầu vào của người dùng
Phần tiêu đề “Thoát khỏi đầu vào của người dùng”$sql = sprintf( "SELECT * FROM %s WHERE id = %d AND title = %s", $db->prefix('mymodule_items'), intval($id), $db->quoteString($title));Sử dụng Giao dịch
Phần tiêu đề “Sử dụng Giao dịch”$db->query('START TRANSACTION');
try { $handler->insert($article); $handler->insert($metadata); $db->query('COMMIT');} catch (\Exception $e) { $db->query('ROLLBACK'); throw $e;}Bảo mật
Phần tiêu đề “Bảo mật”Luôn xác thực đầu vào
Phần tiêu đề “Luôn xác thực đầu vào”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');}Sử dụng Token CSRF
Phần tiêu đề “Sử dụng Token CSRF”// In form$form->addElement(new XoopsFormHiddenToken());
// On submitif (!$GLOBALS['xoopsSecurity']->check()) { redirect_header('index.php', 3, 'Invalid token');}Kiểm tra quyền
Phần tiêu đề “Kiểm tra quyền”if (!$helper->isUserAdmin()) { redirect_header('index.php', 3, _NOPERM);}
if (!$permHandler->isGranted('edit', $categoryId)) { throw new UnauthorizedException();}Hiệu suất
Phần tiêu đề “Hiệu suất”Sử dụng bộ nhớ đệm
Phần tiêu đề “Sử dụng bộ nhớ đệm”$cache = $helper->getCache();$cacheKey = "articles_{$categoryId}_{$limit}";
$articles = $cache->read($cacheKey);if ($articles === false) { $articles = $handler->getArticles($categoryId, $limit); $cache->write($cacheKey, $articles, 3600);}Tối ưu hóa truy vấn
Phần tiêu đề “Tối ưu hóa truy vấn”// 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);Thử nghiệm
Phần tiêu đề “Thử nghiệm”Viết bài kiểm tra đơn vị
Phần tiêu đề “Viết bài kiểm tra đơn vị”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);}Tài liệu liên quan
Phần tiêu đề “Tài liệu liên quan”- Clean-Code - Nguyên tắc clean code
- Mã-Tổ chức - Cấu trúc dự án
- Kiểm tra - Hướng dẫn kiểm tra
- ../02-Core-Concepts/Security/Security-Best-Thực hành - Hướng dẫn bảo mật