LoadDataFixturesCommand::processFixtures()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 9.4285
cc 1
eloc 10
nc 1
nop 3
1
<?php
2
3
namespace RDV\Bundle\MigrationBundle\Command;
4
5
use RDV\Bundle\MigrationBundle\Migration\Loader\DataFixturesLoader;
6
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
11
12
class LoadDataFixturesCommand extends ContainerAwareCommand
13
{
14
    /**
15
     * {@inheritdoc}
16
     */
17
    protected function configure()
18
    {
19
        $this->setName('rdv:migration:data:load')
20
            ->setDescription('Load data fixtures.')
21
            ->addOption(
22
                'fixtures-type',
23
                null,
24
                InputOption::VALUE_OPTIONAL,
25
                sprintf(
26
                    'Select fixtures type to be loaded (%s or %s). By default - %s',
27
                    DataFixturesLoader::MAIN_FIXTURES_TYPE,
28
                    DataFixturesLoader::DEMO_FIXTURES_TYPE,
29
                    DataFixturesLoader::MAIN_FIXTURES_TYPE
30
                ),
31
                DataFixturesLoader::MAIN_FIXTURES_TYPE
32
            )
33
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs list of fixtures without apply them')
34
            ->addOption(
35
                'bundles',
36
                null,
37
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
38
                'A list of bundle names to load data from'
39
            )
40
            ->addOption(
41
                'exclude',
42
                null,
43
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
44
                'A list of bundle names which fixtures should be skipped'
45
            );
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    protected function execute(InputInterface $input, OutputInterface $output)
52
    {
53
        $fixtures = null;
54
        try {
55
            $fixtures = $this->getFixtures($input);
56
        } catch (\RuntimeException $ex) {
57
            $output->writeln('');
58
            $output->writeln(sprintf('<error>%s</error>', $ex->getMessage()));
59
60
            return $ex->getCode() == 0 ? 1 : $ex->getCode();
61
        }
62
63
        if (!empty($fixtures)) {
64
            if ($input->getOption('dry-run')) {
65
                $this->outputFixtures($input, $output, $fixtures);
66
            } else {
67
                $this->processFixtures($input, $output, $fixtures);
68
            }
69
        }
70
71
        return 0;
72
    }
73
74
    /**
75
     * @param InputInterface  $input
76
     * @return array
77
     * @throws \RuntimeException if loading of data fixtures should be terminated
78
     */
79
    protected function getFixtures(InputInterface $input)
80
    {
81
        /** @var DataFixturesLoader $loader */
82
        $loader              = $this->getContainer()->get('rdv_migration.data_fixtures.loader');
83
        $bundles             = $input->getOption('bundles');
84
        $excludeBundles      = $input->getOption('exclude');
85
        $fixtureRelativePath = $this->getFixtureRelativePath($input);
86
87
        /** @var BundleInterface $bundle */
88
        foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Console\Application as the method getKernel() does only exist in the following sub-classes of Symfony\Component\Console\Application: Symfony\Bundle\FrameworkBundle\Console\Application. 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...
89
            if (!empty($bundles) && !in_array($bundle->getName(), $bundles)) {
90
                continue;
91
            }
92
            if (!empty($excludeBundles) && in_array($bundle->getName(), $excludeBundles)) {
93
                continue;
94
            }
95
            $path = $bundle->getPath() . $fixtureRelativePath;
96
            if (is_dir($path)) {
97
                $loader->loadFromDirectory($path);
98
            }
99
        }
100
101
        return $loader->getFixtures();
102
    }
103
104
    /**
105
     * Output list of fixtures
106
     *
107
     * @param InputInterface  $input
108
     * @param OutputInterface $output
109
     * @param array           $fixtures
110
     */
111
    protected function outputFixtures(InputInterface $input, OutputInterface $output, $fixtures)
112
    {
113
        $output->writeln(
114
            sprintf(
115
                'List of "%s" data fixtures ...',
116
                $this->getTypeOfFixtures($input)
117
            )
118
        );
119
        foreach ($fixtures as $fixture) {
120
            $output->writeln(sprintf('  <comment>></comment> <info>%s</info>', get_class($fixture)));
121
        }
122
    }
123
124
    /**
125
     * Process fixtures
126
     *
127
     * @param InputInterface  $input
128
     * @param OutputInterface $output
129
     * @param array           $fixtures
130
     */
131
    protected function processFixtures(InputInterface $input, OutputInterface $output, $fixtures)
132
    {
133
        $output->writeln(
134
            sprintf(
135
                'Loading "%s" data fixtures ...',
136
                $this->getTypeOfFixtures($input)
137
            )
138
        );
139
140
        $executor = $this->getContainer()->get('rdv_migration.data_fixtures.executor');
141
        $executor->setLogger(
142
            function ($message) use ($output) {
143
                $output->writeln(sprintf('  <comment>></comment> <info>%s</info>', $message));
144
            }
145
        );
146
        $executor->execute($fixtures, true);
147
    }
148
149
    /**
150
     * @param InputInterface $input
151
     * @return string
152
     */
153
    protected function getTypeOfFixtures(InputInterface $input)
154
    {
155
        return $input->getOption('fixtures-type');
156
    }
157
158
    /**
159
     * @param InputInterface $input
160
     * @return string
161
     */
162
    protected function getFixtureRelativePath(InputInterface $input)
163
    {
164
        switch ($this->getTypeOfFixtures($input)) {
165
            case DataFixturesLoader::MAIN_FIXTURES_TYPE:
166
                $path = $this->getContainer()->getParameter('rdv_migration.fixtures_path_main');
167
                break;
168
            case DataFixturesLoader::DEMO_FIXTURES_TYPE:
169
                $path = $this->getContainer()->getParameter('rdv_migration.fixtures_path_main');
170
                break;
171
            default:
172
                throw new \InvalidArgumentException('Unknown fixture type');
173
        }
174
175
        return str_replace('/', DIRECTORY_SEPARATOR, '/' . $path);
176
    }
177
}
178