1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Dtc\QueueBundle\Command; |
4
|
|
|
|
5
|
|
|
use Dtc\QueueBundle\Exception\ClassNotSubclassException; |
6
|
|
|
use Dtc\QueueBundle\Model\Job; |
7
|
|
|
use Dtc\QueueBundle\Run\Loop; |
8
|
|
|
use Dtc\QueueBundle\Util\Util; |
9
|
|
|
use Psr\Log\LoggerInterface; |
10
|
|
|
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
11
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
12
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
13
|
|
|
use Symfony\Component\Console\Input\InputOption; |
14
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
15
|
|
|
use Symfony\Component\HttpKernel\Kernel; |
16
|
|
|
|
17
|
|
|
class RunCommand extends ContainerAwareCommand |
18
|
|
|
{ |
19
|
|
|
protected $loggerPrivate = false; |
20
|
|
|
protected $nanoSleepOption = null; |
21
|
|
|
|
22
|
1 |
|
protected function symfonyDetect() |
23
|
|
|
{ |
24
|
1 |
|
$this->nanoSleepOption = null; |
25
|
1 |
|
if (class_exists('Symfony\Component\HttpKernel\Kernel')) { |
26
|
1 |
|
if (Kernel::VERSION_ID >= 30000) { |
27
|
1 |
|
$this->nanoSleepOption = 's'; |
28
|
|
|
} |
29
|
1 |
|
if (Kernel::VERSION_ID >= 30400) { |
30
|
1 |
|
$this->loggerPrivate = true; |
31
|
|
|
} |
32
|
|
|
} |
33
|
1 |
|
} |
34
|
|
|
|
35
|
1 |
|
protected function configure() |
36
|
|
|
{ |
37
|
1 |
|
$this->symfonyDetect(); |
38
|
|
|
$options = array( |
39
|
1 |
|
new InputArgument('worker-name', InputArgument::OPTIONAL, 'Name of worker', null), |
40
|
1 |
|
new InputArgument('method', InputArgument::OPTIONAL, 'DI method of worker', null), |
41
|
1 |
|
new InputOption( |
42
|
1 |
|
'id', |
43
|
1 |
|
'i', |
44
|
1 |
|
InputOption::VALUE_REQUIRED, |
45
|
1 |
|
'Id of Job to run', |
46
|
1 |
|
null |
47
|
|
|
), |
48
|
1 |
|
new InputOption( |
49
|
1 |
|
'max-count', |
50
|
1 |
|
'm', |
51
|
1 |
|
InputOption::VALUE_REQUIRED, |
52
|
1 |
|
'Maximum number of jobs to work on before exiting', |
53
|
1 |
|
null |
54
|
|
|
), |
55
|
1 |
|
new InputOption( |
56
|
1 |
|
'duration', |
57
|
1 |
|
'd', |
58
|
1 |
|
InputOption::VALUE_REQUIRED, |
59
|
1 |
|
'Duration to run for in seconds', |
60
|
1 |
|
null |
61
|
|
|
), |
62
|
1 |
|
new InputOption( |
63
|
1 |
|
'timeout', |
64
|
1 |
|
't', |
65
|
1 |
|
InputOption::VALUE_REQUIRED, |
66
|
1 |
|
'Process timeout in seconds (hard exit of process regardless)', |
67
|
1 |
|
3600 |
68
|
|
|
), |
69
|
1 |
|
new InputOption( |
70
|
1 |
|
'nano-sleep', |
71
|
1 |
|
$this->nanoSleepOption, |
72
|
1 |
|
InputOption::VALUE_REQUIRED, |
73
|
1 |
|
'If using duration, this is the time to sleep when there\'s no jobs in nanoseconds', |
74
|
1 |
|
500000000 |
75
|
|
|
), |
76
|
1 |
|
new InputOption( |
77
|
1 |
|
'disable-gc', |
78
|
1 |
|
null, |
79
|
1 |
|
InputOption::VALUE_NONE, |
80
|
1 |
|
'Disable garbage collection' |
81
|
|
|
), |
82
|
|
|
); |
83
|
|
|
|
84
|
|
|
// Symfony 4 and Symfony 3.4 out-of-the-box makes the logger private |
85
|
1 |
|
if (!$this->loggerPrivate) { |
86
|
|
|
$options[] = |
87
|
|
|
new InputOption( |
88
|
|
|
'logger', |
89
|
|
|
'l', |
90
|
|
|
InputOption::VALUE_REQUIRED, |
91
|
|
|
'Log using the logger service specified, or output to console if null (or an invalid logger service id) is passed in' |
92
|
|
|
); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
$this |
96
|
1 |
|
->setName('dtc:queue:run') |
97
|
1 |
|
->setDefinition($options) |
98
|
1 |
|
->setDescription('Start up a job in queue'); |
99
|
1 |
|
} |
100
|
|
|
|
101
|
1 |
|
protected function execute(InputInterface $input, OutputInterface $output) |
102
|
|
|
{ |
103
|
1 |
|
$start = microtime(true); |
104
|
1 |
|
$container = $this->getContainer(); |
105
|
1 |
|
$loop = $container->get('dtc_queue.run.loop'); |
106
|
1 |
|
$loop->setOutput($output); |
107
|
1 |
|
$workerName = $input->getArgument('worker-name'); |
108
|
1 |
|
$methodName = $input->getArgument('method'); |
109
|
1 |
|
$maxCount = $input->getOption('max-count'); |
110
|
1 |
|
$duration = $input->getOption('duration'); |
111
|
1 |
|
$processTimeout = $input->getOption('timeout'); |
112
|
1 |
|
$nanoSleep = $input->getOption('nano-sleep'); |
113
|
1 |
|
$loggerService = !$this->loggerPrivate ? $input->getOption('logger', null) : null; |
|
|
|
|
114
|
1 |
|
$disableGc = $input->getOption('disable-gc', false); |
|
|
|
|
115
|
1 |
|
$this->setGc($disableGc); |
116
|
|
|
|
117
|
1 |
|
$this->setLoggerService($loop, $loggerService); |
118
|
|
|
|
119
|
1 |
|
$maxCount = Util::validateIntNull('max_count', $maxCount, 32); |
120
|
1 |
|
$duration = Util::validateIntNull('duration', $duration, 32); |
121
|
1 |
|
$nanoSleep = Util::validateIntNull('nano_sleep', $nanoSleep, 63); |
122
|
1 |
|
$processTimeout = Util::validateIntNull('timeout', $processTimeout, 32); |
123
|
1 |
|
$loop->checkMaxCountDuration($maxCount, $duration, $processTimeout); |
124
|
|
|
|
125
|
|
|
// Check to see if there are other instances |
126
|
1 |
|
set_time_limit($processTimeout); // Set timeout on the process |
127
|
|
|
|
128
|
1 |
|
if ($jobId = $input->getOption('id')) { |
129
|
|
|
return $loop->runJobById($start, $jobId); // Run a single job |
130
|
|
|
} |
131
|
|
|
|
132
|
1 |
|
return $loop->runLoop($start, $workerName, $methodName, $maxCount, $duration, $nanoSleep); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @param bool $disableGc |
137
|
|
|
*/ |
138
|
1 |
|
protected function setGc($disableGc) |
139
|
|
|
{ |
140
|
1 |
|
if ($disableGc) { |
141
|
|
|
if (gc_enabled()) { |
142
|
|
|
gc_disable(); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
return; |
146
|
|
|
} |
147
|
|
|
|
148
|
1 |
|
if (!gc_enabled()) { |
149
|
|
|
gc_enable(); |
150
|
|
|
} |
151
|
1 |
|
} |
152
|
|
|
|
153
|
1 |
|
protected function setLoggerService(Loop $loop, $loggerService) |
154
|
|
|
{ |
155
|
1 |
|
if (!$loggerService) { |
156
|
1 |
|
return; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
$container = $this->getContainer(); |
160
|
|
|
if (!$container->has($loggerService)) { |
161
|
|
|
return; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
$logger = $container->get($loggerService); |
165
|
|
|
if (!$logger instanceof LoggerInterface) { |
166
|
|
|
throw new ClassNotSubclassException("$loggerService must be instance of Psr\\Log\\LoggerInterface"); |
167
|
|
|
} |
168
|
|
|
$loop->setLogger($logger); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.