Completed
Pull Request — 4.0 (#48)
by Marc
05:03
created

TDBMDaoGenerator::setComposerFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
651
     */
652
    public function setComposerFile($composerFile)
653
    {
654
        $this->rootPath = dirname($composerFile).'/';
655
        $this->composerFile = basename($composerFile);
656
    }
657
658
    /**
659
     * Transforms a DBAL type into a PHP type (for PHPDoc purpose).
660
     *
661
     * @param Type $type The DBAL type
662
     *
663
     * @return string The PHP type
664
     */
665
    public static function dbalTypeToPhpType(Type $type)
666
    {
667
        $map = [
668
            Type::TARRAY => 'array',
669
            Type::SIMPLE_ARRAY => 'array',
670
            Type::JSON_ARRAY => 'array',
671
            Type::BIGINT => 'string',
672
            Type::BOOLEAN => 'bool',
673
            Type::DATETIME => '\DateTimeInterface',
674
            Type::DATETIMETZ => '\DateTimeInterface',
675
            Type::DATE => '\DateTimeInterface',
676
            Type::TIME => '\DateTimeInterface',
677
            Type::DECIMAL => 'float',
678
            Type::INTEGER => 'int',
679
            Type::OBJECT => 'string',
680
            Type::SMALLINT => 'int',
681
            Type::STRING => 'string',
682
            Type::TEXT => 'string',
683
            Type::BINARY => 'string',
684
            Type::BLOB => 'string',
685
            Type::FLOAT => 'float',
686
            Type::GUID => 'string',
687
        ];
688
689
        return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
690
    }
691
692
    /**
693
     * @param string $beanNamespace
694
     *
695
     * @return \string[] Returns a map mapping table name to beans name
696
     */
697
    public function buildTableToBeanMap($beanNamespace)
698
    {
699
        $tableToBeanMap = [];
700
701
        $tables = $this->schema->getTables();
702
703
        foreach ($tables as $table) {
704
            $tableName = $table->getName();
705
            $tableToBeanMap[$tableName] = $beanNamespace.'\\'.self::getBeanNameFromTableName($tableName);
706
        }
707
708
        return $tableToBeanMap;
709
    }
710
}
711