1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of tenside/core-bundle. |
5
|
|
|
* |
6
|
|
|
* (c) Christian Schiffler <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
* |
11
|
|
|
* This project is provided in good faith and hope to be usable by anyone. |
12
|
|
|
* |
13
|
|
|
* @package tenside/core-bundle |
14
|
|
|
* @author Christian Schiffler <[email protected]> |
15
|
|
|
* @copyright 2015 Christian Schiffler <[email protected]> |
16
|
|
|
* @license https://github.com/tenside/core-bundle/blob/master/LICENSE MIT |
17
|
|
|
* @link https://github.com/tenside/core-bundle |
18
|
|
|
* @filesource |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace Tenside\CoreBundle\Command; |
22
|
|
|
|
23
|
|
|
use Psr\Log\LoggerInterface; |
24
|
|
|
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
25
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
26
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
27
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
28
|
|
|
use Tenside\Core\Task\Runner; |
29
|
|
|
use Tenside\Core\Util\FunctionAvailabilityCheck; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* This class executes a queued task in detached mode. |
33
|
|
|
*/ |
34
|
|
|
class RunTaskCommand extends ContainerAwareCommand |
35
|
|
|
{ |
36
|
|
|
/** |
37
|
|
|
* {@inheritdoc} |
38
|
|
|
*/ |
39
|
|
|
protected function configure() |
40
|
|
|
{ |
41
|
|
|
$this |
42
|
|
|
->setName('tenside:runtask') |
43
|
|
|
->setDescription('Execute a queued task') |
44
|
|
|
->setHelp('You most likely do not want to use this from CLI - use the web UI') |
45
|
|
|
->addArgument('taskId', InputArgument::REQUIRED, 'The task id of the task to run.'); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* {@inheritdoc} |
50
|
|
|
* |
51
|
|
|
* @throws \RuntimeException When another task is already running. |
52
|
|
|
*/ |
53
|
|
|
public function run(InputInterface $input, OutputInterface $output) |
54
|
|
|
{ |
55
|
|
|
// If successfully forked, exit now as the parenting process is done. |
56
|
|
|
if ($this->fork()) { |
57
|
|
|
$output->writeln('Forked into background.'); |
58
|
|
|
return 0; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
return parent::run($input, $output); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* {@inheritdoc} |
66
|
|
|
* |
67
|
|
|
* @throws \InvalidArgumentException When an invalid task id has been passed. |
68
|
|
|
*/ |
69
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
70
|
|
|
{ |
71
|
|
|
$container = $this->getContainer(); |
72
|
|
|
$taskList = $container->get('tenside.tasks'); |
73
|
|
|
$task = $taskList->getTask($input->getArgument('taskId')); |
74
|
|
|
|
75
|
|
|
if (!$task) { |
76
|
|
|
throw new \InvalidArgumentException('Task not found: ' . $input->getArgument('taskId')); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
$runner = new Runner($task, $container->get('tenside.taskrun_lock'), $container->get('logger')); |
|
|
|
|
80
|
|
|
|
81
|
|
|
return $runner->run( |
82
|
|
|
$container->get('kernel')->getLogDir() . DIRECTORY_SEPARATOR . 'task-' . $task->getId() . '.log' |
83
|
|
|
) ? 0 : 1; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Try to fork. |
88
|
|
|
* |
89
|
|
|
* The return value determines if the caller shall exit (when forking was successful and it is the forking process) |
90
|
|
|
* or rather proceed execution (is the fork or unable to fork). |
91
|
|
|
* |
92
|
|
|
* True means exit, false means go on in this process. |
93
|
|
|
* |
94
|
|
|
* @return bool |
95
|
|
|
* |
96
|
|
|
* @throws \RuntimeException When the forking caused an error. |
97
|
|
|
*/ |
98
|
|
|
private function fork() |
99
|
|
|
{ |
100
|
|
|
/** @var LoggerInterface $logger */ |
101
|
|
|
$logger = $this->getContainer()->get('logger'); |
102
|
|
|
if (!$this->getContainer()->get('tenside.config')->isForkingAvailable()) { |
103
|
|
|
$logger->warning('Forking disabled by configuration, execution will block until the command has finished.'); |
104
|
|
|
|
105
|
|
|
return false; |
106
|
|
|
} elseif (!FunctionAvailabilityCheck::isFunctionEnabled('pcntl_fork', 'pcntl')) { |
107
|
|
|
$logger->warning('pcntl_fork() is not available, execution will block until the command has finished.'); |
108
|
|
|
|
109
|
|
|
return false; |
110
|
|
|
} else { |
111
|
|
|
$pid = pcntl_fork(); |
112
|
|
|
if (-1 === $pid) { |
113
|
|
|
throw new \RuntimeException('pcntl_fork() returned -1.'); |
114
|
|
|
} elseif (0 !== $pid) { |
115
|
|
|
// Tell the calling method to exit now. |
116
|
|
|
$logger->info('Forked process ' . posix_getpid() . ' to pid ' . $pid); |
117
|
|
|
return true; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
$logger->info('Processing task in forked process with pid ' . posix_getpid()); |
121
|
|
|
return false; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
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.