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
Bug
introduced
by
![]() |
|||||
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
![]() |
|||||
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 |