projectrules.ai

Enhanced PHP & Drupal Development Standards

PHPDrupalCoding StandardsBest PracticesModularity

Description

PHP & Drupal Development Standards and Best Practices

Globs

*.php, *.module, *.theme, *.inc, *.info, *.install
---
description: PHP & Drupal Development Standards and Best Practices
globs: *.php, *.module, *.theme, *.inc, *.info, *.install
---

# Enhanced PHP & Drupal Development Standards

Defines comprehensive coding standards and best practices for PHP and Drupal development, with a focus on modern PHP features, Drupal 10+ standards, and modularity.

<rule>
name: enhanced_php_drupal_best_practices
description: Enforce PHP 8.3+ features, Drupal 10+ coding standards, and modularity
filters:
  - type: file_extension
    pattern: "\\.(php|module|inc|install|theme)$"
  - type: file_path
    pattern: "web/modules/custom/|web/themes/custom/"

actions:
  - type: enforce
    conditions:
      - pattern: "^(?!declare\\(strict_types=1\\);)"
        message: "Add 'declare(strict_types=1);' at the beginning of PHP files for type safety."

      - pattern: "(?<!\\bTRUE\\b)\\btrue\\b|(?<!\\bFALSE\\b)\\bfalse\\b|(?<!\\bNULL\\b)\\bnull\\b"
        message: "Use uppercase for TRUE, FALSE, and NULL constants."

      - pattern: "(?i)\\/\\/\\s[a-z]"
        message: "Ensure inline comments begin with a capital letter and end with a period."

      - pattern: "class\\s+\\w+\\s*(?!\\{[^}]*readonly\\s+\\$)"
        message: "Consider using readonly properties where immutability is required."

      - pattern: "public\\s+function\\s+\\w+\\([^)]*\\)\\s*(?!:)"
        message: "Add return type declarations for all methods to ensure type safety."

      - pattern: "extends\\s+\\w+\\s*\\{[^}]*public\\s+function\\s+\\w+\\([^)]*\\)\\s*(?!#\\[Override\\])"
        message: "Add #[Override] attribute for overridden methods for clarity."

      - pattern: "\\$\\w+\\s*(?!:)"
        message: "Use typed properties with proper nullability for better code maintainability."

      - pattern: "function\\s+hook_\\w+\\([^)]*\\)\\s*(?!:)"
        message: "Add type hints and return types for all hooks to leverage PHP's type system."

      - pattern: "new\\s+\\w+\\([^)]*\\)\\s*(?!;\\s*//\\s*@inject)"
        message: "Use proper dependency injection with services for better testability and modularity."

      - pattern: "extends\\s+FormBase\\s*\\{[^}]*validate"
        message: "Implement proper form validation in FormBase classes for security."

      - pattern: "function\\s+\\w+\\s*\\([^)]*\\)\\s*\\{[^}]*\\$this->t\\("
        message: "Use Drupal's t() function for strings that need translation."

      - pattern: "\\$this->config\\('\\w+'\\)"
        message: "Use ConfigFactory for configuration management."
      
      - pattern: "array\\s*\\("
        message: "Use short array syntax ([]) instead of array() for consistent code style."
      
      - pattern: "(?<!\\()\\s+\\(int\\)\\s*\\$"
        message: "Put a space between the (type) and the $variable in a cast: (int) $mynumber."
      
      - pattern: "\\n[\\t ]+\\n"
        message: "Remove whitespace from empty lines."
      
      - pattern: "\\s+$"
        message: "Remove trailing whitespace at the end of lines."
      
      - pattern: "^(?!.*\\n$)"
        message: "Ensure files end with a single newline character."
      
      - pattern: "if\\s*\\([^)]*\\)\\s*\\{[^{]*\\}\\s*else\\s*\\{"
        message: "Place the opening brace on the same line as the statement for control structures."
      
      - pattern: "\\$_GET|\\$_POST|\\$_REQUEST"
        message: "Never use superglobals directly; use Drupal's input methods."
      
      - pattern: "mysql_|mysqli_"
        message: "Use Drupal's database API instead of direct MySQL functions."
      
      - pattern: "\\t+"
        message: "Use 2 spaces for indentation, not tabs."
      
      - pattern: "function\\s+\\w+\\s*\\([^)]*\\)\\s*\\{[^}]*\\becho\\b"
        message: "Don't use echo; use return values or Drupal's messenger service."
      
      - pattern: "(?<!\\/\\*\\*)\\s*\\*\\s+@"
        message: "Use proper DocBlock formatting for documentation."
      
      - pattern: "\\bdie\\b|\\bexit\\b"
        message: "Don't use die() or exit(); throw exceptions instead."
      
      - pattern: "\\$entity->get\\([^)]+\\)->getValue\\(\\)"
        message: "Use $entity->get('field_name')->value instead of getValue() when possible."
      
      - pattern: "\\bvar_dump\\b|\\bprint_r\\b|\\bdump\\b"
        message: "Don't use debug functions in production code; use Drupal's logger instead."
      
      - pattern: "\\bnew\\s+DateTime\\b"
        message: "Use Drupal's DateTimeInterface and DrupalDateTime instead of PHP's DateTime."
      
      - pattern: "\\beval\\b"
        message: "Never use eval() as it poses security risks."
      
      - pattern: "function\\s+\\w+_menu_callback\\("
        message: "Use controller classes with route definitions instead of hook_menu() callbacks."
      
      - pattern: "\\/\\*\\*(?:[^*]|\\*[^/])*?@file(?:[^*]|\\*[^/])*?\\*\\/"
        message: "All PHP files must include proper @file documentation in the docblock."
      
      - pattern: "function\\s+\\w+\\s*\\((?:[^)]|\\([^)]*\\))*\\)\\s*\\{(?:[^}]|\\{[^}]*\\})*\\$_SESSION"
        message: "Use Drupal's user session handling instead of $_SESSION."
      
      - pattern: "function\\s+theme_\\w+\\("
        message: "Theme functions should be replaced with Twig templates in Drupal 8+."
      
      - pattern: "drupal_add_js|drupal_add_css"
        message: "Use #attached in render arrays instead of drupal_add_js() or drupal_add_css()."
      
      - pattern: "function\\s+\\w+_implements_hook_\\w+\\("
        message: "Use proper hook implementation format: module_name_hook_name()."
        
      - pattern: "use\\s+[^;]+,\\s*[^;]+"
        message: "Specify a single class per use statement. Do not specify multiple classes in a single use statement."
        
      - pattern: "use\\s+\\\\[A-Za-z]"
        message: "When importing a class with 'use', do not include a leading backslash (\\)."
        
      - pattern: "\\bnew\\s+\\\\DateTime\\(\\)"
        message: "Non-namespaced global classes (like Exception) must be fully qualified with a leading backslash (\\) when used in a namespaced file."
        
      - pattern: "(?<!namespace )Drupal\\\\(?!\\w+\\\\)"
        message: "Modules should place classes inside a custom namespace: Drupal\\module_name\\..."
        
      - pattern: "class\\s+\\w+\\s*(?:extends|implements)(?:[^{]+)\\{\\s*[^\\s]"
        message: "Leave an empty line between start of class/interface definition and property/method definition."
        
      - pattern: "Drupal\\\\(?!Core|Component)[A-Z]"
        message: "Module namespaces should be Drupal\\module_name, not Drupal\\ModuleName (camelCase not PascalCase)."
        
      - pattern: "\\\\Drupal::request\\(\\)->attributes->set\\('([^_][^']*)',"
        message: "Request attributes added by modules should be prefixed with underscore (e.g., '_context_value')."
        
      - pattern: "\\\\Drupal::request\\(\\)->attributes->get\\('(_(system_path|title|route|route_object|controller|content|account))'\\)"
        message: "Avoid overwriting reserved Symfony or Drupal core request attributes."
        
      - pattern: "(?<!service provider)\\s+class\\s+\\w+Provider(?!Interface)"
        message: "Classes that provide services should use the 'Provider' suffix (e.g., MyServiceProvider)."
        
      - pattern: "\\.services\\.yml[^}]*\\s+class:\\s+[^\\n]+\\s+arguments:"
        message: "Services should use dependency injection through constructor arguments defined in services.yml."

  - type: suggest
    message: |
      **PHP/Drupal Development Best Practices:**
      
      ### General Code Structure
      - **File Structure:** Each PHP file should have the proper structures: <?php tag, namespace declaration (if applicable), use statements, docblock, and implementation.
      - **Line Length:** Keep lines under 80 characters whenever possible.
      - **Indentation:** Use 2 spaces for indentation, never tabs.
      - **Empty Lines:** Use empty lines to separate logical blocks of code, but avoid multiple empty lines.
      - **File Endings:** All files must end with a single newline character.
      
      ### PHP Language Features
      - **PHP Version:** Use PHP 8.3+ features where appropriate.
      - **Strict Types:** Use declare(strict_types=1) at the top of files to enforce type safety.
      - **Type Hints:** Always use parameter and return type hints.
      - **Named Arguments:** Use named arguments for clarity in complex function calls.
      - **Attributes:** Use PHP 8 attributes like #[Override] for better code comprehension.
      - **Match Expressions:** Prefer match over switch for cleaner conditionals.
      - **Null Coalescing:** Use ?? and ??= operators where appropriate.
      
      ### Drupal-Specific Standards
      - **Fields API:** Use hasField(), get(), and value() methods when working with entity fields.
      - **Exception Handling:** Use try/catch for exception handling with proper logging.
      - **Database Layer:** Use Drupal's database abstraction layer for all queries.
      - **Schema Updates:** Implement hook_update_N() for schema changes during updates.
      - **Dependency Injection:** Use services.yml and proper container injection.
      - **Routing:** Define routes in routing.yml with proper access checks.
      - **Forms:** Extend FormBase or ConfigFormBase with proper validation and submission handling.
      - **Entity API:** Follow entity API best practices for loading, creating, and editing entities.
      - **Plugins:** Use plugin system appropriately with proper annotations.
      
      ### Service & Request Standards
      - **Service Naming:** Use descriptive service names and appropriate naming patterns (Provider suffix for service providers).
      - **Service Definition:** Define services in the module's *.services.yml file with appropriate tags and arguments.
      - **Request Attributes:** When adding attributes to the Request object, prefix custom attributes with underscore (e.g., `_context_value`).
      - **Reserved Attributes:** Avoid overwriting core-reserved request attributes like `_system_path`, `_title`, `_account`, `_route`, `_route_object`, `_controller`, `_content`.
      - **Service Container:** Use dependency injection rather than the service container directly.
      - **Factory Services:** Use factory methods for complex service instantiation.
      
      ### Namespace Standards
      - **Module Namespace:** Use Drupal\\module_name\\... for all custom module code.
      - **PSR-4 Autoloading:** Class in folder module/src/SubFolder should use namespace Drupal\\module_name\\SubFolder.
      - **Use Statements:** Each class should have its own use statement; don't combine multiple classes in one use.
      - **No Leading Backslash:** Don't add a leading backslash (\\) in use statements.
      - **Global Classes:** Global classes (like Exception) must be fully qualified with a leading backslash (\\) when used in a namespaced file.
      - **Class Aliasing:** Only alias classes to avoid name collisions, using meaningful names like BarBaz and ThingBaz.
      - **String Class Names:** When specifying a class name in a string, use full name including namespace without leading backslash. Prefer single quotes.
      - **Class Placement:** A class named Drupal\\module_name\\Foo should be in file module_name/src/Foo.php.
      
      ### Security Practices
      - **Input Validation:** Always validate and sanitize user input.
      - **Access Checks:** Implement proper access checks for all routes and content.
      - **CSRF Protection:** Use Form API with proper form tokens for all forms.
      - **SQL Injection:** Use parameterized queries with placeholders.
      - **XSS Prevention:** Use Xss::filter() or t() with appropriate placeholders.
      - **File Security:** Validate uploaded files and restrict access properly.
      
      ### Documentation and Comments
      - **PHPDoc Blocks:** Document all classes, methods, and properties with proper PHPDoc.
      - **Function Comments:** Describe parameters, return values, and exceptions.
      - **Inline Comments:** Use meaningful comments for complex logic.
      - **Comment Format:** Begin comments with a capital letter and end with a period.
      - **API Documentation:** Follow Drupal's API documentation standards.
      
      ### Performance
      - **Caching:** Implement proper cache tags, contexts, and max-age.
      - **Database Queries:** Optimize queries with proper indices and JOINs.
      - **Lazy Loading:** Use lazy loading for expensive operations.
      - **Batch Processing:** Use batch API for long-running operations.
      - **Static Caching:** Implement static caching for repeated operations.
      
      ### Testing
      - **Unit Tests:** Write PHPUnit tests for business logic.
      - **Kernel Tests:** Use kernel tests for integration with Drupal subsystems.
      - **Functional Tests:** Implement functional tests for user interactions.
      - **Mocking:** Use proper mocking techniques for dependencies.
      - **Test Coverage:** Aim for high test coverage of critical functionality.
      
      ### API Documentation Examples
      
      #### File Documentation
      
      **Module Files (.module)**
      ```php
      <?php
      
      /**
       * @file
       * Provides [module functionality description].
       */
      ```
      
      **Install Files (.install)**
      ```php
      <?php
      
      /**
       * @file
       * Install, update and uninstall functions for the [module name] module.
       */
      ```
      
      **Include Files (.inc)**
      ```php
      <?php
      
      /**
       * @file
       * [Specific functionality] for the [module name] module.
       */
      ```
      
      **Class Files (in namespaced directories)**
      ```php
      <?php
      
      namespace Drupal\module_name\ClassName;
      
      use Drupal\Core\SomeClass;
      
      /**
       * Provides [class functionality description].
       *
       * [Extended description if needed]
       */
      class ClassName implements InterfaceName {
      ```
      
      #### Function Documentation
      
      **Standard Function**
      ```php
      /**
       * Returns [what the function returns or does].
       *
       * [Additional explanation if needed]
       *
       * @param string $param1
       *   Description of parameter.
       * @param int $param2
       *   Description of parameter.
       *
       * @return array
       *   Description of returned data.
       *
       * @throws \Exception
       *   Exception thrown when [condition].
       *
       * @see related_function()
       */
      function module_function_name($param1, $param2) {
      ```
      
      **Hook Implementation**
      ```php
      /**
       * Implements hook_form_alter().
       */
      function module_name_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
      ```
      
      **Update Hook**
      ```php
      /**
       * Implements hook_update_N().
       *
       * [Description of what the update does].
       */
      function module_name_update_8001() {
      ```
      
      #### Class Documentation
      
      **Class Properties**
      ```php
      /**
       * The entity type manager.
       *
       * @var \Drupal\Core\Entity\EntityTypeManagerInterface
       */
      protected $entityTypeManager;
      ```
      
      **Method Documentation**
      ```php
      /**
       * Gets entities of a specific type.
       *
       * @param string $entity_type
       *   The entity type ID.
       * @param array $conditions
       *   (optional) An array of conditions to match. Defaults to an empty array.
       *
       * @return \Drupal\Core\Entity\EntityInterface[]
       *   An array of entity objects indexed by their IDs.
       *
       * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
       *   Thrown if the entity type doesn't exist.
       * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
       *   Thrown if the storage handler couldn't be loaded.
       */
      public function getEntities(string $entity_type, array $conditions = []): array {
      ```
      
      **Interface Method**
      ```php
      /**
       * Implements \SomeInterface::methodName().
       */
      public function methodName() {
      ```
      
      #### Namespace Examples
      
      **Using Classes From Other Namespaces**
      ```php
      namespace Drupal\mymodule\Tests\Foo;
      
      use Drupal\simpletest\WebTestBase;
      
      /**
       * Tests that the foo bars.
       */
      class BarTest extends WebTestBase {
      ```
      
      **Class Aliasing for Name Collisions**
      ```php
      use Foo\Bar\Baz as BarBaz;
      use Stuff\Thing\Baz as ThingBaz;
      
      /**
       * Tests stuff for the whichever.
       */
      function test() {
        $a = new BarBaz(); // This will be Foo\Bar\Baz
        $b = new ThingBaz(); // This will be Stuff\Thing\Baz
      }
      ```
      
      **Using Global Classes in Namespaced Files**
      ```php
      namespace Drupal\Subsystem;
      
      // Bar is a class in the Drupal\Subsystem namespace in another file.
      // It is already available without any importing.
      
      /**
       * Defines a Foo.
       */
      class Foo {
      
        /**
         * Constructs a new Foo object.
         */
        public function __construct(Bar $b) {
          // Global classes must be prefixed with a \ character.
          $d = new \DateTime();
        }
      }
      ```
      
      #### Service Definition Example
      
      **services.yml File**
      ```yaml
      services:
        mymodule.my_service:
          class: Drupal\mymodule\MyService
          arguments: ['@entity_type.manager', '@current_user']
          tags:
            - { name: cache.context }
      ```
      
      **Request Attribute Handling**
      ```php
      // Correctly adding a request attribute (with underscore prefix)
      \Drupal::request()->attributes->set('_context_value', $myvalue);
      
      // Correctly retrieving a request attribute
      $contextValue = \Drupal::request()->attributes->get('_context_value');
      ```

  - type: validate
    conditions:
      - pattern: "web/modules/custom/[^/]+/\\.info\\.yml$"
        message: "Ensure each custom module has a required .info.yml file."

      - pattern: "web/modules/custom/[^/]+/\\.module$"
        message: "Ensure module has .module file if hooks are implemented."

      - pattern: "web/modules/custom/[^/]+/src/Form/\\w+Form\\.php$"
        message: "Place form classes in the Form directory for organization."

      - pattern: "try\\s*\\{[^}]*\\}\\s*catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}"
        message: "Implement proper exception handling in catch blocks."
      
      - pattern: "namespace\\s+Drupal\\\\(?!\\w+\\\\)"
        message: "Namespace should be Drupal\\ModuleName\\..."
      
      - pattern: "class\\s+[^\\s]+\\s+implements\\s+[^\\s]+Interface"
        message: "Follow PSR-4 for class naming and organization."
      
      - pattern: "\\*\\s+@return\\s+[a-z]+\\|null"
        message: "Use nullable return types (e.g., ?string) instead of type|null in docblocks."
      
      - pattern: "function\\s+__construct\\([^)]*\\)\\s*\\{[^}]*parent::"
        message: "Call parent::__construct() if extending a class with a constructor."
      
      - pattern: "function\\s+[gs]et[A-Z]\\w+\\("
        message: "Use camelCase for method names (e.g., getId instead of get_id)."
      
      - pattern: "\\/\\*\\*(?:(?!\\@file).)*?\\*\\/"
        message: "Add proper @file docblock for PHP files."
      
      - pattern: "function\\s+hook_[a-z0-9_]+\\("
        message: "Replace 'hook_' prefix with your module name in hook implementations."
      
      - pattern: "(?<!\\s\\*)\\s+@(?:param|return|throws)\\b"
        message: "DocBlock tags should be properly aligned with leading asterisks."
      
      - pattern: "@param\\s+(?!(?:array|bool|callable|float|int|mixed|object|resource|string|void|null|\\\\)[\\s|])"
        message: "Use proper data types in @param tags (array, bool, int, string, etc.)."
      
      - pattern: "@return\\s+(?!(?:array|bool|callable|float|int|mixed|object|resource|string|void|null|\\\\)[\\s|])"
        message: "Use proper data types in @return tags (array, bool, int, string, etc.)."
      
      - pattern: "\\*\\s*@param[^\\n]*?(?:(?!\\s{3,})[^\\n])*$"
        message: "Parameter description should be separated by at least 3 spaces from the param type/name."
      
      - pattern: "function\\s+theme\\w+\\([^)]*\\)\\s*\\{[^}]*?(?!\\@ingroup\\s+themeable)"
        message: "Theme functions should include @ingroup themeable in their docblock."
      
      - pattern: "\\*\\s*@code(?!\\s+[a-z]+\\s+)"
        message: "@code blocks should specify the language (e.g., @code php)."
      
      - pattern: "namespace\\s+(?!Drupal\\\\)"
        message: "Namespaces should start with 'Drupal\\'."
        
      - pattern: "web/modules/custom/[^/]+/\\.services\\.yml$"
        message: "Every module using services should have a services.yml file."
      
      - pattern: "web/modules/custom/[^/]+/src/[^/]+Provider\\.php$"
        message: "Service providers should be in the module's root namespace (src/ directory)."

metadata:
  priority: critical
  version: 1.5
</rule>
Enhanced PHP & Drupal Development Standards