Completed
Push — master ( c0208a...6ad896 )
by Thomas
14s
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
	/** @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
			$this->assertOwnCloudFound();
153
			$configReader = $this->diContainer['utils.configReader'];
154
			$commandName = $this->getCommandName($input);
155
			try{
156
				$configReader->init();
157
				if (!isset($this->diContainer['utils.docLink'])) {
158
					$this->diContainer['utils.docLink'] = function ($c) {
159
						$locator = $c['utils.locator'];
160
						$installedVersion = implode('.', $locator->getInstalledVersion());
161
						return new DocLink($installedVersion);
162
					};
163
				}
164
			} catch (ProcessFailedException $e){
165
				if (!in_array($commandName, $this->allowFailure)){
166
					$this->logException($e);
167
					$output->writeln("<error>Initialization failed with message:</error>");
168
					$output->writeln($e->getProcess()->getOutput());
169
					$output->writeln('<info>Use upgrade:checkpoint --list to view a list of checkpoints</info>');
170
					$output->writeln('<info>upgrade:checkpoint --restore [checkpointid] to revert to the last checkpoint</info>');
171
					$output->writeln('Please attach your update.log to the issues you reporting.');
172
					return 1;
173
				}
174
			}
175
			// TODO: check if the current command needs a valid OC instance
176
			if (!isset($this->diContainer['logger'])) {
177
				$locator = $this->diContainer['utils.locator'];
178
				$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...
179
			}
180
			
181
			return parent::doRun($input, $output);
182
		} catch (\Exception $e){
183
			$this->logException($e);
184
			throw $e;
185
		}
186
	}
187
188
	/**
189
	 * @param Command $command
190
	 * @param InputInterface $input
191
	 * @param OutputInterface $output
192
	 * @return int
193
	 * @throws \Exception
194
	 */
195
	protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output){
196
		if ($command instanceof \Owncloud\Updater\Command\Command){
197
			$command->setContainer($this->getContainer());
198
			$commandName = $this->getCommandName($input);
199
			$this->getLogger()->info('Execution of ' . $commandName . ' command started');
200
			if (!empty($command->getMessage())){
201
				$message = sprintf('<info>%s</info>', $command->getMessage());
202
				$output->writeln($message);
203
			}
204
			$exitCode = parent::doRunCommand($command, $input, $output);
205
			$this->getLogger()->info(
206
					'Execution of ' . $commandName . ' command stopped. Exit code is ' . $exitCode
207
			);
208
		} else {
209
			$exitCode = parent::doRunCommand($command, $input, $output);
210
		}
211
		return $exitCode;
212
	}
213
214
	/**
215
	 * @param string $baseDir
216
	 */
217
	protected function initLogger($baseDir){
218
		$container = $this->getContainer();
219
		$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...
220
			$stream = @fopen($baseDir . '/update.log', 'a+');
221
			if ($stream === false){
222
				$stream = @fopen('php://stderr', 'a');
223
			}
224
			return new StreamOutput($stream, StreamOutput::VERBOSITY_DEBUG, false);
225
		};
226
		$container['logger'] = function($c){
227
			return new ConsoleLogger($c['logger.output']);
228
		};
229
	}
230
231
	/**
232
	 * Check for ownCloud instance
233
	 * @throws \RuntimeException
234
	 */
235
	public function assertOwnCloudFound(){
236
		$container = $this->getContainer();
237
		/** @var Locator $locator */
238
		$locator = $container['utils.locator'];
239
		$fsHelper = $container['utils.filesystemhelper'];
240
		/** @var OccRunner $occRunner */
241
		$occRunner = $container['utils.occrunner'];
242
243
		// has to be installed
244
		$file = $locator->getPathToConfigFile();
245
		$this->assertFileExists($file, 'ownCloud in ' . dirname(dirname($file)) . ' is not installed.');
246
247
		// version.php should exist
248
		$file = $locator->getPathToVersionFile();
249
		$this->assertFileExists($file, 'ownCloud is not found in ' . dirname($file));
250
251
		$status = $occRunner->runJson('status');
252
		if (!isset($status['installed'])  || $status['installed'] != 'true'){
253
			throw new \RuntimeException('ownCloud in ' . dirname($file) . ' is not installed.');
254
		}
255
256
		// datadir should exist
257
		$dataDir = $locator->getDataDir();
258
		if (!$fsHelper->fileExists($dataDir)){
259
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' does not exist.');
260
		}
261
262
		// datadir should be writable
263
		if (!$fsHelper->isWritable($dataDir)){
264
			throw new \RuntimeException('Datadirectory ' . $dataDir . ' is not writable.');
265
		}
266
267
		// assert minimum version
268
		$installedVersion = implode('.', $locator->getInstalledVersion());
269
		if (version_compare($installedVersion, '9.0.0', '<')){
270
			throw new \RuntimeException("Minimum ownCloud version 9.0.0 is required for the updater - $installedVersion was found in " . $locator->getOwnCloudRootPath());
271
		}
272
273
		if (!$fsHelper->fileExists($locator->getUpdaterBaseDir())){
274
			$fsHelper->mkdir($locator->getUpdaterBaseDir());
275
		}
276
277
		if (!$fsHelper->fileExists($locator->getDownloadBaseDir())){
278
			$fsHelper->mkdir($locator->getDownloadBaseDir());
279
		}
280
		if (!$fsHelper->fileExists($locator->getCheckpointDir())){
281
			$fsHelper->mkdir($locator->getCheckpointDir());
282
		}
283
	}
284
285
	/**
286
	 * @param string $path
287
	 * @param string $message
288
	 */
289
	protected function assertFileExists($path, $message){
290
		if (!file_exists($path) || !is_file($path)){
291
			throw new \RuntimeException($message);
292
		}
293
	}
294
}
295