Completed
Push — master ( 75e91c...b3846f )
by Christian
02:44
created

RunTaskCommand   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 11
c 3
b 0
f 0
lcom 1
cbo 8
dl 0
loc 91
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 8 1
A run() 0 10 2
A execute() 0 16 3
B fork() 0 26 5
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'));
0 ignored issues
show
Unused Code introduced by
The call to Runner::__construct() has too many arguments starting with $container->get('tenside.taskrun_lock').

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.

Loading history...
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