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

tempestphp / tempest-framework / 14024978163

23 Mar 2025 05:55PM UTC coverage: 79.391% (-0.05%) from 79.441%
14024978163

push

github

web-flow
feat(view): cache Blade and Twig templates in internal storage (#1061)

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

912 existing lines in 110 files now uncovered.

10478 of 13198 relevant lines covered (79.39%)

91.09 hits per line

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

76.0
/src/Tempest/Console/src/GenericConsole.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Console;
6

7
use BackedEnum;
8
use Closure;
9
use InvalidArgumentException;
10
use Stringable;
11
use Symfony\Component\Process\Process;
12
use Tempest\Console\Actions\ExecuteConsoleCommand;
13
use Tempest\Console\Components\Interactive\ConfirmComponent;
14
use Tempest\Console\Components\Interactive\MultipleChoiceComponent;
15
use Tempest\Console\Components\Interactive\PasswordComponent;
16
use Tempest\Console\Components\Interactive\ProgressBarComponent;
17
use Tempest\Console\Components\Interactive\SearchComponent;
18
use Tempest\Console\Components\Interactive\SingleChoiceComponent;
19
use Tempest\Console\Components\Interactive\TaskComponent;
20
use Tempest\Console\Components\Interactive\TextInputComponent;
21
use Tempest\Console\Components\InteractiveComponentRenderer;
22
use Tempest\Console\Components\Renderers\InstructionsRenderer;
23
use Tempest\Console\Components\Renderers\KeyValueRenderer;
24
use Tempest\Console\Components\Renderers\MessageRenderer;
25
use Tempest\Console\Exceptions\UnsupportedComponent;
26
use Tempest\Console\Highlight\TempestConsoleLanguage\TempestConsoleLanguage;
27
use Tempest\Console\Input\ConsoleArgumentBag;
28
use Tempest\Container\Tag;
29
use Tempest\Highlight\Highlighter;
30
use Tempest\Highlight\Language;
31
use Tempest\Support\Conditions\HasConditions;
32
use UnitEnum;
33

34
use function Tempest\Support\Arr\wrap;
35

36
final class GenericConsole implements Console
37
{
38
    use HasConditions;
39

40
    private ?string $label = null;
41

42
    private bool $isForced = false;
43

44
    private bool $supportsPrompting = true;
45

46
    private ?InteractiveComponentRenderer $componentRenderer = null;
47

48
    public function __construct(
247✔
49
        private readonly OutputBuffer $output,
50
        private readonly InputBuffer $input,
51
        #[Tag('console')]
52
        private readonly Highlighter $highlighter,
53
        private readonly ExecuteConsoleCommand $executeConsoleCommand,
54
        private readonly ConsoleArgumentBag $argumentBag,
55
    ) {
56
    }
247✔
57

58
    public function call(string|array $command, string|array $arguments = []): ExitCode|int
2✔
59
    {
60
        return ($this->executeConsoleCommand)($command, $arguments);
2✔
61
    }
62

63
    public function setComponentRenderer(InteractiveComponentRenderer $componentRenderer): self
81✔
64
    {
65
        $this->componentRenderer = $componentRenderer;
81✔
66

67
        return $this;
81✔
68
    }
69

70
    public function setForced(): self
7✔
71
    {
72
        $this->isForced = true;
7✔
73

74
        return $this;
7✔
75
    }
76

77
    public function disablePrompting(): self
39✔
78
    {
79
        $this->supportsPrompting = false;
39✔
80

81
        return $this;
39✔
82
    }
83

UNCOV
84
    public function read(int $bytes): string
×
85
    {
UNCOV
86
        if (! $this->supportsPrompting()) {
×
UNCOV
87
            return '';
×
88
        }
89

UNCOV
90
        return $this->input->read($bytes);
×
91
    }
92

93
    public function readln(): string
76✔
94
    {
95
        if (! $this->supportsPrompting()) {
76✔
UNCOV
96
            return '';
×
97
        }
98

99
        return $this->input->readln();
76✔
100
    }
101

102
    public function write(string $contents): static
186✔
103
    {
104
        $this->writeWithLanguage($contents, new TempestConsoleLanguage());
186✔
105

106
        return $this;
186✔
107
    }
108

109
    public function header(string $header, ?string $subheader = null): static
10✔
110
    {
111
        $this->writeln();
10✔
112
        $this->writeln("<style='dim fg-blue'>//</style> <style='bold fg-blue'>" . mb_strtoupper($header) . '</style>');
10✔
113

114
        if ($subheader) {
10✔
115
            $this->writeln($subheader);
4✔
116
        }
117

118
        return $this;
10✔
119
    }
120

UNCOV
121
    public function instructions(array|string $lines): static
×
122
    {
UNCOV
123
        $this->writeln(new InstructionsRenderer()->render(wrap($lines)));
×
124

UNCOV
125
        return $this;
×
126
    }
127

128
    public function writeln(string $line = ''): static
177✔
129
    {
130
        $this->write($line . PHP_EOL);
177✔
131

132
        return $this;
177✔
133
    }
134

135
    public function writeWithLanguage(string $contents, Language $language): self
187✔
136
    {
137
        if ($this->label) {
187✔
138
            $contents = "<style='dim fg-blue'>//</style> <style='bold fg-blue'>" . $this->label . "\n{$contents}";
4✔
139
        }
140

141
        $this->output->write($this->highlighter->parse($contents, $language));
187✔
142

143
        return $this;
187✔
144
    }
145

146
    public function writeRaw(string $contents): self
1✔
147
    {
148
        $this->output->write($contents);
1✔
149

150
        return $this;
1✔
151
    }
152

153
    public function info(string $contents, ?string $title = null): self
4✔
154
    {
155
        $this->writeln(new MessageRenderer('𝓲', 'blue')->render($contents));
4✔
156

157
        return $this;
4✔
158
    }
159

160
    public function error(string $contents, ?string $title = null): self
7✔
161
    {
162
        $this->writeln(new MessageRenderer('×', 'red')->render($contents, $title));
7✔
163

164
        return $this;
7✔
165
    }
166

UNCOV
167
    public function warning(string $contents, ?string $title = null): self
×
168
    {
UNCOV
169
        $this->writeln(new MessageRenderer('⚠', 'yellow')->render($contents, $title));
×
170

171
        return $this;
×
172
    }
173

174
    public function success(string $contents, ?string $title = null): self
54✔
175
    {
176
        $this->writeln(new MessageRenderer('✓', 'green')->render($contents, $title));
54✔
177

178
        return $this;
54✔
179
    }
180

181
    public function withLabel(string $label): self
4✔
182
    {
183
        $clone = clone $this;
4✔
184

185
        $clone->label = $label;
4✔
186

187
        return $clone;
4✔
188
    }
189

190
    public function keyValue(string $key, ?string $value = null): self
8✔
191
    {
192
        $this->writeln(new KeyValueRenderer()->render($key, $value));
8✔
193

194
        return $this;
8✔
195
    }
196

197
    public function component(InteractiveConsoleComponent $component, array $validation = []): mixed
82✔
198
    {
199
        if ($this->componentRenderer !== null && $this->supportsPrompting() && $this->componentRenderer->isComponentSupported($this, $component)) {
82✔
UNCOV
200
            return $this->componentRenderer->render($this, $component, $validation);
×
201
        }
202

203
        if ($component instanceof HasStaticComponent) {
82✔
204
            return $component->staticComponent->render($this);
82✔
205
        }
206

UNCOV
207
        throw new UnsupportedComponent($component);
×
208
    }
209

210
    public function ask(
78✔
211
        string $question,
212
        null|iterable|string $options = null,
213
        mixed $default = null,
214
        bool $multiple = false,
215
        bool $multiline = false,
216
        ?string $placeholder = null,
217
        ?string $hint = null,
218
        array $validation = [],
219
    ): null|int|string|Stringable|UnitEnum|array {
220
        if ($this->isForced && $default) {
78✔
221
            return $default;
×
222
        }
223

224
        if (is_iterable($options)) {
78✔
225
            $options = wrap($options);
29✔
226
        }
227

228
        if (is_string($options) && is_a($options, BackedEnum::class, allow_string: true)) {
78✔
229
            $options = $options::cases();
×
230
        }
231

232
        if (! $multiple && is_array($default)) {
78✔
233
            throw new InvalidArgumentException('The default value cannot be an array when `multiple` is `false`.');
×
234
        }
235

236
        if ($options === null || $options === []) {
78✔
237
            return $this->component(new TextInputComponent(
52✔
238
                label: $question,
52✔
239
                default: $default,
52✔
240
                placeholder: $placeholder,
52✔
241
                hint: $hint,
52✔
242
                multiline: $multiline,
52✔
243
            ));
52✔
244
        }
245

246
        if ($multiple) {
29✔
247
            return $this->component(new MultipleChoiceComponent(
7✔
248
                label: $question,
7✔
249
                options: $options,
7✔
250
                default: wrap($default),
7✔
251
            ));
7✔
252
        }
253

254
        $component = new SingleChoiceComponent(
29✔
255
            label: $question,
29✔
256
            options: $options,
29✔
257
            default: $default,
29✔
258
        );
29✔
259

260
        return $this->component($component, $validation);
29✔
261
    }
262

263
    public function confirm(string $question, bool $default = false, ?string $yes = null, ?string $no = null): bool
22✔
264
    {
265
        if ($this->isForced) {
22✔
266
            return true;
6✔
267
        }
268

269
        return $this->component(new ConfirmComponent($question, $default, $yes, $no));
16✔
270
    }
271

UNCOV
272
    public function password(string $label = 'Password', bool $confirm = false, array $validation = []): ?string
×
273
    {
UNCOV
274
        if (! $confirm) {
×
UNCOV
275
            return $this->component(new PasswordComponent($label), $validation);
×
276
        }
277

UNCOV
278
        $password = null;
×
UNCOV
279
        $passwordConfirm = null;
×
280

UNCOV
281
        while ($password === null || $password !== $passwordConfirm) { // @mago-expect security/no-insecure-comparison
×
UNCOV
282
            if ($password !== null) {
×
UNCOV
283
                $this->error("Passwords don't match");
×
284
            }
285

UNCOV
286
            $password = $this->component(new PasswordComponent($label));
×
UNCOV
287
            $passwordConfirm = $this->component(new PasswordComponent('Please confirm'));
×
288
        }
289

UNCOV
290
        return $password;
×
291
    }
292

293
    public function progressBar(iterable $data, Closure $handler): array
1✔
294
    {
295
        return $this->component(new ProgressBarComponent($data, $handler));
1✔
296
    }
297

UNCOV
298
    public function task(string $label, null|Process|Closure $handler = null): bool
×
299
    {
UNCOV
300
        return $this->component(new TaskComponent($label, $handler));
×
301
    }
302

303
    public function search(string $label, Closure $search, bool $multiple = false, null|string|array $default = null): mixed
5✔
304
    {
305
        return $this->component(new SearchComponent($label, $search, $multiple, $default));
5✔
306
    }
307

308
    public function supportsPrompting(): bool
81✔
309
    {
310
        if ($this->supportsPrompting === false) {
81✔
311
            return false;
5✔
312
        }
313

314
        if ($this->argumentBag->get('interaction')?->value === false) {
76✔
UNCOV
315
            return false;
×
316
        }
317

318
        return true;
76✔
319
    }
320
}
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