| name | standards-coding |
| description | Follow Magento 2 coding standards — service contracts, strict types, PHPCS rules, repository pattern, and extension attribute conventions |
| installed_version | 1.0.0 |
| magehub_version | 0.1.13 |
Coding Standards
Activation
Use When
- The task is to write, refactor, or review Magento PHP code for coding standards, service contracts, repositories, strict types, PHPCS, or extension attributes.
- The task mentions Magento coding standard, Api interfaces, repository pattern, phpcs.xml, strict_types, or maintainability conventions.
Do Not Use When
- The task is purely operational, performance profiling, frontend Hyva migration, or database schema work with a more specific skill.
- The user explicitly wants a quick prototype and accepts non-production scaffolding.
Required Inputs
- Target module/path, project coding standard command, and whether the code is public API, internal service, model, controller, or setup code.
- Existing project conventions for service contracts, repositories, data interfaces, and static analysis.
Workflow
- Inspect nearby module code and project PHPCS/static-analysis configuration before changing style or architecture.
- Add strict types, explicit visibility, constructor injection, interface-oriented APIs, and repository boundaries consistent with the module.
- Keep refactors narrowly tied to the requested change; avoid opportunistic architecture rewrites.
- Run formatting, PHPCS, static analysis, or targeted tests appropriate to the touched files.
Guardrails
- Do not introduce service contracts or repositories for one-off internal code unless they remove real coupling or match project conventions.
- Do not change public interfaces without backward-compatibility review and downstream implementer impact. (approval required)
- Do not suppress PHPCS/static-analysis errors without a local justification.
Verification
- Run vendor/bin/phpcs --standard=Magento2 or the project equivalent for touched PHP/XML files.
- Run targeted PHPUnit/static-analysis commands when method signatures or service behavior change.
- Report any standard command that is unavailable in the current project.
Output Contract
- List style/API changes, public contract changes, and exact quality commands run.
- Flag any intentionally deferred PHPCS/static-analysis issue.
Magento 2 Coding Standards Overview
Magento enforces coding standards through the magento/magento-coding-standard
PHPCS ruleset. All custom code must pass this ruleset before merge. Beyond
automated checks, Magento's architecture relies on structural conventions —
service contracts, repository patterns, and strict type declarations — that
determine how modules interoperate.
Strict Types
Add declare(strict_types=1); to every PHP file. Strict typing catches
type coercion bugs at the exact call site rather than producing silent data
corruption downstream. This is mandatory for all new Magento 2 code and
enforced by the coding standard.
Service Contracts
Define public APIs in Api/ interfaces. Data interfaces go in Api/Data/.
Repository interfaces follow the pattern: getById(), save(), delete(),
getList(). Other modules must depend on these interfaces, never on concrete
model classes. Service contracts are the only stable API — Magento may change
internal implementations between patch releases.
Repository Pattern
Implement repositories in Model/ that fulfill the Api/ interface contract.
Use resource models for persistence and collection factories for getList().
Bind the interface to the implementation via di.xml preference. Never
expose the resource model or collection directly — always route through the
repository.
Extension Attributes
Use extension_attributes.xml to add custom fields to existing data
interfaces without modifying core code. Extension attributes are type-safe,
auto-generated by Magento, and compatible with REST/GraphQL serialization.
Prefer extension attributes over EAV attributes when the data is module-
specific and not user-editable.
PHPCS and Static Analysis
Run vendor/bin/phpcs --standard=Magento2 <path> on every module. Address
all errors — the Magento Marketplace and many CI pipelines reject submissions
with PHPCS violations. Key rules: method visibility must be explicit, method
length under 20 lines preferred, cyclomatic complexity under 10.
XML Configuration Conventions
Always include the xsi:noNamespaceSchemaLocation attribute in XML config
files. This enables IDE validation and matches the coding standard. Use
lowercase underscore-separated names for XML node identifiers.
Conventions
- Add declare(strict_types=1) to every PHP file without exception
Example: <?php\ndeclare(strict_types=1);\n\nnamespace Vendor\Module\Model;
Rationale: Strict types prevent implicit type coercion that causes subtle bugs — a string '0' silently becoming integer 0, or a float losing precision when passed as int. Catching these at the call site makes debugging immediate.
- Define module APIs as interfaces in Api/ and bind implementations via di.xml preferences
Example: Api/EntityRepositoryInterface with getById(), save(), delete(), getList() methods
Rationale: Service contracts are the only API that Magento guarantees across versions. Other modules that depend on concrete classes break during upgrades because internal implementations are not part of the public API contract.
- Keep method cyclomatic complexity below 10 and method length below 20 lines
Example: Extract validation, transformation, and persistence into separate private methods or services
Rationale: The Magento coding standard flags high complexity. Short methods with single responsibilities are easier to test, review, and extend through plugins.
Examples
Service contract interface
Repository interface following the Magento service contract pattern with full PHPDoc for web API compatibility
<?php
declare(strict_types=1);
namespace Vendor\Module\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Vendor\Module\Api\Data\EntityInterface;
use Vendor\Module\Api\Data\EntitySearchResultsInterface;
interface EntityRepositoryInterface
{
public function getById(int $entityId): EntityInterface;
public function save(EntityInterface $entity): EntityInterface;
public function delete(EntityInterface $entity): bool;
public function getList(SearchCriteriaInterface $searchCriteria): EntitySearchResultsInterface;
}
Repository implementation
Concrete repository that implements the service contract using resource models for persistence and collection for list queries
<?php
declare(strict_types=1);
namespace Vendor\Module\Model;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Vendor\Module\Api\Data\EntityInterface;
use Vendor\Module\Api\Data\EntitySearchResultsInterface;
use Vendor\Module\Api\Data\EntitySearchResultsInterfaceFactory;
use Vendor\Module\Api\EntityRepositoryInterface;
use Vendor\Module\Model\ResourceModel\Entity as EntityResource;
use Vendor\Module\Model\ResourceModel\Entity\CollectionFactory;
class EntityRepository implements EntityRepositoryInterface
{
public function __construct(
private readonly EntityResource $resource,
private readonly EntityFactory $entityFactory,
private readonly CollectionFactory $collectionFactory,
private readonly CollectionProcessorInterface $collectionProcessor,
private readonly EntitySearchResultsInterfaceFactory $searchResultsFactory
) {
}
public function getById(int $entityId): EntityInterface
{
$entity = $this->entityFactory->create();
$this->resource->load($entity, $entityId);
if (!$entity->getId()) {
throw new NoSuchEntityException(
__('Entity with ID "%1" does not exist.', $entityId)
);
}
return $entity;
}
public function save(EntityInterface $entity): EntityInterface
{
try {
$this->resource->save($entity);
} catch (\Exception $e) {
throw new CouldNotSaveException(
__('Could not save entity: %1', $e->getMessage()),
$e
);
}
return $entity;
}
public function delete(EntityInterface $entity): bool
{
$this->resource->delete($entity);
return true;
}
public function getList(SearchCriteriaInterface $searchCriteria): EntitySearchResultsInterface
{
$collection = $this->collectionFactory->create();
$this->collectionProcessor->process($searchCriteria, $collection);
$searchResults = $this->searchResultsFactory->create();
$searchResults->setSearchCriteria($searchCriteria);
$searchResults->setItems($collection->getItems());
$searchResults->setTotalCount($collection->getSize());
return $searchResults;
}
}
PHPCS configuration for CI
Composer script and phpcs.xml configuration to run the Magento 2 coding standard in a CI pipeline
<?xml version="1.0"?>
<ruleset name="Vendor Module">
<description>Magento 2 coding standard for Vendor Module</description>
<rule ref="Magento2"/>
<file>.</file>
<exclude-pattern>*/Test/*</exclude-pattern>
<exclude-pattern>*/vendor/*</exclude-pattern>
<arg name="extensions" value="php"/>
<arg name="colors"/>
<arg value="sp"/>
</ruleset>
Anti-patterns
- Depending on concrete model classes instead of service contract interfaces: Concrete classes are internal implementation. Magento may refactor, rename, or restructure them between patch versions. Modules depending on concrete classes break during upgrades without any deprecation notice.
Solution: Type-hint all injected dependencies and method parameters with Api/ interfaces. Bind concrete implementations via di.xml preferences so the dependency graph uses interfaces throughout.
- Putting unrelated utility methods into a single Helper class: Helper classes become procedural grab-bags that grow without bounds. They accumulate unrelated dependencies, become difficult to mock in tests, and create implicit coupling between features that share the helper.
Solution: Replace each group of related methods with a focused service class in Model/ or Service/. Inject only the dependencies each service actually needs. Delete the helper when all methods have been extracted.
- Omitting declare(strict_types=1) from PHP files: Without strict types, PHP silently coerces arguments — string to int, float to int, null to empty string. These coercions cause data integrity issues that manifest far from the source, making bugs extremely difficult to trace.
Solution: Add declare(strict_types=1); immediately after the opening <?php tag in every file. Configure PHPCS to enforce this rule and reject non-strict files in CI.
File Templates
Api/.php
Path template:
Api/{{interfaceName}}.php
Service contract repository interface with standard CRUD methods
<?php
declare(strict_types=1);
namespace {{vendor}}\{{module}}\Api;
use Magento\Framework\Exception\NoSuchEntityException;
use {{vendor}}\{{module}}\Api\Data\{{dataInterface}};
interface {{interfaceName}}
{
public function getById(int $id): {{dataInterface}};
}
phpcs.xml
Path template:
phpcs.xml
PHPCS configuration applying the Magento 2 coding standard to the module
<?xml version="1.0"?>
<ruleset name="{{vendor}} {{module}}">
<description>Magento 2 coding standard for {{vendor}} {{module}}</description>
<rule ref="Magento2"/>
<file>.</file>
<exclude-pattern>*/Test/*</exclude-pattern>
<arg name="extensions" value="php"/>
<arg name="colors"/>
</ruleset>
References
Freshness
- Last reviewed: 2026-06-28
- Sources to re-check: Magento coding standard and Adobe Commerce 2.4.x PHP development docs