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

dragomano / scss-php / 23953478554

03 Apr 2026 04:26PM UTC coverage: 99.596% (+7.0%) from 92.642%
23953478554

push

github

dragomano
Adopt @PER-CS ruleset and reformat codebase

349 of 349 new or added lines in 51 files covered. (100.0%)

31 existing lines in 13 files now uncovered.

12325 of 12375 relevant lines covered (99.6%)

95.88 hits per line

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

98.0
/src/Builtins/AbstractModule.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Bugo\SCSS\Builtins;
6

7
use Bugo\SCSS\Exceptions\DeprecatedBuiltinFunctionException;
8
use Bugo\SCSS\Nodes\AstNode;
9
use Bugo\SCSS\Nodes\BooleanNode;
10
use Bugo\SCSS\Nodes\NamedArgumentNode;
11
use Bugo\SCSS\Nodes\NullNode;
12
use Bugo\SCSS\Runtime\BuiltinCallContext;
13
use Bugo\SCSS\Values\AstValueDescriber;
14
use Bugo\SCSS\Values\ValueFactory;
15

16
use function array_combine;
17
use function array_map;
18
use function array_merge;
19
use function str_contains;
20
use function strtolower;
21

22
abstract class AbstractModule implements ModuleInterface
23
{
24
    protected ?string $activeBuiltinDisplayName = null;
25

26
    protected ?BuiltinCallContext $activeBuiltinContext = null;
27

28
    abstract public function getName(): string;
29

30
    /**
31
     * @return array<int, string>
32
     */
33
    abstract public function getFunctions(): array;
34

35
    /**
36
     * @return array<string, string>
37
     */
38
    abstract public function getGlobalAliases(): array;
39

40
    /**
41
     * @param array<int, AstNode> $positional
42
     * @param array<string, AstNode> $named
43
     */
44
    abstract public function call(
45
        string $name,
46
        array $positional,
47
        array $named,
48
        ?BuiltinCallContext $context,
49
    ): AstNode;
50

51
    /**
52
     * @return array<string, AstNode>
53
     */
54
    public function getVariables(): array
55
    {
56
        return [];
170✔
57
    }
58

59
    protected function boolNode(bool $value): BooleanNode
60
    {
61
        return $this->valueFactory()->createBooleanNode($value);
67✔
62
    }
63

64
    protected function nullNode(): NullNode
65
    {
66
        return $this->valueFactory()->createNullNode();
11✔
67
    }
68

69
    /**
70
     * @param array<int, string> $passthrough
71
     * @param array<string, string> $mapped
72
     * @return array<string, string>
73
     */
74
    protected function globalAliases(array $passthrough = [], array $mapped = []): array
75
    {
76
        if ($passthrough === []) {
445✔
77
            return $mapped;
150✔
78
        }
79

80
        $identity = array_combine($passthrough, $passthrough);
438✔
81

82
        return array_merge($identity, $mapped);
438✔
83
    }
84

85
    protected function valueFactory(): ValueFactory
86
    {
87
        /** @var ValueFactory|null $valueFactory */
88
        static $valueFactory = null;
89✔
89

90
        if (! $valueFactory instanceof ValueFactory) {
89✔
91
            $valueFactory = new ValueFactory();
1✔
92
        }
93

94
        return $valueFactory;
89✔
95
    }
96

97
    protected function beginBuiltinCall(string $name, ?BuiltinCallContext $context): ?string
98
    {
99
        $previousDisplayName = $this->activeBuiltinDisplayName;
634✔
100

101
        $this->activeBuiltinContext = $context;
634✔
102

103
        if ($context === null || $context->builtinDisplayName === null) {
634✔
104
            $this->activeBuiltinDisplayName = strtolower($name);
231✔
105
        } else {
106
            $this->activeBuiltinDisplayName = strtolower($context->builtinDisplayName);
404✔
107
        }
108

109
        return $previousDisplayName;
634✔
110
    }
111

112
    protected function endBuiltinCall(?string $previousDisplayName): void
113
    {
114
        $this->activeBuiltinDisplayName = $previousDisplayName;
634✔
115
        $this->activeBuiltinContext     = null;
634✔
116
    }
117

118
    protected function builtinErrorContext(string $fallback): string
119
    {
120
        $displayName = $this->activeBuiltinDisplayName ?? strtolower($fallback);
86✔
121

122
        if (str_contains($displayName, '.')) {
86✔
123
            return $displayName;
11✔
124
        }
125

126
        return $displayName . '() (' . $this->getName() . ' module)';
75✔
127
    }
128

129
    protected function builtinCallReference(string $fallback): string
130
    {
131
        $context = $this->builtinErrorContext($fallback);
26✔
132

133
        if (str_contains($context, '()')) {
26✔
134
            return $context;
19✔
135
        }
136

137
        return $context . '()';
7✔
138
    }
139

140
    protected function isGlobalBuiltinCall(): bool
141
    {
142
        return ! str_contains($this->activeBuiltinDisplayName ?? '', '.');
283✔
143
    }
144

145
    protected function deprecatedBuiltinFunctionReference(string $fallback): string
146
    {
147
        $displayName = $this->activeBuiltinDisplayName ?? strtolower($fallback);
101✔
148

149
        return $displayName . '()';
101✔
150
    }
151

152
    protected function describeBuiltinValue(AstNode $value): string
153
    {
154
        return AstValueDescriber::describe($value);
58✔
155
    }
156

157
    /**
158
     * @param array<int, AstNode> $arguments
159
     * @return array<int, string>
160
     */
161
    protected function describeBuiltinArguments(array $arguments): array
162
    {
163
        return array_map($this->describeBuiltinValue(...), $arguments);
58✔
164
    }
165

166
    /**
167
     * @return array<int, AstNode>
168
     */
169
    protected function rawPositionalArguments(): array
170
    {
171
        if ($this->activeBuiltinContext === null || $this->activeBuiltinContext->rawArguments === null) {
55✔
172
            return [];
1✔
173
        }
174

175
        $positional = [];
54✔
176

177
        foreach ($this->activeBuiltinContext->rawArguments as $argument) {
54✔
178
            if ($argument instanceof NamedArgumentNode) {
52✔
UNCOV
179
                continue;
×
180
            }
181

182
            $positional[] = $argument;
52✔
183
        }
184

185
        return $positional;
54✔
186
    }
187

188
    protected function hasRawArguments(): bool
189
    {
190
        return $this->activeBuiltinContext !== null
120✔
191
            && $this->activeBuiltinContext->rawArguments !== null;
120✔
192
    }
193

194
    protected function warnAboutDeprecatedBuiltinFunction(
195
        ?BuiltinCallContext $context,
196
        string $suggestions,
197
        string $fallback,
198
        bool $multipleSuggestions = false,
199
    ): void {
200
        if ($context === null) {
203✔
201
            return;
105✔
202
        }
203

204
        $context->warn((new DeprecatedBuiltinFunctionException(
101✔
205
            $this->deprecatedBuiltinFunctionReference($fallback),
101✔
206
            $suggestions,
101✔
207
            $multipleSuggestions,
101✔
208
        ))->getMessage());
101✔
209
    }
210

211
    protected function warnAboutDeprecatedBuiltinFunctionWithSingleSuggestion(
212
        ?BuiltinCallContext $context,
213
        string $suggestion,
214
        string $fallback,
215
    ): void {
216
        $this->warnAboutDeprecatedBuiltinFunction($context, $suggestion, $fallback);
171✔
217
    }
218
}
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