These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of EC-CUBE |
||
5 | * |
||
6 | * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved. |
||
7 | * |
||
8 | * http://www.ec-cube.co.jp/ |
||
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 Eccube\Command; |
||
15 | |||
16 | use Symfony\Component\Console\Command\Command; |
||
17 | use Symfony\Component\Console\Exception\InvalidArgumentException; |
||
18 | use Symfony\Component\Console\Input\InputInterface; |
||
19 | use Symfony\Component\Console\Input\InputOption; |
||
20 | use Symfony\Component\Console\Output\OutputInterface; |
||
21 | use Symfony\Component\Console\Style\SymfonyStyle; |
||
22 | use Symfony\Component\DependencyInjection\Container; |
||
23 | use Symfony\Component\DependencyInjection\ContainerInterface; |
||
24 | use Symfony\Component\Filesystem\Filesystem; |
||
25 | |||
26 | class PluginGenerateCommand extends Command |
||
27 | { |
||
28 | protected static $defaultName = 'eccube:plugin:generate'; |
||
29 | |||
30 | /** |
||
31 | * @var SymfonyStyle |
||
32 | */ |
||
33 | protected $io; |
||
34 | |||
35 | /** |
||
36 | * @var Filesystem |
||
37 | */ |
||
38 | protected $fs; |
||
39 | |||
40 | /** |
||
41 | * @var ContainerInterface |
||
42 | */ |
||
43 | protected $container; |
||
44 | |||
45 | public function __construct(ContainerInterface $container) |
||
46 | { |
||
47 | parent::__construct(); |
||
48 | $this->container = $container; |
||
49 | } |
||
50 | |||
51 | protected function configure() |
||
52 | { |
||
53 | $this |
||
54 | ->addArgument('name', InputOption::VALUE_REQUIRED, 'plugin name') |
||
55 | ->addArgument('code', InputOption::VALUE_REQUIRED, 'plugin code') |
||
56 | ->addArgument('ver', InputOption::VALUE_REQUIRED, 'plugin version') |
||
57 | ->setDescription('Generate plugin skeleton.'); |
||
58 | } |
||
59 | |||
60 | protected function initialize(InputInterface $input, OutputInterface $output) |
||
61 | { |
||
62 | $this->io = new SymfonyStyle($input, $output); |
||
63 | $this->fs = new Filesystem(); |
||
64 | } |
||
65 | |||
66 | protected function interact(InputInterface $input, OutputInterface $output) |
||
67 | { |
||
68 | if (null !== $input->getArgument('name') && null !== $input->getArgument('code') && null !== $input->getArgument('ver')) { |
||
69 | return; |
||
70 | } |
||
71 | |||
72 | $this->io->title('EC-CUBE Plugin Generator Interactive Wizard'); |
||
73 | |||
74 | // Plugin name. |
||
75 | $name = $input->getArgument('name'); |
||
76 | View Code Duplication | if (null !== $name) { |
|
77 | $this->io->text(' > <info>name</info>: '.$name); |
||
78 | } else { |
||
79 | $name = $this->io->ask('name', 'EC-CUBE Sample Plugin'); |
||
80 | $input->setArgument('name', $name); |
||
81 | } |
||
82 | |||
83 | // Plugin code. |
||
84 | $code = $input->getArgument('code'); |
||
85 | View Code Duplication | if (null !== $code) { |
|
86 | $this->io->text(' > <info>code</info>: '.$code); |
||
87 | } else { |
||
88 | $code = $this->io->ask('code', 'Sample', [$this, 'validateCode']); |
||
89 | $input->setArgument('code', $code); |
||
90 | } |
||
91 | |||
92 | // Plugin version. |
||
93 | $version = $input->getArgument('ver'); |
||
94 | View Code Duplication | if (null !== $version) { |
|
95 | $this->io->text(' > <info>ver</info>: '.$version); |
||
96 | } else { |
||
97 | $version = $this->io->ask('ver', '1.0.0', [$this, 'validateVersion']); |
||
98 | $input->setArgument('ver', $version); |
||
99 | } |
||
100 | } |
||
101 | |||
102 | protected function execute(InputInterface $input, OutputInterface $output) |
||
103 | { |
||
104 | $name = $input->getArgument('name'); |
||
105 | $code = $input->getArgument('code'); |
||
106 | $version = $input->getArgument('ver'); |
||
107 | |||
108 | $this->validateCode($code); |
||
109 | $this->validateVersion($version); |
||
0 ignored issues
–
show
|
|||
110 | |||
111 | $pluginDir = $this->container->getParameter('kernel.project_dir').'/app/Plugin/'.$code; |
||
112 | |||
113 | $this->createDirectories($pluginDir); |
||
114 | $this->createConfig($pluginDir, $name, $code, $version); |
||
115 | $this->createEvent($pluginDir, $code); |
||
116 | $this->createMessages($pluginDir); |
||
117 | $this->createNav($pluginDir, $code); |
||
118 | $this->createTwigBlock($pluginDir, $code); |
||
119 | $this->createConfigController($pluginDir, $code); |
||
120 | |||
121 | $this->io->success(sprintf('Plugin was successfully created: %s %s %s', $name, $code, $version)); |
||
122 | } |
||
123 | |||
124 | public function validateCode($code) |
||
125 | { |
||
126 | if (empty($code)) { |
||
127 | throw new InvalidArgumentException('The code can not be empty.'); |
||
128 | } |
||
129 | if (strlen($code) > 255) { |
||
130 | throw new InvalidArgumentException('The code can enter up to 255 characters'); |
||
131 | } |
||
132 | if (1 !== preg_match('/^\w+$/', $code)) { |
||
133 | throw new InvalidArgumentException('The code [a-zA-Z_] is available.'); |
||
134 | } |
||
135 | |||
136 | $pluginDir = $this->container->getParameter('kernel.project_dir').'/app/Plugin/'.$code; |
||
137 | if (file_exists($pluginDir)) { |
||
138 | throw new InvalidArgumentException('Plugin directory exists.'); |
||
139 | } |
||
140 | |||
141 | return $code; |
||
142 | } |
||
143 | |||
144 | public function validateVersion($version) |
||
145 | { |
||
146 | // TODO |
||
147 | return $version; |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * @param string $pluginDir |
||
152 | */ |
||
153 | protected function createDirectories($pluginDir) |
||
154 | { |
||
155 | $dirs = [ |
||
156 | 'Controller/Admin', |
||
157 | 'Entity', |
||
158 | 'Repository', |
||
159 | 'Form/Type', |
||
160 | 'Form/Extension', |
||
161 | 'Resource/doctrine', |
||
162 | 'Resource/locale', |
||
163 | 'Resource/template/admin', |
||
164 | ]; |
||
165 | |||
166 | foreach ($dirs as $dir) { |
||
167 | $this->fs->mkdir($pluginDir.'/'.$dir); |
||
168 | } |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * @param string $pluginDir |
||
173 | */ |
||
174 | protected function createConfig($pluginDir, $name, $code, $version) |
||
175 | { |
||
176 | $source = <<<EOL |
||
177 | { |
||
178 | "name": "ec-cube/$code", |
||
179 | "version": "$version", |
||
180 | "description": "$name", |
||
181 | "type": "eccube-plugin", |
||
182 | "require": { |
||
183 | "ec-cube/plugin-installer": "~0.0.7" |
||
184 | }, |
||
185 | "extra": { |
||
186 | "code": "$code" |
||
187 | } |
||
188 | } |
||
189 | EOL; |
||
190 | |||
191 | $this->fs->dumpFile($pluginDir.'/composer.json', $source); |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * @param string $pluginDir |
||
196 | */ |
||
197 | protected function createMessages($pluginDir) |
||
198 | { |
||
199 | $this->fs->dumpFile($pluginDir.'/Resource/locale/messages.ja.yaml', ''); |
||
200 | $this->fs->dumpFile($pluginDir.'/Resource/locale/validators.ja.yaml', ''); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @param string $pluginDir |
||
205 | */ |
||
206 | protected function createTwigBlock($pluginDir, $code) |
||
207 | { |
||
208 | $source = <<<EOL |
||
209 | <?php |
||
210 | |||
211 | namespace Plugin\\${code}; |
||
212 | |||
213 | use Eccube\\Common\\EccubeTwigBlock; |
||
214 | |||
215 | class TwigBlock implements EccubeTwigBlock |
||
216 | { |
||
217 | /** |
||
218 | * @return array |
||
219 | */ |
||
220 | public static function getTwigBlock() |
||
221 | { |
||
222 | return []; |
||
223 | } |
||
224 | } |
||
225 | |||
226 | EOL; |
||
227 | $this->fs->dumpFile($pluginDir.'/TwigBlock.php', $source); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * @param string $pluginDir |
||
232 | */ |
||
233 | protected function createNav($pluginDir, $code) |
||
234 | { |
||
235 | $source = <<<EOL |
||
236 | <?php |
||
237 | |||
238 | namespace Plugin\\${code}; |
||
239 | |||
240 | use Eccube\\Common\\EccubeNav; |
||
241 | |||
242 | class Nav implements EccubeNav |
||
243 | { |
||
244 | /** |
||
245 | * @return array |
||
246 | */ |
||
247 | public static function getNav() |
||
248 | { |
||
249 | return []; |
||
250 | } |
||
251 | } |
||
252 | |||
253 | EOL; |
||
254 | $this->fs->dumpFile($pluginDir.'/Nav.php', $source); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * @param string $pluginDir |
||
259 | */ |
||
260 | protected function createEvent($pluginDir, $code) |
||
261 | { |
||
262 | $source = <<<EOL |
||
263 | <?php |
||
264 | |||
265 | namespace Plugin\\${code}; |
||
266 | |||
267 | use Symfony\\Component\\EventDispatcher\\EventSubscriberInterface; |
||
268 | |||
269 | class Event implements EventSubscriberInterface |
||
270 | { |
||
271 | /** |
||
272 | * @return array |
||
273 | */ |
||
274 | public static function getSubscribedEvents() |
||
275 | { |
||
276 | return []; |
||
277 | } |
||
278 | } |
||
279 | |||
280 | EOL; |
||
281 | $this->fs->dumpFile($pluginDir.'/Event.php', $source); |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * @param string $pluginDir |
||
286 | */ |
||
287 | protected function createConfigController($pluginDir, $code) |
||
288 | { |
||
289 | $snakecased = Container::underscore($code); |
||
290 | |||
291 | $source = <<<EOL |
||
292 | <?php |
||
293 | |||
294 | namespace Plugin\\${code}\\Controller\\Admin; |
||
295 | |||
296 | use Eccube\\Controller\\AbstractController; |
||
297 | use Plugin\\${code}\\Form\\Type\\Admin\\ConfigType; |
||
298 | use Plugin\\${code}\\Repository\\ConfigRepository; |
||
299 | use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template; |
||
300 | use Symfony\\Component\\HttpFoundation\\Request; |
||
301 | use Symfony\\Component\\Routing\\Annotation\\Route; |
||
302 | |||
303 | class ConfigController extends AbstractController |
||
304 | { |
||
305 | /** |
||
306 | * @var ConfigRepository |
||
307 | */ |
||
308 | protected \$configRepository; |
||
309 | |||
310 | /** |
||
311 | * ConfigController constructor. |
||
312 | * |
||
313 | * @param ConfigRepository \$configRepository |
||
314 | */ |
||
315 | public function __construct(ConfigRepository \$configRepository) |
||
316 | { |
||
317 | \$this->configRepository = \$configRepository; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @Route("/%eccube_admin_route%/${snakecased}/config", name="${snakecased}_admin_config") |
||
322 | * @Template("@${code}/admin/config.twig") |
||
323 | */ |
||
324 | public function index(Request \$request) |
||
325 | { |
||
326 | \$Config = \$this->configRepository->get(); |
||
327 | \$form = \$this->createForm(ConfigType::class, \$Config); |
||
328 | \$form->handleRequest(\$request); |
||
329 | |||
330 | if (\$form->isSubmitted() && \$form->isValid()) { |
||
331 | \$Config = \$form->getData(); |
||
332 | \$this->entityManager->persist(\$Config); |
||
333 | \$this->entityManager->flush(\$Config); |
||
334 | \$this->addSuccess('登録しました。', 'admin'); |
||
335 | |||
336 | return \$this->redirectToRoute('${snakecased}_admin_config'); |
||
337 | } |
||
338 | |||
339 | return [ |
||
340 | 'form' => \$form->createView(), |
||
341 | ]; |
||
342 | } |
||
343 | } |
||
344 | |||
345 | EOL; |
||
346 | |||
347 | $this->fs->dumpFile($pluginDir.'/Controller/Admin/ConfigController.php', $source); |
||
348 | |||
349 | $source = <<<EOL |
||
350 | <?php |
||
351 | |||
352 | namespace Plugin\\${code}\\Entity; |
||
353 | |||
354 | use Doctrine\\ORM\\Mapping as ORM; |
||
355 | |||
356 | /** |
||
357 | * Config |
||
358 | * |
||
359 | * @ORM\Table(name="plg_${snakecased}_config") |
||
360 | * @ORM\Entity(repositoryClass="Plugin\\${code}\\Repository\\ConfigRepository") |
||
361 | */ |
||
362 | class Config |
||
363 | { |
||
364 | /** |
||
365 | * @var int |
||
366 | * |
||
367 | * @ORM\Column(name="id", type="integer", options={"unsigned":true}) |
||
368 | * @ORM\Id |
||
369 | * @ORM\GeneratedValue(strategy="IDENTITY") |
||
370 | */ |
||
371 | private \$id; |
||
372 | |||
373 | /** |
||
374 | * @var string |
||
375 | * |
||
376 | * @ORM\Column(name="name", type="string", length=255) |
||
377 | */ |
||
378 | private \$name; |
||
379 | |||
380 | /** |
||
381 | * @return int |
||
382 | */ |
||
383 | public function getId() |
||
384 | { |
||
385 | return \$this->id; |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * @return string |
||
390 | */ |
||
391 | public function getName() |
||
392 | { |
||
393 | return \$this->name; |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * @param string \$name |
||
398 | * |
||
399 | * @return \$this; |
||
400 | */ |
||
401 | public function setName(\$name) |
||
402 | { |
||
403 | \$this->name = \$name; |
||
404 | |||
405 | return \$this; |
||
406 | } |
||
407 | } |
||
408 | |||
409 | EOL; |
||
410 | |||
411 | $this->fs->dumpFile($pluginDir.'/Entity/Config.php', $source); |
||
412 | |||
413 | $source = <<<EOL |
||
414 | <?php |
||
415 | |||
416 | namespace Plugin\\${code}\\Repository; |
||
417 | |||
418 | use Eccube\\Repository\\AbstractRepository; |
||
419 | use Plugin\\${code}\\Entity\\Config; |
||
420 | use Symfony\\Bridge\\Doctrine\\RegistryInterface; |
||
421 | |||
422 | /** |
||
423 | * ConfigRepository |
||
424 | * |
||
425 | * This class was generated by the Doctrine ORM. Add your own custom |
||
426 | * repository methods below. |
||
427 | */ |
||
428 | class ConfigRepository extends AbstractRepository |
||
429 | { |
||
430 | /** |
||
431 | * ConfigRepository constructor. |
||
432 | * |
||
433 | * @param RegistryInterface \$registry |
||
434 | */ |
||
435 | public function __construct(RegistryInterface \$registry) |
||
436 | { |
||
437 | parent::__construct(\$registry, Config::class); |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * @param int \$id |
||
442 | * |
||
443 | * @return null|Config |
||
444 | */ |
||
445 | public function get(\$id = 1) |
||
446 | { |
||
447 | return \$this->find(\$id); |
||
448 | } |
||
449 | } |
||
450 | |||
451 | EOL; |
||
452 | |||
453 | $this->fs->dumpFile($pluginDir.'/Repository/ConfigRepository.php', $source); |
||
454 | |||
455 | $source = <<<EOL |
||
456 | <?php |
||
457 | |||
458 | namespace Plugin\\${code}\\Form\\Type\\Admin; |
||
459 | |||
460 | use Plugin\\${code}\\Entity\\Config; |
||
461 | use Symfony\\Component\\Form\\AbstractType; |
||
462 | use Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType; |
||
463 | use Symfony\\Component\\Form\\FormBuilderInterface; |
||
464 | use Symfony\\Component\\OptionsResolver\\OptionsResolver; |
||
465 | use Symfony\Component\Validator\Constraints\Length; |
||
466 | use Symfony\Component\Validator\Constraints\NotBlank; |
||
467 | |||
468 | class ConfigType extends AbstractType |
||
469 | { |
||
470 | /** |
||
471 | * {@inheritdoc} |
||
472 | */ |
||
473 | public function buildForm(FormBuilderInterface \$builder, array \$options) |
||
474 | { |
||
475 | \$builder->add('name', TextType::class, [ |
||
476 | 'constraints' => [ |
||
477 | new NotBlank(), |
||
478 | new Length(['max' => 255]), |
||
479 | ], |
||
480 | ]); |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * {@inheritdoc} |
||
485 | */ |
||
486 | public function configureOptions(OptionsResolver \$resolver) |
||
487 | { |
||
488 | \$resolver->setDefaults([ |
||
489 | 'data_class' => Config::class, |
||
490 | ]); |
||
491 | } |
||
492 | } |
||
493 | |||
494 | EOL; |
||
495 | |||
496 | $this->fs->dumpFile($pluginDir.'/Form/Type/Admin/ConfigType.php', $source); |
||
497 | |||
498 | $source = <<<EOL |
||
499 | {% extends '@admin/default_frame.twig' %} |
||
500 | |||
501 | {% set menus = ['store', 'plugin', 'plugin_list'] %} |
||
502 | |||
503 | {% block title %}${code}{% endblock %} |
||
504 | {% block sub_title %}プラグイン一覧{% endblock %} |
||
505 | |||
506 | {% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %} |
||
507 | |||
508 | {% block stylesheet %}{% endblock stylesheet %} |
||
509 | |||
510 | {% block javascript %}{% endblock javascript %} |
||
511 | |||
512 | {% block main %} |
||
513 | <form role="form" method="post"> |
||
514 | |||
515 | {{ form_widget(form._token) }} |
||
516 | |||
517 | <div class="c-contentsArea__cols"> |
||
518 | <div class="c-contentsArea__primaryCol"> |
||
519 | <div class="c-primaryCol"> |
||
520 | <div class="card rounded border-0 mb-4"> |
||
521 | <div class="card-header"><span>設定</span></div> |
||
522 | <div class="card-body"> |
||
523 | <div class="row"> |
||
524 | <div class="col-3"><span>名前</span><span |
||
525 | class="badge badge-primary ml-1">必須</span></div> |
||
526 | <div class="col mb-2"> |
||
527 | {{ form_widget(form.name) }} |
||
528 | {{ form_errors(form.name) }} |
||
529 | </div> |
||
530 | </div> |
||
531 | </div> |
||
532 | </div> |
||
533 | </div> |
||
534 | </div> |
||
535 | </div> |
||
536 | <div class="c-conversionArea"> |
||
537 | <div class="c-conversionArea__container"> |
||
538 | <div class="row justify-content-between align-items-center"> |
||
539 | <div class="col-6"> |
||
540 | <div class="c-conversionArea__leftBlockItem"> |
||
541 | <a class="c-baseLink" |
||
542 | href="{{ url('admin_store_plugin') }}"> |
||
543 | <i class="fa fa-backward" aria-hidden="true"></i> |
||
544 | <span>プラグイン一覧</span> |
||
545 | </a> |
||
546 | </div> |
||
547 | </div> |
||
548 | <div class="col-6"> |
||
549 | <div class="row align-items-center justify-content-end"> |
||
550 | <div class="col-auto"> |
||
551 | <button class="btn btn-ec-conversion px-5" |
||
552 | type="submit">登録</button> |
||
553 | </div> |
||
554 | </div> |
||
555 | </div> |
||
556 | </div> |
||
557 | </div> |
||
558 | </div> |
||
559 | </form> |
||
560 | {% endblock %} |
||
561 | |||
562 | EOL; |
||
563 | $this->fs->dumpFile($pluginDir.'/Resource/template/admin/config.twig', $source); |
||
564 | } |
||
565 | } |
||
566 |
PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.
Let’s take a look at an example:
If we look at the
getEmail()
method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:On the hand, if we look at the
setEmail()
, this method _has_ side-effects. In the following case, we could not remove the method call: