Completed
Pull Request — master (#581)
by Victor
01:40
created

OccRunner::runAsRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 13
cp 0
rs 9.568
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 6
1
<?php
2
/**
3
 * @author Victor Dubiniuk <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2015, ownCloud, Inc.
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace Owncloud\Updater\Utils;
23
24
use GuzzleHttp\Client;
25
use GuzzleHttp\RequestOptions;
26
use Owncloud\Updater\Console\Application;
27
use Symfony\Component\Process\Process;
28
use Symfony\Component\Process\ProcessUtils;
29
use Symfony\Component\Process\Exception\ProcessFailedException;
30
31
/**
32
 * Class OccRunner
33
 *
34
 * @package Owncloud\Updater\Utils
35
 */
36
class OccRunner {
37
	/**
38
	 * @var Locator $locator
39
	 */
40
	protected $locator;
41
42
	/**
43
	 * @var bool
44
	 */
45
	protected $canUseProcess;
46
47
	/**
48
	 *
49
	 * @param Locator $locator
50
	 * @param bool $canUseProcess
51
	 */
52 2
	public function __construct(Locator $locator, $canUseProcess){
53 2
		$this->locator = $locator;
54 2
		$this->canUseProcess = $canUseProcess;
55 2
	}
56
57
	/**
58
	 * @param bool $canUseProcess
59
	 */
60
	public function setCanUseProcess($canUseProcess){
61
		$this->canUseProcess = $canUseProcess;
62
	}
63
64
	/**
65
	 * @param $command
66
	 * @param array $args
67
	 * @param bool $asJson
68
	 * @return string
69
	 */
70 2
	public function run($command, $args = [], $asJson = false){
71 2
		if ($this->canUseProcess){
72 1
			$extra = $asJson ? '--output=json' : '';
73 1
			$cmdLine = trim($command . ' ' . $extra);
74 1
			foreach ($args as $optionTitle => $optionValue){
75
				if (strpos($optionTitle, '--') === 0){
76
					$line = trim("$optionTitle=$optionValue");
77
				} else {
78
					$line = $optionValue;
79
				}
80
				$escapedLine = ProcessUtils::escapeArgument($line);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Proces...Utils::escapeArgument() has been deprecated with message: since version 3.3, to be removed in 4.0. Use a command line array or give env vars to the `Process::start/run()` method instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
81
				$cmdLine .= " $escapedLine";
82
			}
83 1
			return $this->runAsProcess($cmdLine);
84
		} else {
85 1
			if ($asJson){
86 1
				$args['--output'] = 'json';
87
			}
88 1
			$response = $this->runAsRequest($command, $args);
89 1
			$decodedResponse = json_decode($response, true);
90 1
			return $decodedResponse['response'];
91
		}
92
	}
93
94
	/**
95
	 * @param $command
96
	 * @param array $args
97
	 * @return mixed
98
	 */
99 5
	public function runJson($command, $args = []){
100 5
		$plain = $this->run($command, $args, true);
101
		// trim response to always be a valid json. Capture everything between the first and the last curly brace
102 5
		preg_match_all('!(\{.*\})!ms', $plain, $matches);
103 5
		$clean = isset($matches[1][0]) ? $matches[1][0] : '';
104 5
		$decoded = json_decode($clean, true);
105 5
		if (!is_array($decoded)){
106 1
			throw new \UnexpectedValueException('Could not parse a response for ' . $command . '. Please check if the current shell user can run occ command. Raw output: ' . PHP_EOL . $plain);
107
		}
108 4
		return $decoded;
109
	}
110
111
	/**
112
	 * @param $command
113
	 * @param $args
114
	 * @return string
115
	 */
116
	protected function runAsRequest($command, $args){
117
		$application = $this->getApplication();
118
		$client = new Client();
119
		$endpointBase = $application->getEndpoint();
120
		$params = [
121
			RequestOptions::TIMEOUT => 0,
122
			RequestOptions::JSON => [
123
				'token' => $application->getAuthToken(),
124
				'params'=> $args
125
			]
126
		];
127
128
		// Skip SSL validation for localhost only as localhost never has a valid cert
129
		if (preg_match('/^https:\/\/localhost\/.*/i', $endpointBase)){
130
			$params[RequestOptions::VERIFY] = false;
131
		}
132
133
		$response = $client->request('POST', $endpointBase . $command, $params);
134
135
		$responseBody = $response->getBody()->getContents();
136
		return $responseBody;
137
	}
138
139
	/**
140
	 * @return mixed
141
	 */
142
	protected function getApplication(){
143
		$container = Application::$container;
144
		$application = $container['application'];
145
		return $application;
146
	}
147
148
	/**
149
	 * @param $cmdLine
150
	 * @return string
151
	 */
152
	protected function runAsProcess($cmdLine){
153
		$occPath = $this->locator->getPathToOccFile();
154
		$cmd = "php $occPath --no-warnings $cmdLine";
155
		$process = new Process($cmd);
156
		$process->setTimeout(null);
157
		$process->run();
158
159
		if (!$process->isSuccessful()){
160
			throw new ProcessFailedException($process);
161
		}
162
		return $process->getOutput();
163
	}
164
}
165