Completed
Pull Request — master (#354)
by Victor
02:13
created

Application::assertFileExists()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 5
cp 0
rs 9.4285
cc 3
eloc 3
nc 2
nop 2
crap 12
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
55
	/** @var array */
56
	protected $allowFailure = [
57
		'upgrade:executeCoreUpgradeScripts',
58
		'upgrade:checkpoint',
59
		'upgrade:maintenanceMode',
60
		'help',
61
		'list'
62
	];
63
64
	/**
65
	 * Pass Pimple container into application
66
	 * @param Container $container
67
	 */
68
	public function setContainer(Container $container){
69
		$this->diContainer = $container;
70
		self::$container = $container;
71
	}
72
73
	/**
74
	 * Get Pimple container
75
	 * @return Container
76
	 */
77
	public function getContainer(){
78
		return $this->diContainer;
79
	}
80
81
	/**
82
	 * Get logger instance
83
	 * @return \Psr\Log\LoggerInterface
84
	 */
85
	public function getLogger(){
86
		if (isset($this->diContainer['logger'])) {
87
			return $this->diContainer['logger'];
88
		}
89
90
		// Logger is not available yet, fallback to stdout
91
		if (is_null($this->fallbackLogger)){
92
			$output = new ConsoleOutput();
93
			$this->fallbackLogger = new ConsoleLogger($output);
94
		}
95
96
		return $this->fallbackLogger;
97
	}
98
99
	/**
100
	 * Log exception with trace
101
	 * @param \Exception $e
102
	 */
103
	public function logException($e){
104
		$buffer = new BufferedOutput(OutputInterface::VERBOSITY_VERBOSE);
105
		$this->renderException($e, $buffer);
106
		$this->getLogger()->error($buffer->fetch());
107
	}
108
109
	/**
110
	 * Runs the current application.
111
	 *
112
	 * @param InputInterface $input An Input instance
113
	 * @param OutputInterface $output An Output instance
114
	 * @return int 0 if everything went fine, or an error code
115
	 * @throws \Exception
116
	 */
117
	public function doRun(InputInterface $input, OutputInterface $output){
118
		try{
119
			$this->assertOwnCloudFound();
120
			$configReader = $this->diContainer['utils.configReader'];
121
			$commandName = $this->getCommandName($input);
122
			try{
123
				$configReader->init();
124
				if (!isset($this->diContainer['utils.docLink'])) {
125
					$this->diContainer['utils.docLink'] = function ($c) {
126
						$locator = $c['utils.locator'];
127
						$installedVersion = implode('.', $locator->getInstalledVersion());
128
						return new DocLink($installedVersion);
129
					};
130
				}
131
			} catch (ProcessFailedException $e){
132
				if (!in_array($commandName, $this->allowFailure)){
133
					$this->logException($e);
134
					$output->writeln("<error>Initialization failed with message:</error>");
135
					$output->writeln($e->getProcess()->getOutput());
136
					$output->writeln('<info>Use upgrade:checkpoint --list to view a list of checkpoints</info>');
137
					$output->writeln('<info>upgrade:checkpoint --restore [checkpointid] to revert to the last checkpoint</info>');
138
					$output->writeln('Please attach your update.log to the issues you reporting.');
139
					return 1;
140
				}
141
			}
142
			// TODO: check if the current command needs a valid OC instance
143
			if (!isset($this->diContainer['logger'])) {
144
				$locator = $this->diContainer['utils.locator'];
145
				$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...
146
			}
147
			
148
			return parent::doRun($input, $output);
149
		} catch (\Exception $e){
150
			$this->logException($e);
151
			throw $e;
152
		}
153
	}
154
155
	/**
156
	 * @param Command $command
157
	 * @param InputInterface $input
158
	 * @param OutputInterface $output
159
	 * @return int
160
	 * @throws \Exception
161
	 */
162
	protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output){
163
		if ($command instanceof \Owncloud\Updater\Command\Command){
164
			$command->setContainer($this->getContainer());
165
			$commandName = $this->getCommandName($input);
166
			$this->getLogger()->info('Execution of ' . $commandName . ' command started');
167
			if (!empty($command->getMessage())){
168
				$message = sprintf('<info>%s</info>', $command->getMessage());
169
				$output->writeln($message);
170
			}
171
			$exitCode = parent::doRunCommand($command, $input, $output);
172
			$this->getLogger()->info(
173
					'Execution of ' . $commandName . ' command stopped. Exit code is ' . $exitCode
174
			);
175
		} else {
176
			$exitCode = parent::doRunCommand($command, $input, $output);
177
		}
178
		return $exitCode;
179
	}
180
181
	/**
182
	 * @param string $baseDir
183
	 */
184
	protected function initLogger($baseDir){
185
		$container = $this->getContainer();
186
		$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...
187
			$stream = @fopen($baseDir . '/update.log', 'a+');
188
			if ($stream === false){
189
				$stream = @fopen('php://stderr', 'a');
190
			}
191
			return new StreamOutput($stream, StreamOutput::VERBOSITY_DEBUG, false);
192
		};
193
		$container['logger'] = function($c){
194
			return new ConsoleLogger($c['logger.output']);
195
		};
196
	}
197
198
	/**
199
	 * Check for ownCloud instance
200
	 * @throws \RuntimeException
201
	 */
202
	public function assertOwnCloudFound(){
203
		$container = $this->getContainer();
204
		/** @var Locator $locator */
205
		$locator = $container['utils.locator'];
206
		$fsHelper = $container['utils.filesystemhelper'];
207
		/** @var OccRunner $occRunner */
208
		$occRunner = $container['utils.occrunner'];
209
210
		// has to be installed
211
		$file = $locator->getPathToConfigFile();
212
		$this->assertFileExists($file, 'ownCloud in ' . dirname(dirname($file)) . ' is not installed.');
213
214
		// version.php should exist
215
		$file = $locator->getPathToVersionFile();
216
		$this->assertFileExists($file, 'ownCloud is not found in ' . dirname($file));
217
218
		$status = $occRunner->runJson('status');
219
		if (!isset($status['installed'])  || $status['installed'] != 'true'){
220
			throw new \RuntimeException('ownCloud in ' . dirname($file) . ' is not installed.');
221
		}
222
223
		// datadir should exist
224
		$dataDir = $locator->getDataDir();
225
		if (!$fsHelper->fileExists($dataDir)){
226
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' does not exist.');
227
		}
228
229
		// datadir should be writable
230
		if (!$fsHelper->isWritable($dataDir)){
231
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' is not writable.');
232
		}
233
234
		// assert minimum version
235
		$installedVersion = implode('.', $locator->getInstalledVersion());
236
		if (version_compare($installedVersion, '9.0.0', '<')){
237
			throw new \RuntimeException("Minimum ownCloud version 9.0.0 is required for the updater - $installedVersion was found in " . $locator->getOwnCloudRootPath());
238
		}
239
240
		if (!$fsHelper->fileExists($locator->getUpdaterBaseDir())){
241
			$fsHelper->mkdir($locator->getUpdaterBaseDir());
242
		}
243
244
		if (!$fsHelper->fileExists($locator->getDownloadBaseDir())){
245
			$fsHelper->mkdir($locator->getDownloadBaseDir());
246
		}
247
		if (!$fsHelper->fileExists($locator->getCheckpointDir())){
248
			$fsHelper->mkdir($locator->getCheckpointDir());
249
		}
250
	}
251
252
	/**
253
	 * @param string $path
254
	 * @param string $message
255
	 */
256
	protected function assertFileExists($path, $message){
257
		if (!file_exists($path) || !is_file($path)){
258
			throw new \RuntimeException($message);
259
		}
260
	}
261
}
262