Passed
Push — develop ( ac3052...955933 )
by nguereza
04:41
created

MakeCommand::fileExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 10
1
<?php
2
3
/**
4
 * Platine Framework
5
 *
6
 * Platine Framework is a lightweight, high-performance, simple and elegant
7
 * PHP Web framework
8
 *
9
 * This content is released under the MIT License (MIT)
10
 *
11
 * Copyright (c) 2020 Platine Framework
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining a copy
14
 * of this software and associated documentation files (the "Software"), to deal
15
 * in the Software without restriction, including without limitation the rights
16
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
 * copies of the Software, and to permit persons to whom the Software is
18
 * furnished to do so, subject to the following conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all
21
 * copies or substantial portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
 * SOFTWARE.
30
 */
31
32
/**
33
 *  @file MakeCommand.php
34
 *
35
 *  The Make Command base class
36
 *
37
 *  @package    Platine\Framework\Console
38
 *  @author Platine Developers team
39
 *  @copyright  Copyright (c) 2020
40
 *  @license    http://opensource.org/licenses/MIT  MIT License
41
 *  @link   http://www.iacademy.cf
42
 *  @version 1.0.0
43
 *  @filesource
44
 */
45
46
declare(strict_types=1);
47
48
namespace Platine\Framework\Console;
49
50
use Platine\Console\Command\Command;
51
use Platine\Console\Input\Reader;
52
use Platine\Console\Output\Writer;
53
use Platine\Filesystem\Filesystem;
54
use Platine\Framework\App\Application;
55
use Platine\Stdlib\Helper\Path;
56
use Platine\Stdlib\Helper\Str;
57
58
/**
59
 * @class MakeCommand
60
 * @package Platine\Framework\Console
61
 */
62
abstract class MakeCommand extends Command
63
{
64
    /**
65
     * The Application instance
66
     * @var Application
67
     */
68
    protected Application $application;
69
70
    /**
71
     * The file system to use
72
     * @var Filesystem
73
     */
74
    protected Filesystem $filesystem;
75
76
    /**
77
     * The application root name space
78
     * @var string
79
     */
80
    protected string $rootNamespace;
81
82
    /**
83
     * The type of class
84
     * @var string
85
     */
86
    protected string $type = '';
87
88
    /**
89
     * The class full name given by user
90
     * @var string
91
     */
92
    protected string $className = '';
93
94
    /**
95
     * The action properties
96
     * @var array<string, array<string, string>>
97
     */
98
    protected array $properties = [];
99
100
    /**
101
     * Create new instance
102
     * @param Application $application
103
     * @param Filesystem $filesystem
104
     */
105
    public function __construct(
106
        Application $application,
107
        Filesystem $filesystem
108
    ) {
109
        parent::__construct('make', 'Command to generate class skeleton');
110
        $this->application = $application;
111
        $this->filesystem = $filesystem;
112
        $this->rootNamespace = $application->getNamespace();
113
        $this->addArgument('name', 'The full class name (can include root namespace', null, false);
114
        $this->addOption('-f|--force', 'Overwrite existing files.', false, false);
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function execute()
121
    {
122
        $io = $this->io();
123
        $writer = $io->writer();
124
        $name = $this->className;
125
126
        $className = $this->getFullClassName($name);
127
        $path = $this->getPath();
128
        $namespace = $this->getNamespace();
129
130
        $writer->boldGreen(sprintf(
131
            'Generation of new %s class [%s]',
132
            $this->type,
133
            $className
134
        ), true)->eol();
135
136
137
        if ($this->fileExists() && !$this->getOptionValue('force')) {
138
            $writer->red(sprintf(
139
                'File [%s] already exists.',
140
                $path
141
            ), true);
142
143
            return;
144
        }
145
146
        $writer->bold('Class: ');
147
        $writer->boldBlueBgBlack($className, true);
148
149
        $writer->bold('Path: ');
150
        $writer->boldBlueBgBlack($path, true);
151
152
        $writer->bold('Namespace: ');
153
        $writer->boldBlueBgBlack($namespace, true);
154
155
        if ($io->confirm(sprintf('Are you confirm the generation of [%s] ?', $className), 'y')) {
156
            $this->createParentDirectory($path);
157
            $content = $this->createClass();
158
159
            $file = $this->filesystem->file($path);
160
            $file->write($content);
161
            $writer->boldGreen(sprintf('Class [%s] generated successfully.', $className), true);
162
        }
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function interact(Reader $reader, Writer $writer): void
169
    {
170
        $writer->boldYellow('GENERATION OF NEW CLASS', true)->eol();
171
        $name = $this->getArgumentValue('name');
172
        if (empty($name)) {
173
            $io = $this->io();
174
            $name = $io->prompt('Enter the full class name (can include root namespace)', null);
175
        }
176
177
        $this->className = $name;
178
    }
179
180
    /**
181
     * Return the the class template
182
     * @return string
183
     */
184
    abstract public function getClassTemplate(): string;
185
186
    /**
187
     * Return the real path for the given name
188
     * @return string
189
     */
190
    protected function getPath(): string
191
    {
192
        $class = Str::replaceFirst($this->rootNamespace, '', $this->className);
193
194
        $path = sprintf(
195
            '%s/%s.php',
196
            $this->application->getAppPath(),
197
            str_replace('\\', '/', $class)
198
        );
199
200
        return Path::normalizePathDS($path);
201
    }
202
203
    /**
204
     * Return the class name with the root name space
205
     * @param string $name
206
     * @return string
207
     */
208
    protected function getFullClassName(string $name): string
209
    {
210
        $classClean = ltrim($name, '/\\');
211
        $class = str_replace('/', '\\', $classClean);
212
213
        if (Str::startsWith($this->rootNamespace, $class)) {
214
            return $class;
215
        }
216
217
        $fullyClass = $this->getFullClassName(sprintf(
218
            '%s\\%s',
219
            trim($this->rootNamespace, '\\'),
220
            $class
221
        ));
222
223
        return $fullyClass;
224
    }
225
226
    /**
227
     * Return the full name space for the given class
228
     * @return string
229
     */
230
    protected function getNamespace(): string
231
    {
232
        $class = str_replace('/', '\\', $this->className);
233
234
        return $this->rootNamespace . trim(implode(
235
            '\\',
236
            array_slice(explode('\\', $class), 0, -1)
237
        ), '\\');
238
    }
239
240
    /**
241
     * Whether the file for the given name already exists
242
     * @return bool
243
     */
244
    protected function fileExists(): bool
245
    {
246
        $path = $this->getPath();
247
248
        return $this->filesystem->file($path)->exists();
249
    }
250
251
    /**
252
     * Create the class for the given name
253
     * @return string
254
     */
255
    protected function createClass(): string
256
    {
257
        $template = $this->getClassTemplate();
258
259
        $replaceNamespace = $this->replaceNamespace($template);
260
        $replaceUses = $this->replaceClassUses($replaceNamespace);
261
        $replaceClasses = $this->replaceClasses($replaceUses);
262
        $replaceProperties = $this->replaceProperties($replaceClasses);
263
        $replaceConstructor = $this->replaceConstructor($replaceProperties);
264
265
        return $replaceConstructor;
266
    }
267
268
    /**
269
     * Return the short class name
270
     * @return string
271
     */
272
    protected function getShortClassName(): string
273
    {
274
        $namespace = $this->getNamespace();
275
276
        return Str::replaceFirst(
277
            $namespace . '\\',
278
            '',
279
            $this->getFullClassName($this->className)
280
        );
281
    }
282
283
    /**
284
     * Create the class parent(s) directory if it does not exist
285
     * @param string $path
286
     * @return void
287
     */
288
    protected function createParentDirectory(string $path): void
289
    {
290
        $file = $this->filesystem->file($path);
291
        $location = $file->getLocation();
292
        if (!empty($location)) {
293
            $directory = $this->filesystem->directory($location);
294
            if (!$directory->exists()) {
295
                $directory->create('', 0775, true);
296
            }
297
        }
298
    }
299
300
    /**
301
     * Replace the name space
302
     * @param string $content
303
     * @return string
304
     */
305
    protected function replaceNamespace(string $content): string
306
    {
307
        $namespace = $this->getNamespace();
308
        return str_replace('%namespace%', $namespace, $content);
309
    }
310
311
    /**
312
     * Replace the properties
313
     * @param string $content
314
     * @return string
315
     */
316
    protected function replaceProperties(string $content): string
317
    {
318
        $replaceContent = $this->getPropertiesContent();
319
        return str_replace('%properties%', $replaceContent, $content);
320
    }
321
322
    /**
323
     * Replace the constructor
324
     * @param string $content
325
     * @return string
326
     */
327
    protected function replaceConstructor(string $content): string
328
    {
329
        $replaceContent = $this->getConstructorContent();
330
331
        return str_replace('%constructor%', $replaceContent, $content);
332
    }
333
334
    /**
335
     * Replace the class uses instructions
336
     * @param string $content
337
     * @return string
338
     */
339
    protected function replaceClassUses(string $content): string
340
    {
341
        $replaceContent = $this->getUsesContent();
342
        return str_replace('%uses%', $replaceContent, $content);
343
    }
344
345
    /**
346
     * Replace the classes
347
     * @param string $content
348
     * @return string
349
     */
350
    protected function replaceClasses(string $content): string
351
    {
352
        $shortClassName = $this->getShortClassName();
353
        $fullClassName = $this->getFullClassName($this->className);
354
355
        $replaced = str_replace('%classname%', $shortClassName, $content);
356
357
        return str_replace('%fullclassname%', $fullClassName, $replaced);
358
    }
359
360
    /**
361
     * Return the properties content
362
     * @return string
363
     */
364
    protected function getPropertiesContent(): string
365
    {
366
        if (empty($this->properties)) {
367
            return '';
368
        }
369
370
        $content = '';
371
372
        foreach ($this->properties as $className => $info) {
373
            $content .= $this->getPropertyTemplate($className, $info);
374
        }
375
376
        return $content;
377
    }
378
379
    /**
380
     * Return the name space uses content
381
     * @return string
382
     */
383
    protected function getUsesContent(): string
384
    {
385
        if (empty($this->properties)) {
386
            return '';
387
        }
388
389
        $content = '';
390
391
        foreach ($this->properties as $className => $info) {
392
            $content .= $this->getUsesTemplate($className);
393
        }
394
395
        return $content;
396
    }
397
398
399
    /**
400
     * Return the constructor content
401
     * @return string
402
     */
403
    protected function getConstructorContent(): string
404
    {
405
        if (empty($this->properties)) {
406
            return '';
407
        }
408
409
        $docblock = $this->getConstructorDocBlockContent();
410
        $params = $this->getConstructorParamsContent();
411
        $body = $this->getConstructorBodyContent();
412
413
        return <<<EOF
414
        $docblock
415
            public function __construct(
416
               $params
417
            ){
418
                $body
419
            }
420
        EOF;
421
    }
422
423
424
    /**
425
     * Return the constructor document block comment content
426
     * @return string
427
     */
428
    protected function getConstructorDocBlockContent(): string
429
    {
430
        $content = '';
431
        foreach ($this->properties as $className => $info) {
432
            $content .= $this->getConstructorDocBlockTemplate($className, $info);
433
        }
434
435
        return <<<EOF
436
        /**
437
            * Create new instance
438
            $content*/
439
        EOF;
440
    }
441
442
    /**
443
     * Return the constructor parameters content
444
     * @return string
445
     */
446
    protected function getConstructorParamsContent(): string
447
    {
448
        $content = '';
449
        $i = 1;
450
        $count = count($this->properties);
451
        foreach ($this->properties as $className => $info) {
452
            $content .= $this->getConstructorParamsTemplate($className, $info, $i === $count);
453
            $i++;
454
        }
455
456
        return $content;
457
    }
458
459
    /**
460
     * Return the constructor body content
461
     * @return string
462
     */
463
    protected function getConstructorBodyContent(): string
464
    {
465
        $content = '';
466
        $i = 1;
467
        $count = count($this->properties);
468
        foreach ($this->properties as $className => $info) {
469
            $content .= $this->getConstructorBodyTemplate($className, $info, $i === $count);
470
            $i++;
471
        }
472
473
        return $content;
474
    }
475
476
    /**
477
     * Return the constructor document block template for the given class
478
     * @param string $className
479
     * @param array<string, string> $info
480
     * @return string
481
     */
482
    protected function getConstructorDocBlockTemplate(string $className, array $info): string
0 ignored issues
show
Unused Code introduced by
The parameter $className is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

482
    protected function getConstructorDocBlockTemplate(/** @scrutinizer ignore-unused */ string $className, array $info): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
483
    {
484
        $shortClass = $info['short'];
485
        $name = $info['name'];
486
487
        return <<<EOF
488
        * @param $shortClass \$$name 
489
            
490
        EOF;
491
    }
492
493
    /**
494
     * Return the constructor arguments template for the given class
495
     * @param string $className
496
     * @param array<string, string> $info
497
     * @param bool $isLast
498
     * @return string
499
     */
500
    protected function getConstructorParamsTemplate(
501
        string $className,
0 ignored issues
show
Unused Code introduced by
The parameter $className is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

501
        /** @scrutinizer ignore-unused */ string $className,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
502
        array $info,
503
        bool $isLast = false
504
    ): string {
505
        $shortClass = $info['short'];
506
        $name = $info['name'];
507
        $comma = $isLast ? '' : ',';
508
509
        if ($isLast) {
510
            return <<<EOF
511
            $shortClass \$$name$comma
512
            EOF;
513
        }
514
515
        return <<<EOF
516
        $shortClass \$$name$comma
517
               
518
        EOF;
519
    }
520
521
    /**
522
     * Return the constructor body template for the given class
523
     * @param string $className
524
     * @param array<string, string> $info
525
     * @param bool $isLast
526
     * @return string
527
     */
528
    protected function getConstructorBodyTemplate(string $className, array $info, bool $isLast = false): string
0 ignored issues
show
Unused Code introduced by
The parameter $className is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

528
    protected function getConstructorBodyTemplate(/** @scrutinizer ignore-unused */ string $className, array $info, bool $isLast = false): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
529
    {
530
        $name = $info['name'];
531
532
        if ($isLast) {
533
            return <<<EOF
534
            \$this->$name = \$$name;
535
            EOF;
536
        }
537
538
        return <<<EOF
539
        \$this->$name = \$$name;
540
                
541
        EOF;
542
    }
543
544
    /**
545
     * Return the property template for the given class
546
     * @param string $className
547
     * @param array<string, string> $info
548
     * @return string
549
     */
550
    protected function getPropertyTemplate(string $className, array $info): string
551
    {
552
        $shortClass = $info['short'];
553
        $name = $info['name'];
554
555
        return <<<EOF
556
        /**
557
            * The $shortClass instance
558
            * @var $shortClass
559
            */
560
            protected $shortClass \$$name;
561
        
562
            
563
        EOF;
564
    }
565
566
    /**
567
     * Return the name space use template for the given class
568
     * @param string $className
569
     * @return string
570
     */
571
    protected function getUsesTemplate(string $className): string
572
    {
573
        return <<<EOF
574
        use $className; 
575
        
576
        EOF;
577
    }
578
}
579