Completed
Push — 5.3 ( 958546...1cc96e )
by Jeroen
14:02 queued 07:05
created

GeneratorBundle/Generator/AdminTestsGenerator.php (1 issue)

Labels
Severity

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 Kunstmaan\GeneratorBundle\Helper\GeneratorUtils;
6
use Sensio\Bundle\GeneratorBundle\Generator\Generator;
7
use Symfony\Component\Console\Output\OutputInterface;
8
use Symfony\Component\DependencyInjection\ContainerInterface;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\Finder\Finder;
11
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
12
use Symfony\Component\HttpKernel\Kernel;
13
14
/**
15
 * Generates tests to test the admin backend generated by the default-site generator
16
 */
17
class AdminTestsGenerator extends Generator
18
{
19
    /**
20
     * @var ContainerInterface
21
     */
22
    private $container;
23
24
    /**
25
     * @var Filesystem
26
     */
27
    private $filesystem;
28
29
    /**
30
     * @var string
31
     */
32
    private $fullSkeletonDir;
33
34
    /**
35
     * @param ContainerInterface $container   The container
36
     * @param Filesystem         $filesystem  The filesytem
37
     * @param string             $skeletonDir The skeleton directory
38
     */
39
    public function __construct(ContainerInterface $container, Filesystem $filesystem, $skeletonDir)
40
    {
41
        $this->container = $container;
42
        $this->filesystem = $filesystem;
43
        $this->fullSkeletonDir = GeneratorUtils::getFullSkeletonPath($skeletonDir);
44
    }
45
46
    /**
47
     * @param BundleInterface $bundle
48
     * @param OutputInterface $output
49
     */
50
    public function generate(BundleInterface $bundle, OutputInterface $output)
51
    {
52
        // This is needed so the renderFile method will search for the files
53
        // in the correct location
54
        $this->setSkeletonDirs(array($this->fullSkeletonDir));
55
56
        $parameters = array(
57
            'namespace' => $bundle->getNamespace(),
58
            'bundle' => $bundle,
59
            'isV4' => Kernel::VERSION_ID >= 40000,
60
        );
61
62
        $this->generateBehatTests($bundle, $output, $parameters);
63
    }
64
65
    /**
66
     * @param BundleInterface $bundle
67
     * @param OutputInterface $output
68
     * @param array           $parameters
69
     */
70
    public function generateBehatTests(BundleInterface $bundle, OutputInterface $output, array $parameters)
71
    {
72
        $dirPath = Kernel::VERSION_ID >= 40000 ? sprintf('%s/features', $this->container->getParameter('kernel.project_dir')) : sprintf('%s/Features', $bundle->getPath());
73
        $skeletonDir = sprintf('%s/Features', $this->fullSkeletonDir);
74
75
        // Copy all default feature files
76
        $featureFiles = (new Finder())
77
            ->files()
78
            ->in($skeletonDir)
79
            ->filter(function (\SplFileInfo $fileinfo) {
80
                return false !== strpos($fileinfo->getRelativePathName(), '.feature');
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class SplFileInfo as the method getRelativePathName() does only exist in the following sub-classes of SplFileInfo: Symfony\Component\Finder\SplFileInfo, Symfony\Component\Finder...terator\MockSplFileInfo. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
81
            })
82
            ->getIterator();
83
84
        foreach ($featureFiles as $file) {
85
            $this->filesystem->copy($file, $dirPath . '/' . $file->getFilename());
86
        }
87
88
        // Copy dummy media files used in scenarios
89
        $this->filesystem->mirror($skeletonDir . '/Media', $dirPath . '/bootstrap/Media');
90
91
        // Render the Context files to replace the namespace etc.
92
        if ($handle = opendir($skeletonDir . '/Context')) {
93
            $targetPath = Kernel::VERSION_ID >= 40000 ? $dirPath . '/bootstrap/' : $dirPath . '/Context/';
94
            while (false !== ($entry = readdir($handle))) {
95
                // Check to make sure we skip hidden folders
96
                // And we render the files ending in .php
97
                if (substr($entry, 0, 1) != '.' && substr($entry, -strlen('.php')) === '.php') {
98
                    $this->renderFile('/Features/Context/' . $entry, $targetPath . $entry, $parameters);
99
                }
100
            }
101
102
            closedir($handle);
103
        }
104
105
        // Replace admin password
106
        $contextPath = Kernel::VERSION_ID >= 40000 ? $dirPath . '/bootstrap/FeatureContext.php' : $dirPath . '/Context/FeatureContext.php';
107
        $featureContext = $contextPath . '/FeatureContext.php';
108
        if ($this->filesystem->exists($featureContext)) {
109
            $contents = file_get_contents($featureContext);
110
            $contents = str_replace(
111
                '-adminpwd-',
112
                $this->container->getParameter('kunstmaan_admin.admin_password'),
113
                $contents
114
            );
115
            file_put_contents($featureContext, $contents);
116
        }
117
118
        $output->writeln('Generating Behat Tests : <info>OK</info>');
119
    }
120
}
121