שיטות עבודה מומלצות לפיתוח מודול
סקירה כללית
Section titled “סקירה כללית”מסמך זה מאגד שיטות עבודה מומלצות לפיתוח מודולים XOOPS באיכות גבוהה. הקפדה על הנחיות אלה מבטיחה מודולים ניתנים לתחזוקה, מאובטחים ובעלי ביצועים.
אדריכלות
Section titled “אדריכלות”עקוב אחר Clean Architecture
Section titled “עקוב אחר Clean Architecture”ארגן את הקוד לשכבות:
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 “בטיחות אפסית”הימנעו מ-nul במידת האפשר:
// 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);בריחה מקלט משתמש
Section titled “בריחה מקלט משתמש”$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 “ביצועים”השתמש ב-Caching
Section titled “השתמש ב-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);}בצע אופטימיזציה של שאילתות
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 - עקרונות קוד נקי
- קוד-ארגון - מבנה הפרויקט
- בדיקות - מדריך בדיקות
- ../02-Core-Concepts/Security/Security-Best-Practices - מדריך אבטחה