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
- 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:
- Fix immediately
- Create GitLab issue
- 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
-
Settings > PHP > Quality Tools > PHP_CodeSniffer
- Configuration:
/path/to/vendor/bin/phpcs - Coding standard: Drupal
- Configuration:
-
Settings > PHP > Quality Tools > PHPStan
- Configuration:
/path/to/vendor/bin/phpstan - Configuration file:
phpstan.neon
- Configuration:
-
Enable inspections
- Settings > Editor > Inspections
- Enable: PHP_CodeSniffer validation
- Enable: PHPStan validation
VS Code
Install extensions:
ikappas.phpcs- PHP_CodeSnifferSanderRonde.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 }