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

dragomano / scss-php / 23594295754

26 Mar 2026 12:19PM UTC coverage: 84.536%. First build
23594295754

push

github

web-flow
Merge pull request #51 from dragomano/refactor

Rewrite compiler

10140 of 12016 new or added lines in 162 files covered. (84.39%)

10272 of 12151 relevant lines covered (84.54%)

83.37 hits per line

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

85.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\ColorNode;
11
use Bugo\SCSS\Nodes\ListNode;
12
use Bugo\SCSS\Nodes\MapNode;
13
use Bugo\SCSS\Nodes\NullNode;
14
use Bugo\SCSS\Nodes\NumberNode;
15
use Bugo\SCSS\Nodes\StringNode;
16
use Bugo\SCSS\Nodes\VariableReferenceNode;
17
use Bugo\SCSS\Runtime\BuiltinCallContext;
18
use Bugo\SCSS\Values\ValueFactory;
19

20
use function array_combine;
21
use function array_map;
22
use function array_merge;
23
use function implode;
24
use function str_contains;
25
use function strtolower;
26

27
abstract class AbstractModule implements ModuleInterface
28
{
29
    protected ?string $activeBuiltinDisplayName = null;
30

31
    protected ?BuiltinCallContext $activeBuiltinContext = null;
32

33
    abstract public function getName(): string;
34

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

40
    /**
41
     * @return array<string, string>
42
     */
43
    abstract public function getGlobalAliases(): array;
44

45
    /**
46
     * @param array<int, AstNode> $positional
47
     * @param array<string, AstNode> $named
48
     */
49
    abstract public function call(
50
        string $name,
51
        array $positional,
52
        array $named,
53
        ?BuiltinCallContext $context
54
    ): AstNode;
55

56
    /**
57
     * @return array<string, AstNode>
58
     */
59
    public function getVariables(): array
60
    {
61
        return [];
170✔
62
    }
63

64
    protected function boolNode(bool $value): BooleanNode
65
    {
66
        return $this->valueFactory()->createBooleanNode($value);
59✔
67
    }
68

69
    protected function nullNode(): NullNode
70
    {
71
        return $this->valueFactory()->createNullNode();
7✔
72
    }
73

74
    /**
75
     * @param array<int, string> $passthrough
76
     * @param array<string, string> $mapped
77
     * @return array<string, string>
78
     */
79
    protected function globalAliases(array $passthrough = [], array $mapped = []): array
80
    {
81
        if ($passthrough === []) {
438✔
82
            return $mapped;
147✔
83
        }
84

85
        $identity = array_combine($passthrough, $passthrough);
431✔
86

87
        return array_merge($identity, $mapped);
431✔
88
    }
89

90
    protected function valueFactory(): ValueFactory
91
    {
92
        /** @var ValueFactory|null $valueFactory */
93
        static $valueFactory = null;
75✔
94

95
        if (! $valueFactory instanceof ValueFactory) {
75✔
96
            $valueFactory = new ValueFactory();
1✔
97
        }
98

99
        return $valueFactory;
75✔
100
    }
101

102
    protected function beginBuiltinCall(string $name, ?BuiltinCallContext $context): ?string
103
    {
104
        $previousDisplayName = $this->activeBuiltinDisplayName;
560✔
105

106
        $this->activeBuiltinContext = $context;
560✔
107

108
        if ($context === null || $context->builtinDisplayName === null) {
560✔
109
            $this->activeBuiltinDisplayName = strtolower($name);
164✔
110
        } else {
111
            $this->activeBuiltinDisplayName = strtolower($context->builtinDisplayName);
397✔
112
        }
113

114
        return $previousDisplayName;
560✔
115
    }
116

117
    protected function endBuiltinCall(?string $previousDisplayName): void
118
    {
119
        $this->activeBuiltinDisplayName = $previousDisplayName;
560✔
120
        $this->activeBuiltinContext     = null;
560✔
121
    }
122

123
    protected function builtinErrorContext(string $fallback): string
124
    {
125
        $displayName = $this->activeBuiltinDisplayName ?? strtolower($fallback);
55✔
126

127
        if (str_contains($displayName, '.')) {
55✔
128
            return $displayName;
10✔
129
        }
130

131
        return $displayName . '() (' . $this->getName() . ' module)';
45✔
132
    }
133

134
    protected function builtinCallReference(string $fallback): string
135
    {
136
        $context = $this->builtinErrorContext($fallback);
20✔
137

138
        if (str_contains($context, '()')) {
20✔
139
            return $context;
13✔
140
        }
141

142
        return $context . '()';
7✔
143
    }
144

145
    protected function isGlobalBuiltinCall(): bool
146
    {
147
        return ! str_contains($this->activeBuiltinDisplayName ?? '', '.');
249✔
148
    }
149

150
    protected function deprecatedBuiltinFunctionReference(string $fallback): string
151
    {
152
        $displayName = $this->activeBuiltinDisplayName ?? strtolower($fallback);
92✔
153

154
        return $displayName . '()';
92✔
155
    }
156

157
    protected function describeBuiltinValue(AstNode $value): string
158
    {
159
        if ($value instanceof VariableReferenceNode) {
41✔
160
            return '$' . $value->name;
3✔
161
        }
162

163
        if ($value instanceof NumberNode) {
41✔
164
            return "$value->value" . ($value->unit ?? '');
18✔
165
        }
166

167
        if ($value instanceof StringNode) {
41✔
168
            return $value->quoted ? '"' . $value->value . '"' : $value->value;
41✔
169
        }
170

171
        if ($value instanceof ListNode) {
19✔
NEW
172
            $items = $this->describeBuiltinArguments($value->items);
×
NEW
173
            $glue  = $value->separator === 'comma' ? ', ' : ' ';
×
NEW
174
            $text  = implode($glue, $items);
×
175

NEW
176
            if ($value->bracketed) {
×
NEW
177
                return '[' . $text . ']';
×
178
            }
179

NEW
180
            return $text;
×
181
        }
182

183
        if ($value instanceof MapNode) {
19✔
184
            $pairs = [];
19✔
185

186
            foreach ($value->pairs as $pair) {
19✔
187
                $pairs[] = $this->describeBuiltinValue($pair['key']) . ': ' . $this->describeBuiltinValue($pair['value']);
19✔
188
            }
189

190
            return '(' . implode(', ', $pairs) . ')';
19✔
191
        }
192

NEW
193
        if ($value instanceof ColorNode) {
×
NEW
194
            return $value->value;
×
195
        }
196

NEW
197
        return '';
×
198
    }
199

200
    /**
201
     * @param array<int, AstNode> $arguments
202
     * @return array<int, string>
203
     */
204
    protected function describeBuiltinArguments(array $arguments): array
205
    {
206
        return array_map($this->describeBuiltinValue(...), $arguments);
41✔
207
    }
208

209
    protected function warnAboutDeprecatedBuiltinFunction(
210
        ?BuiltinCallContext $context,
211
        string $suggestions,
212
        string $fallback,
213
        bool $multipleSuggestions = false
214
    ): void {
215
        if ($context === null) {
167✔
216
            return;
78✔
217
        }
218

219
        $context->warn((new DeprecatedBuiltinFunctionException(
92✔
220
            $this->deprecatedBuiltinFunctionReference($fallback),
92✔
221
            $suggestions,
92✔
222
            $multipleSuggestions
92✔
223
        ))->getMessage());
92✔
224
    }
225

226
    protected function warnAboutDeprecatedBuiltinFunctionWithSingleSuggestion(
227
        ?BuiltinCallContext $context,
228
        string $suggestion,
229
        string $fallback
230
    ): void {
231
        $this->warnAboutDeprecatedBuiltinFunction($context, $suggestion, $fallback);
137✔
232
    }
233
}
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