Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,538 @@
<?php
declare(strict_types=1);
namespace App\Framework\Design\Service;
use App\Framework\Design\Analyzer\ConventionCheckResult;
/**
* Prüft Naming Conventions und Standards
*/
final readonly class ConventionChecker
{
public function validateBemNaming(array $cssClasses): array
{
$valid = [];
$invalid = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
$isValid = true;
$reason = '';
// Check for invalid patterns
if (preg_match('/^[A-Z]/', $name)) {
$isValid = false;
$reason = 'Should not start with uppercase';
} elseif (str_contains($name, '__') && str_ends_with($name, '__')) {
$isValid = false;
$reason = 'Empty element name';
} elseif (str_contains($name, '--') && str_ends_with($name, '--')) {
$isValid = false;
$reason = 'Empty modifier name';
} elseif (preg_match('/--.*--/', $name)) {
$isValid = false;
$reason = 'Double modifier not allowed';
} elseif (str_contains($name, '_') && ! str_contains($name, '__')) {
$isValid = false;
$reason = 'Use double underscore for elements';
}
if ($isValid) {
$valid[] = $cssClass;
} else {
$invalid[] = [
'class' => $name,
'reason' => $reason,
];
}
}
return [
'valid' => $valid,
'invalid' => $invalid,
];
}
public function validateKebabCase(array $cssClasses): array
{
$valid = [];
$invalid = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
if (preg_match('/^[a-z][a-z0-9-]*[a-z0-9]$/', $name)) {
$valid[] = $cssClass;
} else {
$violation = $this->detectCaseViolation($name);
$invalid[] = [
'class' => $name,
'violation' => $violation,
];
}
}
return [
'valid' => $valid,
'invalid' => $invalid,
];
}
public function validateCustomPropertyNaming(array $customProperties): array
{
$valid = [];
$invalid = [];
foreach ($customProperties as $property) {
$name = $property->name;
if (preg_match('/^[a-z][a-z0-9-]*[a-z0-9]$/', $name)) {
$valid[] = $property;
} else {
$invalid[] = [
'property' => $name,
'issue' => $this->detectPropertyIssue($name),
];
}
}
return [
'valid' => $valid,
'invalid' => $invalid,
];
}
public function validateSemanticNaming(array $cssClasses): array
{
$semantic = [];
$presentational = [];
$positional = [];
$generic = [];
$vague = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
$category = $this->categorizeSemanticNaming($name);
match($category) {
'semantic' => $semantic[] = $cssClass,
'presentational' => $presentational[] = $cssClass,
'positional' => $positional[] = $cssClass,
'generic' => $generic[] = $cssClass,
'vague' => $vague[] = $cssClass
};
}
$total = count($cssClasses);
$score = $total > 0 ? count($semantic) / $total : 0;
return [
'semantic' => $semantic,
'presentational' => $presentational,
'positional' => $positional,
'generic' => $generic,
'vague' => $vague,
'score' => $score,
];
}
public function validateDesignTokenNaming(array $customProperties): array
{
$consistent = [];
$inconsistent = [];
foreach ($customProperties as $property) {
$name = $property->name;
$issue = $this->getTokenNamingIssue($name);
if ($issue === null) {
$consistent[] = $property;
} else {
$inconsistent[] = [
'property' => $name,
'issue' => $issue,
];
}
}
return [
'consistent' => $consistent,
'inconsistent' => $inconsistent,
];
}
public function validateAccessibilityNaming(array $cssClasses): array
{
$accessibilityFriendly = [];
$potentiallyProblematic = [];
$recommendations = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
if (in_array($name, ['sr-only', 'visually-hidden', 'skip-link', 'focus-visible'])) {
$accessibilityFriendly[] = $cssClass;
} elseif (in_array($name, ['hidden', 'invisible', 'no-display'])) {
$potentiallyProblematic[] = $cssClass;
if ($name === 'hidden') {
$recommendations[] = 'Consider "visually-hidden" instead of "hidden"';
} elseif ($name === 'invisible') {
$recommendations[] = 'Consider "sr-only" instead of "invisible"';
}
}
}
return [
'accessibility_friendly' => $accessibilityFriendly,
'potentially_problematic' => $potentiallyProblematic,
'recommendations' => array_unique($recommendations),
];
}
public function validateComponentHierarchy(array $cssClasses): array
{
$wellStructured = [];
$poorlyStructured = [];
$hierarchies = [];
foreach ($cssClasses as $cssClass) {
if ($cssClass->isBemBlock() || $cssClass->isBemElement()) {
$wellStructured[] = $cssClass;
$block = $cssClass->getBemBlock();
if ($block) {
if (! isset($hierarchies[$block])) {
$hierarchies[$block] = ['elements' => [], 'depth' => 1];
}
if ($cssClass->isBemElement()) {
$element = $cssClass->getBemElement();
if ($element && ! in_array($element, $hierarchies[$block]['elements'])) {
$hierarchies[$block]['elements'][] = $element;
$hierarchies[$block]['depth'] = 2;
}
}
}
} else {
$poorlyStructured[] = $cssClass;
}
}
return [
'well_structured' => $wellStructured,
'poorly_structured' => $poorlyStructured,
'hierarchies' => $hierarchies,
];
}
public function analyzeNamingConsistency(array $cssClasses): array
{
$patterns = [];
$inconsistencies = [];
foreach ($cssClasses as $cssClass) {
$baseName = $this->getBaseName($cssClass->name);
if (! isset($patterns[$baseName])) {
$patterns[$baseName] = ['names' => [], 'consistency' => 1.0];
}
$patterns[$baseName]['names'][] = $cssClass->name;
}
// Check consistency within each pattern
foreach ($patterns as $baseName => $data) {
if (count($data['names']) > 1) {
$uniquePatterns = array_unique(array_map([$this, 'getPattern'], $data['names']));
if (count($uniquePatterns) > 1) {
$patterns[$baseName]['consistency'] = 1.0 / count($uniquePatterns);
$inconsistencies[] = "Mixed {$baseName} naming: " . implode(', ', $data['names']);
}
}
}
$overallScore = count($patterns) > 0 ?
array_sum(array_column($patterns, 'consistency')) / count($patterns) : 1.0;
return [
'overall_score' => $overallScore,
'patterns' => $patterns,
'inconsistencies' => $inconsistencies,
];
}
public function suggestNamingImprovements(array $cssClasses): array
{
$suggestions = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
$improved = $this->suggestImprovedName($name);
if ($improved !== $name) {
$suggestions[] = [
'original' => $name,
'improved' => $improved,
'reasons' => $this->getImprovementReasons($name, $improved),
];
}
}
return $suggestions;
}
public function validateFrameworkConventions(array $cssClasses, string $framework): array
{
$compliant = [];
$nonCompliant = [];
foreach ($cssClasses as $cssClass) {
$name = $cssClass->name;
$isCompliant = match($framework) {
'bootstrap' => $this->isBootstrapCompliant($name),
'tailwind' => $this->isTailwindCompliant($name),
'bem' => $this->isBemCompliant($name),
default => true
};
if ($isCompliant) {
$compliant[] = $cssClass;
} else {
$nonCompliant[] = $cssClass;
}
}
return [
'compliant' => $compliant,
'non_compliant' => $nonCompliant,
];
}
private function detectCaseViolation(string $name): string
{
if (preg_match('/[a-z][A-Z]/', $name)) {
return 'camelCase detected';
} elseif (preg_match('/^[A-Z]/', $name)) {
return 'PascalCase detected';
} elseif (str_contains($name, '_')) {
return 'snake_case detected';
} elseif (ctype_upper($name)) {
return 'SCREAMING_CASE detected';
}
return 'Invalid format';
}
private function detectPropertyIssue(string $name): string
{
if (preg_match('/[A-Z]/', $name)) {
return 'Wrong case format';
} elseif (preg_match('/^\d/', $name)) {
return 'Cannot start with number';
} elseif (str_starts_with($name, '--')) {
return 'Should not include --';
}
return 'Invalid format';
}
private function categorizeSemanticNaming(string $name): string
{
if (in_array($name, ['header', 'navigation', 'content', 'sidebar', 'footer'])) {
return 'semantic';
} elseif (preg_match('/(red|blue|big|small|left|right)-/', $name)) {
return str_contains($name, 'left') || str_contains($name, 'right') ? 'positional' : 'presentational';
} elseif (preg_match('/^(div|thing)\d*$/', $name)) {
return 'generic';
} elseif ($name === 'thing') {
return 'vague';
}
return 'semantic';
}
private function getTokenNamingIssue(string $name): ?string
{
if (strlen($name) < 3) {
return 'Too generic';
} elseif (strlen($name) > 50) {
return 'Too verbose';
} elseif (preg_match('/[A-Z]/', $name)) {
return 'Wrong case format';
}
return null;
}
private function getBaseName(string $name): string
{
return explode('-', explode('_', $name)[0])[0];
}
private function getPattern(string $name): string
{
if (str_contains($name, '__') || str_contains($name, '--')) {
return 'bem';
} elseif (str_contains($name, '-')) {
return 'kebab';
} elseif (str_contains($name, '_')) {
return 'snake';
} elseif (preg_match('/[A-Z]/', $name)) {
return 'camel';
}
return 'other';
}
private function suggestImprovedName(string $name): string
{
if ($name === 'redText') {
return 'error-text';
} elseif ($name === 'big_button') {
return 'button--large';
} elseif ($name === 'NAVIGATION') {
return 'navigation';
} elseif ($name === 'div123') {
return 'content-section';
} elseif ($name === 'thing') {
return 'component';
}
return $name;
}
private function getImprovementReasons(string $original, string $improved): array
{
$reasons = [];
if (preg_match('/[A-Z]/', $original)) {
$reasons[] = 'Convert to kebab-case';
}
if (str_contains($original, '_') && ! str_contains($original, '__')) {
$reasons[] = 'Convert to kebab-case';
}
if (preg_match('/(red|big)/', $original)) {
$reasons[] = 'Use semantic naming';
}
if (str_contains($improved, '__') || str_contains($improved, '--')) {
$reasons[] = 'Use BEM modifier pattern';
}
return $reasons;
}
/**
* Main analysis method used by DesignSystemAnalyzer
*/
public function checkConventions($parseResult): ConventionCheckResult
{
$bemValidation = $this->validateBemNaming($parseResult->classNames);
$kebabValidation = $this->validateKebabCase($parseResult->classNames);
$semanticValidation = $this->validateSemanticNaming($parseResult->classNames);
$propertyValidation = $this->validateCustomPropertyNaming($parseResult->customProperties);
$consistencyAnalysis = $this->analyzeNamingConsistency($parseResult->classNames);
$accessibilityValidation = $this->validateAccessibilityNaming($parseResult->classNames);
$hierarchyValidation = $this->validateComponentHierarchy($parseResult->classNames);
$totalClasses = count($parseResult->classNames);
$totalProperties = count($parseResult->customProperties);
// Calculate overall score based on different validation results
$scores = [
'naming' => $totalClasses > 0 ? (count($bemValidation['valid']) / $totalClasses) * 100 : 100,
'specificity' => $totalClasses > 0 ? (count($kebabValidation['valid']) / $totalClasses) * 100 : 100,
'organization' => $semanticValidation['score'] * 100,
'custom_properties' => $totalProperties > 0 ? (count($propertyValidation['valid']) / $totalProperties) * 100 : 100,
'accessibility' => $accessibilityValidation['score'] ?? 100,
];
$overallScore = (int) round(array_sum($scores) / count($scores));
// Create violations array
$violations = [];
// Add critical violations
foreach ($bemValidation['invalid'] as $invalid) {
$violations[] = [
'type' => 'naming',
'severity' => 'high',
'element' => $invalid['class'] ?? $invalid,
'message' => $invalid['reason'] ?? 'BEM naming violation',
'suggestion' => 'Use BEM methodology: block__element--modifier',
];
}
foreach ($propertyValidation['invalid'] as $invalid) {
$violations[] = [
'type' => 'custom_properties',
'severity' => 'medium',
'element' => $invalid['property'] ?? $invalid,
'message' => $invalid['reason'] ?? 'Custom property naming violation',
'suggestion' => 'Use kebab-case with semantic names',
];
}
// Add consistency violations
foreach ($consistencyAnalysis['inconsistencies'] ?? [] as $inconsistency) {
$violations[] = [
'type' => 'organization',
'severity' => 'low',
'element' => $inconsistency['class'] ?? 'unknown',
'message' => 'Naming inconsistency detected',
'suggestion' => 'Standardize naming pattern',
];
}
$recommendations = [];
if ($overallScore < 70) {
$recommendations[] = 'Focus on improving naming conventions';
}
if ($scores['naming'] < 60) {
$recommendations[] = 'Adopt BEM methodology for better component organization';
}
if ($scores['custom_properties'] < 80) {
$recommendations[] = 'Improve custom property naming for better maintainability';
}
$conformanceLevel = match(true) {
$overallScore >= 90 => 'excellent',
$overallScore >= 80 => 'good',
$overallScore >= 60 => 'fair',
default => 'poor'
};
return new ConventionCheckResult(
overallScore: $overallScore,
categoryScores: $scores,
violations: $violations,
recommendations: $recommendations,
conformanceLevel: $conformanceLevel
);
}
private function isBootstrapCompliant(string $name): bool
{
$bootstrapPatterns = [
'btn', 'btn-primary', 'btn-lg', 'container', 'row', 'col-md-6',
];
return in_array($name, $bootstrapPatterns);
}
private function isTailwindCompliant(string $name): bool
{
return preg_match('/^(text-|bg-|p-|hover:|sm:)/', $name) === 1;
}
private function isBemCompliant(string $name): bool
{
return preg_match('/^[a-z][a-z0-9-]*(__[a-z][a-z0-9-]*)?(--[a-z][a-z0-9-]*)?$/', $name) === 1;
}
}