Craft 3 Documentation

Coding Guidelines

Do your best to follow these guidelines when writing code for Craft and Craft plugins.

Code Style #

Best Practices #

The Php Inspections (EA Extended) PhpStorm plugin can help you locate and fix these sorts of best practice issues.

Namespaces & Class Names #

Method Names #

Getter methods (methods whose primary responsibility is to return something, rather than do something) that don’t accept any arguments should begin with get , and there should be a corresponding @property tag in the class’s docblock to document the corresponding magic getter property.

Getter methods that accept one or more arguments (regardless of whether they can be omitted) should only begin with get if it “sounds right”.

Static methods should generally not start with get.

Type Declarations #

Argument Types #

Use PHP 7.0-supported argument type declarations for all function arguments whenever possible. The only exceptions should be:

If an argument accepts two types and one of them is null, the argument should have a type declaration for the non-null type, and a default value of null.

public function foo(string $bar = null)

Do this even if there are required arguments following the argument that accepts null. This is the only way to enforce an argument type while also allowing null in PHP.

Return Types #

Use PHP 7.0-supported return type declarations for all methods whenever possible. The only exceptions should be:

Docblocks #

Interfaces vs. Implementation Classes #

@param , @return , @var , @method and @property tags on public service methods should reference Interfaces (when applicable), not their implementation class:

// Bad:
/**
 * @param \craft\base\Element $element
 * @param \craft\base\ElementInterface|\craft\base\Element $element
 */

// Better:
/**
 * @param \craft\base\ElementInterface $element
 */

Inline @var tags should reference implementation classes, not their interfaces:

// Bad:
/** @var \craft\base\ElementInterface $element */
/** @var \craft\base\ElementInterface|\craft\base\Element $element */

// Better:
/** @var \craft\base\Element $element */

Control Flow #

Happy Paths #

Use them. In general the execution of a method should only make it all the way to the end if everything went as expected.

// Bad:
if ($condition) {
    // Do stuff

    return true;
}

return false;

// Better:
if (!$condition) {
    return false;
}

// Do stuff

return true;

ifreturnelse #

Don’t do this. There’s no point, and can be misleading at first glance.

// Bad:
if ($condition) {
    return $foo;
} else {
    return $bar;
}

// Better:
if ($condition) {
    return $foo;
}

return $bar;

Controllers #

Return Types #

Controller actions that should complete the request must return either a string (HTML) or a Response object.

// Bad:
$this->asJson($obj);
$this->renderTemplate($template, $variables);

// Better:
return $this->asJson($obj);
return $this->renderTemplate($template, $variables);

JSON Actions #

Controller actions that have the option of returning JSON should do so if the request explicitly accepts a JSON response; not if it’s an Ajax request.

// Bad:
if (\Craft::$app->getRequest()->getIsAjax()) {
    return $this->asJson([...]);
}

// Better:
if (\Craft::$app->getRequest()->getAcceptsJson()) {
    return $this->asJson([...]);
}

Controller actions that only return JSON should require that the request accepts JSON.

$this->requireAcceptsJson();

Exceptions #

DB Queries #

Conditions #

// Bad:
$query->where('foo.thing is null');
$query->innerJoin('{{%bar}} bar', 'bar.fooId = foo.id');

// Better:
$query->where(['foo.thing' => null]);
$query->innerJoin('{{%bar}} bar', '[[bar.fooId]] = [[foo.id]]');

Getters & Setters #

Getter and setter methods should have a corresponding @property tag in the class’s docblock, so IDEs like PhpStorm can be aware of the magic properties.

/**
 * @property User $author
 */
class Entry
{
    private $_author;

    /**
     * @return User
     */
    public function getAuthor()
    {
        return $this->_author;
    }
}

For a slight performance improvement and easier debugging, you should generally stick with calling the getter and setter methods directly rather than going through their magic properties.

// Bad:
$oldAuthor = $entry->author;
$entry->author = $newAuthor;

// Better:
$oldAuthor = $entry->getAuthor();
$entry->setAuthor($newAuthor);

App Component Getters #

App components should have their own getter functions, which call the app component getter method get() directly:

/**
 * @return Entries
 */
public function getEntries()
{
    return $this->get('entries');
}

And you should use those instead of their magic properties:

// Bad:
\Craft::$app->entries->saveEntry($entry);

// Better:
\Craft::$app->getEntries()->saveEntry($entry);

If you will be referencing the same app component multiple times within the same method, save a local reference to it.

// Bad:
\Craft::$app->getEntries()->saveEntry($entry1);
\Craft::$app->getEntries()->saveEntry($entry2);

// Better:
$entriesService = \Craft::$app->getEntries();
$entriesService->saveEntry($entry1);
$entriesService->saveEntry($entry2);