• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

move-elevator / composer-translation-validator / 24133345533

08 Apr 2026 11:38AM UTC coverage: 95.573% (+0.1%) from 95.451%
24133345533

push

github

web-flow
Merge pull request #111 from move-elevator/refactor/split-key-naming-convention-validator

refactor: split KeyNamingConventionValidator into KeyConverter and ConventionDetector

119 of 121 new or added lines in 3 files covered. (98.35%)

2 existing lines in 1 file now uncovered.

2375 of 2485 relevant lines covered (95.57%)

8.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.08
/src/Validator/KeyConverter.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the "composer-translation-validator" Composer plugin.
7
 *
8
 * (c) 2025-2026 Konrad Michalik <km@move-elevator.de>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace MoveElevator\ComposerTranslationValidator\Validator;
15

16
use MoveElevator\ComposerTranslationValidator\Enum\KeyNamingConvention;
17

18
use function count;
19

20
/**
21
 * KeyConverter.
22
 *
23
 * @author Konrad Michalik <km@move-elevator.de>
24
 * @license GPL-3.0-or-later
25
 */
26
final class KeyConverter
27
{
28
    public function toSnakeCase(string $key): string
29✔
29
    {
30
        // Convert camelCase/PascalCase to snake_case
31
        $result = preg_replace('/([a-z])([A-Z])/', '$1_$2', $key);
29✔
32
        // Convert kebab-case and dot.notation to snake_case
33
        $result = str_replace(['-', '.'], '_', $result ?? $key);
29✔
34

35
        // Convert to lowercase
36
        return strtolower($result);
29✔
37
    }
38

39
    public function toCamelCase(string $key): string
20✔
40
    {
41
        // Handle camelCase/PascalCase first (only if no delimiters are present)
42
        if (preg_match('/[A-Z]/', $key) && !preg_match('/[_\-.]/', $key)) {
20✔
43
            // Convert PascalCase to camelCase
44
            return lcfirst($key);
4✔
45
        }
46

47
        // Convert snake_case, kebab-case, and dot.notation to camelCase
48
        $parts = preg_split('/[_\-.]+/', $key);
16✔
49
        if (false === $parts) {
16✔
NEW
50
            return $key;
×
51
        }
52

53
        $result = strtolower($parts[0] ?? '');
16✔
54
        for ($i = 1, $iMax = count($parts); $i < $iMax; ++$i) {
16✔
55
            $result .= ucfirst(strtolower($parts[$i]));
12✔
56
        }
57

58
        return $result;
16✔
59
    }
60

61
    public function toKebabCase(string $key): string
9✔
62
    {
63
        // Convert camelCase/PascalCase to kebab-case
64
        $result = preg_replace('/([a-z])([A-Z])/', '$1-$2', $key);
9✔
65
        // Convert snake_case and dot.notation to kebab-case
66
        $result = str_replace(['_', '.'], '-', $result ?? $key);
9✔
67

68
        return strtolower($result);
9✔
69
    }
70

71
    public function toPascalCase(string $key): string
11✔
72
    {
73
        // Handle camelCase/PascalCase first (only if no delimiters are present)
74
        if (preg_match('/[A-Z]/', $key) && !preg_match('/[_\-.]/', $key)) {
11✔
75
            // Already in PascalCase or camelCase, just ensure first letter is uppercase
76
            return ucfirst($key);
4✔
77
        }
78

79
        // Convert snake_case, kebab-case, and dot.notation to PascalCase
80
        $parts = preg_split('/[_\-.]+/', $key);
7✔
81
        if (false === $parts) {
7✔
NEW
82
            return ucfirst($key);
×
83
        }
84

85
        return implode('', array_map(ucfirst(...), array_map(strtolower(...), $parts)));
7✔
86
    }
87

88
    public function toDotNotation(string $key): string
8✔
89
    {
90
        // Convert camelCase/PascalCase to dot.notation
91
        $result = preg_replace('/([a-z])([A-Z])/', '$1.$2', $key);
8✔
92
        // Convert snake_case and kebab-case to dot.notation
93
        $result = str_replace(['_', '-'], '.', $result ?? $key);
8✔
94

95
        return strtolower($result);
8✔
96
    }
97

98
    public function convertDotSeparatedKey(string $key, ?KeyNamingConvention $convention): string
11✔
99
    {
100
        if (null === $convention) {
11✔
101
            return $key;
2✔
102
        }
103

104
        $segments = explode('.', $key);
9✔
105
        $convertedSegments = [];
9✔
106

107
        foreach ($segments as $segment) {
9✔
108
            $convertedSegments[] = match ($convention) {
9✔
109
                KeyNamingConvention::SNAKE_CASE => $this->toSnakeCase($segment),
9✔
110
                KeyNamingConvention::CAMEL_CASE => $this->toCamelCase($segment),
6✔
111
                KeyNamingConvention::KEBAB_CASE => $this->toKebabCase($segment),
3✔
112
                KeyNamingConvention::PASCAL_CASE => $this->toPascalCase($segment),
3✔
113
                KeyNamingConvention::DOT_NOTATION => $this->toDotNotation($segment),
2✔
114
            };
9✔
115
        }
116

117
        return implode('.', $convertedSegments);
9✔
118
    }
119

120
    /**
121
     * Convert a key to match a target convention.
122
     */
123
    public function convertKey(string $key, KeyNamingConvention $convention): string
30✔
124
    {
125
        if (str_contains($key, '.')) {
30✔
126
            return $this->convertDotSeparatedKey($key, $convention);
5✔
127
        }
128

129
        return match ($convention) {
130
            KeyNamingConvention::SNAKE_CASE => $this->toSnakeCase($key),
25✔
131
            KeyNamingConvention::CAMEL_CASE => $this->toCamelCase($key),
10✔
132
            KeyNamingConvention::KEBAB_CASE => $this->toKebabCase($key),
5✔
133
            KeyNamingConvention::PASCAL_CASE => $this->toPascalCase($key),
4✔
134
            KeyNamingConvention::DOT_NOTATION => $this->toDotNotation($key),
25✔
135
        };
136
    }
137
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc