Completed
Push — master ( 06c1ce...67d37c )
by Jeroen
06:20
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\Common\Inflector\Inflector;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Doctrine\ORM\Mapping\UnderscoreNamingStrategy;
8
use Doctrine\ORM\Tools\EntityGenerator;
9
use Doctrine\ORM\Tools\EntityRepositoryGenerator;
10
use Kunstmaan\GeneratorBundle\Helper\CommandAssistant;
11
use Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
12
use Sensio\Bundle\GeneratorBundle\Generator\Generator;
13
use Symfony\Bridge\Doctrine\RegistryInterface;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
use Symfony\Component\Filesystem\Filesystem;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
18
use Symfony\Component\HttpKernel\Kernel;
19
use Twig\Environment;
20
use Twig\Lexer;
21
use Twig\Loader\FilesystemLoader;
22
23
/**
24
 * Class that contains all common generator logic.
25
 */
26
class KunstmaanGenerator extends Generator
27
{
28
    /**
29
     * @var Filesystem
30
     */
31
    protected $filesystem;
32
33
    /**
34
     * @var RegistryInterface
35
     */
36
    protected $registry;
37
38
    /**
39
     * @var string
40
     */
41
    protected $skeletonDir;
42
43
    /**
44
     * @var CommandAssistant
45
     */
46
    protected $assistant;
47
48
    /**
49
     * @var ContainerInterface
50
     */
51
    protected $container;
52
53
    /**
54
     * @param Filesystem         $filesystem  The filesystem
55
     * @param RegistryInterface  $registry    The registry
56
     * @param string             $skeletonDir The directory of the skeleton
57
     * @param CommandAssistant   $assistant   The command assistant
58
     * @param ContainerInterface $container   The container
59
     */
60 1
    public function __construct(
61
        Filesystem $filesystem,
62
        RegistryInterface $registry,
63
        $skeletonDir,
64
        CommandAssistant $assistant,
65
        ContainerInterface $container = null
66
    ) {
67 1
        $this->filesystem = $filesystem;
68 1
        $this->registry = $registry;
69 1
        $this->skeletonDir = GeneratorUtils::getFullSkeletonPath($skeletonDir);
70 1
        $this->assistant = $assistant;
71 1
        $this->container = $container;
72
73 1
        $this->setSkeletonDirs(array($this->skeletonDir, GeneratorUtils::getFullSkeletonPath('/common')));
74 1
    }
75
76
    /**
77
     * Check that the keyword is a reserved word for the database system.
78
     *
79
     * @param string $keyword
80
     *
81
     * @return bool
82
     */
83
    public function isReservedKeyword($keyword)
84
    {
85
        return $this->registry->getConnection()->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($keyword);
86
    }
87
88
    /**
89
     * Generate the entity PHP code.
90
     *
91
     * @param BundleInterface $bundle
92
     * @param string          $name
93
     * @param array           $fields
94
     * @param string          $namePrefix
95
     * @param string          $dbPrefix
96
     * @param string|null     $extendClass
97
     * @param bool            $withRepository
98
     *
99
     * @return array
100
     *
101
     * @throws \RuntimeException
102
     */
103
    protected function generateEntity(
104
        BundleInterface $bundle,
105
        $name,
106
        $fields,
107
        $namePrefix,
108
        $dbPrefix,
109
        $extendClass = null,
110
        $withRepository = false
111
    ) {
112
        // configure the bundle (needed if the bundle does not contain any Entities yet)
113
        $config = $this->registry->getManager(null)->getConfiguration();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\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...
114
        $config->setEntityNamespaces(
115
            array_merge(
116
                array($bundle->getName() => $bundle->getNamespace() . '\\Entity' . ($namePrefix ? '\\' . $namePrefix : '')),
117
                $config->getEntityNamespaces()
118
            )
119
        );
120
121
        $entityClass = $this->registry->getAliasNamespace($bundle->getName()) . ($namePrefix ? '\\' . $namePrefix : '') . '\\' . $name;
122
        $entityPath = $bundle->getPath() . '/Entity/' . ($namePrefix ? $namePrefix . '/' : '') . str_replace('\\', '/', $name) . '.php';
123
        if (file_exists($entityPath)) {
124
            throw new \RuntimeException(sprintf('Entity "%s" already exists.', $entityClass));
125
        }
126
127
        $class = new ClassMetadataInfo($entityClass, new UnderscoreNamingStrategy());
128
        if ($withRepository) {
129
            if ($this->isSymfony4()) {
130
                $repositoryClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1) . 'Repository';
131
                $class->customRepositoryClassName = $repositoryClass;
132
                $this->getSymfony4RepositoryGenerator()->writeEntityRepositoryClass($entityClass, $repositoryClass, $bundle->getPath());
133
            } else {
134
                $entityClass = preg_replace('/\\\\Entity\\\\/', '\\Repository\\', $entityClass, 1);
135
                $class->customRepositoryClassName = $entityClass.'Repository';
136
                $path = $bundle->getPath().str_repeat('/..', substr_count(get_class($bundle), '\\'));
137
                $this->getRepositoryGenerator()->writeEntityRepositoryClass($class->customRepositoryClassName, $path);
138
            }
139
        }
140
141
        foreach ($fields as $fieldSet) {
142
            foreach ($fieldSet as $fieldArray) {
143
                foreach ($fieldArray as $field) {
144
                    if (array_key_exists('joinColumn', $field)) {
145
                        $class->mapManyToOne($field);
146
                    } elseif (array_key_exists('joinTable', $field)) {
147
                        $class->mapManyToMany($field);
148
                    } else {
149
                        $class->mapField($field);
150
                    }
151
                }
152
            }
153
        }
154
        $class->setPrimaryTable(
155
            array(
156
                'name' => strtolower($dbPrefix) . Inflector::tableize(Inflector::pluralize($name)),
157
            )
158
        );
159
        $entityCode = $this->getEntityGenerator($extendClass)->generateEntityClass($class);
160
161
        return array($entityCode, $entityPath);
162
    }
163
164
    /**
165
     * Get a Doctrine EntityGenerator instance.
166
     *
167
     * @param string|null $classToExtend
168
     *
169
     * @return EntityGenerator
170
     */
171
    protected function getEntityGenerator($classToExtend = null)
172
    {
173
        $entityGenerator = new EntityGenerator();
174
        if (!is_null($classToExtend)) {
175
            $entityGenerator->setClassToExtend($classToExtend);
176
        }
177
        $entityGenerator->setGenerateAnnotations(true);
178
        $entityGenerator->setGenerateStubMethods(true);
179
        $entityGenerator->setRegenerateEntityIfExists(false);
180
        $entityGenerator->setUpdateEntityIfExists(true);
181
        $entityGenerator->setNumSpaces(4);
182
        $entityGenerator->setAnnotationPrefix('ORM\\');
183
184
        return $entityGenerator;
185
    }
186
187
    /**
188
     * Generate the entity admin type.
189
     *
190
     * @param        $bundle
191
     * @param        $entityName
192
     * @param        $entityPrefix
193
     * @param array  $fields
194
     * @param string $extendClass
195
     */
196
    protected function generateEntityAdminType(
197
        $bundle,
198
        $entityName,
199
        $entityPrefix,
200
        array $fields,
201
        $extendClass = '\Symfony\Component\Form\AbstractType'
202
    ) {
203
        $className = $entityName . 'AdminType';
204
        $savePath = $bundle->getPath() . '/Form/' . $entityPrefix . '/' . $className . '.php';
205
        $name = str_replace(
206
                '\\',
207
                '_',
208
                strtolower($bundle->getNamespace())
209
            ) . '_' . strtolower($entityName) . 'type';
210
211
        $params = array(
212
            'className' => $className,
213
            'name' => $name,
214
            'namespace' => $bundle->getNamespace(),
215
            'entity' => '\\' . $bundle->getNamespace() . '\Entity\\' . $entityPrefix . '\\' . $entityName,
216
            'fields' => $fields,
217
            'entity_prefix' => $entityPrefix,
218
            'extend_class' => $extendClass,
219
        );
220
        $this->renderFile('/Form/EntityAdminType.php', $savePath, $params);
221
    }
222
223
    /**
224
     * Install the default page templates.
225
     *
226
     * @param BundleInterface $bundle
227
     */
228
    protected function installDefaultPageTemplates($bundle)
229
    {
230
        // Configuration templates
231 View Code Duplication
        if ($this->isSymfony4()) {
232
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pagetemplates/';
233
        } else {
234
            $dirPath = sprintf('%s/Resources/config/pagetemplates/', $bundle->getPath());
235
        }
236
237
        $skeletonDir = sprintf('%s/Resources/config/pagetemplates/', GeneratorUtils::getFullSkeletonPath('/common'));
238
239
        // Only copy templates over when the folder does not exist yet...
240
        if (!$this->filesystem->exists($dirPath)) {
241
            $files = array(
242
                'default-one-column.yml',
243
                'default-two-column-left.yml',
244
                'default-two-column-right.yml',
245
                'default-three-column.yml',
246
            );
247
            foreach ($files as $file) {
248
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
249
                GeneratorUtils::replace('~~~BUNDLE~~~', $bundle->getName(), $dirPath . $file);
250
            }
251
        }
252
253
        // Twig templates
254
        $dirPath = $this->getTemplateDir($bundle) . '/Pages/Common/';
255
256
        $skeletonDir = sprintf('%s/Resources/views/Pages/Common/', GeneratorUtils::getFullSkeletonPath('/common'));
257
258
        if (!$this->filesystem->exists($dirPath)) {
259
            $files = array(
260
                'one-column-pagetemplate.html.twig',
261
                'two-column-left-pagetemplate.html.twig',
262
                'two-column-right-pagetemplate.html.twig',
263
                'three-column-pagetemplate.html.twig',
264
            );
265
            foreach ($files as $file) {
266
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
267
            }
268
            $this->filesystem->copy($skeletonDir . 'view.html.twig', $dirPath . 'view.html.twig', false);
269
        }
270
271
        $contents = file_get_contents($dirPath . 'view.html.twig');
272
273
        $twigFile = $this->isSymfony4() ?
274
            $twigFile = "{% extends 'Layout/layout.html.twig' %}\n" :
275
            $twigFile = "{% extends '".$bundle->getName().":Layout:layout.html.twig' %}\n"
276
        ;
277
278
        if (strpos($contents, '{% extends ') === false) {
279
            GeneratorUtils::prepend(
280
                $twigFile,
281
                $dirPath . 'view.html.twig'
282
            );
283
        }
284
    }
285
286
    /**
287
     * Install the default pagepart configuration.
288
     *
289
     * @param BundleInterface $bundle
290
     */
291
    protected function installDefaultPagePartConfiguration($bundle)
292
    {
293
        // Pagepart configuration
294 View Code Duplication
        if ($this->isSymfony4()) {
295
            $dirPath = $this->container->getParameter('kernel.project_dir') . '/config/kunstmaancms/pageparts/';
296
        } else {
297
            $dirPath = sprintf('%s/Resources/config/pageparts/', $bundle->getPath());
298
        }
299
300
        $skeletonDir = sprintf('%s/Resources/config/pageparts/', GeneratorUtils::getFullSkeletonPath('/common'));
301
302
        // Only copy when folder does not exist yet
303
        if (!$this->filesystem->exists($dirPath)) {
304
            $files = array('footer.yml', 'main.yml', 'left-sidebar.yml', 'right-sidebar.yml');
305
            foreach ($files as $file) {
306
                $this->filesystem->copy($skeletonDir . $file, $dirPath . $file, false);
307
            }
308
        }
309
    }
310
311
    /**
312
     * Render all files in the source directory and copy them to the target directory.
313
     *
314
     * @param string $sourceDir  The source directory where we need to look in
315
     * @param string $targetDir  The target directory where we need to copy the files too
316
     * @param array  $parameters The parameters that will be passed to the templates
317
     * @param bool   $override   Whether to override an existing file or not
318
     * @param bool   $recursive  Whether to render all files recursively or not
319
     */
320 1
    public function renderFiles($sourceDir, $targetDir, array $parameters, $override = false, $recursive = true)
321
    {
322
        // Make sure the source -and target dir contain a trailing slash
323 1
        $sourceDir = rtrim($sourceDir, '/') . '/';
324 1
        $targetDir = rtrim($targetDir, '/') . '/';
325
326 1
        $this->setSkeletonDirs(array($sourceDir));
327
328 1
        $finder = new Finder();
329 1
        $finder->files()->in($sourceDir);
330 1
        if (!$recursive) {
331
            $finder->depth('== 0');
332
        }
333
334
        // Get all files in the source directory
335 1
        foreach ($finder as $file) {
336 1
            $name = $file->getRelativePathname();
337
338
            // Check that we are allowed to overwrite the file if it already exists
339 1 View Code Duplication
            if (!is_file($targetDir . $name) || $override === true) {
340 1
                $fileParts = explode('.', $name);
341 1
                if (end($fileParts) === 'twig') {
342 1
                    $this->renderTwigFile($name, $targetDir . $name, $parameters, $sourceDir);
343
                } else {
344
                    $this->renderFile($name, $targetDir . $name, $parameters);
345
                }
346
            }
347
        }
348 1
    }
349
350
    /**
351
     * Render all files in the source directory and copy them to the target directory.
352
     *
353
     * @param string      $sourceDir      The source directory where we need to look in
354
     * @param string      $targetDir      The target directory where we need to copy the files too
355
     * @param string      $filename       The name of the file that needs to be rendered
356
     * @param array       $parameters     The parameters that will be passed to the templates
357
     * @param bool        $override       Whether to override an existing file or not
358
     * @param string|null $targetFilename The name of the target file (if null, then use $filename)
359
     */
360 1
    public function renderSingleFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $targetFilename = null)
361
    {
362
        // Make sure the source -and target dir contain a trailing slash
363 1
        $sourceDir = rtrim($sourceDir, '/') . '/';
364 1
        $targetDir = rtrim($targetDir, '/') . '/';
365 1
        if (is_null($targetFilename)) {
366 1
            $targetFilename = $filename;
367
        }
368
369 1
        $this->setSkeletonDirs(array($sourceDir));
370
371 1 View Code Duplication
        if (is_file($sourceDir . $filename)) {
372
            // Check that we are allowed the overwrite the file if it already exists
373 1
            if (!is_file($targetDir . $targetFilename) || $override === true) {
374 1
                $fileParts = explode('.', $filename);
375 1
                if (end($fileParts) === 'twig') {
376 1
                    $this->renderTwigFile($filename, $targetDir . $targetFilename, $parameters, $sourceDir);
377
                } else {
378 1
                    $this->renderFile($filename, $targetDir . $targetFilename, $parameters);
379
                }
380
            }
381
        }
382 1
    }
383
384
    /**
385
     * Render a file and make it executable.
386
     *
387
     * @param string $sourceDir  The source directory where we need to look in
388
     * @param string $targetDir  The target directory where we need to copy the files too
389
     * @param string $filename   The name of the file that needs to be rendered
390
     * @param array  $parameters The parameters that will be passed to the templates
391
     * @param bool   $override   Whether to override an existing file or not
392
     * @param int    $mode       The mode
393
     */
394
    public function renderExecutableFile($sourceDir, $targetDir, $filename, array $parameters, $override = false, $mode = 0774)
395
    {
396
        $this->renderSingleFile($sourceDir, $targetDir, $filename, $parameters, $override);
397
398
        $targetDir = rtrim($targetDir, '/') . '/';
399
        $targetFile = $targetDir . $filename;
400
        $this->filesystem->chmod($targetFile, $mode);
401
    }
402
403
    /**
404
     * Copy all files in the source directory to the target directory.
405
     *
406
     * @param string $sourceDir The source directory where we need to look in
407
     * @param string $targetDir The target directory where we need to copy the files too
408
     * @param bool   $override  Whether to override an existing file or not
409
     */
410
    public function copyFiles($sourceDir, $targetDir, $override = false)
411
    {
412
        // Make sure the source -and target dir contain a trailing slash
413
        $sourceDir = rtrim($sourceDir, '/') . '/';
414
        $targetDir = rtrim($targetDir, '/') . '/';
415
416
        $this->filesystem->mirror($sourceDir, $targetDir, null, array('override' => $override));
417
    }
418
419
    /**
420
     * Remove a directory from the filesystem.
421
     *
422
     * @param string $targetDir
423
     */
424
    public function removeDirectory($targetDir)
425
    {
426
        // Make sure the target dir contain a trailing slash
427
        $targetDir = rtrim($targetDir, '/') . '/';
428
429
        $this->filesystem->remove($targetDir);
430
    }
431
432
    /**
433
     * Remove a file from the filesystem.
434
     *
435
     * @param string $file
436
     */
437
    public function removeFile($file)
438
    {
439
        $this->filesystem->remove($file);
440
    }
441
442
    /**
443
     * Render a twig file with custom twig tags.
444
     *
445
     * @param string $template
446
     * @param array  $parameters
447
     * @param string $sourceDir
448
     *
449
     * @return string
450
     */
451 1
    public function renderTwig($template, array $parameters, $sourceDir)
452
    {
453 1
        $twig = new Environment(
454 1
            new FilesystemLoader(array($sourceDir)), array(
455 1
                'debug' => true,
456
                'cache' => false,
457
                'strict_variables' => true,
458
                'autoescape' => false,
459
            )
460
        );
461
462
        // Ruby erb template syntax
463 1
        $lexer = new Lexer(
464 1
            $twig, array(
465 1
                'tag_comment' => array('<%#', '%>'),
466
                'tag_block' => array('<%', '%>'),
467
                'tag_variable' => array('<%=', '%>'),
468
            )
469
        );
470
471 1
        $twig->setLexer($lexer);
472
473 1
        return $twig->render($template, $parameters);
474
    }
475
476
    /**
477
     * Render a twig file, and save it to disk.
478
     *
479
     * @param string $template
480
     * @param string $target
481
     * @param array  $parameters
482
     * @param string $sourceDir
483
     *
484
     * @return int
485
     */
486 1
    public function renderTwigFile($template, $target, array $parameters, $sourceDir)
487
    {
488 1
        if (!is_dir(dirname($target))) {
489 1
            mkdir(dirname($target), 0777, true);
490
        }
491
492 1
        return file_put_contents($target, $this->renderTwig($template, $parameters, $sourceDir));
493
    }
494
495
    /**
496
     * @return \Doctrine\ORM\Tools\EntityRepositoryGenerator
497
     */
498
    protected function getRepositoryGenerator()
499
    {
500
        return new EntityRepositoryGenerator();
501
    }
502
503
    /**
504
     * @return \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator
505
     */
506
    protected function getSymfony4RepositoryGenerator()
507
    {
508
        return new \Kunstmaan\GeneratorBundle\Generator\Symfony4EntityRepositoryGenerator();
509
    }
510
511
    /**
512
     * @internal
513
     */
514 1 View Code Duplication
    protected function getTemplateDir(BundleInterface $bundle)
515
    {
516 1
        if ($this->isSymfony4()) {
517 1
            return $this->container->getParameter('kernel.project_dir') . '/templates';
518
        }
519
520
        return $bundle->getPath() . '/Resources/views';
521
    }
522
523
    /**
524
     * @internal
525
     */
526 View Code Duplication
    protected function getAssetsDir(BundleInterface $bundle)
527
    {
528
        if ($this->isSymfony4()) {
529
            return $this->container->getParameter('kernel.project_dir') . '/assets';
530
        }
531
532
        return $bundle->getPath() . '/Resources';
533
    }
534
535
    /**
536
     * @internal
537
     */
538 1
    protected function isSymfony4()
539
    {
540 1
        return Kernel::VERSION_ID >= 40000;
541
    }
542
}
543