• 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

74.0
/src/Tempest/Generation/src/StubFileGenerator.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Generation;
6

7
use Closure;
8
use Tempest\Generation\DataObjects\StubFile;
9
use Tempest\Generation\Enums\StubFileType;
10
use Tempest\Generation\Exceptions\FileGenerationAbortedException;
11
use Tempest\Generation\Exceptions\FileGenerationFailedException;
12
use Tempest\Support\Str\ImmutableString;
13
use Throwable;
14

15
use function Tempest\Support\Namespace\to_base_class_name;
16
use function Tempest\Support\Namespace\to_main_namespace;
17
use function Tempest\Support\path;
18
use function Tempest\Support\str;
19

20
/**
21
 * This class can generate a file from a stub file with additional useful methods.
22
 * It only works with PHP class files.
23
 */
24
final class StubFileGenerator
25
{
26
    /**
27
     * @param StubFile $stubFile The stub file to use for the generation. It must be of type CLASS_FILE.
28
     * @param string $targetPath The path where the generated file will be saved including the filename and extension.
29
     * @param bool $shouldOverride Whether the generator should override the file if it already exists.
30
     * @param array<string, string> $replacements An array of key-value pairs to replace in the stub file.
31
     *     The keys are the placeholders in the stub file (e.g. 'DummyNamespace')
32
     *     The values are the replacements for the placeholders (e.g. 'App\Models')
33
     *
34
     * @param array<Closure(ClassManipulator): ClassManipulator> $manipulations An array of manipulations to apply to the generated class.
35
     *
36
     * @throws FileGenerationFailedException
37
     */
38
    public function generateClassFile(
35✔
39
        StubFile $stubFile,
40
        string $targetPath,
41
        bool $shouldOverride = false,
42
        array $replacements = [],
43
        array $manipulations = [],
44
    ): void {
45
        try {
46
            if ($stubFile->type !== StubFileType::CLASS_FILE) {
35✔
UNCOV
47
                throw new FileGenerationFailedException(sprintf('The stub file must be of type CLASS_FILE, <em>%s</em> given.', $stubFile->type->name));
×
48
            }
49

50
            if (file_exists($targetPath) && ! $shouldOverride) {
35✔
UNCOV
51
                throw new FileGenerationAbortedException(sprintf('The file <em>%s</em> already exists and the operation has been aborted.', $targetPath));
×
52
            }
53

54
            $this->prepareFilesystem($targetPath);
35✔
55

56
            // Transform stub to class
57
            $namespace = to_main_namespace($targetPath);
35✔
58
            $classname = to_base_class_name($targetPath);
35✔
59
            $classManipulator = new ClassManipulator($stubFile->filePath)
35✔
60
                ->setNamespace($namespace)
35✔
61
                ->setClassName($classname);
35✔
62

63
            foreach ($replacements as $placeholder => $replacement) {
35✔
64
                // @phpstan-ignore function.alreadyNarrowedType
65
                if (! is_string($replacement)) {
10✔
66
                    continue;
3✔
67
                }
68

69
                $classManipulator->manipulate(fn (ImmutableString $code) => $code->replace($placeholder, $replacement));
7✔
70
            }
71

72
            // Run all manipulations
73
            $classManipulator = array_reduce(
35✔
74
                array: $manipulations,
35✔
75
                callback: fn (ClassManipulator $manipulator, Closure $manipulation) => $manipulation($manipulator),
35✔
76
                initial: $classManipulator,
35✔
77
            );
35✔
78

79
            if (file_exists($targetPath) && $shouldOverride) {
35✔
UNCOV
80
                @unlink($targetPath);
×
81
            }
82

83
            $classManipulator->save($targetPath);
35✔
UNCOV
84
        } catch (Throwable $throwable) {
×
UNCOV
85
            throw new FileGenerationFailedException(sprintf('The file could not be written. %s', $throwable->getMessage()));
×
86
        }
87
    }
88

89
    /**
90
     * @param StubFile $stubFile The stub file to use for the generation. It must be of type RAW_FILE.
91
     * @param string $targetPath The path where the generated file will be saved including the filename and extension.
92
     * @param bool $shouldOverride Whether the generator should override the file if it already exists.
93
     * @param array<string, string> $replacements An array of key-value pairs to replace in the stub file.
94
     *     The keys are the placeholders in the stub file (e.g. 'dummy-content')
95
     *     The values are the replacements for the placeholders (e.g. 'real content')
96
     *
97
     * @param array<Closure(ImmutableString): ImmutableString> $manipulations An array of manipulations to apply to the generated file raw content.
98
     *
99
     * @throws FileGenerationFailedException
100
     */
101
    public function generateRawFile(
13✔
102
        StubFile $stubFile,
103
        string $targetPath,
104
        bool $shouldOverride = false,
105
        array $replacements = [],
106
        array $manipulations = [],
107
    ): void {
108
        try {
109
            if ($stubFile->type !== StubFileType::RAW_FILE) {
13✔
UNCOV
110
                throw new FileGenerationFailedException(sprintf('The stub file must be of type RAW_FILE, "%s" given.', $stubFile->type->name));
×
111
            }
112

113
            if (file_exists($targetPath) && ! $shouldOverride) {
13✔
UNCOV
114
                throw new FileGenerationAbortedException(sprintf('The file "%s" already exists and the operation has been aborted.', $targetPath));
×
115
            }
116

117
            $this->prepareFilesystem($targetPath);
13✔
118
            $fileContent = file_get_contents($stubFile->filePath);
13✔
119

120
            foreach ($replacements as $placeholder => $replacement) {
13✔
121
                // @phpstan-ignore function.alreadyNarrowedType
122
                if (! is_string($replacement)) {
×
UNCOV
123
                    continue;
×
124
                }
125

UNCOV
126
                $fileContent = str($fileContent)->replace($placeholder, $replacement);
×
127
            }
128

129
            // Run all manipulations
130
            $fileContent = array_reduce(
13✔
131
                array: $manipulations,
13✔
132
                initial: $fileContent,
13✔
133
                callback: fn (ImmutableString $content, Closure $manipulation) => $manipulation($content),
13✔
134
            );
13✔
135

136
            if (file_exists($targetPath) && $shouldOverride) {
13✔
137
                @unlink($targetPath);
×
138
            }
139

140
            file_put_contents($targetPath, $fileContent);
13✔
UNCOV
141
        } catch (Throwable $throwable) {
×
UNCOV
142
            throw new FileGenerationFailedException(sprintf('The file could not be written. %s', $throwable->getMessage()));
×
143
        }
144
    }
145

146
    /**
147
     * Prepare the directory structure for the new file.
148
     * It will delete the target file if it exists and we force the override.
149
     *
150
     * @param string $targetPath The path where the generated file will be saved including the filename and extension.
151
     */
152
    private function prepareFilesystem(string $targetPath): void
46✔
153
    {
154
        // Recursively create directories before writing the file
155
        $directory = dirname($targetPath);
46✔
156
        if (! is_dir($directory)) {
46✔
157
            mkdir($directory, recursive: true);
21✔
158
        }
159
    }
160
}
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