Application   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 275
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 13
dl 0
loc 275
ccs 0
cts 156
cp 0
rs 9.36
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A setContainer() 0 4 1
A getContainer() 0 3 1
A setEndpoint() 0 3 1
A getEndpoint() 0 3 1
A setAuthToken() 0 3 1
A getAuthToken() 0 3 1
A getLogger() 0 13 3
A initConfig() 0 20 4
A logException() 0 5 1
A doRunCommand() 0 19 3
B assertOwnCloudFound() 0 49 9
A assertFileExists() 0 5 3
B doRun() 0 38 7
A initLogger() 0 13 2
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
	public function initConfig(){
133
		$configReader = $this->diContainer['utils.configReader'];
134
		try {
135
			$configReader->init();
136
		} catch (\UnexpectedValueException $e){
137
			// try fallback to localhost
138
			preg_match_all('/https?:\/\/([^\/]*).*$/', $this->getEndpoint(), $matches);
139
			if (isset($matches[1][0])){
140
				$newEndPoint = str_replace($matches[1][0], 'localhost', $this->getEndpoint());
141
				$this->setEndpoint($newEndPoint);
142
				try {
143
					$configReader->init();
144
				} catch (\UnexpectedValueException $e){
145
					// fallback to CLI
146
					$this->diContainer['utils.occrunner']->setCanUseProcess(true);
147
					$configReader->init();
148
				}
149
			}
150
		}
151
	}
152
153
	/**
154
	 * Log exception with trace
155
	 * @param \Exception $e
156
	 */
157
	public function logException($e){
158
		$buffer = new BufferedOutput(OutputInterface::VERBOSITY_VERBOSE);
159
		$this->renderException($e, $buffer);
160
		$this->getLogger()->error($buffer->fetch());
161
	}
162
163
	/**
164
	 * Runs the current application.
165
	 *
166
	 * @param InputInterface $input An Input instance
167
	 * @param OutputInterface $output An Output instance
168
	 * @return int 0 if everything went fine, or an error code
169
	 * @throws \Exception
170
	 */
171
	public function doRun(InputInterface $input, OutputInterface $output){
172
		$commandName = $this->getCommandName($input);
173
		try{
174
			$this->assertOwnCloudFound();
175
			try{
176
				$this->initConfig();
177
				if (!isset($this->diContainer['utils.docLink'])) {
178
					$this->diContainer['utils.docLink'] = function ($c) {
179
						/** @var Locator $locator */
180
						$locator = $c['utils.locator'];
181
						$installedVersion = implode('.', $locator->getInstalledVersion());
182
						return new DocLink($installedVersion);
183
					};
184
				}
185
			} catch (ProcessFailedException $e){
186
				if (!in_array($commandName, $this->allowFailure)){
187
					$this->logException($e);
188
					$output->writeln("<error>Initialization failed with message:</error>");
189
					$output->writeln($e->getProcess()->getOutput());
190
					$output->writeln('<info>Use upgrade:checkpoint --list to view a list of checkpoints</info>');
191
					$output->writeln('<info>upgrade:checkpoint --restore [checkpointid] to revert to the last checkpoint</info>');
192
					$output->writeln('Please attach your update.log to the issues you reporting.');
193
					return 1;
194
				}
195
			}
196
			// TODO: check if the current command needs a valid OC instance
197
			if (!isset($this->diContainer['logger'])) {
198
				$locator = $this->diContainer['utils.locator'];
199
				$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...
200
			}
201
		} catch (\Exception $e){
202
			if (!in_array($commandName, $this->allowFailure)){
203
				$this->logException($e);
204
				throw $e;
205
			}
206
		}
207
		return parent::doRun($input, $output);
208
	}
209
210
	/**
211
	 * @param Command $command
212
	 * @param InputInterface $input
213
	 * @param OutputInterface $output
214
	 * @return int
215
	 * @throws \Exception
216
	 */
217
	protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output){
218
		if ($command instanceof \Owncloud\Updater\Command\Command){
219
			$command->setContainer($this->getContainer());
220
			$commandName = $this->getCommandName($input);
221
			$this->getLogger()->info('Execution of ' . $commandName . ' command started');
222
			$message = $command->getMessage();
223
			if (!empty($message)){
224
				$message = sprintf('<info>%s</info>', $message);
225
				$output->writeln($message);
226
			}
227
			$exitCode = parent::doRunCommand($command, $input, $output);
228
			$this->getLogger()->info(
229
					'Execution of ' . $commandName . ' command stopped. Exit code is ' . $exitCode
230
			);
231
		} else {
232
			$exitCode = parent::doRunCommand($command, $input, $output);
233
		}
234
		return $exitCode;
235
	}
236
237
	/**
238
	 * @param string $baseDir
239
	 */
240
	protected function initLogger($baseDir){
241
		$container = $this->getContainer();
242
		$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...
243
			$stream = @fopen($baseDir . '/update.log', 'a+');
244
			if ($stream === false){
245
				$stream = @fopen('php://stderr', 'a');
246
			}
247
			return new StreamOutput($stream, StreamOutput::VERBOSITY_DEBUG, false);
248
		};
249
		$container['logger'] = function($c){
250
			return new ConsoleLogger($c['logger.output']);
251
		};
252
	}
253
254
	/**
255
	 * Check for ownCloud instance
256
	 * @throws \RuntimeException
257
	 */
258
	public function assertOwnCloudFound(){
259
		$container = $this->getContainer();
260
		/** @var Locator $locator */
261
		$locator = $container['utils.locator'];
262
		$fsHelper = $container['utils.filesystemhelper'];
263
		/** @var OccRunner $occRunner */
264
		$occRunner = $container['utils.occrunner'];
265
266
		// has to be installed
267
		$file = $locator->getPathToConfigFile();
268
		$this->assertFileExists($file, 'ownCloud in ' . dirname(dirname($file)) . ' is not installed.');
269
270
		// version.php should exist
271
		$file = $locator->getPathToVersionFile();
272
		$this->assertFileExists($file, 'ownCloud is not found in ' . dirname($file));
273
274
		$status = $occRunner->runJson('status');
275
		if (!isset($status['installed'])  || $status['installed'] != 'true'){
276
			throw new \RuntimeException('ownCloud in ' . dirname($file) . ' is not installed.');
277
		}
278
279
		// datadir should exist
280
		$dataDir = $locator->getDataDir();
281
		if (!$fsHelper->fileExists($dataDir)){
282
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' does not exist.');
283
		}
284
285
		// datadir should be writable
286
		if (!$fsHelper->isWritable($dataDir)){
287
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' is not writable.');
288
		}
289
290
		// assert minimum version
291
		$installedVersion = implode('.', $locator->getInstalledVersion());
292
		if (version_compare($installedVersion, '9.0.0', '<')){
293
			throw new \RuntimeException("Minimum ownCloud version 9.0.0 is required for the updater - $installedVersion was found in " . $locator->getOwnCloudRootPath());
294
		}
295
296
		if (!$fsHelper->fileExists($locator->getUpdaterBaseDir())){
297
			$fsHelper->mkdir($locator->getUpdaterBaseDir());
298
		}
299
300
		if (!$fsHelper->fileExists($locator->getDownloadBaseDir())){
301
			$fsHelper->mkdir($locator->getDownloadBaseDir());
302
		}
303
		if (!$fsHelper->fileExists($locator->getCheckpointDir())){
304
			$fsHelper->mkdir($locator->getCheckpointDir());
305
		}
306
	}
307
308
	/**
309
	 * @param string $path
310
	 * @param string $message
311
	 */
312
	protected function assertFileExists($path, $message){
313
		if (!file_exists($path) || !is_file($path)){
314
			throw new \RuntimeException($message);
315
		}
316
	}
317
}
318