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

orchestral / canvas-core / 6168643703

13 Sep 2023 06:09AM UTC coverage: 71.818% (-22.4%) from 94.203%
6168643703

push

github

crynobone
Merge branch '8.x'

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>

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

237 of 330 relevant lines covered (71.82%)

2.62 hits per line

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

26.47
/src/Commands/Generator.php
1
<?php
2

3
namespace Orchestra\Canvas\Core\Commands;
4

5
use Illuminate\Console\Concerns\CreatesMatchingTest;
6
use Illuminate\Contracts\Console\PromptsForMissingInput;
7
use Orchestra\Canvas\Core\CodeGenerator;
8
use Orchestra\Canvas\Core\Contracts\GeneratesCodeListener;
9
use Orchestra\Canvas\Core\GeneratesCode;
10
use Orchestra\Canvas\Core\Presets\Preset;
11
use Orchestra\Canvas\Core\TestGenerator;
12
use Symfony\Component\Console\Input\InputArgument;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Finder\Finder;
16

17
/**
18
 * @property string|null  $name
19
 * @property string|null  $description
20
 */
21
abstract class Generator extends Command implements GeneratesCodeListener, PromptsForMissingInput
22
{
23
    use CodeGenerator, TestGenerator;
24

25
    /**
26
     * The console command name.
27
     *
28
     * @var string
29
     */
30
    protected $name;
31

32
    /**
33
     * The console command description.
34
     *
35
     * @var string|null
36
     */
37
    protected $description;
38

39
    /**
40
     * The filesystem instance.
41
     *
42
     * @var \Illuminate\Filesystem\Filesystem
43
     */
44
    protected $files;
45

46
    /**
47
     * The type of class being generated.
48
     */
49
    protected string $type;
50

51
    /**
52
     * The type of file being generated.
53
     */
54
    protected string $fileType = 'class';
55

56
    /**
57
     * Generator processor.
58
     *
59
     * @var class-string<\Orchestra\Canvas\Core\GeneratesCode>
60
     */
61
    protected string $processor = GeneratesCode::class;
62

63
    /**
64
     * Reserved names that cannot be used for generation.
65
     *
66
     * @var array<int, string>
67
     */
68
    protected array $reservedNames = [
69
        '__halt_compiler',
70
        'abstract',
71
        'and',
72
        'array',
73
        'as',
74
        'break',
75
        'callable',
76
        'case',
77
        'catch',
78
        'class',
79
        'clone',
80
        'const',
81
        'continue',
82
        'declare',
83
        'default',
84
        'die',
85
        'do',
86
        'echo',
87
        'else',
88
        'elseif',
89
        'empty',
90
        'enddeclare',
91
        'endfor',
92
        'endforeach',
93
        'endif',
94
        'endswitch',
95
        'endwhile',
96
        'enum',
97
        'eval',
98
        'exit',
99
        'extends',
100
        'false',
101
        'final',
102
        'finally',
103
        'fn',
104
        'for',
105
        'foreach',
106
        'function',
107
        'global',
108
        'goto',
109
        'if',
110
        'implements',
111
        'include',
112
        'include_once',
113
        'instanceof',
114
        'insteadof',
115
        'interface',
116
        'isset',
117
        'list',
118
        'match',
119
        'namespace',
120
        'new',
121
        'or',
122
        'print',
123
        'private',
124
        'protected',
125
        'public',
126
        'readonly',
127
        'require',
128
        'require_once',
129
        'return',
130
        'self',
131
        'static',
132
        'switch',
133
        'throw',
134
        'trait',
135
        'true',
136
        'try',
137
        'unset',
138
        'use',
139
        'var',
140
        'while',
141
        'xor',
142
        'yield',
143
        '__CLASS__',
144
        '__DIR__',
145
        '__FILE__',
146
        '__FUNCTION__',
147
        '__LINE__',
148
        '__METHOD__',
149
        '__NAMESPACE__',
150
        '__TRAIT__',
151
    ];
152

153
    /**
154
     * Construct a new generator command.
155
     */
156
    public function __construct(Preset $preset)
157
    {
158
        $this->files = $preset->filesystem();
6✔
159

160
        parent::__construct($preset);
6✔
161
    }
162

163
    /**
164
     * Configure the command options.
165
     *
166
     * @return void
167
     */
168
    protected function configure()
169
    {
170
        $this->ignoreValidationErrors();
6✔
171

172
        $this->setName($this->getName())
6✔
173
            ->setDescription($this->getDescription())
6✔
174
            ->addArgument('name', InputArgument::REQUIRED, "The name of the {$this->fileType}");
6✔
175

176
        if (\in_array(CreatesMatchingTest::class, class_uses_recursive($this))) {
6✔
177
            /** @phpstan-ignore-next-line */
178
            $this->addTestOptions();
×
179
        }
180
    }
181

182
    /**
183
     * Execute the command.
184
     *
185
     * @return int 0 if everything went fine, or an exit code
186
     *
187
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
188
     */
189
    protected function execute(InputInterface $input, OutputInterface $output)
190
    {
191
        // First we need to ensure that the given name is not a reserved word within the PHP
192
        // language and that the class name will actually be valid. If it is not valid we
193
        // can error now and prevent from polluting the filesystem using invalid files.
194
        if ($this->isReservedName($name = $this->generatorName())) {
4✔
195
            $this->components->error('The name "'.$name.'" is reserved by PHP.');
×
196

197
            return Command::FAILURE;
×
198
        }
199

200
        $force = $this->hasOption('force') && $this->option('force') === true;
4✔
201

202
        return $this->generateCode($force);
4✔
203
    }
204

205
    /**
206
     * Handle generating code.
207
     */
208
    public function generatingCode(string $stub, string $className): string
209
    {
210
        return $stub;
4✔
211
    }
212

213
    /**
214
     * Run after code successfully generated.
215
     */
216
    public function afterCodeHasBeenGenerated(string $className, string $path): void
217
    {
218
        if (\in_array(CreatesMatchingTest::class, class_uses_recursive($this))) {
4✔
219
            $this->handleTestCreationUsingCanvas($path);
×
220
        }
221
    }
222

223
    /**
224
     * Get the published stub file for the generator.
225
     */
226
    public function getPublishedStubFileName(): ?string
227
    {
228
        return null;
1✔
229
    }
230

231
    /**
232
     * Get the desired class name from the input.
233
     */
234
    public function generatorName(): string
235
    {
236
        return transform($this->argument('name'), function ($name) {
4✔
237
            /** @var string $name */
238
            return trim($name);
4✔
239
        });
4✔
240
    }
241

242
    /**
243
     * Checks whether the given name is reserved.
244
     */
245
    protected function isReservedName(string $name): bool
246
    {
247
        $name = strtolower($name);
4✔
248

249
        return \in_array($name, $this->reservedNames);
4✔
250
    }
251

252
    /**
253
     * Get a list of possible model names.
254
     *
255
     * @return array<int, string>
256
     */
257
    protected function possibleModels()
258
    {
259
        $sourcePath = $this->preset->sourcePath();
×
260

261
        $modelPath = is_dir("{$sourcePath}/Models") ? "{$sourcePath}/Models" : $sourcePath;
×
262

263
        return collect((new Finder)->files()->depth(0)->in($modelPath))
×
264
            ->map(fn ($file) => $file->getBasename('.php'))
×
265
            ->sort()
×
266
            ->values()
×
267
            ->all();
×
268
    }
269

270
    /**
271
     * Get a list of possible event names.
272
     *
273
     * @return array<int, string>
274
     */
275
    protected function possibleEvents()
276
    {
277
        $eventPath = sprintf('%s/Events', $this->preset->sourcePath());
×
278

279
        if (! is_dir($eventPath)) {
×
280
            return [];
×
281
        }
282

283
        return collect((new Finder)->files()->depth(0)->in($eventPath))
×
284
            ->map(fn ($file) => $file->getBasename('.php'))
×
285
            ->sort()
×
286
            ->values()
×
287
            ->all();
×
288
    }
289

290
    /**
291
     * Prompt for missing input arguments using the returned questions.
292
     *
293
     * @return array
294
     */
295
    protected function promptForMissingArgumentsUsing()
296
    {
297
        return [
×
298
            'name' => [
×
299
                'What should the '.strtolower($this->type).' be named?',
×
300
                match ($this->type) {
×
301
                    'Cast' => 'E.g. Json',
×
302
                    'Channel' => 'E.g. OrderChannel',
×
303
                    'Console command' => 'E.g. SendEmails',
×
304
                    'Component' => 'E.g. Alert',
×
305
                    'Controller' => 'E.g. UserController',
×
306
                    'Event' => 'E.g. PodcastProcessed',
×
307
                    'Exception' => 'E.g. InvalidOrderException',
×
308
                    'Factory' => 'E.g. PostFactory',
×
309
                    'Job' => 'E.g. ProcessPodcast',
×
310
                    'Listener' => 'E.g. SendPodcastNotification',
×
311
                    'Mailable' => 'E.g. OrderShipped',
×
312
                    'Middleware' => 'E.g. EnsureTokenIsValid',
×
313
                    'Model' => 'E.g. Flight',
×
314
                    'Notification' => 'E.g. InvoicePaid',
×
315
                    'Observer' => 'E.g. UserObserver',
×
316
                    'Policy' => 'E.g. PostPolicy',
×
317
                    'Provider' => 'E.g. ElasticServiceProvider',
×
318
                    'Request' => 'E.g. StorePodcastRequest',
×
319
                    'Resource' => 'E.g. UserResource',
×
320
                    'Rule' => 'E.g. Uppercase',
×
321
                    'Scope' => 'E.g. TrendingScope',
×
322
                    'Seeder' => 'E.g. UserSeeder',
×
323
                    'Test' => 'E.g. UserTest',
×
324
                    default => '',
×
325
                },
×
326
            ],
×
327
        ];
×
328
    }
329
}
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