Completed
Push — 0.4.5 ( f33756...7e44db )
by Peter
05:07 queued 02:56
created

CommandExecutor::executeCommandInBackground()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 14
c 1
b 0
f 0
ccs 0
cts 10
cp 0
rs 9.4285
cc 3
eloc 9
nc 2
nop 1
crap 12
1
<?php
2
/**
3
 * AnimeDb package
4
 *
5
 * @package   AnimeDb
6
 * @author    Peter Gribanov <[email protected]>
7
 * @copyright Copyright (c) 2011, Peter Gribanov
8
 * @license   http://opensource.org/licenses/GPL-3.0 GPL v3
9
 */
10
11
namespace AnimeDb\Bundle\AppBundle\Service;
12
13
use Symfony\Component\HttpFoundation\RequestStack;
14
use Symfony\Component\Routing\RouterInterface;
15
use Symfony\Component\Process\Process;
16
17
/**
18
 * Command executor
19
 *
20
 * Example:
21
 * <code>
22
 *   php app/console cache:clear > /dev/null 2>&1
23
 *   php composer.phar update
24
 *   ping > ping.log
25
 * <code>
26
 */
27
class CommandExecutor
28
{
29
    /**
30
     * @var string
31
     */
32
    protected $host;
33
34
    /**
35
     * @var string
36
     */
37
    protected $path;
38
39
    /**
40
     * @var string
41
     */
42
    protected $cwd;
43
44
    /**
45
     * @var string
46
     */
47
    protected $console;
48
49
    /**
50
     * @var PhpFinder
51
     */
52
    protected $finder;
53
54
    /**
55
     * @var int
56
     */
57
    const TIMEOUT = 2;
58
59
    /**
60
     * @param PhpFinder $finder
61
     * @param RouterInterface $router
62
     * @param RequestStack $request_stack
63
     * @param string $root_dir
64
     */
65 5
    public function __construct(PhpFinder $finder, RouterInterface $router, RequestStack $request_stack, $root_dir)
66
    {
67 5
        $this->finder = $finder;
68 5
        $this->cwd = $root_dir.'/../';
69 5
        $this->console = escapeshellarg($root_dir.DIRECTORY_SEPARATOR.'console');
70 5
        $this->path = $router->generate('command_exec');
71 5
        if ($request = $request_stack->getCurrentRequest()) {
72 4
            $this->host = $request->getHost().':'.$request->getPort();
73 4
        }
74 5
    }
75
76
    /**
77
     * @deprecated see self::send()
78
     *
79
     * @throws \InvalidArgumentException
80
     *
81
     * @param string $command
82
     */
83
    public function exec($command)
84
    {
85
        if (!$command) {
86
            throw new \InvalidArgumentException('Unknown command');
87
        }
88
        $this->send('php app/console '.$command);
89
    }
90
91
    /**
92
     * Execute command
93
     *
94
     * If timeout <= 0 and callback is null then command will be executed in background
95
     *
96
     * @param string $command
97
     * @param int $timeout
98
     * @param callable|null $callback
99
     */
100
    public function execute($command, $timeout = 300, $callback = null)
101
    {
102
        if ($timeout > 0 || is_callable($callback)) {
103
            $this->executeCommand($command, $timeout, $callback);
104
        } else {
105
            $this->executeCommandInBackground($command);
106
        }
107
    }
108
109
    /**
110
     * @param string $command
111
     * @param int $timeout
112
     * @param callable|null $callback
113
     */
114
    public function console($command, $timeout = 300, $callback = null)
115
    {
116
        $this->execute('php app/console '.$command, $timeout, $callback);
117
    }
118
119
    /**
120
     * @throws \RuntimeException
121
     *
122
     * @param string $command
123
     * @param int $timeout
124
     * @param callable|null $callback
125
     */
126
    protected function executeCommand($command, $timeout = 300, $callback = null)
127
    {
128
        $process = new Process($this->prepare($command), $this->cwd, null, null, $timeout);
129
        $process->run($callback);
130
        if (!$process->isSuccessful()) {
131
            throw new \RuntimeException(sprintf('An error occurred when executing the "%s" command.', $command));
132
        }
133
    }
134
135
    /**
136
     * @param string $command
137
     */
138
    protected function executeCommandInBackground($command)
139
    {
140
        $cwd = getcwd();
141
        chdir($this->cwd);
142
143
        $command = $this->prepare($command);
144
        if (defined('PHP_WINDOWS_VERSION_BUILD') && function_exists('popen')) {
145
            pclose(popen('start /b call '.$command, 'r'));
146
        } else {
147
            exec($command.' &');
148
        }
149
150
        chdir($cwd);
151
    }
152
153
    /**
154
     * Send the command to perform in a new thread
155
     *
156
     * @param string $command
157
     * @param string $host
158
     */
159 1
    public function send($command, $host = '')
160
    {
161 1
        $host = $host ?: $this->host;
162 1
        if (!$host) {
163 1
            throw new \InvalidArgumentException('Unknown host that will run the command');
164
        }
165
        $content = 'command='.urlencode($command);
166
167
        $fp = fsockopen($this->host, 80, $errno, $errstr, self::TIMEOUT);
168
        $request  = "POST ".$this->path." HTTP/1.1\r\n";
169
        $request .= "Host: ".$host."\r\n";
170
        $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
171
        $request .= "Content-Length: ".strlen($content)."\r\n";
172
        $request .= "Connection: Close\r\n\r\n";
173
        $request .= $content;
174
        fwrite($fp, $request);
175
        fclose($fp);
176
    }
177
178
    /**
179
     * @param string $command
180
     *
181
     * @return string
182
     */
183 3
    public function prepare($command)
184
    {
185
        // change path for php
186 3
        if (substr($command, 0, 4) == 'php ') {
187 2
            $command = $this->finder->getPath().substr($command, 3);
188 2
        }
189
190
        // change path to console
191 3
        $command = str_replace(' app/console ', ' '.$this->console.' ', $command);
192
193
        // change /dev/null for Windows
194 3
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
195
            $command = str_replace('/dev/null', 'nul', $command);
196
        }
197
198 3
        return $command;
199
    }
200
}
201