Completed
Push — 0.4.5 ( ab71c2...f33756 )
by Peter
17:25
created

CommandExecutor::prepare()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

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