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

GeneratorBundle/Generator/AdminTestsGenerator.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 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