Drupal Coding Standards
Comprehensive guide for Drupal coding standards, PHPCS, PHPStan, and code quality in the LLM Platform.
Table of Contents
- Overview
- PHPCS Configuration
- PHPStan Configuration
- Code Quality Tools
- Design Taste Validation
- Common Violations
- Auto-Fixing
Overview
The LLM Platform enforces strict coding standards to ensure code quality, maintainability, and security.
Standards Applied
- Drupal: Core Drupal coding standards
- DrupalPractice: Best practices for Drupal development
- PHPStan: Static analysis for type safety
- 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
- Settings > PHP > Quality Tools > PHP_CodeSniffer
- Configuration:
/path/to/vendor/bin/phpcs -
Coding standard: Drupal
-
Settings > PHP > Quality Tools > PHPStan
- Configuration:
/path/to/vendor/bin/phpstan -
Configuration file:
phpstan.neon -
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
}