1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace TheCodingMachine\TDBM\Utils; |
||||
6 | |||||
7 | use Doctrine\DBAL\Types\Types; |
||||
8 | use Doctrine\Inflector\Inflector; |
||||
9 | use Doctrine\DBAL\Schema\Schema; |
||||
10 | use Doctrine\DBAL\Schema\Table; |
||||
11 | use Doctrine\DBAL\Types\Type; |
||||
12 | use Doctrine\Inflector\InflectorFactory; |
||||
13 | use Psr\Container\ContainerInterface; |
||||
14 | use TheCodingMachine\TDBM\Schema\ForeignKeys; |
||||
15 | use TheCodingMachine\TDBM\TDBMService; |
||||
16 | use Laminas\Code\Generator\AbstractMemberGenerator; |
||||
17 | use Laminas\Code\Generator\ClassGenerator; |
||||
18 | use Laminas\Code\Generator\DocBlock\Tag\VarTag; |
||||
19 | use Laminas\Code\Generator\DocBlockGenerator; |
||||
20 | use Laminas\Code\Generator\FileGenerator; |
||||
21 | use Laminas\Code\Generator\MethodGenerator; |
||||
22 | use Laminas\Code\Generator\ParameterGenerator; |
||||
23 | use Laminas\Code\Generator\PropertyGenerator; |
||||
24 | use TheCodingMachine\TDBM\ConfigurationInterface; |
||||
25 | use TheCodingMachine\TDBM\TDBMException; |
||||
26 | use TheCodingMachine\TDBM\TDBMSchemaAnalyzer; |
||||
27 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
||||
28 | use Symfony\Component\Filesystem\Filesystem; |
||||
29 | |||||
30 | use function str_replace; |
||||
31 | use function strpos; |
||||
32 | use function substr; |
||||
33 | use function var_export; |
||||
34 | |||||
35 | use const PHP_EOL; |
||||
36 | |||||
37 | /** |
||||
38 | * This class generates automatically DAOs and Beans for TDBM. |
||||
39 | */ |
||||
40 | class TDBMDaoGenerator |
||||
41 | { |
||||
42 | /** |
||||
43 | * @var TDBMSchemaAnalyzer |
||||
44 | */ |
||||
45 | private $tdbmSchemaAnalyzer; |
||||
46 | |||||
47 | /** |
||||
48 | * @var GeneratorListenerInterface |
||||
49 | */ |
||||
50 | private $eventDispatcher; |
||||
51 | |||||
52 | /** |
||||
53 | * @var NamingStrategyInterface |
||||
54 | */ |
||||
55 | private $namingStrategy; |
||||
56 | /** |
||||
57 | * @var ConfigurationInterface |
||||
58 | */ |
||||
59 | private $configuration; |
||||
60 | /** |
||||
61 | * @var Inflector |
||||
62 | */ |
||||
63 | private static $inflector; |
||||
64 | |||||
65 | /** |
||||
66 | * Constructor. |
||||
67 | * |
||||
68 | * @param ConfigurationInterface $configuration |
||||
69 | * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer |
||||
70 | */ |
||||
71 | public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer) |
||||
72 | { |
||||
73 | $this->configuration = $configuration; |
||||
74 | $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer; |
||||
75 | $this->namingStrategy = $configuration->getNamingStrategy(); |
||||
76 | $this->eventDispatcher = $configuration->getGeneratorEventDispatcher(); |
||||
77 | } |
||||
78 | |||||
79 | /** |
||||
80 | * Generates all the daos and beans. |
||||
81 | * |
||||
82 | * @throws TDBMException |
||||
83 | */ |
||||
84 | public function generateAllDaosAndBeans(bool $fromLock = false): void |
||||
85 | { |
||||
86 | if (!$fromLock) { |
||||
87 | $this->tdbmSchemaAnalyzer->generateLockFile(); |
||||
0 ignored issues
–
show
Deprecated Code
introduced
by
![]() |
|||||
88 | } |
||||
89 | $schema = $this->tdbmSchemaAnalyzer->getSchema(); |
||||
0 ignored issues
–
show
The function
TheCodingMachine\TDBM\TD...maAnalyzer::getSchema() has been deprecated.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
90 | |||||
91 | // TODO: check that no class name ends with "Base". Otherwise, there will be name clash. |
||||
92 | $tableList = $schema->getTables(); |
||||
93 | |||||
94 | // Remove all beans and daos from junction tables |
||||
95 | $junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true); |
||||
96 | $junctionTableNames = array_map(function (Table $table) { |
||||
97 | return $table->getName(); |
||||
98 | }, $junctionTables); |
||||
99 | |||||
100 | $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) { |
||||
101 | return !in_array($table->getName(), $junctionTableNames, true); |
||||
102 | }); |
||||
103 | |||||
104 | $this->cleanUpGenerated(); |
||||
105 | |||||
106 | $beanDescriptors = []; |
||||
107 | |||||
108 | $beanRegistry = new BeanRegistry($this->configuration, $schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy); |
||||
109 | foreach ($tableList as $table) { |
||||
110 | $beanDescriptors[] = $beanRegistry->addBeanForTable($table); |
||||
111 | } |
||||
112 | foreach ($beanDescriptors as $beanDescriptor) { |
||||
113 | $beanDescriptor->initBeanPropertyDescriptors(); |
||||
114 | } |
||||
115 | foreach ($beanDescriptors as $beanDescriptor) { |
||||
116 | $this->generateBean($beanDescriptor); |
||||
117 | $this->generateDao($beanDescriptor); |
||||
118 | $this->generateResultIterator($beanDescriptor); |
||||
119 | } |
||||
120 | |||||
121 | $this->generateFactory($beanDescriptors); |
||||
122 | |||||
123 | // Let's call the list of listeners |
||||
124 | $this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors); |
||||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * Removes all files from the Generated folders. |
||||
129 | * This is a way to ensure that when a table is deleted, the matching bean/dao are deleted. |
||||
130 | * Note: only abstract generated classes are deleted. We do not delete the code that might have been customized |
||||
131 | * by the user. The user will need to delete this code him/herself |
||||
132 | */ |
||||
133 | private function cleanUpGenerated(): void |
||||
134 | { |
||||
135 | $generatedBeanDir = $this->configuration->getPathFinder()->getPath($this->configuration->getBeanNamespace().'\\Generated\\Xxx')->getPath(); |
||||
136 | $this->deleteAllPhpFiles($generatedBeanDir); |
||||
137 | |||||
138 | $generatedDaoDir = $this->configuration->getPathFinder()->getPath($this->configuration->getDaoNamespace().'\\Generated\\Xxx')->getPath(); |
||||
139 | $this->deleteAllPhpFiles($generatedDaoDir); |
||||
140 | |||||
141 | $generatedResultIteratorDir = $this->configuration->getPathFinder()->getPath($this->configuration->getResultIteratorNamespace().'\\Generated\\Xxx')->getPath(); |
||||
142 | $this->deleteAllPhpFiles($generatedResultIteratorDir); |
||||
143 | } |
||||
144 | |||||
145 | private function deleteAllPhpFiles(string $directory): void |
||||
146 | { |
||||
147 | $files = glob($directory.'/*.php', GLOB_NOSORT); |
||||
148 | if ($files === false) { |
||||
149 | return; |
||||
150 | } |
||||
151 | $fileSystem = new Filesystem(); |
||||
152 | $fileSystem->remove($files); |
||||
153 | } |
||||
154 | |||||
155 | /** |
||||
156 | * Writes the PHP bean file with all getters and setters from the table passed in parameter. |
||||
157 | * |
||||
158 | * @param BeanDescriptor $beanDescriptor |
||||
159 | * |
||||
160 | * @throws TDBMException |
||||
161 | */ |
||||
162 | public function generateBean(BeanDescriptor $beanDescriptor): void |
||||
163 | { |
||||
164 | $className = $beanDescriptor->getBeanClassName(); |
||||
165 | $baseClassName = $beanDescriptor->getBaseBeanClassName(); |
||||
166 | $table = $beanDescriptor->getTable(); |
||||
167 | $beannamespace = $this->configuration->getBeanNamespace(); |
||||
168 | $file = $beanDescriptor->generatePhpCode(); |
||||
169 | if ($file === null) { |
||||
170 | return; |
||||
171 | } |
||||
172 | |||||
173 | $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
||||
174 | |||||
175 | $fileContent = $file->generate(); |
||||
176 | |||||
177 | // Hard code PSR-2 fix |
||||
178 | $fileContent = $this->psr2Fix($fileContent); |
||||
179 | // Add the declare strict-types directive |
||||
180 | $commentEnd = strpos($fileContent, ' */') + 3; |
||||
181 | $fileContent = substr($fileContent, 0, $commentEnd) . "\n\ndeclare(strict_types=1);" . substr($fileContent, $commentEnd + 1); |
||||
182 | |||||
183 | $this->dumpFile($possibleBaseFileName, $fileContent); |
||||
184 | |||||
185 | $possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname(); |
||||
186 | |||||
187 | if (!file_exists($possibleFileName)) { |
||||
188 | $tableName = $table->getName(); |
||||
189 | $str = "<?php |
||||
190 | /* |
||||
191 | * This file has been automatically generated by TDBM. |
||||
192 | * You can edit this file as it will not be overwritten. |
||||
193 | */ |
||||
194 | |||||
195 | declare(strict_types=1); |
||||
196 | |||||
197 | namespace {$beannamespace}; |
||||
198 | |||||
199 | use {$beannamespace}\\Generated\\{$baseClassName}; |
||||
200 | |||||
201 | /** |
||||
202 | * The $className class maps the '$tableName' table in database. |
||||
203 | */ |
||||
204 | class $className extends $baseClassName |
||||
205 | { |
||||
206 | } |
||||
207 | "; |
||||
208 | |||||
209 | $this->dumpFile($possibleFileName, $str); |
||||
210 | } |
||||
211 | } |
||||
212 | |||||
213 | /** |
||||
214 | * Writes the PHP bean DAO with simple functions to create/get/save objects. |
||||
215 | * |
||||
216 | * @param BeanDescriptor $beanDescriptor |
||||
217 | * |
||||
218 | * @throws TDBMException |
||||
219 | */ |
||||
220 | private function generateDao(BeanDescriptor $beanDescriptor): void |
||||
221 | { |
||||
222 | $className = $beanDescriptor->getDaoClassName(); |
||||
223 | $baseClassName = $beanDescriptor->getBaseDaoClassName(); |
||||
224 | $beanClassName = $beanDescriptor->getBeanClassName(); |
||||
225 | $table = $beanDescriptor->getTable(); |
||||
226 | $file = $beanDescriptor->generateDaoPhpCode(); |
||||
227 | if ($file === null) { |
||||
228 | return; |
||||
229 | } |
||||
230 | $daonamespace = $this->configuration->getDaoNamespace(); |
||||
231 | $tableName = $table->getName(); |
||||
232 | |||||
233 | $beanClassWithoutNameSpace = $beanClassName; |
||||
234 | |||||
235 | $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname(); |
||||
236 | |||||
237 | $fileContent = $file->generate(); |
||||
238 | |||||
239 | // Hard code PSR-2 fix |
||||
240 | $fileContent = $this->psr2Fix($fileContent); |
||||
241 | // Add the declare strict-types directive |
||||
242 | $commentEnd = strpos($fileContent, ' */') + 3; |
||||
243 | $fileContent = substr($fileContent, 0, $commentEnd) . "\n\ndeclare(strict_types=1);" . substr($fileContent, $commentEnd + 1); |
||||
244 | |||||
245 | $this->dumpFile($possibleBaseFileName, $fileContent); |
||||
246 | |||||
247 | |||||
248 | $possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname(); |
||||
249 | |||||
250 | // Now, let's generate the "editable" class |
||||
251 | if (!file_exists($possibleFileName)) { |
||||
252 | $str = "<?php |
||||
253 | /* |
||||
254 | * This file has been automatically generated by TDBM. |
||||
255 | * You can edit this file as it will not be overwritten. |
||||
256 | */ |
||||
257 | |||||
258 | declare(strict_types=1); |
||||
259 | |||||
260 | namespace {$daonamespace}; |
||||
261 | |||||
262 | use {$daonamespace}\\Generated\\{$baseClassName}; |
||||
263 | |||||
264 | /** |
||||
265 | * The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table. |
||||
266 | */ |
||||
267 | class $className extends $baseClassName |
||||
268 | { |
||||
269 | } |
||||
270 | "; |
||||
271 | $this->dumpFile($possibleFileName, $str); |
||||
272 | } |
||||
273 | } |
||||
274 | |||||
275 | /** |
||||
276 | * Fixes PSR-2 for files generated by Zend-Code |
||||
277 | */ |
||||
278 | private function psr2Fix(string $content): string |
||||
279 | { |
||||
280 | // Removes the extra blank line at the end (before: `\n\n`, after: `\n`) |
||||
281 | return rtrim($content, PHP_EOL) . PHP_EOL; |
||||
282 | } |
||||
283 | |||||
284 | /** |
||||
285 | * Writes the PHP ResultIterator file with typed accessors. |
||||
286 | */ |
||||
287 | private function generateResultIterator(BeanDescriptor $beanDescriptor): void |
||||
288 | { |
||||
289 | $resultIteratorClassName = $beanDescriptor->getResultIteratorClassName(); |
||||
290 | $resultIteratorBaseClassName = $beanDescriptor->getBaseResultIteratorClassName(); |
||||
291 | $resultIteratorNamespace = $this->configuration->getResultIteratorNamespace(); |
||||
292 | $resultIteratorBaseNamespace = $resultIteratorNamespace . '\\Generated'; |
||||
293 | $beanClassWithoutNameSpace = $beanDescriptor->getBeanClassName(); |
||||
294 | $file = $beanDescriptor->generateResultIteratorPhpCode(); |
||||
295 | if ($file === null) { |
||||
296 | return; |
||||
297 | } |
||||
298 | |||||
299 | $fileContent = $this->psr2Fix($file->generate()); |
||||
300 | $commentEnd = strpos($fileContent, ' */') + 3; |
||||
301 | $fileContent = substr($fileContent, 0, $commentEnd) . "\n\ndeclare(strict_types=1);" . substr($fileContent, $commentEnd + 1); |
||||
302 | |||||
303 | $baseResultIteratorFilePath = $this->configuration->getPathFinder()->getPath($resultIteratorBaseNamespace.'\\'.$resultIteratorBaseClassName)->getPathname(); |
||||
304 | $this->dumpFile($baseResultIteratorFilePath, $fileContent); |
||||
305 | |||||
306 | $resultIteratorFilePath = $this->configuration->getPathFinder()->getPath($resultIteratorNamespace.'\\'.$resultIteratorClassName)->getPathname(); |
||||
307 | // Now, let's generate the "editable" class |
||||
308 | if (!file_exists($resultIteratorFilePath)) { |
||||
309 | $str = "<?php |
||||
310 | /* |
||||
311 | * This file has been automatically generated by TDBM. |
||||
312 | * You can edit this file as it will not be overwritten. |
||||
313 | */ |
||||
314 | |||||
315 | declare(strict_types=1); |
||||
316 | |||||
317 | namespace {$resultIteratorNamespace}; |
||||
318 | |||||
319 | use {$resultIteratorBaseNamespace}\\{$resultIteratorBaseClassName}; |
||||
320 | |||||
321 | /** |
||||
322 | * The $resultIteratorClassName class will iterate over results of $beanClassWithoutNameSpace class. |
||||
323 | */ |
||||
324 | class $resultIteratorClassName extends $resultIteratorBaseClassName |
||||
325 | { |
||||
326 | } |
||||
327 | "; |
||||
328 | $this->dumpFile($resultIteratorFilePath, $str); |
||||
329 | } |
||||
330 | } |
||||
331 | |||||
332 | /** |
||||
333 | * Generates the factory bean. |
||||
334 | * |
||||
335 | * @param BeanDescriptor[] $beanDescriptors |
||||
336 | * @throws TDBMException |
||||
337 | */ |
||||
338 | private function generateFactory(array $beanDescriptors): void |
||||
339 | { |
||||
340 | $daoNamespace = $this->configuration->getDaoNamespace(); |
||||
341 | $daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName(); |
||||
342 | |||||
343 | $file = new FileGenerator(); |
||||
344 | $file->setDocBlock( |
||||
345 | new DocBlockGenerator( |
||||
346 | <<<DOC |
||||
347 | This file has been automatically generated by TDBM. |
||||
348 | DO NOT edit this file, as it might be overwritten. |
||||
349 | DOC |
||||
350 | ) |
||||
351 | ); |
||||
352 | $class = new ClassGenerator(); |
||||
353 | $file->setClass($class); |
||||
354 | $class->setName($daoFactoryClassName); |
||||
355 | $file->setNamespace($daoNamespace.'\\Generated'); |
||||
356 | |||||
357 | $class->setDocBlock(new DocBlockGenerator("The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.")); |
||||
358 | |||||
359 | $containerProperty = new PropertyGenerator('container'); |
||||
360 | $containerProperty->setVisibility(AbstractMemberGenerator::VISIBILITY_PRIVATE); |
||||
361 | $containerProperty->setDocBlock(new DocBlockGenerator(null, null, [new VarTag(null, ['\\'.ContainerInterface::class])])); |
||||
362 | $class->addPropertyFromGenerator($containerProperty); |
||||
363 | |||||
364 | $constructorMethod = new MethodGenerator( |
||||
365 | '__construct', |
||||
366 | [ new ParameterGenerator('container', ContainerInterface::class) ], |
||||
367 | MethodGenerator::FLAG_PUBLIC, |
||||
368 | '$this->container = $container;' |
||||
369 | ); |
||||
370 | $constructorMethod = $this->configuration->getCodeGeneratorListener()->onDaoFactoryConstructorGenerated($constructorMethod, $beanDescriptors, $this->configuration, $class); |
||||
371 | if ($constructorMethod !== null) { |
||||
372 | $class->addMethodFromGenerator($constructorMethod); |
||||
373 | } |
||||
374 | |||||
375 | // For each table, let's write a property. |
||||
376 | foreach ($beanDescriptors as $beanDescriptor) { |
||||
377 | $daoClassName = $beanDescriptor->getDaoClassName(); |
||||
378 | $daoInstanceName = self::toVariableName($daoClassName); |
||||
379 | |||||
380 | $daoInstanceProperty = new PropertyGenerator($daoInstanceName); |
||||
381 | $daoInstanceProperty->setVisibility(AbstractMemberGenerator::VISIBILITY_PRIVATE); |
||||
382 | $daoInstanceProperty->setDocBlock(new DocBlockGenerator(null, null, [new VarTag(null, ['\\' . $daoNamespace . '\\' . $daoClassName, 'null'])])); |
||||
383 | $class->addPropertyFromGenerator($daoInstanceProperty); |
||||
384 | |||||
385 | $fullClassNameVarExport = var_export($daoNamespace.'\\'.$daoClassName, true); |
||||
386 | $getterBody = <<<BODY |
||||
387 | if (!\$this->$daoInstanceName) { |
||||
388 | \$this->$daoInstanceName = \$this->container->get($fullClassNameVarExport); |
||||
389 | } |
||||
390 | |||||
391 | return \$this->$daoInstanceName; |
||||
392 | BODY; |
||||
393 | |||||
394 | $getterMethod = new MethodGenerator( |
||||
395 | 'get' . $daoClassName, |
||||
396 | [], |
||||
397 | MethodGenerator::FLAG_PUBLIC, |
||||
398 | $getterBody |
||||
399 | ); |
||||
400 | $getterMethod->setReturnType($daoNamespace.'\\'.$daoClassName); |
||||
401 | $getterMethod = $this->configuration->getCodeGeneratorListener()->onDaoFactoryGetterGenerated($getterMethod, $beanDescriptor, $this->configuration, $class); |
||||
402 | if ($getterMethod !== null) { |
||||
403 | $class->addMethodFromGenerator($getterMethod); |
||||
404 | } |
||||
405 | |||||
406 | $setterMethod = new MethodGenerator( |
||||
407 | 'set' . $daoClassName, |
||||
408 | [new ParameterGenerator($daoInstanceName, '\\' . $daoNamespace . '\\' . $daoClassName)], |
||||
409 | MethodGenerator::FLAG_PUBLIC, |
||||
410 | '$this->' . $daoInstanceName . ' = $' . $daoInstanceName . ';' |
||||
411 | ); |
||||
412 | $setterMethod->setReturnType('void'); |
||||
413 | $setterMethod = $this->configuration->getCodeGeneratorListener()->onDaoFactorySetterGenerated($setterMethod, $beanDescriptor, $this->configuration, $class); |
||||
414 | if ($setterMethod !== null) { |
||||
415 | $class->addMethodFromGenerator($setterMethod); |
||||
416 | } |
||||
417 | } |
||||
418 | |||||
419 | $file = $this->configuration->getCodeGeneratorListener()->onDaoFactoryGenerated($file, $beanDescriptors, $this->configuration); |
||||
420 | |||||
421 | if ($file !== null) { |
||||
422 | $possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname(); |
||||
423 | |||||
424 | $fileContent = $file->generate(); |
||||
425 | |||||
426 | // Hard code PSR-2 fix |
||||
427 | $fileContent = $this->psr2Fix($fileContent); |
||||
428 | // Add the declare strict-types directive |
||||
429 | $commentEnd = strpos($fileContent, ' */') + 3; |
||||
430 | $fileContent = substr($fileContent, 0, $commentEnd) . "\n\ndeclare(strict_types=1);" . substr($fileContent, $commentEnd + 1); |
||||
431 | |||||
432 | $this->dumpFile($possibleFileName, $fileContent); |
||||
433 | } |
||||
434 | } |
||||
435 | |||||
436 | /** |
||||
437 | * Transforms a string to camelCase (except the first letter will be uppercase too). |
||||
438 | * Underscores and spaces are removed and the first letter after the underscore is uppercased. |
||||
439 | * Quoting is removed if present. |
||||
440 | * |
||||
441 | * @param string $str |
||||
442 | * |
||||
443 | * @return string |
||||
444 | */ |
||||
445 | public static function toCamelCase(string $str): string |
||||
446 | { |
||||
447 | $str = str_replace(array('`', '"', '[', ']'), '', $str); |
||||
448 | |||||
449 | $str = strtoupper(substr($str, 0, 1)).substr($str, 1); |
||||
450 | while (true) { |
||||
451 | $pos = strpos($str, '_'); |
||||
452 | if ($pos === false) { |
||||
453 | $pos = strpos($str, ' '); |
||||
454 | if ($pos === false) { |
||||
455 | break; |
||||
456 | } |
||||
457 | } |
||||
458 | |||||
459 | $before = substr($str, 0, $pos); |
||||
460 | $after = substr($str, $pos + 1); |
||||
461 | $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1); |
||||
462 | } |
||||
463 | |||||
464 | return $str; |
||||
465 | } |
||||
466 | |||||
467 | /** |
||||
468 | * Tries to put string to the singular form (if it is plural). |
||||
469 | * We assume the table names are in english. |
||||
470 | * |
||||
471 | * @param string $str |
||||
472 | * |
||||
473 | * @return string |
||||
474 | */ |
||||
475 | public static function toSingular(string $str): string |
||||
476 | { |
||||
477 | if (self::$inflector === null) { |
||||
478 | self::$inflector = InflectorFactory::create()->build(); |
||||
479 | } |
||||
480 | |||||
481 | return self::$inflector->singularize($str); |
||||
482 | } |
||||
483 | |||||
484 | /** |
||||
485 | * Tries to put string to the plural form (if it is singular). |
||||
486 | * We assume the string is in english. |
||||
487 | */ |
||||
488 | public static function toPlural(string $str): string |
||||
489 | { |
||||
490 | if (self::$inflector === null) { |
||||
491 | self::$inflector = InflectorFactory::create()->build(); |
||||
492 | } |
||||
493 | |||||
494 | return self::$inflector->pluralize($str); |
||||
495 | } |
||||
496 | |||||
497 | /** |
||||
498 | * Put the first letter of the string in lower case. |
||||
499 | * Very useful to transform a class name into a variable name. |
||||
500 | * |
||||
501 | * @param string $str |
||||
502 | * |
||||
503 | * @return string |
||||
504 | */ |
||||
505 | public static function toVariableName(string $str): string |
||||
506 | { |
||||
507 | return strtolower(substr($str, 0, 1)).substr($str, 1); |
||||
508 | } |
||||
509 | |||||
510 | /** |
||||
511 | * Ensures the file passed in parameter can be written in its directory. |
||||
512 | * |
||||
513 | * @param string $fileName |
||||
514 | * |
||||
515 | * @throws TDBMException |
||||
516 | */ |
||||
517 | private function ensureDirectoryExist(string $fileName): void |
||||
518 | { |
||||
519 | $dirName = dirname($fileName); |
||||
520 | if (!file_exists($dirName)) { |
||||
521 | $old = umask(0); |
||||
522 | $result = mkdir($dirName, 0775, true); |
||||
523 | umask($old); |
||||
524 | if ($result === false) { |
||||
525 | throw new TDBMException("Unable to create directory: '".$dirName."'."); |
||||
526 | } |
||||
527 | } |
||||
528 | } |
||||
529 | |||||
530 | private function dumpFile(string $fileName, string $content): void |
||||
531 | { |
||||
532 | $this->ensureDirectoryExist($fileName); |
||||
533 | $fileSystem = new Filesystem(); |
||||
534 | $fileSystem->dumpFile($fileName, $content); |
||||
535 | @chmod($fileName, 0664); |
||||
0 ignored issues
–
show
It seems like you do not handle an error condition for
chmod() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||
536 | } |
||||
537 | |||||
538 | /** |
||||
539 | * Transforms a DBAL type into a PHP type (for PHPDoc purpose). |
||||
540 | * |
||||
541 | * @param Type $type The DBAL type |
||||
542 | * |
||||
543 | * @return string The PHP type |
||||
544 | */ |
||||
545 | public static function dbalTypeToPhpType(Type $type): string |
||||
546 | { |
||||
547 | $map = [ |
||||
548 | Types::ARRAY => 'array', |
||||
0 ignored issues
–
show
The constant
Doctrine\DBAL\Types\Types::ARRAY has been deprecated: Use {@link Types::JSON} instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This class constant has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead. ![]() |
|||||
549 | Types::SIMPLE_ARRAY => 'array', |
||||
550 | Types::JSON => 'array', |
||||
551 | Types::BIGINT => 'string', |
||||
552 | Types::BOOLEAN => 'bool', |
||||
553 | Types::DATETIME_IMMUTABLE => '\DateTimeImmutable', |
||||
554 | Types::DATETIMETZ_IMMUTABLE => '\DateTimeImmutable', |
||||
555 | Types::DATE_IMMUTABLE => '\DateTimeImmutable', |
||||
556 | Types::TIME_IMMUTABLE => '\DateTimeImmutable', |
||||
557 | Types::DECIMAL => 'string', |
||||
558 | Types::INTEGER => 'int', |
||||
559 | Types::OBJECT => 'string', |
||||
0 ignored issues
–
show
The constant
Doctrine\DBAL\Types\Types::OBJECT has been deprecated: Use {@link Types::JSON} instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This class constant has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead. ![]() |
|||||
560 | Types::SMALLINT => 'int', |
||||
561 | Types::STRING => 'string', |
||||
562 | Types::TEXT => 'string', |
||||
563 | Types::BINARY => 'resource', |
||||
564 | Types::BLOB => 'resource', |
||||
565 | Types::FLOAT => 'float', |
||||
566 | Types::GUID => 'string', |
||||
567 | ]; |
||||
568 | |||||
569 | return $map[$type->getName()] ?? $type->getName(); |
||||
0 ignored issues
–
show
The function
Doctrine\DBAL\Types\Type::getName() has been deprecated: this method will be removed in Doctrine DBAL 4.0, use {@see TypeRegistry::lookupName()} instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
570 | } |
||||
571 | |||||
572 | /** |
||||
573 | * @param Table $table |
||||
574 | * @return string[] |
||||
575 | * @throws TDBMException |
||||
576 | */ |
||||
577 | public static function getPrimaryKeyColumnsOrFail(Table $table): array |
||||
578 | { |
||||
579 | if ($table->getPrimaryKey() === null) { |
||||
580 | // Security check: a table MUST have a primary key |
||||
581 | throw new TDBMException(sprintf('Table "%s" does not have any primary key', $table->getName())); |
||||
582 | } |
||||
583 | return $table->getPrimaryKey()->getUnquotedColumns(); |
||||
584 | } |
||||
585 | } |
||||
586 |