AssetsInstallCommandListener::execute()   F
last analyzed

Complexity

Conditions 17
Paths 3458

Size

Total Lines 101

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 101
rs 0.8399
c 0
b 0
f 0
cc 17
nc 3458
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\EventListener;
15
16
use Symfony\Bundle\FrameworkBundle\Console\Application as FrameworkApplication;
17
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
18
use Symfony\Component\Console\Exception\InvalidArgumentException;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Style\SymfonyStyle;
22
use Symfony\Component\DependencyInjection\ContainerInterface;
23
use Symfony\Component\Filesystem\Exception\IOException;
24
use Symfony\Component\Filesystem\Filesystem;
25
use Symfony\Component\Finder\Finder;
26
use Symfony\Component\HttpKernel\KernelInterface;
27
28
/**
29
 * This listener extends `assets:install` command when SonataCoreBundle will be not register. Files from `Resources/private/SonataCoreBundleAssets`
30
 * will be copy with the same result like SonataCoreBundle is register.
31
 *
32
 * This class should be remove when support for Bootstrap 3 will be ended or assets system will be remove in favor for encore webpack.
33
 */
34
final class AssetsInstallCommandListener
35
{
36
    public const METHOD_COPY = 'copy';
37
    public const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink';
38
    public const METHOD_RELATIVE_SYMLINK = 'relative symlink';
39
40
    protected static $defaultName = 'assets:install';
41
42
    private $filesystem;
43
    private $projectDir;
44
45
    public function __construct(Filesystem $filesystem, string $projectDir)
46
    {
47
        $this->filesystem = $filesystem;
48
        $this->projectDir = $projectDir;
49
    }
50
51
    public function copySonataCoreBundleAssets(ConsoleTerminateEvent $event)
52
    {
53
        $command = $event->getCommand();
54
        $application = $command->getApplication();
55
        \assert($application instanceof FrameworkApplication);
56
57
        try {
58
            $coreBundle = $application->getKernel()->getBundle('SonataCoreBundle');
59
        } catch (\Exception $e) {
60
            $coreBundle = null;
61
        }
62
63
        if ('assets:install' !== $command->getName() || null !== $coreBundle) {
64
            return;
65
        }
66
67
        $this->execute($event->getInput(), $event->getOutput(), $application);
68
    }
69
70
    protected function execute(InputInterface $input, OutputInterface $output, FrameworkApplication $application): int
71
    {
72
        /**
73
         * @var KernelInterface
74
         */
75
        $kernel = $application->getKernel();
76
77
        $targetArg = rtrim($input->getArgument('target') ?? '', '/');
78
79
        if (!$targetArg) {
80
            $targetArg = $this->getPublicDirectory($kernel->getContainer());
81
        }
82
83
        if (!is_dir($targetArg)) {
84
            $targetArg = $kernel->getProjectDir().'/'.$targetArg;
85
86
            if (!is_dir($targetArg)) {
87
                throw new InvalidArgumentException(sprintf(
88
                    'The target directory "%s" does not exist.',
89
                    $input->getArgument('target')
90
                ));
91
            }
92
        }
93
94
        $bundlesDir = $targetArg.'/bundles/';
95
96
        $io = new SymfonyStyle($input, $output);
97
        $io->newLine();
98
99
        if ($input->getOption('relative')) {
100
            $expectedMethod = self::METHOD_RELATIVE_SYMLINK;
101
            $io->text('Trying to install deprecated SonataCoreBundle assets from SonataAdminBundle as <info>relative symbolic links</info>.');
102
        } elseif ($input->getOption('symlink')) {
103
            $expectedMethod = self::METHOD_ABSOLUTE_SYMLINK;
104
            $io->text('Trying to install deprecated SonataCoreBundle assets from SonataAdminBundle as <info>absolute symbolic links</info>.');
105
        } else {
106
            $expectedMethod = self::METHOD_COPY;
107
            $io->text('Installing deprecated SonataCoreBundle assets from SonataAdminBundle as <info>hard copies</info>.');
108
        }
109
110
        $io->newLine();
111
112
        $copyUsed = false;
113
        $exitCode = 0;
114
        $validAssetDirs = [];
115
116
        $bundle = $kernel->getBundle('SonataAdminBundle');
117
        $originDir = $bundle->getPath().'/Resources/private/SonataCoreBundleAssets';
118
119
        $assetDir = preg_replace('/bundle$/', '', 'sonatacore');
120
        $targetDir = $bundlesDir.$assetDir;
121
        $validAssetDirs[] = $assetDir;
122
123
        if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
124
            $message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir);
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
125
        } else {
126
            $message = $bundle->getName();
0 ignored issues
show
Unused Code introduced by
$message is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
127
        }
128
129
        try {
130
            $this->filesystem->remove($targetDir);
131
132
            if (self::METHOD_RELATIVE_SYMLINK === $expectedMethod) {
133
                $method = $this->relativeSymlinkWithFallback($originDir, $targetDir);
134
            } elseif (self::METHOD_ABSOLUTE_SYMLINK === $expectedMethod) {
135
                $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir);
136
            } else {
137
                $method = $this->hardCopy($originDir, $targetDir);
138
            }
139
140
            if (self::METHOD_COPY === $method) {
141
                $copyUsed = true;
142
            }
143
144
            if ($method === $expectedMethod) {
145
                $ioMethod = 'success';
146
            } else {
147
                $ioMethod = 'warning';
148
            }
149
        } catch (\Exception $e) {
150
            $exitCode = 1;
151
            $ioMethod = 'error';
152
        }
153
154
        if (0 !== $exitCode) {
155
            $io->error('Some errors occurred while installing assets.');
156
        } else {
157
            if ($copyUsed) {
158
                $io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.');
159
            }
160
161
            switch ($ioMethod) {
162
                case 'success':
163
                case 'warning':$io->$ioMethod('All deprecated SonataCoreBundle assets from SonataAdminBundle were successfully installed.'); break;
164
                case 'error':
165
                default: $io->$ioMethod('No deprecated SonataCoreBundle assets from SonataAdminBundle were provided by any bundle.'); break;
166
            }
167
        }
168
169
        return $exitCode;
170
    }
171
172
    /**
173
     * Try to create relative symlink.
174
     *
175
     * Falling back to absolute symlink and finally hard copy.
176
     */
177
    private function relativeSymlinkWithFallback(string $originDir, string $targetDir): string
178
    {
179
        try {
180
            $this->symlink($originDir, $targetDir, true);
181
            $method = self::METHOD_RELATIVE_SYMLINK;
182
        } catch (IOException $e) {
183
            $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir);
184
        }
185
186
        return $method;
187
    }
188
189
    /**
190
     * Try to create absolute symlink.
191
     *
192
     * Falling back to hard copy.
193
     */
194
    private function absoluteSymlinkWithFallback(string $originDir, string $targetDir): string
195
    {
196
        try {
197
            $this->symlink($originDir, $targetDir);
198
            $method = self::METHOD_ABSOLUTE_SYMLINK;
199
        } catch (IOException $e) {
200
            // fall back to copy
201
            $method = $this->hardCopy($originDir, $targetDir);
202
        }
203
204
        return $method;
205
    }
206
207
    /**
208
     * Creates symbolic link.
209
     *
210
     * @throws IOException if link can not be created
211
     */
212
    private function symlink(string $originDir, string $targetDir, bool $relative = false)
213
    {
214
        if ($relative) {
215
            $this->filesystem->mkdir(\dirname($targetDir));
216
            $originDir = $this->filesystem->makePathRelative($originDir, realpath(\dirname($targetDir)));
217
        }
218
        $this->filesystem->symlink($originDir, $targetDir);
219
        if (!file_exists($targetDir)) {
220
            throw new IOException(sprintf(
221
                'Symbolic link "%s" was created but appears to be broken.',
222
                $targetDir
223
            ), 0, null, $targetDir);
224
        }
225
    }
226
227
    /**
228
     * Copies origin to target.
229
     */
230
    private function hardCopy(string $originDir, string $targetDir): string
231
    {
232
        $this->filesystem->mkdir($targetDir, 0777);
233
        // We use a custom iterator to ignore VCS files
234
        $this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir));
235
236
        return self::METHOD_COPY;
237
    }
238
239
    private function getPublicDirectory(ContainerInterface $container): string
240
    {
241
        $defaultPublicDir = 'public';
242
243
        if (null === $this->projectDir && !$container->hasParameter('kernel.project_dir')) {
244
            return $defaultPublicDir;
245
        }
246
247
        $composerFilePath = ($this->projectDir ?? $container->getParameter('kernel.project_dir')).'/composer.json';
248
249
        if (!file_exists($composerFilePath)) {
250
            return $defaultPublicDir;
251
        }
252
253
        $composerConfig = json_decode(file_get_contents($composerFilePath), true);
254
255
        if (isset($composerConfig['extra']['public-dir'])) {
256
            return $composerConfig['extra']['public-dir'];
257
        }
258
259
        return $defaultPublicDir;
260
    }
261
}
262