Completed
Pull Request — 4.0 (#55)
by Huberty
05:04
created

TDBMDaoGenerator::toCamelCase()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 19
rs 8.8571
cc 5
eloc 12
nc 2
nop 1
1
<?php
2
3
namespace Mouf\Database\TDBM\Utils;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\Schema;
8
use Doctrine\DBAL\Schema\Table;
9
use Doctrine\DBAL\Types\Type;
10
use Mouf\Composer\ClassNameMapper;
11
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
12
use Mouf\Database\TDBM\TDBMException;
13
use Mouf\Database\TDBM\TDBMSchemaAnalyzer;
14
15
/**
16
 * This class generates automatically DAOs and Beans for TDBM.
17
 */
18
class TDBMDaoGenerator
19
{
20
    /**
21
     * @var SchemaAnalyzer
22
     */
23
    private $schemaAnalyzer;
24
25
    /**
26
     * @var Schema
27
     */
28
    private $schema;
29
30
    /**
31
     * The root directory of the project.
32
     *
33
     * @var string
34
     */
35
    private $rootPath;
36
37
    /**
38
     * Name of composer file.
39
     *
40
     * @var string
41
     */
42
    private $composerFile;
43
44
    /**
45
     * @var TDBMSchemaAnalyzer
46
     */
47
    private $tdbmSchemaAnalyzer;
48
49
    /**
50
     * Constructor.
51
     *
52
     * @param SchemaAnalyzer     $schemaAnalyzer
53
     * @param Schema             $schema
54
     * @param TDBMSchemaAnalyzer $tdbmSchemaAnalyzer
55
     */
56
    public function __construct(SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer)
57
    {
58
        $this->schemaAnalyzer = $schemaAnalyzer;
59
        $this->schema = $schema;
60
        $this->tdbmSchemaAnalyzer = $tdbmSchemaAnalyzer;
61
        $this->rootPath = __DIR__.'/../../../../../../../../';
62
        $this->composerFile = 'composer.json';
63
    }
64
65
    /**
66
     * Generates all the daos and beans.
67
     *
68
     * @param string $daoFactoryClassName The classe name of the DAO factory
69
     * @param string $daonamespace        The namespace for the DAOs, without trailing \
70
     * @param string $beannamespace       The Namespace for the beans, without trailing \
71
     * @param bool   $storeInUtc          If the generated daos should store the date in UTC timezone instead of user's timezone.
72
     *
73
     * @return \string[] the list of tables
74
     *
75
     * @throws TDBMException
76
     */
77
    public function generateAllDaosAndBeans($daoFactoryClassName, $daonamespace, $beannamespace, $storeInUtc)
78
    {
79
        // TODO: extract ClassNameMapper in its own package!
80
        $classNameMapper = ClassNameMapper::createFromComposerFile($this->rootPath.$this->composerFile);
81
        // TODO: check that no class name ends with "Base". Otherwise, there will be name clash.
82
83
        $tableList = $this->schema->getTables();
84
85
        // Remove all beans and daos from junction tables
86
        $junctionTables = $this->schemaAnalyzer->detectJunctionTables();
87
        $junctionTableNames = array_map(function (Table $table) {
88
            return $table->getName();
89
        }, $junctionTables);
90
91
        $tableList = array_filter($tableList, function (Table $table) use ($junctionTableNames) {
92
            return !in_array($table->getName(), $junctionTableNames);
93
        });
94
95
        foreach ($tableList as $table) {
96
            $this->generateDaoAndBean($table, $daonamespace, $beannamespace, $classNameMapper, $storeInUtc);
97
        }
98
99
        $this->generateFactory($tableList, $daoFactoryClassName, $daonamespace, $classNameMapper);
100
101
        // Ok, let's return the list of all tables.
102
        // These will be used by the calling script to create Mouf instances.
103
104
        return array_map(function (Table $table) { return $table->getName(); }, $tableList);
105
    }
106
107
    /**
108
     * Generates in one method call the daos and the beans for one table.
109
     *
110
     * @param Table           $table
111
     * @param string          $daonamespace
112
     * @param string          $beannamespace
113
     * @param ClassNameMapper $classNameMapper
114
     * @param bool            $storeInUtc
115
     *
116
     * @throws TDBMException
117
     */
118
    public function generateDaoAndBean(Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc)
119
    {
120
        $tableName = $table->getName();
121
        $daoName = $this->getDaoNameFromTableName($tableName);
122
        $beanName = $this->getBeanNameFromTableName($tableName);
123
        $baseBeanName = $this->getBaseBeanNameFromTableName($tableName);
124
        $baseDaoName = $this->getBaseDaoNameFromTableName($tableName);
125
126
        $beanDescriptor = new BeanDescriptor($table, $this->schemaAnalyzer, $this->schema, $this->tdbmSchemaAnalyzer);
127
        $this->generateBean($beanDescriptor, $beanName, $baseBeanName, $table, $beannamespace, $classNameMapper, $storeInUtc);
128
        $this->generateDao($beanDescriptor, $daoName, $baseDaoName, $beanName, $table, $daonamespace, $beannamespace, $classNameMapper);
129
    }
130
131
    /**
132
     * Returns the name of the bean class from the table name.
133
     *
134
     * @param $tableName
135
     *
136
     * @return string
137
     */
138
    public static function getBeanNameFromTableName($tableName)
139
    {
140
        return self::toSingular(self::toCamelCase($tableName)).'Bean';
141
    }
142
143
    /**
144
     * Returns the name of the DAO class from the table name.
145
     *
146
     * @param $tableName
147
     *
148
     * @return string
149
     */
150
    public static function getDaoNameFromTableName($tableName)
151
    {
152
        return self::toSingular(self::toCamelCase($tableName)).'Dao';
153
    }
154
155
    /**
156
     * Returns the name of the base bean class from the table name.
157
     *
158
     * @param $tableName
159
     *
160
     * @return string
161
     */
162
    public static function getBaseBeanNameFromTableName($tableName)
163
    {
164
        return self::toSingular(self::toCamelCase($tableName)).'BaseBean';
165
    }
166
167
    /**
168
     * Returns the name of the base DAO class from the table name.
169
     *
170
     * @param $tableName
171
     *
172
     * @return string
173
     */
174
    public static function getBaseDaoNameFromTableName($tableName)
175
    {
176
        return self::toSingular(self::toCamelCase($tableName)).'BaseDao';
177
    }
178
179
    /**
180
     * Writes the PHP bean file with all getters and setters from the table passed in parameter.
181
     *
182
     * @param BeanDescriptor  $beanDescriptor
183
     * @param string          $className       The name of the class
184
     * @param string          $baseClassName   The name of the base class which will be extended (name only, no directory)
185
     * @param Table           $table           The table
186
     * @param string          $beannamespace   The namespace of the bean
187
     * @param ClassNameMapper $classNameMapper
188
     *
189
     * @throws TDBMException
190
     */
191
    public function generateBean(BeanDescriptor $beanDescriptor, $className, $baseClassName, Table $table, $beannamespace, ClassNameMapper $classNameMapper, $storeInUtc)
0 ignored issues
show
Unused Code introduced by
The parameter $storeInUtc is not used and could be removed.

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

Loading history...
192
    {
193
        $str = $beanDescriptor->generatePhpCode($beannamespace);
194
195
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\Generated\\'.$baseClassName);
196
        if (empty($possibleBaseFileNames)) {
197
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$baseClassName.'" is not autoloadable.');
198
        }
199
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
200
201
        $this->ensureDirectoryExist($possibleBaseFileName);
202
        file_put_contents($possibleBaseFileName, $str);
203
        @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...
204
205
        $possibleFileNames = $classNameMapper->getPossibleFileNames($beannamespace.'\\'.$className);
206
        if (empty($possibleFileNames)) {
207
            // @codeCoverageIgnoreStart
208
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$beannamespace.'\\'.$className.'" is not autoloadable.');
209
            // @codeCoverageIgnoreEnd
210
        }
211
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
212 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...
213
            $tableName = $table->getName();
214
            $str = "<?php
215
/*
216
 * This file has been automatically generated by TDBM.
217
 * You can edit this file as it will not be overwritten.
218
 */
219
220
namespace {$beannamespace};
221
222
use {$beannamespace}\\Generated\\{$baseClassName};
223
224
/**
225
 * The $className class maps the '$tableName' table in database.
226
 */
227
class $className extends $baseClassName
228
{
229
230
}";
231
            $this->ensureDirectoryExist($possibleFileName);
232
            file_put_contents($possibleFileName, $str);
233
            @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...
234
        }
235
    }
236
237
    /**
238
     * Tries to find a @defaultSort annotation in one of the columns.
239
     *
240
     * @param Table $table
241
     *
242
     * @return array First item: column name, Second item: column order (asc/desc)
243
     */
244
    private function getDefaultSortColumnFromAnnotation(Table $table)
245
    {
246
        $defaultSort = null;
247
        $defaultSortDirection = null;
248
        foreach ($table->getColumns() as $column) {
249
            $comments = $column->getComment();
250
            $matches = [];
251
            if (preg_match('/@defaultSort(\((desc|asc)\))*/', $comments, $matches) != 0) {
252
                $defaultSort = $column->getName();
253
                if (count($matches) === 3) {
254
                    $defaultSortDirection = $matches[2];
255
                } else {
256
                    $defaultSortDirection = 'ASC';
257
                }
258
            }
259
        }
260
261
        return [$defaultSort, $defaultSortDirection];
262
    }
263
264
    /**
265
     * Writes the PHP bean DAO with simple functions to create/get/save objects.
266
     *
267
     * @param BeanDescriptor  $beanDescriptor
268
     * @param string          $className       The name of the class
269
     * @param string          $baseClassName
270
     * @param string          $beanClassName
271
     * @param Table           $table
272
     * @param string          $daonamespace
273
     * @param string          $beannamespace
274
     * @param ClassNameMapper $classNameMapper
275
     *
276
     * @throws TDBMException
277
     */
278
    public function generateDao(BeanDescriptor $beanDescriptor, $className, $baseClassName, $beanClassName, Table $table, $daonamespace, $beannamespace, ClassNameMapper $classNameMapper)
279
    {
280
        $tableName = $table->getName();
281
        $primaryKeyColumns = $table->getPrimaryKeyColumns();
282
283
        list($defaultSort, $defaultSortDirection) = $this->getDefaultSortColumnFromAnnotation($table);
284
285
        // FIXME: lowercase tables with _ in the name should work!
286
        $tableCamel = self::toSingular(self::toCamelCase($tableName));
287
288
        $beanClassWithoutNameSpace = $beanClassName;
289
        $beanClassName = $beannamespace.'\\'.$beanClassName;
290
291
        list($usedBeans, $findByDaoCode) = $beanDescriptor->generateFindByDaoCode($beannamespace, $beanClassWithoutNameSpace);
292
293
        $usedBeans[] = $beanClassName;
294
        // Let's suppress duplicates in used beans (if any)
295
        $usedBeans = array_flip(array_flip($usedBeans));
296
        $useStatements = array_map(function ($usedBean) {
297
            return "use $usedBean;\n";
298
        }, $usedBeans);
299
300
        $str = "<?php
301
302
/*
303
 * This file has been automatically generated by TDBM.
304
 * DO NOT edit this file, as it might be overwritten.
305
 * If you need to perform changes, edit the $className class instead!
306
 */
307
308
namespace {$daonamespace}\\Generated;
309
310
use Mouf\\Database\\TDBM\\TDBMService;
311
use Mouf\\Database\\TDBM\\ResultIterator;
312
use Mouf\\Database\\TDBM\\ArrayIterator;
313
".implode('', $useStatements)."
314
315
/**
316
 * The $baseClassName class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
317
 *
318
 */
319
class $baseClassName
320
{
321
322
    /**
323
     * @var TDBMService
324
     */
325
    protected \$tdbmService;
326
327
    /**
328
     * The default sort column.
329
     *
330
     * @var string
331
     */
332
    private \$defaultSort = ".($defaultSort ? "'$defaultSort'" : 'null').';
333
334
    /**
335
     * The default sort direction.
336
     *
337
     * @var string
338
     */
339
    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...
340
341
    /**
342
     * Sets the TDBM service used by this DAO.
343
     *
344
     * @param TDBMService \$tdbmService
345
     */
346
    public function __construct(TDBMService \$tdbmService)
347
    {
348
        \$this->tdbmService = \$tdbmService;
349
    }
350
351
    /**
352
     * Persist the $beanClassWithoutNameSpace instance.
353
     *
354
     * @param $beanClassWithoutNameSpace \$obj The bean to save.
355
     */
356
    public function save($beanClassWithoutNameSpace \$obj)
357
    {
358
        \$this->tdbmService->save(\$obj);
359
    }
360
361
    /**
362
     * Get all $tableCamel records.
363
     *
364
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
365
     */
366
    public function findAll()
367
    {
368
        if (\$this->defaultSort) {
369
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
370
        } else {
371
            \$orderBy = null;
372
        }
373
        return \$this->tdbmService->findObjects('$tableName',  null, [], \$orderBy);
374
    }
375
    ";
376
377
        if (count($primaryKeyColumns) === 1) {
378
            $primaryKeyColumn = $primaryKeyColumns[0];
379
            $str .= "
380
    /**
381
     * Get $beanClassWithoutNameSpace specified by its ID (its primary key)
382
     * If the primary key does not exist, an exception is thrown.
383
     *
384
     * @param string|int \$id
385
     * @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.
386
     * @return $beanClassWithoutNameSpace
387
     * @throws TDBMException
388
     */
389
    public function getById(\$id, \$lazyLoading = false)
390
    {
391
        return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);
392
    }
393
    ";
394
        }
395
        $str .= "
396
    /**
397
     * Deletes the $beanClassWithoutNameSpace passed in parameter.
398
     *
399
     * @param $beanClassWithoutNameSpace \$obj object to delete
400
     * @param bool \$cascade if true, it will delete all object linked to \$obj
401
     */
402
    public function delete($beanClassWithoutNameSpace \$obj, \$cascade = false)
403
    {
404
        if (\$cascade === true) {
405
            \$this->tdbmService->deleteCascade(\$obj);
406
        } else {
407
            \$this->tdbmService->delete(\$obj);
408
        }
409
    }
410
411
412
    /**
413
     * Get a list of $beanClassWithoutNameSpace specified by its filters.
414
     *
415
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
416
     * @param array \$parameters The parameters associated with the filter
417
     * @param mixed \$orderBy The order string
418
     * @param array \$additionalTablesFetch A list of additional tables to fetch (for performance improvement)
419
     * @param int \$mode Either TDBMService::MODE_ARRAY or TDBMService::MODE_CURSOR (for large datasets). Defaults to TDBMService::MODE_ARRAY.
420
     * @return {$beanClassWithoutNameSpace}[]|ResultIterator|ResultArray
421
     */
422
    protected function find(\$filter = null, array \$parameters = [], \$orderBy=null, array \$additionalTablesFetch = [], \$mode = null)
423
    {
424
        if (\$this->defaultSort && \$orderBy == null) {
425
            \$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
426
        }
427
        return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode);
428
    }
429
430
    /**
431
     * Get a single $beanClassWithoutNameSpace specified by its filters.
432
     *
433
     * @param mixed \$filter The filter bag (see TDBMService::findObjects for complete description)
434
     * @param array \$parameters The parameters associated with the filter
435
     * @return $beanClassWithoutNameSpace
436
     */
437
    protected function findOne(\$filter=null, array \$parameters = [])
438
    {
439
        return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters);
440
    }
441
442
    /**
443
     * Sets the default column for default sorting.
444
     *
445
     * @param string \$defaultSort
446
     */
447
    public function setDefaultSort(\$defaultSort)
448
    {
449
        \$this->defaultSort = \$defaultSort;
450
    }
451
";
452
453
        $str .= $findByDaoCode;
454
        $str .= '}
455
';
456
457
        $possibleBaseFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\Generated\\'.$baseClassName);
458
        if (empty($possibleBaseFileNames)) {
459
            // @codeCoverageIgnoreStart
460
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$baseClassName.'" is not autoloadable.');
461
            // @codeCoverageIgnoreEnd
462
        }
463
        $possibleBaseFileName = $this->rootPath.$possibleBaseFileNames[0];
464
465
        $this->ensureDirectoryExist($possibleBaseFileName);
466
        file_put_contents($possibleBaseFileName, $str);
467
        @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...
468
469
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daonamespace.'\\'.$className);
470
        if (empty($possibleFileNames)) {
471
            // @codeCoverageIgnoreStart
472
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$className.'" is not autoloadable.');
473
            // @codeCoverageIgnoreEnd
474
        }
475
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
476
477
        // Now, let's generate the "editable" class
478 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...
479
            $str = "<?php
480
481
/*
482
 * This file has been automatically generated by TDBM.
483
 * You can edit this file as it will not be overwritten.
484
 */
485
486
namespace {$daonamespace};
487
488
use {$daonamespace}\\Generated\\{$baseClassName};
489
490
/**
491
 * The $className class will maintain the persistence of $beanClassWithoutNameSpace class into the $tableName table.
492
 */
493
class $className extends $baseClassName
494
{
495
496
}
497
";
498
            $this->ensureDirectoryExist($possibleFileName);
499
            file_put_contents($possibleFileName, $str);
500
            @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...
501
        }
502
    }
503
504
    /**
505
     * Generates the factory bean.
506
     *
507
     * @param Table[] $tableList
508
     */
509
    private function generateFactory(array $tableList, $daoFactoryClassName, $daoNamespace, ClassNameMapper $classNameMapper)
510
    {
511
        // For each table, let's write a property.
512
513
        $str = "<?php
514
515
/*
516
 * This file has been automatically generated by TDBM.
517
 * DO NOT edit this file, as it might be overwritten.
518
 */
519
520
namespace {$daoNamespace}\\Generated;
521
";
522
        foreach ($tableList as $table) {
523
            $tableName = $table->getName();
524
            $daoClassName = $this->getDaoNameFromTableName($tableName);
525
            $str .= "use {$daoNamespace}\\".$daoClassName.";\n";
526
        }
527
528
        $str .= "
529
/**
530
 * The $daoFactoryClassName provides an easy access to all DAOs generated by TDBM.
531
 *
532
 */
533
class $daoFactoryClassName
534
{
535
";
536
537
        foreach ($tableList as $table) {
538
            $tableName = $table->getName();
539
            $daoClassName = $this->getDaoNameFromTableName($tableName);
540
            $daoInstanceName = self::toVariableName($daoClassName);
541
542
            $str .= '    /**
543
     * @var '.$daoClassName.'
544
     */
545
    private $'.$daoInstanceName.';
546
547
    /**
548
     * Returns an instance of the '.$daoClassName.' class.
549
     *
550
     * @return '.$daoClassName.'
551
     */
552
    public function get'.$daoClassName.'()
553
    {
554
        return $this->'.$daoInstanceName.';
555
    }
556
557
    /**
558
     * Sets the instance of the '.$daoClassName.' class that will be returned by the factory getter.
559
     *
560
     * @param '.$daoClassName.' $'.$daoInstanceName.'
561
     */
562
    public function set'.$daoClassName.'('.$daoClassName.' $'.$daoInstanceName.') {
563
        $this->'.$daoInstanceName.' = $'.$daoInstanceName.';
564
    }
565
566
';
567
        }
568
569
        $str .= '
570
}
571
';
572
573
        $possibleFileNames = $classNameMapper->getPossibleFileNames($daoNamespace.'\\Generated\\'.$daoFactoryClassName);
574
        if (empty($possibleFileNames)) {
575
            throw new TDBMException('Sorry, autoload namespace issue. The class "'.$daoNamespace.'\\'.$daoFactoryClassName.'" is not autoloadable.');
576
        }
577
        $possibleFileName = $this->rootPath.$possibleFileNames[0];
578
579
        $this->ensureDirectoryExist($possibleFileName);
580
        file_put_contents($possibleFileName, $str);
581
        @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...
582
    }
583
584
    /**
585
     * Transforms a string to camelCase (except the first letter will be uppercase too).
586
     * Underscores and spaces are removed and the first letter after the underscore is uppercased.
587
     *
588
     * @param $str string
589
     *
590
     * @return string
591
     */
592
    public static function toCamelCase($str)
593
    {
594
        $str = strtoupper(substr($str, 0, 1)).substr($str, 1);
595
        while (true) {
596
            if (strpos($str, '_') === false && strpos($str, ' ') === false) {
597
                break;
598
            }
599
600
            $pos = strpos($str, '_');
601
            if ($pos === false) {
602
                $pos = strpos($str, ' ');
603
            }
604
            $before = substr($str, 0, $pos);
605
            $after = substr($str, $pos + 1);
606
            $str = $before.strtoupper(substr($after, 0, 1)).substr($after, 1);
607
        }
608
609
        return $str;
610
    }
611
612
    /**
613
     * Tries to put string to the singular form (if it is plural).
614
     * We assume the table names are in english.
615
     *
616
     * @param $str string
617
     *
618
     * @return string
619
     */
620
    public static function toSingular($str)
621
    {
622
        return Inflector::singularize($str);
623
    }
624
625
    /**
626
     * Put the first letter of the string in lower case.
627
     * Very useful to transform a class name into a variable name.
628
     *
629
     * @param $str string
630
     *
631
     * @return string
632
     */
633
    public static function toVariableName($str)
634
    {
635
        return strtolower(substr($str, 0, 1)).substr($str, 1);
636
    }
637
638
    /**
639
     * Ensures the file passed in parameter can be written in its directory.
640
     *
641
     * @param string $fileName
642
     *
643
     * @throws TDBMException
644
     */
645
    private function ensureDirectoryExist($fileName)
646
    {
647
        $dirName = dirname($fileName);
648
        if (!file_exists($dirName)) {
649
            $old = umask(0);
650
            $result = mkdir($dirName, 0775, true);
651
            umask($old);
652
            if ($result === false) {
653
                throw new TDBMException("Unable to create directory: '".$dirName."'.");
654
            }
655
        }
656
    }
657
658
    /**
659
     * Absolute path to composer json file.
660
     *
661
     * @param string $composerFile
662
     */
663
    public function setComposerFile($composerFile)
664
    {
665
        $this->rootPath = dirname($composerFile).'/';
666
        $this->composerFile = basename($composerFile);
667
    }
668
669
    /**
670
     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
671
     *
672
     * @param Type $type The DBAL type
673
     *
674
     * @return string The PHP type
675
     */
676
    public static function dbalTypeToPhpType(Type $type)
677
    {
678
        $map = [
679
            Type::TARRAY => 'array',
680
            Type::SIMPLE_ARRAY => 'array',
681
            Type::JSON_ARRAY => 'array',
682
            Type::BIGINT => 'string',
683
            Type::BOOLEAN => 'bool',
684
            Type::DATETIME => '\DateTimeInterface',
685
            Type::DATETIMETZ => '\DateTimeInterface',
686
            Type::DATE => '\DateTimeInterface',
687
            Type::TIME => '\DateTimeInterface',
688
            Type::DECIMAL => 'float',
689
            Type::INTEGER => 'int',
690
            Type::OBJECT => 'string',
691
            Type::SMALLINT => 'int',
692
            Type::STRING => 'string',
693
            Type::TEXT => 'string',
694
            Type::BINARY => 'string',
695
            Type::BLOB => 'string',
696
            Type::FLOAT => 'float',
697
            Type::GUID => 'string',
698
        ];
699
700
        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
701
    }
702
703
    /**
704
     * @param string $beanNamespace
705
     *
706
     * @return \string[] Returns a map mapping table name to beans name
707
     */
708
    public function buildTableToBeanMap($beanNamespace)
709
    {
710
        $tableToBeanMap = [];
711
712
        $tables = $this->schema->getTables();
713
714
        foreach ($tables as $table) {
715
            $tableName = $table->getName();
716
            $tableToBeanMap[$tableName] = $beanNamespace.'\\'.self::getBeanNameFromTableName($tableName);
717
        }
718
719
        return $tableToBeanMap;
720
    }
721
}
722