← Documentation Home

Drupal Coding Standards

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.bluefly.io/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
  2. Configuration: /path/to/vendor/bin/phpcs
  3. Coding standard: Drupal

  4. Settings > PHP > Quality Tools > PHPStan

  5. Configuration: /path/to/vendor/bin/phpstan
  6. Configuration file: phpstan.neon

  7. Enable inspections

  8. Settings > Editor > Inspections
  9. Enable: PHP_CodeSniffer validation
  10. 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