Passed
Pull Request — 4.3 (#142)
by Jean-Baptiste
04:20
created

TDBMDaoGenerator::generateBean()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 18

Duplication

Lines 23
Ratio 62.16 %

Importance

Changes 0
Metric Value
dl 23
loc 37
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 18
nc 2
nop 4
1
<?php
2
3
namespace Mouf\Database\TDBM\Utils;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use Doctrine\DBAL\Schema\Schema;
7
use Doctrine\DBAL\Schema\Table;
8
use Doctrine\DBAL\Types\Type;
9
use Mouf\Database\TDBM\ConfigurationInterface;
10
use Mouf\Database\TDBM\TDBMException;
11
use Mouf\Database\TDBM\TDBMSchemaAnalyzer;
12
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
13
14
/**
15
 * This class generates automatically DAOs and Beans for TDBM.
16
 */
17
class TDBMDaoGenerator
18
{
19
    /**
20
     * @var Schema
21
     */
22
    private $schema;
23
24
    /**
25
     * Name of composer file.
26
     *
27
     * @var string
28
     */
29
    private $composerFile;
30
31
    /**
32
     * @var TDBMSchemaAnalyzer
33
     */
34
    private $tdbmSchemaAnalyzer;
35
36
    /**
37
     * @var GeneratorListenerInterface
38
     */
39
    private $eventDispatcher;
40
41
    /**
42
     * @var NamingStrategyInterface
43
     */
44
    private $namingStrategy;
45
    /**
46
     * @var ConfigurationInterface
47
     */
48
    private $configuration;
49
50
    /**
51
     * Constructor.
52
     *
53
     * @param ConfigurationInterface $configuration
54
     * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer
55
     */
56
    public function __construct(ConfigurationInterface $configuration, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
57
    {
58
        $this->configuration = $configuration;
59
        $this->schema = $tdbmSchemaAnalyzer->getSchema();
60
        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
61
        $this->namingStrategy = $configuration->getNamingStrategy();
62
        $this->eventDispatcher = $configuration->getGeneratorEventDispatcher();
63
    }
64
65
    /**
66
     * Generates all the daos and beans.
67
     *
68
     * @throws TDBMException
69
     */
70
    public function generateAllDaosAndBeans(): void
71
    {
72
        // TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
73
74
        $tableList = $this->schema->getTables();
75
76
        // Remove all beans and daos from junction tables
77
        $junctionTables = $this->configuration->getSchemaAnalyzer()->detectJunctionTables(true);
78
        $junctionTableNames = array_map(function (Table $table) {
79
            return $table->getName();
80
        }, $junctionTables);
81
82
        $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
83
            return !in_array($table->getName(), $junctionTableNames);
84
        });
85
86
        $beanDescriptors = [];
87
88
        foreach ($tableList as $table) {
89
            $beanDescriptors[] = $this->generateDaoAndBean($table);
90
        }
91
92
93
        $this->generateFactory($tableList);
94
95
        // Let's call the list of listeners
96
        $this->eventDispatcher->onGenerate($this->configuration, $beanDescriptors);
97
    }
98
99
    /**
100
     * Generates in one method call the daos and the beans for one table.
101
     *
102
     * @param Table $table
103
     *
104
     * @return BeanDescriptor
105
     * @throws TDBMException
106
     */
107
    private function generateDaoAndBean(Table $table) : BeanDescriptor
108
    {
109
        // TODO: $storeInUtc is NOT USED.
110
        $tableName = $table->getName();
111
        $daoName = $this->namingStrategy->getDaoClassName($tableName);
112
        $beanName = $this->namingStrategy->getBeanClassName($tableName);
113
        $baseBeanName = $this->namingStrategy->getBaseBeanClassName($tableName);
114
        $baseDaoName = $this->namingStrategy->getBaseDaoClassName($tableName);
115
116
        $beanDescriptor = new BeanDescriptor($table, $this->configuration->getSchemaAnalyzer(), $this->schema, $this->tdbmSchemaAnalyzer, $this->namingStrategy);
117
        $this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table);
118
        $this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table);
119
        return $beanDescriptor;
120
    }
121
122
    /**
123
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
124
     *
125
     * @param BeanDescriptor  $beanDescriptor
126
     * @param string          $className       The name of the class
127
     * @param string          $baseClassName   The name of the base class which will be extended (name only, no directory)
128
     * @param Table           $table           The table
129
     *
130
     * @throws TDBMException
131
     */
132
    public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table)
133
    {
134
        $beannamespace = $this->configuration->getBeanNamespace();
135
        $str = $beanDescriptor->generatePhpCode($beannamespace);
136
137
        $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\Generated\\'.$baseClassName)->getPathname();
138
139
        $this->ensureDirectoryExist($possibleBaseFileName);
140
        file_put_contents($possibleBaseFileName, $str);
141
        @chmod($possibleBaseFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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.');
}
Loading history...
142
143
        $possibleFileName = $this->configuration->getPathFinder()->getPath($beannamespace.'\\'.$className)->getPathname();
144
145 View Code Duplication
        if (!file_exists($possibleFileName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
            $tableName = $table->getName();
147
            $str = "<?php
148
/*
149
 * This file has been automatically generated by TDBM.
150
 * You can edit this file as it will not be overwritten.
151
 */
152
153
namespace {$beannamespace};
154
155
use {$beannamespace}\\Generated\\{$baseClassName};
156
157
/**
158
 * The $className class maps the '$tableName' table in database.
159
 */
160
class $className extends $baseClassName
161
{
162
}
163
";
164
            $this->ensureDirectoryExist($possibleFileName);
165
            file_put_contents($possibleFileName, $str);
166
            @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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.');
}
Loading history...
167
        }
168
    }
169
170
    /**
171
     * Tries to find a @defaultSort annotation in one of the columns.
172
     *
173
     * @param Table $table
174
     *
175
     * @return array First item: column name, Second item: column order (asc/desc)
176
     */
177
    private function getDefaultSortColumnFromAnnotation(Table $table)
178
    {
179
        $defaultSort = null;
180
        $defaultSortDirection = null;
181
        foreach ($table->getColumns() as $column) {
182
            $comments = $column->getComment();
183
            $matches = [];
184
            if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
185
                $defaultSort = $column->getName();
186
                if (count($matches) === 3) {
187
                    $defaultSortDirection = $matches[2];
188
                } else {
189
                    $defaultSortDirection = 'ASC';
190
                }
191
            }
192
        }
193
194
        return [$defaultSort, $defaultSortDirection];
195
    }
196
197
    /**
198
     * Writes the PHP bean DAO with simple functions to create/get/save objects.
199
     *
200
     * @param BeanDescriptor  $beanDescriptor
201
     * @param string          $className       The name of the class
202
     * @param string          $baseClassName
203
     * @param string          $beanClassName
204
     * @param Table           $table
205
     *
206
     * @throws TDBMException
207
     */
208
    private function generateDao(BeanDescriptor $beanDescriptor, string $className, string $baseClassName, string $beanClassName, Table $table)
209
    {
210
        $daonamespace = $this->configuration->getDaoNamespace();
211
        $beannamespace = $this->configuration->getBeanNamespace();
212
        $tableName = $table->getName();
213
        $primaryKeyColumns = $table->getPrimaryKeyColumns();
214
215
        list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table);
216
217
        // FIXME: lowercase tables with _ in the name should work!
218
        $tableCamel = self::toSingular(self::toCamelCase($tableName));
219
220
        $beanClassWithoutNameSpace = $beanClassName;
221
        $beanClassName = $beannamespace.'\\'.$beanClassName;
222
223
        list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace);
224
225
        $usedBeans[] = $beanClassName;
226
        // Let's suppress duplicates in used beans (if any)
227
        $usedBeans = array_flip(array_flip($usedBeans));
228
        $useStatements = array_map(function ($usedBean) {
229
            return "use $usedBean;\n";
230
        }, $usedBeans);
231
232
        $str = "<?php
233
234
/*
235
 * This file has been automatically generated by TDBM.
236
 * DO NOT edit this file, as it might be overwritten.
237
 * If you need to perform changes, edit the $className class instead!
238
 */
239
240
namespace {$daonamespace}\\Generated;
241
242
use Mouf\\Database\\TDBM\\TDBMService;
243
use Mouf\\Database\\TDBM\\ResultIterator;
244
use Mouf\\Database\\TDBM\\ArrayIterator;
245
".implode('', $useStatements)."
246
/**
247
 * The $baseClassName class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
248
 *
249
 */
250
class $baseClassName
251
{
252
253
    /**
254
     * @var TDBMService
255
     */
256
    protected \$tdbmService;
257
258
    /**
259
     * The default sort column.
260
     *
261
     * @var string
262
     */
263
    private \$defaultSort = ".($defaultSort ? "'$defaultSort'" : 'null').';
264
265
    /**
266
     * The default sort direction.
267
     *
268
     * @var string
269
     */
270
    private $defaultDirection = '.($defaultSort && $defaultSortDirection ? "'$defaultSortDirection'" : "'asc'").";
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultSort of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $defaultSortDirection of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
271
272
    /**
273
     * Sets the TDBM service used by this DAO.
274
     *
275
     * @param TDBMService \$tdbmService
276
     */
277
    public function __construct(TDBMService \$tdbmService)
278
    {
279
        \$this->tdbmService = \$tdbmService;
280
    }
281
282
    /**
283
     * Persist the $beanClassWithoutNameSpace instance.
284
     *
285
     * @param $beanClassWithoutNameSpace \$obj The bean to save.
286
     */
287
    public function save($beanClassWithoutNameSpace \$obj)
288
    {
289
        \$this->tdbmService->save(\$obj);
290
    }
291
292
    /**
293
     * Get all $tableCamel records.
294
     *
295
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
296
     */
297
    public function findAll() : iterable
298
    {
299
        if (\$this->defaultSort) {
300
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
301
        } else {
302
            \$orderBy = null;
303
        }
304
        return \$this->tdbmService->findObjects('$tableName', null, [], \$orderBy);
305
    }
306
    ";
307
308
        if (count($primaryKeyColumns) === 1) {
309
            $primaryKeyColumn = $primaryKeyColumns[0];
310
            $primaryKeyPhpType = self::dbalTypeToPhpType($table->getColumn($primaryKeyColumn)->getType());
311
            $str .= "
312
    /**
313
     * Get $beanClassWithoutNameSpace specified by its ID (its primary key)
314
     * If the primary key does not exist, an exception is thrown.
315
     *
316
     * @param string|int \$id
317
     * @param bool \$lazyLoading If set to true, the object will not be loaded right away. Instead, it will be loaded when you first try to access a method of the object.
318
     * @return $beanClassWithoutNameSpace
319
     * @throws TDBMException
320
     */
321
    public function getById($primaryKeyPhpType \$id, \$lazyLoading = false) : $beanClassWithoutNameSpace
322
    {
323
        return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);
324
    }
325
    ";
326
        }
327
        $str .= "
328
    /**
329
     * Deletes the $beanClassWithoutNameSpace passed in parameter.
330
     *
331
     * @param $beanClassWithoutNameSpace \$obj object to delete
332
     * @param bool \$cascade if true, it will delete all object linked to \$obj
333
     */
334
    public function delete($beanClassWithoutNameSpace \$obj, \$cascade = false) : void
335
    {
336
        if (\$cascade === true) {
337
            \$this->tdbmService->deleteCascade(\$obj);
338
        } else {
339
            \$this->tdbmService->delete(\$obj);
340
        }
341
    }
342
343
344
    /**
345
     * Get a list of $beanClassWithoutNameSpace specified by its filters.
346
     *
347
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
348
     * @param array \$parameters The parameters associated with the filter
349
     * @param mixed \$orderBy The order string
350
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
351
     * @param int \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
352
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
353
     */
354
    protected function find(\$filter = null, array \$parameters = [], \$orderBy=null, array \$additionalTablesFetch = [], \$mode = null) : iterable
355
    {
356
        if (\$this->defaultSort && \$orderBy == null) {
357
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
358
        }
359
        return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode);
360
    }
361
362
    /**
363
     * Get a list of $beanClassWithoutNameSpace specified by its filters.
364
     * Unlike the `find` method that guesses the FROM part of the statement, here you can pass the \$from part.
365
     *
366
     * You should not put an alias on the main table name. So your \$from variable should look like:
367
     *
368
     *   \"$tableName JOIN ... ON ...\"
369
     *
370
     * @param string \$from The sql from statement
371
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
372
     * @param array \$parameters The parameters associated with the filter
373
     * @param mixed \$orderBy The order string
374
     * @param int \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
375
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
376
     */
377
    protected function findFromSql(\$from, \$filter = null, array \$parameters = [], \$orderBy = null, \$mode = null) : iterable
378
    {
379
        if (\$this->defaultSort && \$orderBy == null) {
380
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
381
        }
382
        return \$this->tdbmService->findObjectsFromSql('$tableName', \$from, \$filter, \$parameters, \$orderBy, \$mode);
383
    }
384
385
    /**
386
     * Get a single $beanClassWithoutNameSpace specified by its filters.
387
     *
388
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
389
     * @param array \$parameters The parameters associated with the filter
390
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
391
     * @return $beanClassWithoutNameSpace|null
392
     */
393
    protected function findOne(\$filter = null, array \$parameters = [], array \$additionalTablesFetch = []) : ?$beanClassWithoutNameSpace
394
    {
395
        return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters, \$additionalTablesFetch);
396
    }
397
398
    /**
399
     * Get a single $beanClassWithoutNameSpace specified by its filters.
400
     * Unlike the `find` method that guesses the FROM part of the statement, here you can pass the \$from part.
401
     *
402
     * You should not put an alias on the main table name. So your \$from variable should look like:
403
     *
404
     *   \"$tableName JOIN ... ON ...\"
405
     *
406
     * @param string \$from The sql from statement
407
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
408
     * @param array \$parameters The parameters associated with the filter
409
     * @return $beanClassWithoutNameSpace|null
410
     */
411
    protected function findOneFromSql(\$from, \$filter = null, array \$parameters = []) : ?$beanClassWithoutNameSpace
412
    {
413
        return \$this->tdbmService->findObjectFromSql('$tableName', \$from, \$filter, \$parameters);
414
    }
415
416
    /**
417
     * Sets the default column for default sorting.
418
     *
419
     * @param string \$defaultSort
420
     */
421
    public function setDefaultSort(string \$defaultSort) : void
422
    {
423
        \$this->defaultSort = \$defaultSort;
424
    }
425
";
426
427
        $str .= $findByDaoCode;
428
        $str .= '}
429
';
430
431
        $possibleBaseFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\Generated\\'.$baseClassName)->getPathname();
432
433
        $this->ensureDirectoryExist($possibleBaseFileName);
434
        file_put_contents($possibleBaseFileName, $str);
435
        @chmod($possibleBaseFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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.');
}
Loading history...
436
437
        $possibleFileName = $this->configuration->getPathFinder()->getPath($daonamespace.'\\'.$className)->getPathname();
438
439
        // Now, let's generate the "editable" class
440 View Code Duplication
        if (!file_exists($possibleFileName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
            $str = "<?php
442
443
/*
444
 * This file has been automatically generated by TDBM.
445
 * You can edit this file as it will not be overwritten.
446
 */
447
448
namespace {$daonamespace};
449
450
use {$daonamespace}\\Generated\\{$baseClassName};
451
452
/**
453
 * The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
454
 */
455
class $className extends $baseClassName
456
{
457
}
458
";
459
            $this->ensureDirectoryExist($possibleFileName);
460
            file_put_contents($possibleFileName, $str);
461
            @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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.');
}
Loading history...
462
        }
463
    }
464
465
    /**
466
     * Generates the factory bean.
467
     *
468
     * @param Table[] $tableList
469
     * @throws TDBMException
470
     */
471
    private function generateFactory(array $tableList) : void
472
    {
473
        $daoNamespace = $this->configuration->getDaoNamespace();
474
        $daoFactoryClassName = $this->namingStrategy->getDaoFactoryClassName();
475
476
        // For each table, let's write a property.
477
478
        $str = "<?php
479
480
/*
481
 * This file has been automatically generated by TDBM.
482
 * DO NOT edit this file, as it might be overwritten.
483
 */
484
485
namespace {$daoNamespace}\\Generated;
486
487
";
488
        foreach ($tableList as $table) {
489
            $tableName = $table->getName();
490
            $daoClassName = $this->namingStrategy->getDaoClassName($tableName);
491
            $str .= "use {$daoNamespace}\\".$daoClassName.";\n";
492
        }
493
494
        $str .= "
495
/**
496
 * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.
497
 *
498
 */
499
class $daoFactoryClassName
500
{
501
";
502
503
        foreach ($tableList as $table) {
504
            $tableName = $table->getName();
505
            $daoClassName = $this->namingStrategy->getDaoClassName($tableName);
506
            $daoInstanceName = self::toVariableName($daoClassName);
507
508
            $str .= '    /**
509
     * @var '.$daoClassName.'
510
     */
511
    private $'.$daoInstanceName.';
512
513
    /**
514
     * Returns an instance of the '.$daoClassName.' class.
515
     *
516
     * @return '.$daoClassName.'
517
     */
518
    public function get'.$daoClassName.'() : '.$daoClassName.'
519
    {
520
        return $this->'.$daoInstanceName.';
521
    }
522
523
    /**
524
     * Sets the instance of the '.$daoClassName.' class that will be returned by the factory getter.
525
     *
526
     * @param '.$daoClassName.' $'.$daoInstanceName.'
527
     */
528
    public function set'.$daoClassName.'('.$daoClassName.' $'.$daoInstanceName.') : void
529
    {
530
        $this->'.$daoInstanceName.' = $'.$daoInstanceName.';
531
    }';
532
        }
533
534
        $str .= '
535
}
536
';
537
538
        $possibleFileName = $this->configuration->getPathFinder()->getPath($daoNamespace.'\\Generated\\'.$daoFactoryClassName)->getPathname();
539
540
        $this->ensureDirectoryExist($possibleFileName);
541
        file_put_contents($possibleFileName, $str);
542
        @chmod($possibleFileName, 0664);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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.');
}
Loading history...
543
    }
544
545
    /**
546
     * Transforms a string to camelCase (except the first letter will be uppercase too).
547
     * Underscores and spaces are removed and the first letter after the underscore is uppercased.
548
     *
549
     * @param $str string
550
     *
551
     * @return string
552
     */
553
    public static function toCamelCase($str)
554
    {
555
        $str = strtoupper(substr($str, 0, 1)).substr($str, 1);
556
        while (true) {
557
            if (strpos($str, '_') === false && strpos($str, ' ') === false) {
558
                break;
559
            }
560
561
            $pos = strpos($str, '_');
562
            if ($pos === false) {
563
                $pos = strpos($str, ' ');
564
            }
565
            $before = substr($str, 0, $pos);
566
            $after = substr($str, $pos + 1);
567
            $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
568
        }
569
570
        return $str;
571
    }
572
573
    /**
574
     * Tries to put string to the singular form (if it is plural).
575
     * We assume the table names are in english.
576
     *
577
     * @param $str string
578
     *
579
     * @return string
580
     */
581
    public static function toSingular($str)
582
    {
583
        return Inflector::singularize($str);
584
    }
585
586
    /**
587
     * Put the first letter of the string in lower case.
588
     * Very useful to transform a class name into a variable name.
589
     *
590
     * @param $str string
591
     *
592
     * @return string
593
     */
594
    public static function toVariableName($str)
595
    {
596
        return strtolower(substr($str, 0, 1)).substr($str, 1);
597
    }
598
599
    /**
600
     * Ensures the file passed in parameter can be written in its directory.
601
     *
602
     * @param string $fileName
603
     *
604
     * @throws TDBMException
605
     */
606
    private function ensureDirectoryExist($fileName)
607
    {
608
        $dirName = dirname($fileName);
609
        if (!file_exists($dirName)) {
610
            $old = umask(0);
611
            $result = mkdir($dirName, 0775, true);
612
            umask($old);
613
            if ($result === false) {
614
                throw new TDBMException("Unable to create directory: '".$dirName."'.");
615
            }
616
        }
617
    }
618
619
    /**
620
     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
621
     *
622
     * @param Type $type The DBAL type
623
     *
624
     * @return string The PHP type
625
     */
626
    public static function dbalTypeToPhpType(Type $type)
627
    {
628
        $map = [
629
            Type::TARRAY => 'array',
630
            Type::SIMPLE_ARRAY => 'array',
631
            'json' => 'array',  // 'json' is supported from Doctrine DBAL 2.6 only.
632
            Type::JSON_ARRAY => 'array',
633
            Type::BIGINT => 'string',
634
            Type::BOOLEAN => 'bool',
635
            Type::DATETIME => '\DateTimeInterface',
636
            Type::DATETIMETZ => '\DateTimeInterface',
637
            Type::DATE => '\DateTimeInterface',
638
            Type::TIME => '\DateTimeInterface',
639
            Type::DECIMAL => 'float',
640
            Type::INTEGER => 'int',
641
            Type::OBJECT => 'string',
642
            Type::SMALLINT => 'int',
643
            Type::STRING => 'string',
644
            Type::TEXT => 'string',
645
            Type::BINARY => 'string',
646
            Type::BLOB => 'string',
647
            Type::FLOAT => 'float',
648
            Type::GUID => 'string',
649
        ];
650
651
        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
652
    }
653
}
654