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