Skip to main content

coding-conventions

Coding principles and conventions for this Shopsys project — DRY/KISS/reuse, comment and docblock quality, test-code rules, the project visibility/typing rules, and documentation best practices. Read it before writing or modifying PHP so your code matches the codebase.

معلومات المصدر

المستودع
shopsys/project-base
آخر نشاط في المصدر
٢ نوفمبر ٢٠٢٦ في ٠٧:٢٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٣٨
التفرعات
١٢

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
coding-conventions
description
Coding principles and conventions for this Shopsys project — DRY/KISS/reuse, comment and docblock quality, test-code rules, the project visibility/typing rules, and documentation best practices. Read it before writing or modifying PHP so your code matches the codebase.
> **Monorepo note:** if the repository has top-level `packages/` and `project-base/` directories (the shopsys/shopsys monorepo layout, not a standalone project), also read `.agents/skills/monorepo-vs-project/SKILL.md` and apply its delta (package-first + path/role remap) on top of this skill. # Shopsys coding conventions Follow these whenever you write or modify code in this project. ## Core principles ### DRY (Don't Repeat Yourself) - **First**, check whether existing functionality can be reused — utilities, helpers, methods, established patterns (in `app/src/` and in `vendor/shopsys/`). - Extend or compose existing solutions rather than duplicating logic; extract common patterns into reusable components. ### KISS (Keep It Simple) - Choose the simplest solution that works; avoid over-engineering and clever abstractions. - Ask: "Is there a simpler way using existing code?" ### Reuse & composition - Leverage tested, proven functionality; build complex behavior from simple existing blocks. - Use framework conventions; prefer composition over inheritance or duplication. ### Validation & guard clauses - Prefer the `Webmozart\Assert\Assert` package over hand-written `if (...) { throw new ... }` guards for argument and state validation. It is already a dependency, keeps the guard on one line, and makes the intent obvious (`Assert::notSame()`, `Assert::isEmpty()`, `Assert::allInstanceOf()`, `Assert::keyExists()`, …). - Narrow `object` or interface-typed parameters with `Assert::isInstanceOf($entity, Order::class)` instead of a `/** @var */` docblock or a manual `instanceof` check. PHPStan understands the assert (`phpstan/phpstan-webmozart-assert` is enabled in `phpstan.neon`), so the following code is analysed with the concrete type, and a wrong instance fails fast at runtime with a clear message. - Pass the message as a closure when building it is not trivial — `Assert::isEmpty($keys, fn (): string => sprintf(...))` — so it is only built when the assertion actually fails. - The message is passed through `sprintf()` by the library, so avoid a literal `%` in it. - Write a dedicated exception class instead when callers need to catch that specific failure — `Assert` always throws `\InvalidArgumentException`. ### Maintainability first - Write code that's easy to understand, modify, and debug; change things in the fewest places. - Follow existing conventions; favor long-term maintainability over short-term convenience. ### Before writing new code, ask: - Does this already exist in the codebase (`app/src/`, `vendor/shopsys/`)? Can I combine existing functions/methods? - What's the simplest approach using what's available? Will it be easy to maintain? - Does each comment add value? Do my arrays/callables/complex types have proper docblocks? ## Comments — quality over quantity - **Only** add comments that provide real value. Write self-documenting code with clear names. - Explain **why**, not **what**; focus on business logic, complex algorithms, non-obvious decisions. ```php // GOOD // Handle edge case where role doesn't have FULL permission available // Workaround for legacy database structure - remove after migration to v2.0 // Algorithm based on RFC 3986 specification // BAD // This method returns the name public function getName(): string // Set the user ID $user->setId($id); ``` ## DocBlocks — always specify non-obvious types Add docblocks for non-obvious parameters and return types (helps IDEs and static analysis). Always specify: `array` contents, `class-string` targets, `callable` signatures, `iterable` element types, complex/generic return types. ```php // GOOD /** * @param class-string<\Shopsys\FrameworkBundle\Component\Context\AbstractContext> $context * @param array<string> $excludedRoles * @param array<int, \Shopsys\FrameworkBundle\Component\Security\Role\Role> $roles * @return array<string, mixed> */ public function buildGrid(string $context, array $excludedRoles, array $roles): array /** * @param callable(string): bool $validator * @param iterable<\App\Entity\User> $users * @return \Generator<int, string> */ public function processUsers(callable $validator, iterable $users): \Generator // BAD — what's in the array? which class? array of what? /** * @param array $excludedRoles * @param string $context * @return array */ public function buildGrid(string $context, array $excludedRoles): array ``` ### `{@inheritdoc}` vs a detailed docblock - Use `{@inheritdoc}` when the interface docblock is comprehensive, no extra type specificity is needed, and there's no implementation-specific behavior to document. - Write a detailed docblock when you can add specific array/generic types, concrete examples, or document implementation-specific behavior (e.g. specifying a `mixed` the interface leaves generic). ```php // Simple case → {@inheritdoc} /** * {@inheritdoc} */ public function findById(int $id): ?User { /* ... */ } // Adds value → specific docblock /** * Transforms role identifiers array to multidimensional form structure * * @param array<string>|mixed $value Array of role identifiers (e.g., ['ROLE_ORDER_VIEW']) * @return array<string, array<string, bool>> [roleConstant][permission] = bool */ public function transform(mixed $value): array { /* ... */ } ``` ## Test-code rules - In functional and GraphQL test cases, inject services with the `@inject` annotation — **not** `$this->getContainer()->get(...)`. - Use `.agents/skills/test-writing/SKILL.md` to select the domain guide: `backend-tests` for PHP and admin Jest, `storefront-tests` for Vitest and Cypress routing. ## Entity property hooks Sanitization, single-property validation and null fallbacks of an entity property live in a `set` property hook, never only in a setter (a constructor, `edit()` or a subclass can bypass a setter). Rules and examples: `docs/model/entities.md#property-hooks`. ## Visibility & typing Your project code is a final version used as-is, with no downstream extension surface to preserve: - use `final` on classes; - use `private` visibility (not `protected`); - use typehints and return types everywhere. The framework you extend lives read-only in `vendor/shopsys/` and follows different rules there (it's built to be extended) — but that's its code, not yours. For how to extend it correctly, see `.agents/skills/shopsys-architecture/SKILL.md`.
عرض على GitHub