These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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\Component\Console\Application; |
||
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 = null) |
||
46 | { |
||
47 | if (null === $projectDir) { |
||
48 | @trigger_error(sprintf('Not passing the project directory to the constructor of %s is deprecated since Symfony 4.3 and will not be supported in 5.0.', __CLASS__), E_USER_DEPRECATED); |
||
49 | } |
||
50 | |||
51 | $this->filesystem = $filesystem; |
||
52 | $this->projectDir = $projectDir; |
||
53 | } |
||
54 | |||
55 | public function copySonataCoreBundleAssets(ConsoleTerminateEvent $event) |
||
56 | { |
||
57 | $command = $event->getCommand(); |
||
58 | $application = $command->getApplication(); |
||
59 | |||
60 | try { |
||
61 | $coreBundle = $application->getKernel()->getBundle('SonataCoreBundle'); |
||
62 | } catch (\Exception $e) { |
||
63 | $coreBundle = null; |
||
64 | } |
||
65 | |||
66 | if ('assets:install' !== $command->getName() || null !== $coreBundle) { |
||
67 | return; |
||
68 | } |
||
69 | |||
70 | $output = $event->getOutput(); |
||
0 ignored issues
–
show
|
|||
71 | |||
72 | $this->execute($event->getInput(), $event->getOutput(), $application); |
||
73 | } |
||
74 | |||
75 | protected function execute(InputInterface $input, OutputInterface $output, Application $application): int |
||
76 | { |
||
77 | /** |
||
78 | * @var KernelInterface |
||
79 | */ |
||
80 | $kernel = $application->getKernel(); |
||
0 ignored issues
–
show
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
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
![]() |
|||
81 | |||
82 | $targetArg = rtrim($input->getArgument('target') ?? '', '/'); |
||
83 | |||
84 | if (!$targetArg) { |
||
85 | $targetArg = $this->getPublicDirectory($kernel->getContainer()); |
||
86 | } |
||
87 | |||
88 | if (!is_dir($targetArg)) { |
||
89 | $targetArg = $kernel->getProjectDir().'/'.$targetArg; |
||
90 | |||
91 | if (!is_dir($targetArg)) { |
||
92 | throw new InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); |
||
93 | } |
||
94 | } |
||
95 | |||
96 | $bundlesDir = $targetArg.'/bundles/'; |
||
97 | |||
98 | $io = new SymfonyStyle($input, $output); |
||
99 | $io->newLine(); |
||
100 | |||
101 | if ($input->getOption('relative')) { |
||
102 | $expectedMethod = self::METHOD_RELATIVE_SYMLINK; |
||
103 | $io->text('Trying to install deprecated SonataCoreBundle assets from SonataAdminBundle as <info>relative symbolic links</info>.'); |
||
104 | } elseif ($input->getOption('symlink')) { |
||
105 | $expectedMethod = self::METHOD_ABSOLUTE_SYMLINK; |
||
106 | $io->text('Trying to install deprecated SonataCoreBundle assets from SonataAdminBundle as <info>absolute symbolic links</info>.'); |
||
107 | } else { |
||
108 | $expectedMethod = self::METHOD_COPY; |
||
109 | $io->text('Installing deprecated SonataCoreBundle assets from SonataAdminBundle as <info>hard copies</info>.'); |
||
110 | } |
||
111 | |||
112 | $io->newLine(); |
||
113 | |||
114 | $copyUsed = false; |
||
115 | $exitCode = 0; |
||
116 | $validAssetDirs = []; |
||
117 | |||
118 | $bundle = $kernel->getBundle('SonataAdminBundle'); |
||
119 | $originDir = $bundle->getPath().'/Resources/private/SonataCoreBundleAssets'; |
||
120 | |||
121 | $assetDir = preg_replace('/bundle$/', '', 'sonatacore'); |
||
122 | $targetDir = $bundlesDir.$assetDir; |
||
123 | $validAssetDirs[] = $assetDir; |
||
124 | |||
125 | if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { |
||
126 | $message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir); |
||
127 | } else { |
||
128 | $message = $bundle->getName(); |
||
129 | } |
||
130 | |||
131 | try { |
||
132 | $this->filesystem->remove($targetDir); |
||
133 | |||
134 | if (self::METHOD_RELATIVE_SYMLINK === $expectedMethod) { |
||
135 | $method = $this->relativeSymlinkWithFallback($originDir, $targetDir); |
||
136 | } elseif (self::METHOD_ABSOLUTE_SYMLINK === $expectedMethod) { |
||
137 | $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); |
||
138 | } else { |
||
139 | $method = $this->hardCopy($originDir, $targetDir); |
||
140 | } |
||
141 | |||
142 | if (self::METHOD_COPY === $method) { |
||
143 | $copyUsed = true; |
||
144 | } |
||
145 | |||
146 | if ($method === $expectedMethod) { |
||
147 | $ioMethod = 'success'; |
||
148 | } else { |
||
149 | $ioMethod = 'warning'; |
||
150 | } |
||
151 | } catch (\Exception $e) { |
||
152 | $exitCode = 1; |
||
153 | $ioMethod = 'error'; |
||
154 | } |
||
155 | |||
156 | if (0 !== $exitCode) { |
||
157 | $io->error('Some errors occurred while installing assets.'); |
||
158 | } else { |
||
159 | if ($copyUsed) { |
||
160 | $io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.'); |
||
161 | } |
||
162 | |||
163 | switch ($ioMethod) { |
||
164 | case 'success': |
||
165 | case 'warning':$io->$ioMethod('All deprecated SonataCoreBundle assets from SonataAdminBundle were successfully installed.'); break; |
||
166 | case 'error': |
||
167 | default: $io->$ioMethod('No deprecated SonataCoreBundle assets from SonataAdminBundle were provided by any bundle.'); break; |
||
168 | } |
||
169 | } |
||
170 | |||
171 | return $exitCode; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Try to create relative symlink. |
||
176 | * |
||
177 | * Falling back to absolute symlink and finally hard copy. |
||
178 | */ |
||
179 | private function relativeSymlinkWithFallback(string $originDir, string $targetDir): string |
||
180 | { |
||
181 | try { |
||
182 | $this->symlink($originDir, $targetDir, true); |
||
183 | $method = self::METHOD_RELATIVE_SYMLINK; |
||
184 | } catch (IOException $e) { |
||
185 | $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); |
||
186 | } |
||
187 | |||
188 | return $method; |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Try to create absolute symlink. |
||
193 | * |
||
194 | * Falling back to hard copy. |
||
195 | */ |
||
196 | private function absoluteSymlinkWithFallback(string $originDir, string $targetDir): string |
||
197 | { |
||
198 | try { |
||
199 | $this->symlink($originDir, $targetDir); |
||
200 | $method = self::METHOD_ABSOLUTE_SYMLINK; |
||
201 | } catch (IOException $e) { |
||
202 | // fall back to copy |
||
203 | $method = $this->hardCopy($originDir, $targetDir); |
||
204 | } |
||
205 | |||
206 | return $method; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Creates symbolic link. |
||
211 | * |
||
212 | * @throws IOException if link can not be created |
||
213 | */ |
||
214 | private function symlink(string $originDir, string $targetDir, bool $relative = false) |
||
215 | { |
||
216 | if ($relative) { |
||
217 | $this->filesystem->mkdir(\dirname($targetDir)); |
||
218 | $originDir = $this->filesystem->makePathRelative($originDir, realpath(\dirname($targetDir))); |
||
219 | } |
||
220 | $this->filesystem->symlink($originDir, $targetDir); |
||
221 | if (!file_exists($targetDir)) { |
||
222 | throw new IOException(sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Copies origin to target. |
||
228 | */ |
||
229 | private function hardCopy(string $originDir, string $targetDir): string |
||
230 | { |
||
231 | $this->filesystem->mkdir($targetDir, 0777); |
||
232 | // We use a custom iterator to ignore VCS files |
||
233 | $this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir)); |
||
234 | |||
235 | return self::METHOD_COPY; |
||
236 | } |
||
237 | |||
238 | private function getPublicDirectory(ContainerInterface $container): string |
||
239 | { |
||
240 | $defaultPublicDir = 'public'; |
||
241 | |||
242 | if (null === $this->projectDir && !$container->hasParameter('kernel.project_dir')) { |
||
243 | return $defaultPublicDir; |
||
244 | } |
||
245 | |||
246 | $composerFilePath = ($this->projectDir ?? $container->getParameter('kernel.project_dir')).'/composer.json'; |
||
247 | |||
248 | if (!file_exists($composerFilePath)) { |
||
249 | return $defaultPublicDir; |
||
250 | } |
||
251 | |||
252 | $composerConfig = json_decode(file_get_contents($composerFilePath), true); |
||
253 | |||
254 | if (isset($composerConfig['extra']['public-dir'])) { |
||
255 | return $composerConfig['extra']['public-dir']; |
||
256 | } |
||
257 | |||
258 | return $defaultPublicDir; |
||
259 | } |
||
260 | } |
||
261 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
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.