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

tempestphp / tempest-framework / 14140550176

28 Mar 2025 10:29PM UTC coverage: 80.716% (+1.4%) from 79.334%
14140550176

push

github

web-flow
feat(support): support `$default` on array `first` and `last` methods (#1096)

11 of 12 new or added lines in 2 files covered. (91.67%)

135 existing lines in 16 files now uncovered.

10941 of 13555 relevant lines covered (80.72%)

100.32 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\src_namespace;
16
use function Tempest\Support\Namespace\to_base_class_name;
17
use function Tempest\Support\str;
18

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

© 2025 Coveralls, Inc