1: <?php
2: namespace TIC\CoreBundle\Base;
3:
4: use Symfony\Component\Console\Command\Command;
5:
6: use Symfony\Component\Console\Input\InputOption;
7: use Symfony\Component\Console\Input\InputArgument;
8: use Symfony\Component\Console\Input\InputInterface;
9: use Symfony\Component\Console\Output\OutputInterface;
10: use Symfony\Component\Console\Style\SymfonyStyle;
11: #use Symfony\Component\Console\Logger\ConsoleLogger;
12:
13: use Symfony\Component\Routing\RouterInterface;
14: use Symfony\Contracts\Translation\TranslatorInterface;
15: use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
16:
17: // @TODO remplacer le ps|grep par https://symfony.com/doc/current/console/lockable_trait.html
18: use Symfony\Component\Process\Process;
19:
20: /**
21: * Base Command
22: * @see https://symfony.com/doc/current/console.html
23: * @see https://symfony.com/doc/current/components/console.html
24: *
25: * $output
26: * - verbosity : ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug()
27: * - clickable : ->writeln('<href=https://symfony.com>Symfony Homepage</>');
28: * - style tags : '<info>' '<comment>' '<question>' '<error>'
29: * - color tags : '<fg=green>' '<bg=black;fg=#c0392b>' '<bg=yellow;options=bold,underscore>'
30: *
31: * $io (style)
32: * ->title(),->section(), ->text(), ->listing(), ->table(), ->horizontalTable(), ->definitionList()
33: * ->note(), ->caution(), ->info(), ->warning(), ->error(), ->success()
34: * ->ask(), ->askHidden(), ->confirm(), ->choice()
35: *
36: */
37: abstract class TICCommand extends Command
38: {
39: // Initialisation automatique des propriétés à partir du nom de la classe courante
40: use \TIC\CoreBundle\Traits\ContextProperties; // ctxBundle, ctxPath & ctxSnake
41:
42: protected $router;
43: protected $translator;
44: protected $params;
45:
46: protected $io;
47: protected $out;
48: protected $test;
49: # protected $logger;
50:
51: // the name of the command (the part after "bin/console")
52: protected static $defaultName = null;
53: // the command description shown when running "php bin/console list"
54: protected static $defaultDescription = null;
55:
56: /**
57: * {@inheritDoc}
58: */
59: public function __construct(
60: RouterInterface $router,
61: TranslatorInterface $translator,
62: ParameterBagInterface $params
63: ) {
64: parent::__construct();
65: $this->router = $router;
66: $this->translator = $translator;
67: $this->params = $params;
68: }
69:
70: /**
71: * {@inheritDoc}
72: */
73: protected function configure(): void
74: {
75: // appel manuel pour l'initialisation des propriétés de contexte (l'appel auto n'étant fait qu'après)
76: $this->setContextProperties('Command');
77:
78: // détermination du nom de la commande pour la console
79: if (null === static::$defaultName) {
80: if ($this->ctxBundle) {
81: $commandName = \strtolower(\trim(\preg_replace("/([A-Z][^A-Z])/", '_\1', $this->ctxBundle)));
82: $commandName = \str_replace(['@','_'], ['',':'], $commandName);
83: } else {
84: $commandName = empty($this->ctxPath) ? 'app' : \strtolower($this->ctxPath);
85: }
86: $commandName.= ":" . \strtr($this->ctxSnake, '_', '-');
87: $this->setName($commandName);
88: }
89:
90: // définition d'options génériques communes
91: $this->addOption('test', 't', InputOption::VALUE_NONE, "test only.");
92: }
93:
94: /**
95: * {@inheritDoc}
96: */
97: protected function initialize(InputInterface $input, OutputInterface $output)
98: {
99: parent::initialize($input, $output);
100:
101: // raccourcis utilisables pour toute méthode
102: $this->test = $input->getOption('test');
103: $this->out = $output;
104:
105: // @see https://symfony.com/doc/current/console/style.html
106: $this->io = new SymfonyStyle($input, $output);
107:
108: // @see https://symfony.com/doc/current/components/console/logger.html
109: # $this->logger = new ConsoleLogger($output);
110:
111: // entity manager & options (disable logger & softdelete)
112: if (isset($this->em)) {
113: # $this->EmDisableLogger();
114: # $this->EmDisableFilter('softdeleteable');
115: }
116: }
117:
118: /**
119: * {@inheritDoc}
120: */
121: protected function execute(InputInterface $input, OutputInterface $output): int
122: {
123: # $output->writeln("...");
124: $this->io->success("Done.");
125:
126: # return self::INVALID; // 2
127: # return self::FAILURE; // 1
128: return self::SUCCESS; // 0
129: }
130:
131: // -------------------------------------------------------------------------
132:
133: /**
134: *
135: */
136: protected function trans(string $token, array $parameters = []): string
137: {
138: return $this->translator->trans($token, $parameters);
139: }
140:
141: /**
142: * Méthode testant si un autre processus pour la même commande est déjà en cours d'exécution.
143: * @see https://symfony.com/doc/current/console/lockable_trait.html
144: */
145: protected function isAlreadyRunning($exclude_pid = null): ?bool
146: {
147: $ps = 'ps axwh -o pid,cmd';
148: $cmd = "$ps | sed 's/^ *//'";
149: if ($exclude_pid) $cmd.= " | grep -v '^".(\intval($exclude_pid))." '";
150: $cmd.= " | grep 'console " . $this->getName() . "'";
151: # $cmd.= " | grep -v ' $ps' | grep -v ' grep ' | wc -l";
152: $cmd.= " | grep -v ' $ps' | grep -v ' grep ' | grep -v ' /bin/sh -c ' | wc -l";
153:
154: $process = new Process($cmd);
155: $process->run();
156: if (! $process->isSuccessful()) return null;
157:
158: $output = \intval(\trim($process->getOutput()));
159: if ($output > 1) return true;
160: if ($output == 1) return false;
161: }
162:
163: /**
164: * Méthode utile pour afficher un message d'erreur, suivi du synopsis de la commande, puis exit 1.
165: */
166: protected function exitError(string $message, int $code=1)
167: {
168: $this->io->error($message);
169: $this->io->text('<info>' . $this->getSynopsis() . '</info>');
170: $this->io->newLine();
171: exit($code);
172: }
173:
174: }
175: