Passed
Push — master ( 590453...ad5a85 )
by Arthur
24:49
created

AbstractGeneratorCommand::isShortcutBool()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 1
nc 3
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
crap 12
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: arthur
5
 * Date: 09.03.19
6
 * Time: 17:15.
7
 */
8
9
namespace Foundation\Generator\Abstracts;
10
11
use Foundation\Core\Larapi;
12
use Foundation\Core\Module;
13
use Foundation\Exceptions\Exception;
14
use Foundation\Generator\Support\InputOption;
15
use Foundation\Generator\Support\Stub;
16
use Illuminate\Console\Command;
17
use Illuminate\Contracts\Filesystem\FileExistsException;
18
use Illuminate\Support\Str;
19
use ReflectionClass;
20
use Symfony\Component\Console\Input\InputArgument;
21
22
abstract class AbstractGeneratorCommand extends Command
23
{
24
    /**
25
     * The name of the generated resource.
26
     *
27
     * @var string
28
     */
29
    protected $generatorName;
30
31
    /**
32
     * The stub name.
33
     *
34
     * @var string
35
     */
36
    protected $stub;
37
38
    /**
39
     * The file path.
40
     *
41
     * @var string
42
     */
43
    protected $filePath;
44
45 10
    /**
46
     * The event that will fire when the file is created.
47 10
     *
48
     * @var string
49
     */
50
    protected $event;
51
52
53
    /**
54 12
     * The data that is inputted from the options.
55
     *
56 12
     * @var array
57 12
     */
58
    protected $optionData = [];
59 12
60 1
    /**
61
     * @return string
62
     */
63 12
    protected function getDestinationFilePath(): string
64 2
    {
65
        return $this->getModule()->getPath() . $this->filePath . '/' . $this->getFileName();
66
    }
67 12
68
69 12
    public function handle()
70
    {
71 12
        $path = str_replace('\\', '/', $this->getDestinationFilePath());
72 12
73
        if (file_exists($path) && !$this->isOverwriteable()) {
74
            $this->error("File : {$path} already exists.");
75
            throw new FileExistsException();
76
        }
77 10
78
        $this->handleOptions();
79 10
80
        $stub = new Stub($this->stubName(), array_merge($this->defaultStubOptions(), $this->stubOptions()));
81
82 12
83
        $this->beforeGeneration();
84
85 12
        if ($this->event === null)
86 12
            throw new Exception("No Generator event specified on " . static::class);
87
88
        event(new $this->event($path, $stub));
89 12
        $this->info("Created : {$path}");
90
91
        $this->afterGeneration();
92 12
    }
93 12
94
    /**
95
     * @return string
96 12
     */
97
    protected function getFileName()
98 12
    {
99
        return $this->getClassName() . '.php';
100 12
    }
101
102
    protected function isOverwriteable() :bool
103
    {
104 12
        $overWriteable = $this->option('overwrite');
105
106
        return $overWriteable ?? false;
107
    }
108
109
    protected function getModule(): Module
110
    {
111
        return once(function () {
112
            return Larapi::getModule($this->getModuleName());
113 11
        });
114
    }
115 11
116
    protected function getModuleName(): string
117
    {
118
        return once(function () {
119
            return Str::studly($this->askModuleName());
120 12
        });
121
    }
122 12
123
    private function askModuleName(): string
124 10
    {
125
        $moduleName = $this->argument('module') ?? $this->anticipate('For what module would you like to generate a ' . $this->getGeneratorName() . '.', Larapi::getModuleNames());
126 10
127
        if ($moduleName === null) {
128
            $this->error('module not specified');
129
            throw new \Exception('Name of module not specified.');
130 11
        }
131
132
        return $moduleName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $moduleName could return the type string[] which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
133 11
    }
134 11
135
    /**
136
     * Get class namespace.
137 11
     *
138
     *
139 11
     * @return string
140
     */
141 11
    public function getClassNamespace(): string
142
    {
143
        return $this->getModule()->getNamespace() . str_replace('/', '\\', $this->filePath);
144
    }
145 11
146
147
    protected function beforeGeneration(): void
148
    {
149
    }
150
151
    protected function afterGeneration(): void
152
    {
153 87
    }
154
155
    abstract protected function stubOptions(): array;
156 87
157 87
    protected final function defaultStubOptions(): array
158
    {
159
        return [
160
            "MODULE" => $this->getModuleName()
161
        ];
162
    }
163
164
    /**
165
     * Get the console command options.
166 87
     *
167
     * @return array
168 87
     */
169
    final protected function getOptions()
170
    {
171 87
        $options = $this->setOptions();
172
        $options[] = ['overwrite', null, InputOption::VALUE_NONE, 'Overwrite this file if it already exists?'];
173 87
        return $options;
174
    }
175
176
    /**
177
     * Get the console command arguments.
178
     *
179
     * @return array
180 6
     */
181
    final protected function getArguments()
182 6
    {
183
        return array_merge([
184
            ['module', InputArgument::OPTIONAL, 'The name of module will be used.'],
185
        ],
186
            $this->setArguments());
187
    }
188
189
    /**
190
     * Set the console command arguments.
191
     *
192
     * @return array
193
     */
194
    protected abstract function setArguments(): array;
195
196
    /**
197
     * Set the console command options.
198
     *
199
     * @return array
200
     */
201
    protected abstract function setOptions(): array;
202
203
    protected function getClassName(): string
204
    {
205
        return once(function () {
206
            return Str::studly($this->askClassName());
207
        });
208
    }
209
210
    private function askClassName(): string
211
    {
212
        $className = $this->argument('name') ?? $this->ask('Specify the name of the ' . $this->getGeneratorName() . '.');
213
214
        if ($className === null) {
215
            throw new \Exception('Name of ' . $this->getGeneratorName() . ' not set.');
216
        }
217
218
        return $className;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $className could return the type string[] which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
219
    }
220
221
    protected function getGeneratorName(): string
222
    {
223
        return $this->generatorName ?? 'class';
224
    }
225
226
    /**
227
     * Get the stub file name.
228
     * @return string
229
     */
230
    protected function stubName()
231
    {
232
        return $this->stub;
233
    }
234
235
    protected function handleOptions()
236
    {
237
        foreach ($this->getOptions() as $option) {
238
            $method = 'handle' . ucfirst(strtolower($option[0])) . 'Option';
239
            $originalInput = $this->getOriginalOptionInput();
240
            if (isset($originalInput[$option[0]])) {
241
                $this->optionData[$option[0]] = $originalInput[$option[0]];
242
            } else {
243
                $this->optionData[$option[0]] = method_exists($this, $method) ? $this->$method($option[1], $option[2], $option[3], $option[4]) : $this->option($option[0]);
244
            }
245
        }
246
    }
247
248
    protected function getOption(string $name)
249
    {
250
        return $this->optionData[$name];
251
    }
252
253
    private function buildInputOption($key, $shortcut, $type, $question, $default)
254
    {
255
256
        $originalOptions = $this->getOriginalOptionInput();
0 ignored issues
show
Unused Code introduced by
The assignment to $originalOptions is dead and can be removed.
Loading history...
257
        if ($type === InputOption::VALUE_NONE) {
258
            return $this->option($key);
259
        } elseif ($type === InputOption::VALUE_OPTIONAL || $type === InputOption::VALUE_IS_BOOL) {
260
            if ($type !== InputOption::VALUE_IS_BOOL && ($input = $this->option($key)) !== null) {
261
                if (is_bool($default))
262
                    return (bool)$input;
263
                return $input;
264
            } else {
265
                if ($this->isShortcutBool($shortcut))
266
                    return $this->confirm($question, $default);
267
                else if (is_array($shortcut))
268
                    return $this->anticipate($question, $shortcut, $default);
269
                else
270
                    return $this->ask($question, $default);
271
            }
272
273
        }
274
275
        throw new Exception("input option not supported");
276
    }
277
278
    private function getOriginalOptionInput()
279
    {
280
        $reflection = new ReflectionClass($this->input);
281
        $property = $reflection->getProperty('options');
282
        $property->setAccessible(true);
283
        return $property->getValue($this->input);
284
    }
285
286
    private function isShortcutBool($shortcut)
287
    {
288
        return is_bool($shortcut) || (is_array($shortcut) && is_bool($shortcut[0]));
289
    }
290
291
    public function __call($method, $parameters)
292
    {
293
        $key = str_replace('get', '', $method);
294
        if (array_key_exists(strtolower($method), $this->optionData))
295
            return $this->optionData[$key];
296
    }
297
}
298