pre_rules_constants.py

Code Hygiene Score: 98

Keine Issues gefunden.

Dependencies 6

Funktionen 2

Code

#!/usr/bin/env python3
"""
Pre-Hook Constants Regeln (BLOCK) - Magic Number Enforcement.

P7.x Regeln: Keine Magic Numbers, benannte Konstanten verwenden.
"""

import re
from typing import Optional
from .rule_base import GLOBAL_ALLOWLIST, COMMON_NUMBERS, is_in_allowlist, block


# =============================================================================
# CONSTANTS ALLOWLIST
# =============================================================================

CONSTANTS_ALLOWLIST = [
    "/Constants.php",           # PHP Constants file
    "/constants.py",            # Python constants files
    "/config.py",               # Config files
    "/Config.php",              # PHP config files
    "/config/",                 # Config directories
    "CodeQualityChecker.php",   # Quality checker itself
]

# Erlaubte Magic Numbers (zusätzlich zu COMMON_NUMBERS)
ALLOWED_MAGIC = COMMON_NUMBERS | {
    # Array-Indizes
    '3', '4', '5', '6', '7', '8', '9',
    # HTTP Status
    '202', '206', '300', '303', '307', '308',
    '405', '406', '408', '409', '410', '422', '429', '502', '503', '504',
    # Bit-Operationen
    '16', '32', '64', '128', '256', '512', '1024', '2048', '4096',
}

# Kritische Magic Numbers die IMMER blockiert werden
CRITICAL_MAGIC = {
    '3600': 'SECONDS_PER_HOUR',
    '86400': 'SECONDS_PER_DAY',
    '604800': 'SECONDS_PER_WEEK',
    '2592000': 'SECONDS_PER_MONTH',
    '31536000': 'SECONDS_PER_YEAR',
}


# =============================================================================
# PRÜFUNG 7: CONSTANTS
# =============================================================================

def p7_1_critical_magic_numbers(file_path: str, content: str) -> Optional[dict]:
    """P7.1: Kritische Magic Numbers blockieren (3600, 86400, etc.)."""
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST + CONSTANTS_ALLOWLIST):
        return None

    # Nur PHP und Python Dateien
    if not (file_path.endswith('.php') or file_path.endswith('.py')):
        return None

    lines = content.split('\n')
    for line_num, line in enumerate(lines, 1):
        # Skip Kommentare
        stripped = line.strip()
        if stripped.startswith('//') or stripped.startswith('#') or stripped.startswith('*'):
            continue

        # Skip const/define Definitionen
        if re.search(r'\bconst\b|\bdefine\b|=.*Constants::', line, re.IGNORECASE):
            continue

        # Prüfe auf kritische Magic Numbers
        for magic, constant_name in CRITICAL_MAGIC.items():
            # Nur alleinstehende Zahlen matchen
            pattern = rf'\b{magic}\b'
            if re.search(pattern, line):
                return block(
                    "P7.1",
                    f"Critical magic number {magic} found at line {line_num}. "
                    f"Use Domain\\Constants::{constant_name} instead."
                )

    return None


def p7_2_time_calculations(file_path: str, content: str) -> Optional[dict]:
    """P7.2: Zeit-Berechnungen mit Magic Numbers blockieren."""
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST + CONSTANTS_ALLOWLIST):
        return None

    if not (file_path.endswith('.php') or file_path.endswith('.py')):
        return None

    # Pattern für Zeit-Berechnungen: * 60 * 60, * 24 * 60, etc.
    time_calc_patterns = [
        (r'\*\s*60\s*\*\s*60', 'Use Constants::SECONDS_PER_HOUR'),
        (r'\*\s*24\s*\*\s*60', 'Use Constants for time calculations'),
        (r'\*\s*60\s*\*\s*24', 'Use Constants for time calculations'),
        (r'\*\s*365\s*\*\s*24', 'Use Constants for time calculations'),
    ]

    lines = content.split('\n')
    for line_num, line in enumerate(lines, 1):
        # Skip Kommentare
        stripped = line.strip()
        if stripped.startswith('//') or stripped.startswith('#') or stripped.startswith('*'):
            continue

        for pattern, suggestion in time_calc_patterns:
            if re.search(pattern, line):
                return block(
                    "P7.2",
                    f"Time calculation with magic numbers at line {line_num}. {suggestion}"
                )

    return None


# =============================================================================
# RULE COLLECTION
# =============================================================================

RULES = [
    p7_1_critical_magic_numbers,
    p7_2_time_calculations,
]
← Übersicht