Completed
Push — master ( 0d73ba...d5dbe2 )
by
unknown
11s
created

Application::doRun()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 39
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 0
cts 36
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 29
nc 64
nop 2
crap 56
1
<?php
2
3
/**
4
 * @author Victor Dubiniuk <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2015, ownCloud, Inc.
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace Owncloud\Updater\Console;
24
25
use Owncloud\Updater\Utils\DocLink;
26
use Owncloud\Updater\Utils\Locator;
27
use Owncloud\Updater\Utils\OccRunner;
28
use Pimple\Container;
29
use Symfony\Component\Console\Logger\ConsoleLogger;
30
use Symfony\Component\Console\Output\ConsoleOutput;
31
use Symfony\Component\Console\Output\StreamOutput;
32
use Symfony\Component\Console\Input\InputInterface;
33
use Symfony\Component\Console\Output\BufferedOutput;
34
use Symfony\Component\Console\Output\OutputInterface;
35
use Symfony\Component\Console\Command\Command;
36
use Symfony\Component\Process\Exception\ProcessFailedException;
37
38
/**
39
 * Class Application
40
 *
41
 * @package Owncloud\Updater\Console
42
 */
43
class Application extends \Symfony\Component\Console\Application {
44
45
	/** @var Container */
46
	public static $container;
47
48
	/** @var Container */
49
	protected $diContainer;
50
51
	/** @var ConsoleLogger */
52
	protected $fallbackLogger;
53
54
	/** @var  string */
55
	protected $endpoint;
56
57
	/** @var  string */
58
	protected $authToken;
59
60
	/** @var array */
61
	protected $allowFailure = [
62
		'upgrade:executeCoreUpgradeScripts',
63
		'upgrade:checkpoint',
64
		'upgrade:maintenanceMode',
65
		'help',
66
		'list'
67
	];
68
69
	/**
70
	 * Pass Pimple container into application
71
	 * @param Container $container
72
	 */
73
	public function setContainer(Container $container){
74
		$this->diContainer = $container;
75
		self::$container = $container;
76
	}
77
78
	/**
79
	 * Get Pimple container
80
	 * @return Container
81
	 */
82
	public function getContainer(){
83
		return $this->diContainer;
84
	}
85
86
	/**
87
	 * @param string $endpoint
88
	 */
89
	public function setEndpoint($endpoint){
90
		$this->endpoint = $endpoint;
91
	}
92
93
	/**
94
	 * @return string
95
	 */
96
	public function getEndpoint(){
97
		return $this->endpoint;
98
	}
99
100
	/**
101
	 * @param $token
102
	 */
103
	public function setAuthToken($token){
104
		$this->authToken = $token;
105
	}
106
107
	/**
108
	 * @return string
109
	 */
110
	public function getAuthToken(){
111
		return $this->authToken;
112
	}
113
114
	/**
115
	 * Get logger instance
116
	 * @return \Psr\Log\LoggerInterface
117
	 */
118
	public function getLogger(){
119
		if (isset($this->diContainer['logger'])) {
120
			return $this->diContainer['logger'];
121
		}
122
123
		// Logger is not available yet, fallback to stdout
124
		if (is_null($this->fallbackLogger)){
125
			$output = new ConsoleOutput();
126
			$this->fallbackLogger = new ConsoleLogger($output);
127
		}
128
129
		return $this->fallbackLogger;
130
	}
131
132
	/**
133
	 * Log exception with trace
134
	 * @param \Exception $e
135
	 */
136
	public function logException($e){
137
		$buffer = new BufferedOutput(OutputInterface::VERBOSITY_VERBOSE);
138
		$this->renderException($e, $buffer);
139
		$this->getLogger()->error($buffer->fetch());
140
	}
141
142
	/**
143
	 * Runs the current application.
144
	 *
145
	 * @param InputInterface $input An Input instance
146
	 * @param OutputInterface $output An Output instance
147
	 * @return int 0 if everything went fine, or an error code
148
	 * @throws \Exception
149
	 */
150
	public function doRun(InputInterface $input, OutputInterface $output){
151
		try{
152
			$commandName = $this->getCommandName($input);
153
			$this->assertOwnCloudFound();
154
			$configReader = $this->diContainer['utils.configReader'];
155
			try{
156
				$configReader->init();
157
				if (!isset($this->diContainer['utils.docLink'])) {
158
					$this->diContainer['utils.docLink'] = function ($c) {
159
						/** @var Locator $locator */
160
						$locator = $c['utils.locator'];
161
						$installedVersion = implode('.', $locator->getInstalledVersion());
162
						return new DocLink($installedVersion);
163
					};
164
				}
165
			} catch (ProcessFailedException $e){
166
				if (!in_array($commandName, $this->allowFailure)){
167
					$this->logException($e);
168
					$output->writeln("<error>Initialization failed with message:</error>");
169
					$output->writeln($e->getProcess()->getOutput());
170
					$output->writeln('<info>Use upgrade:checkpoint --list to view a list of checkpoints</info>');
171
					$output->writeln('<info>upgrade:checkpoint --restore [checkpointid] to revert to the last checkpoint</info>');
172
					$output->writeln('Please attach your update.log to the issues you reporting.');
173
					return 1;
174
				}
175
			}
176
			// TODO: check if the current command needs a valid OC instance
177
			if (!isset($this->diContainer['logger'])) {
178
				$locator = $this->diContainer['utils.locator'];
179
				$this->initLogger($locator->getDataDir());
0 ignored issues
show
Unused Code introduced by
The call to the method Owncloud\Updater\Console\Application::initLogger() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
180
			}
181
		} catch (\Exception $e){
182
			if (!in_array($commandName, $this->allowFailure)){
183
				$this->logException($e);
184
				throw $e;
185
			}
186
		}
187
		return parent::doRun($input, $output);
188
	}
189
190
	/**
191
	 * @param Command $command
192
	 * @param InputInterface $input
193
	 * @param OutputInterface $output
194
	 * @return int
195
	 * @throws \Exception
196
	 */
197
	protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output){
198
		if ($command instanceof \Owncloud\Updater\Command\Command){
199
			$command->setContainer($this->getContainer());
200
			$commandName = $this->getCommandName($input);
201
			$this->getLogger()->info('Execution of ' . $commandName . ' command started');
202
			$message = $command->getMessage();
203
			if (!empty($message)){
204
				$message = sprintf('<info>%s</info>', $message);
205
				$output->writeln($message);
206
			}
207
			$exitCode = parent::doRunCommand($command, $input, $output);
208
			$this->getLogger()->info(
209
					'Execution of ' . $commandName . ' command stopped. Exit code is ' . $exitCode
210
			);
211
		} else {
212
			$exitCode = parent::doRunCommand($command, $input, $output);
213
		}
214
		return $exitCode;
215
	}
216
217
	/**
218
	 * @param string $baseDir
219
	 */
220
	protected function initLogger($baseDir){
221
		$container = $this->getContainer();
222
		$container['logger.output'] = function($c) use ($baseDir) {
0 ignored issues
show
Unused Code introduced by
The parameter $c is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
223
			$stream = @fopen($baseDir . '/update.log', 'a+');
224
			if ($stream === false){
225
				$stream = @fopen('php://stderr', 'a');
226
			}
227
			return new StreamOutput($stream, StreamOutput::VERBOSITY_DEBUG, false);
228
		};
229
		$container['logger'] = function($c){
230
			return new ConsoleLogger($c['logger.output']);
231
		};
232
	}
233
234
	/**
235
	 * Check for ownCloud instance
236
	 * @throws \RuntimeException
237
	 */
238
	public function assertOwnCloudFound(){
239
		$container = $this->getContainer();
240
		/** @var Locator $locator */
241
		$locator = $container['utils.locator'];
242
		$fsHelper = $container['utils.filesystemhelper'];
243
		/** @var OccRunner $occRunner */
244
		$occRunner = $container['utils.occrunner'];
245
246
		// has to be installed
247
		$file = $locator->getPathToConfigFile();
248
		$this->assertFileExists($file, 'ownCloud in ' . dirname(dirname($file)) . ' is not installed.');
249
250
		// version.php should exist
251
		$file = $locator->getPathToVersionFile();
252
		$this->assertFileExists($file, 'ownCloud is not found in ' . dirname($file));
253
254
		$status = $occRunner->runJson('status');
255
		if (!isset($status['installed'])  || $status['installed'] != 'true'){
256
			throw new \RuntimeException('ownCloud in ' . dirname($file) . ' is not installed.');
257
		}
258
259
		// datadir should exist
260
		$dataDir = $locator->getDataDir();
261
		if (!$fsHelper->fileExists($dataDir)){
262
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' does not exist.');
263
		}
264
265
		// datadir should be writable
266
		if (!$fsHelper->isWritable($dataDir)){
267
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' is not writable.');
268
		}
269
270
		// assert minimum version
271
		$installedVersion = implode('.', $locator->getInstalledVersion());
272
		if (version_compare($installedVersion, '9.0.0', '<')){
273
			throw new \RuntimeException("Minimum ownCloud version 9.0.0 is required for the updater - $installedVersion was found in " . $locator->getOwnCloudRootPath());
274
		}
275
276
		if (!$fsHelper->fileExists($locator->getUpdaterBaseDir())){
277
			$fsHelper->mkdir($locator->getUpdaterBaseDir());
278
		}
279
280
		if (!$fsHelper->fileExists($locator->getDownloadBaseDir())){
281
			$fsHelper->mkdir($locator->getDownloadBaseDir());
282
		}
283
		if (!$fsHelper->fileExists($locator->getCheckpointDir())){
284
			$fsHelper->mkdir($locator->getCheckpointDir());
285
		}
286
	}
287
288
	/**
289
	 * @param string $path
290
	 * @param string $message
291
	 */
292
	protected function assertFileExists($path, $message){
293
		if (!file_exists($path) || !is_file($path)){
294
			throw new \RuntimeException($message);
295
		}
296
	}
297
}
298