Completed
Push — master ( 1de9b7...830752 )
by Kristof
38:46 queued 24:09
created

GeneratorBundle/Generator/KunstmaanGenerator.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\GeneratorBundle\Generator;
4
5
use Doctrine\ORM\Mapping\ClassMetadataInfo;
6
use Doctrine\ORM\Mapping\UnderscoreNamingStrategy;
7
use Doctrine\ORM\Tools\EntityGenerator;
8
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
9
use Kunstmaan\GeneratorBundle\Helper\CommandAssistant;
10
use Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
11
use Sensio\Bundle\GeneratorBundle\Generator\Generator;
12
use Symfony\Bridge\Doctrine\RegistryInterface;
13
use Symfony\Component\DependencyInjection\ContainerInterface;
14
use Symfony\Component\Filesystem\Filesystem;
15
use Symfony\Component\Finder\Finder;
16
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
17
use Symfony\Component\HttpKernel\Kernel;
18
19
/**
20
 * Class that contains all common generator logic.
21
 */
22
class KunstmaanGenerator extends Generator
23
{
24
    /**
25
     * @var Filesystem
26
     */
27
    protected $filesystem;
28
29
    /**
30
     * @var RegistryInterface
31
     */
32
    protected $registry;
33
34
    /**
35
     * @var string
36
     */
37
    protected $skeletonDir;
38
39
    /**
40
     * @var CommandAssistant
41
     */
42
    protected $assistant;
43
44
    /**
45
     * @var ContainerInterface
46
     */
47
    protected $container;
48
49
    /**
50
     * @param Filesystem         $filesystem  The filesystem
51
     * @param RegistryInterface  $registry    The registry
52
     * @param string             $skeletonDir The directory of the skeleton
53
     * @param CommandAssistant   $assistant   The command assistant
54
     * @param ContainerInterface $container   The container
55
     */
56
    public function __construct(
57
        Filesystem $filesystem,
58
        RegistryInterface $registry,
59
        $skeletonDir,
60
        CommandAssistant $assistant,
61
        ContainerInterface $container = null
62
    ) {
63
        $this->filesystem = $filesystem;
64
        $this->registry = $registry;
65
        $this->skeletonDir = GeneratorUtils::getFullSkeletonPath($skeletonDir);
66
        $this->assistant = $assistant;
67
        $this->container = $container;
68
69
        $this->setSkeletonDirs(array($this->skeletonDir, GeneratorUtils::getFullSkeletonPath('/common')));
70
    }
71
72
    /**
73
     * Check that the keyword is a reserved word for the database system.
74
     *
75
     * @param string $keyword
76
     *
77
     * @return bool
78
     */
79
    public function isReservedKeyword($keyword)
80
    {
81
        return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword);
82
    }
83
84
    /**
85
     * Generate the entity PHP code.
86
     *
87
     * @param BundleInterface $bundle
88
     * @param string          $name
89
     * @param array           $fields
90
     * @param string          $namePrefix
91
     * @param string          $dbPrefix
92
     * @param string|null     $extendClass
93
     * @param bool            $withRepository
94
     *
95
     * @return array
96
     *
97
     * @throws \RuntimeException
98
     */
99
    protected function generateEntity(
100
        BundleInterface $bundle,
101
        $name,
102
        $fields,
103
        $namePrefix,
104
        $dbPrefix,
105
        $extendClass = null,
106
        $withRepository = false
107
    ) {
108
        // configure the bundle (needed if the bundle does not contain any Entities yet)
109
        $config = $this->registry->getManager(null)->getConfiguration();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\ObjectManager as the method getConfiguration() does only exist in the following implementations of said interface: Doctrine\ORM\Decorator\EntityManagerDecorator, Doctrine\ORM\EntityManager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
110
        $config->setEntityNamespaces(
111
            array_merge(
112
                array($bundle->getName() => $bundle->getNamespace() . '\\Entity' . ($namePrefix ? '\\' . $namePrefix : '')),
113
                $config->getEntityNamespaces()
114
            )
115
        );
116
117
        $entityClass = $this->registry->getAliasNamespace($bundle->getName()) . ($namePrefix ? '\\' . $namePrefix : '') . '\\' . $name;
118
        $entityPath = $bundle->getPath() . '/Entity/' . ($namePrefix ? $namePrefix . '/' : '') . str_replace('\\', '/', $name) . '.php';
119
        if (file_exists($entityPath)) {
120
            throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass));
121
        }
122
123
        $class = new ClassMetadataInfo($entityClass, new UnderscoreNamingStrategy());
124
        if ($withRepository) {
125
            if ($this->isSymfony4()) {
126
                $repositoryClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1) . 'Repository';
127
                $class->customRepositoryClassName = $repositoryClass;
128
                $this->getSymfony4RepositoryGenerator()->writeEntityRepositoryClass($entityClass, $repositoryClass, $bundle->getPath());
129
            } else {
130
                $entityClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1);
131
                $class->customRepositoryClassName = $entityClass.'Repository';
132
                $path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\'));
133
                $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path);
134
            }
135
        }
136
137
        foreach ($fields as $fieldSet) {
138
            foreach ($fieldSet as $fieldArray) {
139
                foreach ($fieldArray as $field) {
140
                    if (array_key_exists('joinColumn', $field)) {
141
                        $class->mapManyToOne($field);
142
                    } elseif (array_key_exists('joinTable', $field)) {
143
                        $class->mapManyToMany($field);
144
                    } else {
145
                        $class->mapField($field);
146
                    }
147
                }
148
            }
149
        }
150
        $class->setPrimaryTable(
151
            array(
152
                'name' => strtolower($dbPrefix . strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name))) . 's',
153
            )
154
        );
155
        $entityCode = $this->getEntityGenerator($extendClass)->generateEntityClass($class);
156
157
        return array($entityCode, $entityPath);
158
    }
159
160
    /**
161
     * Get a Doctrine EntityGenerator instance.
162
     *
163
     * @param string|null $classToExtend
164
     *
165
     * @return EntityGenerator
166
     */
167
    protected function getEntityGenerator($classToExtend = null)
168
    {
169
        $entityGenerator = new EntityGenerator();
170
        if (!is_null($classToExtend)) {
171
            $entityGenerator->setClassToExtend($classToExtend);
172
        }
173
        $entityGenerator->setGenerateAnnotations(true);
174
        $entityGenerator->setGenerateStubMethods(true);
175
        $entityGenerator->setRegenerateEntityIfExists(false);
176
        $entityGenerator->setUpdateEntityIfExists(true);
177
        $entityGenerator->setNumSpaces(4);
178
        $entityGenerator->setAnnotationPrefix('ORM\\');
179
180
        return $entityGenerator;
181
    }
182
183
    /**
184
     * Generate the entity admin type.
185
     *
186
     * @param        $bundle
187
     * @param        $entityName
188
     * @param        $entityPrefix
189
     * @param array  $fields
190
     * @param string $extendClass
191
     */
192
    protected function generateEntityAdminType(
193
        $bundle,
194
        $entityName,
195
        $entityPrefix,
196
        array $fields,
197
        $extendClass = '\Symfony\Component\Form\AbstractType'
198
    ) {
199
        $className = $entityName . 'AdminType';
200
        $savePath = $bundle->getPath() . '/Form/' . $entityPrefix . '/' . $className . '.php';
201
        $name = str_replace(
202
                '\\',
203
                '_',
204
                strtolower($bundle->getNamespace())
205
            ) . '_' . strtolower($entityName) . 'type';
206
207
        $params = array(
208
            'className' => $className,
209
            'name' => $name,
210
            'namespace' => $bundle->getNamespace(),
211
            'entity' => '\\' . $bundle->getNamespace() . '\Entity\\' . $entityPrefix . '\\' . $entityName,
212
            'fields' => $fields,
213
            'entity_prefix' => $entityPrefix,
214
            'extend_class' => $extendClass,
215
        );
216
        $this->renderFile('/Form/EntityAdminType.php', $savePath, $params);
217
    }
218
219
    /**
220
     * Install the default page templates.
221
     *
222
     * @param BundleInterface $bundle
223
     */
224
    protected function installDefaultPageTemplates($bundle)
225
    {
226
        // Configuration templates
227 View Code Duplication
        if ($this->isSymfony4()) {
228
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pagetemplates/';
229
        } else {
230
            $dirPath = sprintf('%s/Resources/config/pagetemplates/', $bundle->getPath());
231
        }
232
233
        $skeletonDir = sprintf('%s/Resources/config/pagetemplates/', GeneratorUtils::getFullSkeletonPath('/common'));
234
235
        // Only copy templates over when the folder does not exist yet...
236
        if (!$this->filesystem->exists($dirPath)) {
237
            $files = array(
238
                'default-one-column.yml',
239
                'default-two-column-left.yml',
240
                'default-two-column-right.yml',
241
                'default-three-column.yml',
242
            );
243
            foreach ($files as $file) {
244
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
245
                GeneratorUtils::replace('~~~BUNDLE~~~', $bundle->getName(), $dirPath . $file);
246
            }
247
        }
248
249
        // Twig templates
250
        $dirPath = $this->getTemplateDir($bundle) . '/Pages/Common/';
251
252
        $skeletonDir = sprintf('%s/Resources/views/Pages/Common/', GeneratorUtils::getFullSkeletonPath('/common'));
253
254
        if (!$this->filesystem->exists($dirPath)) {
255
            $files = array(
256
                'one-column-pagetemplate.html.twig',
257
                'two-column-left-pagetemplate.html.twig',
258
                'two-column-right-pagetemplate.html.twig',
259
                'three-column-pagetemplate.html.twig',
260
            );
261
            foreach ($files as $file) {
262
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
263
            }
264
            $this->filesystem->copy($skeletonDir . 'view.html.twig', $dirPath . 'view.html.twig', false);
265
        }
266
267
        $contents = file_get_contents($dirPath . 'view.html.twig');
268
269
        $twigFile = $this->isSymfony4() ?
270
            $twigFile = "{% extends 'Layout/layout.html.twig' %}\n" :
271
            $twigFile = "{% extends '".$bundle->getName().":Layout:layout.html.twig' %}\n"
272
        ;
273
274
        if (strpos($contents, '{% extends ') === false) {
275
            GeneratorUtils::prepend(
276
                $twigFile,
277
                $dirPath . 'view.html.twig'
278
            );
279
        }
280
    }
281
282
    /**
283
     * Install the default pagepart configuration.
284
     *
285
     * @param BundleInterface $bundle
286
     */
287
    protected function installDefaultPagePartConfiguration($bundle)
288
    {
289
        // Pagepart configuration
290 View Code Duplication
        if ($this->isSymfony4()) {
291
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pageparts/';
292
        } else {
293
            $dirPath = sprintf('%s/Resources/config/pageparts/', $bundle->getPath());
294
        }
295
296
        $skeletonDir = sprintf('%s/Resources/config/pageparts/', GeneratorUtils::getFullSkeletonPath('/common'));
297
298
        // Only copy when folder does not exist yet
299
        if (!$this->filesystem->exists($dirPath)) {
300
            $files = array('footer.yml', 'main.yml', 'left-sidebar.yml', 'right-sidebar.yml');
301
            foreach ($files as $file) {
302
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
303
            }
304
        }
305
    }
306
307
    /**
308
     * Render all files in the source directory and copy them to the target directory.
309
     *
310
     * @param string $sourceDir  The source directory where we need to look in
311
     * @param string $targetDir  The target directory where we need to copy the files too
312
     * @param array  $parameters The parameters that will be passed to the templates
313
     * @param bool   $override   Whether to override an existing file or not
314
     * @param bool   $recursive  Whether to render all files recursively or not
315
     */
316
    public function renderFiles($sourceDir, $targetDir, array $parameters, $override = false, $recursive = true)
317
    {
318
        // Make sure the source -and target dir contain a trailing slash
319
        $sourceDir = rtrim($sourceDir, '/') . '/';
320
        $targetDir = rtrim($targetDir, '/') . '/';
321
322
        $this->setSkeletonDirs(array($sourceDir));
323
324
        $finder = new Finder();
325
        $finder->files()->in($sourceDir);
326
        if (!$recursive) {
327
            $finder->depth('== 0');
328
        }
329
330
        // Get all files in the source directory
331
        foreach ($finder as $file) {
332
            $name = $file->getRelativePathname();
333
334
            // Check that we are allowed to overwrite the file if it already exists
335 View Code Duplication
            if (!is_file($targetDir . $name) || $override === true) {
336
                $fileParts = explode('.', $name);
337
                if (end($fileParts) === 'twig') {
338
                    $this->renderTwigFile($name, $targetDir . $name, $parameters, $sourceDir);
339
                } else {
340
                    $this->renderFile($name, $targetDir . $name, $parameters);
341
                }
342
            }
343
        }
344
    }
345
346
    /**
347
     * Render all files in the source directory and copy them to the target directory.
348
     *
349
     * @param string      $sourceDir      The source directory where we need to look in
350
     * @param string      $targetDir      The target directory where we need to copy the files too
351
     * @param string      $filename       The name of the file that needs to be rendered
352
     * @param array       $parameters     The parameters that will be passed to the templates
353
     * @param bool        $override       Whether to override an existing file or not
354
     * @param string|null $targetFilename The name of the target file (if null, then use $filename)
355
     */
356
    public function renderSingleFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $targetFilename = null)
357
    {
358
        // Make sure the source -and target dir contain a trailing slash
359
        $sourceDir = rtrim($sourceDir, '/') . '/';
360
        $targetDir = rtrim($targetDir, '/') . '/';
361
        if (is_null($targetFilename)) {
362
            $targetFilename = $filename;
363
        }
364
365
        $this->setSkeletonDirs(array($sourceDir));
366
367 View Code Duplication
        if (is_file($sourceDir . $filename)) {
368
            // Check that we are allowed the overwrite the file if it already exists
369
            if (!is_file($targetDir . $targetFilename) || $override === true) {
370
                $fileParts = explode('.', $filename);
371
                if (end($fileParts) === 'twig') {
372
                    $this->renderTwigFile($filename, $targetDir . $targetFilename, $parameters, $sourceDir);
373
                } else {
374
                    $this->renderFile($filename, $targetDir . $targetFilename, $parameters);
375
                }
376
            }
377
        }
378
    }
379
380
    /**
381
     * Render a file and make it executable.
382
     *
383
     * @param string $sourceDir  The source directory where we need to look in
384
     * @param string $targetDir  The target directory where we need to copy the files too
385
     * @param string $filename   The name of the file that needs to be rendered
386
     * @param array  $parameters The parameters that will be passed to the templates
387
     * @param bool   $override   Whether to override an existing file or not
388
     * @param int    $mode       The mode
389
     */
390
    public function renderExecutableFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $mode = 0774)
391
    {
392
        $this->renderSingleFile($sourceDir, $targetDir, $filename, $parameters, $override);
393
394
        $targetDir = rtrim($targetDir, '/') . '/';
395
        $targetFile = $targetDir . $filename;
396
        $this->filesystem->chmod($targetFile, $mode);
397
    }
398
399
    /**
400
     * Copy all files in the source directory to the target directory.
401
     *
402
     * @param string $sourceDir The source directory where we need to look in
403
     * @param string $targetDir The target directory where we need to copy the files too
404
     * @param bool   $override  Whether to override an existing file or not
405
     */
406
    public function copyFiles($sourceDir, $targetDir, $override = false)
407
    {
408
        // Make sure the source -and target dir contain a trailing slash
409
        $sourceDir = rtrim($sourceDir, '/') . '/';
410
        $targetDir = rtrim($targetDir, '/') . '/';
411
412
        $this->filesystem->mirror($sourceDir, $targetDir, null, array('override' => $override));
413
    }
414
415
    /**
416
     * Remove a directory from the filesystem.
417
     *
418
     * @param string $targetDir
419
     */
420
    public function removeDirectory($targetDir)
421
    {
422
        // Make sure the target dir contain a trailing slash
423
        $targetDir = rtrim($targetDir, '/') . '/';
424
425
        $this->filesystem->remove($targetDir);
426
    }
427
428
    /**
429
     * Remove a file from the filesystem.
430
     *
431
     * @param string $file
432
     */
433
    public function removeFile($file)
434
    {
435
        $this->filesystem->remove($file);
436
    }
437
438
    /**
439
     * Render a twig file with custom twig tags.
440
     *
441
     * @param string $template
442
     * @param array  $parameters
443
     * @param string $sourceDir
444
     *
445
     * @return string
446
     */
447
    public function renderTwig($template, array $parameters, $sourceDir)
448
    {
449
        $twig = new \Twig_Environment(
450
            new \Twig_Loader_Filesystem(array($sourceDir)), array(
451
                'debug' => true,
452
                'cache' => false,
453
                'strict_variables' => true,
454
                'autoescape' => false,
455
            )
456
        );
457
458
        // Ruby erb template syntax
459
        $lexer = new \Twig_Lexer(
460
            $twig, array(
461
                'tag_comment' => array('<%#', '%>'),
462
                'tag_block' => array('<%', '%>'),
463
                'tag_variable' => array('<%=', '%>'),
464
            )
465
        );
466
467
        $twig->setLexer($lexer);
468
469
        return $twig->render($template, $parameters);
470
    }
471
472
    /**
473
     * Render a twig file, and save it to disk.
474
     *
475
     * @param string $template
476
     * @param string $target
477
     * @param array  $parameters
478
     * @param string $sourceDir
479
     *
480
     * @return int
481
     */
482
    public function renderTwigFile($template, $target, array $parameters, $sourceDir)
483
    {
484
        if (!is_dir(dirname($target))) {
485
            mkdir(dirname($target), 0777, true);
486
        }
487
488
        return file_put_contents($target, $this->renderTwig($template, $parameters, $sourceDir));
489
    }
490
491
    /**
492
     * @return \Doctrine\ORM\Tools\EntityRepositoryGenerator
493
     */
494
    protected function getRepositoryGenerator()
495
    {
496
        return new EntityRepositoryGenerator();
497
    }
498
499
    /**
500
     * @return \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator
501
     */
502
    protected function getSymfony4RepositoryGenerator()
503
    {
504
        return new \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator();
505
    }
506
507
    /**
508
     * @internal
509
     */
510 View Code Duplication
    protected function getTemplateDir(BundleInterface $bundle)
511
    {
512
        if ($this->isSymfony4()) {
513
            return $this->container->getParameter('kernel.project_dir') . '/templates';
514
        }
515
516
        return $bundle->getPath() . '/Resources/views';
517
    }
518
519
    /**
520
     * @internal
521
     */
522 View Code Duplication
    protected function getAssetsDir(BundleInterface $bundle)
523
    {
524
        if ($this->isSymfony4()) {
525
            return $this->container->getParameter('kernel.project_dir') . '/assets';
526
        }
527
528
        return $bundle->getPath() . '/Resources';
529
    }
530
531
    /**
532
     * @internal
533
     */
534
    protected function isSymfony4()
535
    {
536
        return Kernel::VERSION_ID >= 40000;
537
    }
538
}
539