Files
2026-02-04 01:06:15 +01:00

8.2 KiB

Coding Conventions

Analysis Date: 2026-02-04

Reference: This document captures WinterCMS/Laravel PHP conventions that should guide the Scala rewrite. As SummerCMS is a greenfield Scala project, these patterns should be adapted to Scala idioms while maintaining architectural consistency.

Naming Patterns

Files:

  • Classes: PascalCase, one class per file (e.g., User.php, AuthManager.php, FormController.php)
  • Models: Singular PascalCase (e.g., User, UserRole, UserGroup in modules/backend/models/)
  • Controllers: Plural PascalCase with "Controller" suffix (e.g., Users.php, AccessLogs.php in modules/backend/controllers/)
  • Namespaces: Correspond to directory structure with backslash path separators

Functions/Methods:

  • camelCase (e.g., getFullNameAttribute(), sendInvitation(), getPersistCode())
  • Helper methods in models use verb prefixes: get*, set*, is*, has* (e.g., getAvatarThumb(), isSuperUser())
  • Event hooks use specific naming: afterCreate(), afterLogin(), afterUpdate() (see modules/backend/models/User.php lines 129-158)
  • Private methods are lowercase camelCase

Variables:

  • camelCase for all variable names (e.g., $persist_code, $send_invite, $full_name)
  • Snake_case for database column names mapped to model attributes
  • Configuration array keys use lowercase dot notation (e.g., 'cms.pluginsPath', 'database.default')

Types/Classes:

  • Model classes: Base classes in modules/*/models/ (e.g., Backend\Models\User extends Winter\Storm\Auth\Models\User as UserBase)
  • Controller classes: Backend\Controllers\Users implements behaviors
  • Use fully qualified namespaced class names in type declarations

Constants:

  • UPPER_SNAKE_CASE for constants (convention follows Laravel)

Code Style

Formatting:

  • EditorConfig enforced via .editorconfig at repository root:
    • End of line: LF
    • Charset: UTF-8
    • Indent: 4 spaces
    • Trim trailing whitespace: enabled
    • Insert final newline: enabled
    • Workflow files (.github/workflows/): 2-space indent

Spacing:

  • Opening braces on same line (e.g., public function getData() {)
  • Closing braces on new line
  • Method declarations with full type hints and documentation (see modules/backend/models/User.php lines 78-98)

Linting:

  • Tool: PHP CodeSniffer (squizlabs/php_codesniffer) via composer script
  • Command: composer sniff - Checks PSR-1, PSR-2, PSR-4 standards
  • Command: composer lint - Parallel lint checking with php-parallel-lint
  • Excludes: vendor/, storage/, specific test fixtures

Documentation:

  • PSDoc style comments on all public methods with @param, @return, @var annotations
  • Class-level doc blocks include package and author info:
    /**
     * Administrator user model
     *
     * @package winter\wn-backend-module
     * @author Alexey Bobkov, Samuel Georges
     */
    
  • Inline comments explain non-obvious logic (see modules/backend/models/User.php lines 89-95 option explanation)

Import Organization

Order (per file):

  1. PHP namespace declaration (no blank line)
  2. Use statements grouped:
    • Facades first (e.g., use Backend\Facades\Backend;)
    • Models next (e.g., use Backend\Models\UserGroup;)
    • Classes and utilities (e.g., use Winter\Storm\Database\Traits\SoftDelete;)
  3. Blank line before class declaration

Example (modules/backend/models/User.php lines 1-15):

<?php namespace Backend\Models;

use Backend\Facades\Backend;
use Backend\Facades\BackendAuth;
use Illuminate\Support\Facades\Event;
use Winter\Storm\Auth\Models\User as UserBase;
use Winter\Storm\Support\Facades\Mail;

/**
 * Administrator user model
 * ...
 */
class User extends UserBase

Path Aliases:

  • No import aliases in WinterCMS codebase - full namespace paths are used
  • Traits are explicitly used via use keyword within class body (e.g., use \Winter\Storm\Database\Traits\SoftDelete;)

Error Handling

Patterns:

  • No explicit try-catch in models/controllers; exceptions bubble to framework handlers
  • Validation errors handled via model $rules property (e.g., modules/backend/models/User.php lines 27-32)
  • Framework handles model validation failures and redirects with flash messages
  • Exception types: Winter\Storm\Exception\SystemException used for framework-level errors (seen in tests)

Validation:

  • Model rules defined in public $rules array with pipe-separated validators:
    public $rules = [
        'email' => 'required|between:6,255|email|unique:backend_users',
        'login' => 'required|between:2,255|unique:backend_users',
        'password' => 'required:create|min:4|confirmed',
    ];
    

Logging

Framework: Laravel native logging (via Illuminate\Support\Facades\Log or helpers)

Patterns:

  • No explicit logging calls in examined source files
  • Framework routes all errors through Laravel's error handler
  • Debug mode toggled via .env APP_DEBUG setting (defaults to true in config but should be false in production)
  • Log configuration in config/logging.php

Observability:

  • Model lifecycle events fired via Event::fire() for observation/extension:
    Event::fire('backend.user.login', [$this]);
    

Comments

When to Comment:

  • Explain "why", not "what" (code explains what it does)
  • Document non-obvious algorithms or workarounds
  • Mark uncertain logic with @todo, @TODO (seen throughout codebase)

Comment Styles:

  • Single-line: // Comment
  • Multi-line: Block with /** ... */ for docblocks only
  • Inline: After code with // Comment

JSDoc/TSDoc:

  • PHP style with @param, @return, @var, @throws tags
  • Event documentation uses @event tag with example usage (see modules/backend/models/User.php lines 147-158)
  • Database column documentation via @var on property declarations

Function Design

Size:

  • Methods generally 10-40 lines (see examples in User model)
  • Extracted helper methods for reusable logic
  • Model lifecycle hooks are concise (afterCreate, afterLogin, etc.)

Parameters:

  • Type-hinted where applicable
  • Favor explicit parameters over options arrays
  • Options arrays used for backward compatibility (e.g., getAvatarThumb($size = 25, $options = null))

Return Values:

  • Explicit return statements
  • Type hints on return values in docblocks (though PHP 7.4+ allows native return types)
  • Null returns used for "not found" cases

Module Design

Exports:

  • Classes explicitly imported via use statements
  • No wildcard imports (use Namespace\*;)
  • All public classes are explicitly declared

Barrel Files:

  • No barrel/index files observed in WinterCMS
  • Direct class imports required

Class Properties:

  • Public properties for model relations and validation rules:
    public $belongsToMany = [...];
    public $belongsTo = [...];
    public $rules = [...];
    public $implement = [...];  // In controllers
    
  • Protected properties for internal state (e.g., protected $table, protected $dates)
  • Magic properties documented via @var doc comments

Traits:

  • Used for cross-cutting concerns (e.g., SoftDelete, UploadableWidget)
  • Applied within class body via use statement
  • Traits encapsulate reusable behavior patterns

Event System

Framework: Laravel Event system with plugin extension points

Patterns:

  • Events fired at significant lifecycle points (e.g., backend.user.login)
  • Listeners registered via Event::listen() in plugin service providers
  • Event documentation includes example listener code
  • Used for non-breaking extensibility across plugins

Model Extension Pattern

Dynamic Extension:

  • Models extended via extend() method in plugin registration:
    User::extend(function ($model) {
        $model->hasMany['payments'] = [PaymentModel::class, 'key' => 'user_id'];
    });
    
  • Allows plugins to add relations, validation, behaviors without modifying core models

Translation/Localization

Framework: Winter's translation system

Pattern:

  • Backend labels use plugin namespace: 'plugin.namespace::lang.section.key'
  • Translation filter in views: {{ 'String'|_ }}
  • Used for all user-facing strings (labels, help text, messages)

Convention analysis: 2026-02-04 Reference: WinterCMS PHP codebase patterns to inform Scala rewrite