Skip to main content

Drupal Coding Standards

Drupal Coding Standards

Separation of Duties: See Separation of Duties - Drupal standards are responsible for Drupal code quality. They do NOT own agent manifests, execution, or OSSA spec.

Comprehensive guide for Drupal coding standards, PHPCS, PHPStan, and code quality in the LLM Platform.

Table of Contents

Overview

The LLM Platform enforces strict coding standards to ensure code quality, maintainability, and security.

Standards Applied

  1. Drupal: Core Drupal coding standards
  2. DrupalPractice: Best practices for Drupal development
  3. PHPStan: Static analysis for type safety
  4. Design Taste: Timeless, simple, professional code

PHPCS Configuration

Installation

# Included in Drupal core composer require --dev drupal/core-dev # Or standalone composer require --dev drupal/coder composer require --dev dealerdirect/phpcodesniffer-composer-installer

Configuration File

Create phpcs.xml.dist:

<?xml version="1.0" encoding="UTF-8"?> <ruleset name="drupal_module"> <description>PHP CodeSniffer configuration for Drupal modules</description> <!-- Check all PHP files --> <arg name="extensions" value="php,module,inc,install,test,profile,theme,info,txt,md,yml"/> <!-- Use colors and show progress --> <arg name="colors"/> <arg value="p"/> <!-- Exclude patterns --> <exclude-pattern>*/vendor/*</exclude-pattern> <exclude-pattern>*/node_modules/*</exclude-pattern> <exclude-pattern>*/tests/fixtures/*</exclude-pattern> <!-- Include Drupal coding standards --> <rule ref="Drupal"/> <rule ref="DrupalPractice"/> <!-- Specific rules --> <rule ref="Drupal.Commenting.FunctionComment"/> <rule ref="Drupal.Commenting.ClassComment"/> <rule ref="Drupal.Files.LineLength"> <properties> <property name="lineLimit" value="120"/> </properties> </rule> <!-- Files to check --> <file>.</file> </ruleset>

Running PHPCS

# Check module vendor/bin/phpcs --standard=Drupal,DrupalPractice modules/custom/module_name # Check with BuildKit buildkit drupal phpcs modules/custom/module_name # Auto-fix violations buildkit drupal phpcs modules/custom/module_name --fix # Specific file vendor/bin/phpcs --standard=Drupal src/Controller/MyController.php

PHPStan Configuration

Installation

composer require --dev phpstan/phpstan composer require --dev mglaman/phpstan-drupal composer require --dev phpstan/extension-installer

Configuration File

Create phpstan.neon:

includes: - vendor/mglaman/phpstan-drupal/extension.neon parameters: level: 6 paths: - src - tests scanDirectories: - web/core - web/modules/contrib excludePaths: - */tests/fixtures/* - */vendor/* ignoreErrors: # Known Drupal patterns - '#Unsafe usage of new static#' drupal: drupal_root: web

Running PHPStan

# Analyze module vendor/bin/phpstan analyze modules/custom/module_name # Specific level vendor/bin/phpstan analyze --level=8 src/ # Generate baseline for existing errors vendor/bin/phpstan analyze --generate-baseline

Code Quality Tools

1. Design Taste Validation

BuildKit provides design taste validation:

# Validate module design buildkit drupal taste modules/custom/module_name # Validate specific file buildkit drupal taste src/Controller/MyController.php

Checks for:

  • Simplicity: Prefer simple solutions
  • Clarity: Self-documenting code
  • Timelessness: No TODOs, HACKs, or temporary fixes
  • Best Practices: Drupal standards compliance
  • YAML Validation: Config schema validation

2. Token Optimization

Check if agents should be used:

# Analyze token usage buildkit golden optimize # If >500 tokens or >3 files buildkit agents spawn --type worker --task "refactor module_name"

3. Security Audit

# Comprehensive security audit buildkit golden audit # PHPCS security checks vendor/bin/phpcs --standard=Security modules/custom/module_name

Common Violations

1. Line Length

Violation:

public function myVeryLongFunctionNameWithManyParameters($parameter1, $parameter2, $parameter3, $parameter4) {

Fix:

public function myVeryLongFunctionName( $parameter1, $parameter2, $parameter3, $parameter4 ) {

2. Missing Documentation

Violation:

public function getData($id) { return $this->storage->load($id); }

Fix:

/** * Retrieves data by ID. * * @param int $id * The entity ID. * * @return \Drupal\Core\Entity\EntityInterface|null * The loaded entity or NULL. */ public function getData($id) { return $this->storage->load($id); }

3. Array Formatting

Violation:

$data = array('key1' => 'value1', 'key2' => 'value2');

Fix:

$data = [ 'key1' => 'value1', 'key2' => 'value2', ];

4. String Concatenation

Violation:

$message = 'Hello ' . $name . ', welcome to ' . $site . '!';

Fix:

$message = "Hello {$name}, welcome to {$site}!"; // Or use t() for translatable strings $message = $this->t('Hello @name, welcome to @site!', [ '@name' => $name, '@site' => $site, ]);

5. Conditional Formatting

Violation:

if ($condition) return $value;

Fix:

if ($condition) { return $value; }

6. Use Statements

Violation:

use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\node\Entity\Node; use Drupal\Core\Form\FormBase;

Fix:

use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormBase; use Drupal\node\Entity\Node;

(Alphabetically sorted)

7. Service Injection

Violation:

public function myFunction() { $entity = \Drupal::entityTypeManager()->getStorage('node')->load(1); }

Fix:

protected $entityTypeManager; public function __construct(EntityTypeManagerInterface $entity_type_manager) { $this->entityTypeManager = $entity_type_manager; } public function myFunction() { $entity = $this->entityTypeManager->getStorage('node')->load(1); }

8. TODOs and HACKs

Violation:

// TODO: Fix this later // HACK: Temporary workaround public function processData() { // @fixme: Needs refactoring }

Fix: Remove and either:

  1. Fix immediately
  2. Create GitLab issue
  3. Document properly
/** * Processes data according to business rules. * * @see https://gitlab.com/project/-/issues/123 */ public function processData() { // Clean implementation }

Auto-Fixing

PHPCS Auto-Fix

# Auto-fix violations vendor/bin/phpcbf --standard=Drupal modules/custom/module_name # Via BuildKit (recommended) buildkit drupal phpcs modules/custom/module_name --fix # Dry-run to preview changes vendor/bin/phpcbf --standard=Drupal --dry-run modules/custom/module_name

Agent-Assisted Fixing

For >50 violations, use agents:

# Check violation count buildkit drupal phpcs modules/custom/module_name | wc -l # If >50, spawn agent buildkit agents spawn --type worker --task "fix PHPCS violations in module_name"

Pre-Commit Hooks

Git Hooks

Create .git/hooks/pre-commit:

#!/bin/bash # Get list of PHP files being committed FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.php$|\.module$|\.install$') if [ -z "$FILES" ]; then exit 0 fi # Run PHPCS echo "Running PHPCS..." vendor/bin/phpcs --standard=Drupal,DrupalPractice $FILES if [ $? -ne 0 ]; then echo "PHPCS found violations. Please fix before committing." echo "Run: buildkit drupal phpcs <path> --fix" exit 1 fi # Run PHPStan echo "Running PHPStan..." vendor/bin/phpstan analyze $FILES --level=6 if [ $? -ne 0 ]; then echo "PHPStan found errors. Please fix before committing." exit 1 fi exit 0

Make executable:

chmod +x .git/hooks/pre-commit

CI/CD Integration

GitLab CI

# .gitlab-ci.yml phpcs: stage: test script: - composer install - vendor/bin/phpcs --standard=Drupal,DrupalPractice modules/custom/ allow_failure: false phpstan: stage: test script: - composer install - vendor/bin/phpstan analyze modules/custom/ --level=6 allow_failure: false taste: stage: test script: - buildkit drupal taste modules/custom/ allow_failure: true

IDE Integration

PHPStorm

  1. Settings > PHP > Quality Tools > PHP_CodeSniffer

    • Configuration: /path/to/vendor/bin/phpcs
    • Coding standard: Drupal
  2. Settings > PHP > Quality Tools > PHPStan

    • Configuration: /path/to/vendor/bin/phpstan
    • Configuration file: phpstan.neon
  3. Enable inspections

    • Settings > Editor > Inspections
    • Enable: PHP_CodeSniffer validation
    • Enable: PHPStan validation

VS Code

Install extensions:

  • ikappas.phpcs - PHP_CodeSniffer
  • SanderRonde.phpstan-vscode - PHPStan

Configure .vscode/settings.json:

{ "phpcs.enable": true, "phpcs.standard": "Drupal,DrupalPractice", "phpcs.executablePath": "vendor/bin/phpcs", "phpstan.enabled": true, "phpstan.path": "vendor/bin/phpstan", "phpstan.configFile": "phpstan.neon" }

Best Practices

1. Regular Checks

# Before committing buildkit drupal phpcs src/ --fix buildkit drupal taste src/ # Weekly full audit buildkit golden audit

2. Incremental Fixes

# Fix one directory at a time buildkit drupal phpcs src/Controller --fix buildkit drupal phpcs src/Service --fix buildkit drupal phpcs src/Form --fix

3. Baseline Management

# Generate baseline for existing code vendor/bin/phpstan analyze --generate-baseline # Only show new violations vendor/bin/phpstan analyze --no-baseline

4. Documentation Standards

/** * One-line description ending with period. * * Longer description providing context and details. Can span * multiple lines. * * @param string $parameter * Description of parameter. * @param array $options * Array of options: * - key1: Description. * - key2: Description. * * @return array * Description of return value. * * @throws \Exception * When error conditions occur. * * @see https://www.drupal.org/node/1354 */ public function myFunction($parameter, array $options = []) { // Implementation }

Resources

See Also