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