group | title | redirect_from | functional_areas | ||
---|---|---|---|---|---|
coding-standards |
Technical guidelines |
|
|
This document lists the fundamental coding and application design principles that guide Magento 2 developer team members.
Magento core developers use this document as a reference during code reviews; some rules have corresponding code checks in the Magento static tests.
These guidelines came from many years of hard work, experience, and discussions. We strongly believe that new technical initiatives should follow these recommendations, and the existing code should be improved to meet them.
Use RFC2119 to interpret keywords like:
-
MUST and MUST NOT
-
REQUIRED
-
SHALL and SHALL NOT
-
SHOULD and SHOULD NOT
-
RECOMMENDED
-
MAY
-
OPTIONAL
1.1. Function arguments SHOULD NOT be modified.
2.1. Object decomposition MUST follow the SOLID principles.
2.2. Object MUST be ready for use after instantiation. No additional public initialization methods are allowed.
{% collapsible Examples: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
class Config
{
private $data;
} {% endhighlight %} |
{% highlight php %}
class Config
{
private $data;
} {% endhighlight %} |
{:start="2.3"} 2.3. Class constructor can have only dependency assignment operations and/or argument validation operations. No other operations are allowed.
2.3.1. Constructor SHOULD throw an exception when validation of an argument has failed.
{% collapsible Example: %}
class Composite
{
/**
* @var RendererInterface[]
*/
private $renderers;
/**
* @param RendererInterface[] $renderers
* @throws InvalidArgumentException
*/
public function __construct(array $renderers)
{
foreach ($renderers as $renderer) {
if (!$renderer instanceof RendererInterface) {
throw new InvalidArgumentException(
sprintf('Instance of the phrase renderer is expected, got %s instead.', get_class($renderer))
);
}
}
$this->renderers = $renderers;
}
}
{:start="3.2"} 2.3.2. Events MUST NOT be triggered in constructors.
{% collapsible Examples: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
class Config { private $data;
} {% endhighlight %} |
{% highlight php %}
class Config
{
private $fileReader;
} {% endhighlight %} |
{:start="2.4"} 2.4. All dependencies MUST be requested by the most generic type that is required by the client object.
{% collapsible Examples: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
interface SessionAdapterInterface
{}
RedisSessionAdapter implements SessionAdapterInterface {} class SessionManager { public function __construct(RedisSessionAdapter $sessionAdapter) {} } // Breaks polymorphism principle, restricts what types can be passed at the runtime. {% endhighlight %} |
{% highlight php %}
interface SessionAdapterInterface
{}
RedisSessionAdapter implements SessionAdapterInterface {} class SessionManager { public function __construct(SessionAdapterInterface $sessionAdapter) {} } {% endhighlight %} |
2.5. Proxies and interceptors MUST NEVER be explicitly requested in constructors.
2.6. Inheritance SHOULD NOT be used. Composition SHOULD be used for code reuse. {% collapsible Examples: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
class AbstractController extends Action
{
// ...
protected function validate(
$request
) {}
} class Edit extends AbstractController { public function execute() { $errors = $this->validate( $request );
} // Smaller classes, one responsibility, more flexible, easy to understand, more testable. {% endhighlight %} |
{% highlight php %}
class Edit extends Action
{
public function __constructor(
ValidatorInterface $validator,
HashGeneratorInterface $hashGenerator
) {}
} {% endhighlight %} |
2.7. All non-public properties and methods SHOULD be private.
2.8. Abstract classes MUST NOT be marked as public @api
.
2.9. Service classes (ones that provide behavior but not data, like EventManager
) SHOULD NOT have a mutable state.
2.10. Only data objects or entities (Product, Category, etc.) MAY have any observable state.
2.11. "Setters" SHOULD NOT be used. They are only allowed in Data Transfer Objects.
2.12. "Getters" SHOULD NOT change the state of an object.
2.13. Static methods SHOULD NOT be used.
2.14. Temporal coupling MUST be avoided. {% collapsible Example #1: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
$url = new Url();
$url->setBaseUrl($baseUrl);
echo $url->get('custom/path'); // prints full URL
// Developer forgot or didn’t know that you need to call setBaseUrl $url = new Url(); echo $url->get('custom/path'); // Throws exception, which makes issue smaller. If it doesn't throw and exception, it could lead to a hidden bug more likely. // Method with out parameters that doesn’t return anything could be sign of temporal coupling. {% endhighlight %} |
{% highlight php %}
$url = new Url($baseUrl);
echo $url->get('custom/path');
// Or $url = new Url(); echo $url->get($baseUrl, 'custom/path'); // Only one way to use API, no temporal coupling. {% endhighlight %} |
{% collapsible Example #2: %}
Not recommended | Recommended |
---|---|
{% highlight php %}
class Edit extends Action
{
public function execute()
{
// ...
$product = $productResource->load($product, $productSku, 'sku');
$this->registry->register('product', $product);
}
}
class View extends Template { public function getProductName() { $product = $this->registry->get('product'); return $product->getName(); } } {% endhighlight %} |
{% highlight php %}
class Edit extends Action
{
public function execute()
{
// ...
$product = $productRepository->get($productSku);
}
}
class View extends Template { public function getProductName() { // ... $product = $productRepository->get($productSku); return $product->getName(); } } // More flexible, no dependencies between classes, no temporal coupling. {% endhighlight %} {% endcollapsible %} 2.15. Method chaining in class design MUST be avoided. 2.16. Law of Demeter SHOULD be obeyed. 2.17. Patterns 2.17.1. Proxies SHOULD be used for lazy-loading optional dependencies. 2.17.2. Composites SHOULD be used when there is a need to work with a tree as a single object. {% collapsible Example: %}
You need to read configuration from different sources (like database or filesystem) and want to make the reading process configurable: allow extensions to add more configuration sources. In this case, you can create a |