Completed
Pull Request — master (#348)
by Victor
04:50
created

Application   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 199
Duplicated Lines 3.02 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 8
Bugs 1 Features 0
Metric Value
wmc 27
c 8
b 1
f 0
lcom 1
cbo 11
dl 6
loc 199
ccs 0
cts 112
cp 0
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A setContainer() 0 4 1
A getContainer() 0 3 1
A getLogger() 0 13 3
A logException() 0 5 1
B doRun() 0 26 4
A doRunCommand() 0 18 3
A initLogger() 0 13 2
C assertOwnCloudFound() 6 50 12

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\Locator;
26
use Pimple\Container;
27
use Symfony\Component\Console\Logger\ConsoleLogger;
28
use Symfony\Component\Console\Output\ConsoleOutput;
29
use Symfony\Component\Console\Output\StreamOutput;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Output\BufferedOutput;
32
use Symfony\Component\Console\Output\OutputInterface;
33
use Symfony\Component\Console\Command\Command;
34
use \Symfony\Component\Process\Exception\ProcessFailedException;
35
36
/**
37
 * Class Application
38
 *
39
 * @package Owncloud\Updater\Console
40
 */
41
class Application extends \Symfony\Component\Console\Application {
42
43
	/** @var Container */
44
	public static $container;
45
46
	/** @var Container */
47
	protected $diContainer;
48
49
	/** @var ConsoleLogger */
50
	protected $fallbackLogger;
51
52
53
	/** @var array */
54
	protected $allowFailure = [
55
		'upgrade:executeCoreUpgradeScripts',
56
		'upgrade:checkpoint',
57
		'upgrade:maintenanceMode',
58
		'help',
59
		'list'
60
	];
61
62
	/**
63
	 * Pass Pimple container into application
64
	 * @param Container $container
65
	 */
66
	public function setContainer(Container $container){
67
		$this->diContainer = $container;
68
		self::$container = $container;
69
	}
70
71
	/**
72
	 * Get Pimple container
73
	 * @return Container
74
	 */
75
	public function getContainer(){
76
		return $this->diContainer;
77
	}
78
79
	/**
80
	 * Get logger instance
81
	 * @return \Psr\Log\LoggerInterface
82
	 */
83
	public function getLogger(){
84
		if (isset($this->diContainer['logger'])) {
85
			return $this->diContainer['logger'];
86
		}
87
88
		// Logger is not available yet, fallback to stdout
89
		if (is_null($this->fallbackLogger)){
90
			$output = new ConsoleOutput();
91
			$this->fallbackLogger = new ConsoleLogger($output);
92
		}
93
94
		return $this->fallbackLogger;
95
	}
96
97
	/**
98
	 * Log exception with trace
99
	 * @param \Exception $e
100
	 */
101
	public function logException($e){
102
		$buffer = new BufferedOutput(OutputInterface::VERBOSITY_VERBOSE);
103
		$this->renderException($e, $buffer);
104
		$this->getLogger()->error($buffer->fetch());
105
	}
106
107
	/**
108
	 * Runs the current application.
109
	 *
110
	 * @param InputInterface $input An Input instance
111
	 * @param OutputInterface $output An Output instance
112
	 * @return int 0 if everything went fine, or an error code
113
	 * @throws \Exception
114
	 */
115
	public function doRun(InputInterface $input, OutputInterface $output){
116
		try{
117
			$configReader = $this->diContainer['utils.configReader'];
118
			$commandName = $this->getCommandName($input);
119
			try{
120
				$configReader->init();
121
			} catch (ProcessFailedException $e){
122
				if (!in_array($commandName, $this->allowFailure)){
123
					$this->logException($e);
124
					$output->writeln("<error>Initialization failed with message:</error>");
125
					$output->writeln($e->getProcess()->getOutput());
126
					$output->writeln('<info>Use upgrade:checkpoint --list to view a list of checkpoints</info>');
127
					$output->writeln('<info>upgrade:checkpoint --restore [checkpointid] to revert to the last checkpoint</info>');
128
					$output->writeln('Please attach your update.log to the issues you reporting.');
129
					return 1;
130
				}
131
			}
132
			// TODO: check if the current command needs a valid OC instance
133
			$this->assertOwnCloudFound();
134
			
135
			return parent::doRun($input, $output);
136
		} catch (\Exception $e){
137
			$this->logException($e);
138
			throw $e;
139
		}
140
	}
141
142
	/**
143
	 * @param Command $command
144
	 * @param InputInterface $input
145
	 * @param OutputInterface $output
146
	 * @return int
147
	 * @throws \Exception
148
	 */
149
	protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output){
150
		if ($command instanceof \Owncloud\Updater\Command\Command){
151
			$command->setContainer($this->getContainer());
152
			$commandName = $this->getCommandName($input);
153
			$this->getLogger()->info('Execution of ' . $commandName . ' command started');
154
			if (!empty($command->getMessage())){
155
				$message = sprintf('<info>%s</info>', $command->getMessage());
156
				$output->writeln($message);
157
			}
158
			$exitCode = parent::doRunCommand($command, $input, $output);
159
			$this->getLogger()->info(
160
					'Execution of ' . $commandName . ' command stopped. Exit code is ' . $exitCode
161
			);
162
		} else {
163
			$exitCode = parent::doRunCommand($command, $input, $output);
164
		}
165
		return $exitCode;
166
	}
167
168
	/**
169
	 * @param string $baseDir
170
	 */
171
	protected function initLogger($baseDir){
172
		$container = $this->getContainer();
173
		$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...
174
			$stream = @fopen($baseDir . '/update.log', 'a+');
175
			if ($stream === false){
176
				$stream = @fopen('php://stderr', 'a');
177
			}
178
			return new StreamOutput($stream, StreamOutput::VERBOSITY_DEBUG, false);
179
		};
180
		$container['logger'] = function($c){
181
			return new ConsoleLogger($c['logger.output']);
182
		};
183
	}
184
185
	/**
186
	 * Check for ownCloud instance
187
	 * @throws \RuntimeException
188
	 */
189
	protected function assertOwnCloudFound(){
190
		$container = $this->getContainer();
191
		/** @var Locator $locator */
192
		$locator = $container['utils.locator'];
193
		$fsHelper = $container['utils.filesystemhelper'];
194
195
		// assert minimum version
196
		$installedVersion = implode('.', $locator->getInstalledVersion());
197
		if (version_compare($installedVersion, '9.0.0', '<')){
198
			throw new \RuntimeException("Minimum ownCloud version 9.0.0 is required for the updater - $installedVersion was found in " . $locator->getOwnCloudRootPath());
199
		}
200
201
		// has to be installed
202
		$file = $locator->getPathToConfigFile();
203 View Code Duplication
		if (!file_exists($file) || !is_file($file)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
204
			throw new \RuntimeException('ownCloud in ' . dirname(dirname($file)) . ' is not installed.');
205
		}
206
207
		// version.php should exist
208
		$file = $locator->getPathToVersionFile();
209 View Code Duplication
		if (!file_exists($file) || !is_file($file)){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
			throw new \RuntimeException('ownCloud is not found in ' . dirname($file));
211
		}
212
213
		// datadir should exist
214
		$dataDir = $locator->getDataDir();
215
		if (!$fsHelper->fileExists($dataDir)){
216
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' does not exist.');
217
		}
218
219
		// datadir should be writable
220
		if (!$fsHelper->isWritable($dataDir)){
221
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' is not writable.');
222
		}
223
224
		if (!isset($this->diContainer['logger'])) {
225
			$this->initLogger($dataDir);
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...
226
		}
227
228
		if (!$fsHelper->fileExists($locator->getUpdaterBaseDir())){
229
			$fsHelper->mkdir($locator->getUpdaterBaseDir());
230
		}
231
232
		if (!$fsHelper->fileExists($locator->getDownloadBaseDir())){
233
			$fsHelper->mkdir($locator->getDownloadBaseDir());
234
		}
235
		if (!$fsHelper->fileExists($locator->getCheckpointDir())){
236
			$fsHelper->mkdir($locator->getCheckpointDir());
237
		}
238
	}
239
}
240