Issues (590)

src/Console/EntityCommand.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace Bdf\Prime\Console;
4
5
use Bdf\Prime\Entity\EntityGenerator;
6
use Bdf\Prime\Entity\EntityInterface;
7
use Bdf\Prime\Entity\Extensions\ArrayInjector;
8
use Bdf\Prime\Entity\ImportableInterface;
9
use Bdf\Prime\Entity\InitializableInterface;
10
use Bdf\Prime\Entity\Model;
11
use Bdf\Prime\ServiceLocator;
12
use Bdf\Util\Console\BdfStyle;
13
use Bdf\Util\File\ClassFileLocator;
14
use Bdf\Util\File\PhpClassFile;
15
use Symfony\Component\Console\Attribute\AsCommand;
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Input\InputArgument;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Input\InputOption;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Filesystem\Filesystem;
22
23
/**
24
 * EntityCommand
25
 *
26
 * @package Bdf\Prime\Console
27
 *
28
 * @psalm-suppress ReservedWord
29
 * @psalm-suppress InvalidCast
30
 */
31
#[AsCommand('prime:entity', 'Generate entity class by mapper')]
32
class EntityCommand extends Command
33
{
34
    protected static $defaultName = 'prime:entity';
35
36
    /**
37
     * @var ServiceLocator
38
     */
39
    private $locator;
40
41
    /**
42
     * EntityCommand constructor.
43
     *
44
     * @param ServiceLocator $locator
45
     */
46
    public function __construct(ServiceLocator $locator)
47
    {
48
        $this->locator = $locator;
49
50
        parent::__construct(static::$defaultName);
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    protected function configure()
57
    {
58
        $this
59
            ->addArgument('mapper', InputArgument::REQUIRED, 'Mapper file or directory to parse')
60
            ->addOption('backup', 'b', InputOption::VALUE_NONE, 'Backup file if exists')
61
            ->setDescription('Generate entity class by mapper')
62
            ->setHelp(
63
                <<<EOF
64
The <info>%command.name%</info> generate entity from mapper
65
use mapper argument to parse a directory or a mapper file
66
  <info>php %command.full_name% app/models</info>
67
  <info>php %command.full_name% app/models/MyMapper.php</info>
68
69
Backup files with backup option
70
  <info>php %command.full_name% --backup app/models</info>
71
  <info>php %command.full_name% -b app/models</info>
72
73
EOF
74
            )
75
        ;
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    protected function execute(InputInterface $input, OutputInterface $output): int
82
    {
83
        $io = new BdfStyle($input, $output);
84
85
        $path = realpath($io->argument('mapper'));
86
87
        $cache = $this->locator->mappers()->getMetadataCache();
88
        $this->locator->mappers()->setMetadataCache(null);
89
90
        foreach ($this->getClassIterator($path) as $classInfo) {
91
            $className = $classInfo->getClass();
92
93
            if (!$this->locator->mappers()->isMapper($className)) {
94
                $io->debug("'{$className}' is not mapper class");
95
                continue;
96
            }
97
98
            $mapper    = $this->locator->mappers()->createMapper($this->locator, $className);
99
            $generator = new EntityGenerator($this->locator);
100
101
            $this->runUserActions($io, $generator, $mapper, $classInfo);
102
        }
103
104
        $this->locator->mappers()->setMetadataCache($cache);
105
        return 0;
106
    }
107
108
    /**
109
     * @param string $path
110
     *
111
     * @return ClassFileLocator|array
112
     */
113
    protected function getClassIterator($path)
114
    {
115
        if (is_dir($path)) {
116
            return new ClassFileLocator($path);
117
        } else {
118
            $classFile = new PhpClassFile($path);
119
            $classFile->extractClassInfo();
120
121
            return [$classFile];
122
        }
123
    }
124
125
    /**
126
     * @param BdfStyle $io
127
     * @param EntityGenerator $generator
128
     * @param \Bdf\Prime\Mapper\Mapper $mapper
129
     * @param PhpClassFile $classInfo
130
     *
131
     * @return void
132
     */
133
    protected function runUserActions($io, $generator, $mapper, $classInfo)
134
    {
135
        //TODO AAAAAAAAAAAAAAAAAAAAH !!!!!!!
136
        $fileName  = str_replace('Mapper', '', $classInfo->getRealPath());
137
        $className = $mapper->getEntityClass();
138
        $doCreate = false;
139
140
        if (file_exists($fileName)) {
141
            $userChoice = (string) $io->choice("'$fileName' exists. what do you want ?", [
142
                '1'     => 'Regenerate: will replace the existing entity',
143
                '2'     => 'Update: will complete entity with new properties and methods',
144
                '3'     => 'Cancel',
145
            ]);
146
147
            if ($userChoice === 'Cancel') {
148
                return;
149
            } elseif (strpos($userChoice, 'Regenerate') === 0) {
150
                $generator->setRegenerateEntityIfExists(true);
151
            } elseif (strpos($userChoice, 'Update') === 0) {
152
                $generator->setUpdateEntityIfExists(true);
153
            }
154
        }
155
156
        $generator->useTypedProperties();
157
158
        if (PHP_MAJOR_VERSION >= 8) {
159
            $generator->useConstructorPropertyPromotion();
160
        }
161
162
        $choices = [
163
            '0'     => 'Skip',
164
            '1'     => 'Auto+create',
165
            '2'     => 'Auto',
166
            '3'     => 'Create',
167
            '4'     => 'Show',
168
            '5'     => 'Extends class',
169
            '6'     => 'Implements interface',
170
            '7'     => 'Extensions',
171
            '8'     => 'Enable/disable get method shortcut',
172
            '9'     => 'Change field visibility',
173
            10      => 'Enable/disable typed properties (PHP >= 7.4 only)',
174
            11      => 'Enable/disable promoted properties on constructor (PHP >= 8.0 only)',
175
        ];
176
177
        while (true) {
178
            switch ((string) $io->choice("'$className' found", $choices, 'auto')) {
179
                case 'Auto+create':
180
                    $doCreate = true;
181
                    // no break
182
                case 'Auto':
183
                    $generator->setClassToExtend(Model::class);
184
                    $generator->addInterface(InitializableInterface::class);
185
186
                    if (!$doCreate) {
187
                        break;
188
                    }
189
190
                    // no break
191
                case 'Create':
192
                    $filesystem = new Filesystem();
193
194
                    if ($io->option('backup') && $filesystem->exists($fileName)) {
195
                        $filesystem->copy($fileName, $fileName.'.bak');
196
                        $io->comment('backup file "' . $fileName.'.bak' . '"');
197
                    }
198
199
                    $filesystem->dumpFile($fileName, $generator->generate($mapper, $fileName));
0 ignored issues
show
It seems like $generator->generate($mapper, $fileName) can also be of type false; however, parameter $content of Symfony\Component\Filesy...\Filesystem::dumpFile() does only seem to accept resource|string, maybe add an additional type check? ( Ignorable by Annotation )

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

199
                    $filesystem->dumpFile($fileName, /** @scrutinizer ignore-type */ $generator->generate($mapper, $fileName));
Loading history...
200
201
                    $io->info('File "' . $fileName . '" was created');
202
                    return;
203
204
                case 'Show':
205
                    $io->line('File: ' . $fileName);
206
                    $io->line($generator->generate($mapper, $fileName));
0 ignored issues
show
It seems like $generator->generate($mapper, $fileName) can also be of type false; however, parameter $string of Bdf\Util\Console\BdfStyle::line() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

206
                    $io->line(/** @scrutinizer ignore-type */ $generator->generate($mapper, $fileName));
Loading history...
207
                    break;
208
209
                case 'Extends class':
210
                    $generator->setClassToExtend((string) $io->ask('Enter full name class'));
211
                    break;
212
213
                case 'Implements interface':
214
                    $knownInterfaces = [
215
                        '1'     => EntityInterface::class,
216
                        '2'     => InitializableInterface::class,
217
                        '3'     => ImportableInterface::class,
218
                        '4'     => 'other',
219
                    ];
220
221
                    $userChoice = (string) $io->choice('Choose interface', $knownInterfaces);
222
223
                    if ($userChoice === 'other') {
224
                        $generator->addInterface((string) $io->ask('Enter full name class:'));
225
                    } else {
226
                        $generator->addInterface($userChoice);
227
                    }
228
                    break;
229
230
                case 'Extensions':
231
                    $knownTraits = [
232
                        '1'     => ArrayInjector::class,
233
                        '2'     => 'other',
234
                    ];
235
236
                    $userChoice = (string) $io->choice('Choose extension', $knownTraits);
237
238
                    if ($userChoice === 'other') {
239
                        $generator->addTrait((string) $io->ask('Enter full name class:'));
240
                    } else {
241
                        $generator->addTrait($userChoice);
242
                    }
243
                    break;
244
245
                case 'Enable/disable get method shortcut':
246
                    $generator->useGetShortcutMethod($generator->getUseGetShortcutMethod() === false);
247
                    break;
248
249
                case 'Change field visibility':
250
                    $generator->setFieldVisibility(
251
                        $generator->getFieldVisibility() == EntityGenerator::FIELD_VISIBLE_PROTECTED
252
                        ? EntityGenerator::FIELD_VISIBLE_PRIVATE
253
                        : EntityGenerator::FIELD_VISIBLE_PROTECTED
254
                    );
255
                    break;
256
257
                case 'Enable/disable typed properties (PHP >= 7.4 only)':
258
                    $generator->useTypedProperties($generator->getUseTypedProperties() === false);
259
                    break;
260
261
                case 'Enable/disable promoted properties on constructor (PHP >= 8.0 only)':
262
                    $generator->useConstructorPropertyPromotion($generator->getUseConstructorPropertyPromotion() === false);
263
                    break;
264
265
                default:
266
                case 'skip':
267
                    return;
268
            }
269
        }
270
    }
271
}
272