Failed Conditions
Push — master ( dd969d...42da22 )
by Alexander
10:30
created

ConfigCommand::getConfigSettingsByScope()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
ccs 0
cts 6
cp 0
rs 10
cc 3
nc 3
nop 1
crap 12
1
<?php
2
/**
3
 * This file is part of the SVN-Buddy library.
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @copyright Alexander Obuhovich <[email protected]>
8
 * @link      https://github.com/console-helpers/svn-buddy
9
 */
10
11
namespace ConsoleHelpers\SVNBuddy\Command;
12
13
14
use ConsoleHelpers\SVNBuddy\Config\ArrayConfigSetting;
15
use ConsoleHelpers\ConsoleKit\Config\ConfigEditor;
16
use ConsoleHelpers\SVNBuddy\Config\AbstractConfigSetting;
17
use ConsoleHelpers\SVNBuddy\Config\ChoiceConfigSetting;
18
use ConsoleHelpers\SVNBuddy\InteractiveEditor;
19
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
20
use Symfony\Component\Console\Helper\Table;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
26
class ConfigCommand extends AbstractCommand implements IAggregatorAwareCommand
27
{
28
29
	/**
30
	 * Editor.
31
	 *
32
	 * @var InteractiveEditor
33
	 */
34
	private $_editor;
35
36
	/**
37
	 * Config editor.
38
	 *
39
	 * @var ConfigEditor
40
	 */
41
	private $_configEditor;
42
43
	/**
44
	 * Config settings.
45
	 *
46
	 * @var AbstractConfigSetting[]
47
	 */
48
	protected $configSettings = array();
49
50
	/**
51
	 * {@inheritdoc}
52
	 */
53
	protected function configure()
54
	{
55
		$this
56
			->setName('config')
57
			->setDescription('Change configuration settings, that are used by other commands')
58
			->setAliases(array('cfg'))
59
			->addArgument(
60
				'path',
61
				InputArgument::OPTIONAL,
62
				'Working copy path',
63
				'.'
64
			)
65
			->addOption(
66
				'show',
67
				's',
68
				InputOption::VALUE_REQUIRED,
69
				'Shows only given (instead of all) setting value'
70
			)
71
			->addOption(
72
				'edit',
73
				'e',
74
				InputOption::VALUE_REQUIRED,
75
				'Change setting value in the Interactive Editor'
76
			)
77
			->addOption(
78
				'delete',
79
				'd',
80
				InputOption::VALUE_REQUIRED,
81
				'Delete setting'
82
			)
83
			->addOption(
84
				'global',
85
				'g',
86
				InputOption::VALUE_NONE,
87
				'Operate on global instead of working copy-specific settings'
88
			);
89
90
		parent::configure();
91
	}
92
93
	/**
94
	 * Prepare dependencies.
95
	 *
96
	 * @return void
97
	 */
98
	protected function prepareDependencies()
99
	{
100
		parent::prepareDependencies();
101
102
		$container = $this->getContainer();
103
104
		$this->_editor = $container['editor'];
105
		$this->_configEditor = $container['config_editor'];
106
		$this->configSettings = $this->getConfigSettings();
107
	}
108
109
	/**
110
	 * Return possible values for the named option
111
	 *
112
	 * @param string            $optionName Option name.
113
	 * @param CompletionContext $context    Completion context.
114
	 *
115
	 * @return array
116
	 */
117
	public function completeOptionValues($optionName, CompletionContext $context)
118
	{
119
		$ret = parent::completeOptionValues($optionName, $context);
120
121
		if ( in_array($optionName, array('show', 'edit', 'delete')) ) {
122
			return array_keys($this->configSettings);
123
		}
124
125
		return $ret;
126
	}
127
128
	/**
129
	 * {@inheritdoc}
130
	 */
131
	protected function execute(InputInterface $input, OutputInterface $output)
132
	{
133
		if ( $this->processShow() || $this->processEdit() || $this->processDelete() ) {
134
			return;
135
		}
136
137
		$this->listSettings();
138
	}
139
140
	/**
141
	 * Shows setting value.
142
	 *
143
	 * @return boolean
144
	 */
145
	protected function processShow()
146
	{
147
		$setting_name = $this->io->getOption('show');
148
149
		if ( $setting_name === null ) {
150
			return false;
151
		}
152
153
		$this->listSettings($setting_name);
0 ignored issues
show
Bug introduced by
It seems like $setting_name can also be of type string[]; however, parameter $setting_name of ConsoleHelpers\SVNBuddy\...Command::listSettings() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
		$this->listSettings(/** @scrutinizer ignore-type */ $setting_name);
Loading history...
154
155
		return true;
156
	}
157
158
	/**
159
	 * Changes setting value.
160
	 *
161
	 * @return boolean
162
	 */
163
	protected function processEdit()
164
	{
165
		$setting_name = $this->io->getOption('edit');
166
167
		if ( $setting_name === null ) {
168
			return false;
169
		}
170
171
		$config_setting = $this->getConfigSetting($setting_name);
0 ignored issues
show
Bug introduced by
It seems like $setting_name can also be of type string[]; however, parameter $name of ConsoleHelpers\SVNBuddy\...and::getConfigSetting() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

171
		$config_setting = $this->getConfigSetting(/** @scrutinizer ignore-type */ $setting_name);
Loading history...
172
		$value = $config_setting->getValue($this->getValueFilter());
173
		$retry = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $retry is dead and can be removed.
Loading history...
174
175
		if ( $config_setting instanceof ArrayConfigSetting ) {
176
			$value = implode(PHP_EOL, $value);
177
		}
178
179
		do {
180
			try {
181
				$retry = false;
182
183
				if ( $config_setting instanceof ChoiceConfigSetting ) {
184
					$value = $this->io->choose(
185
						'Please choose value for "' . $setting_name . '" config setting [' . $value . ']:',
0 ignored issues
show
Bug introduced by
Are you sure $setting_name of type boolean|string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

185
						'Please choose value for "' . /** @scrutinizer ignore-type */ $setting_name . '" config setting [' . $value . ']:',
Loading history...
186
						$config_setting->getChoices(),
187
						(string)$value,
188
						'Option value "%s" is invalid.'
189
					);
190
				}
191
				else {
192
					$value = $this->openEditor($value);
193
				}
194
195
				$config_setting->setValue($value, $this->getScopeFilter());
196
				$this->io->writeln('Setting <info>' . $setting_name . '</info> was edited.');
197
			}
198
			catch ( \InvalidArgumentException $e ) {
199
				$this->io->writeln(array('<error>' . $e->getMessage() . '</error>', ''));
200
201
				if ( $this->io->askConfirmation('Retry editing', false) ) {
202
					$retry = true;
203
				}
204
			}
205
		} while ( $retry );
206
207
		return true;
208
	}
209
210
	/**
211
	 * Opens value editing.
212
	 *
213
	 * @param mixed $value Value.
214
	 *
215
	 * @return mixed
216
	 */
217
	protected function openEditor($value)
218
	{
219
		return $this->_editor
220
			->setDocumentName('config_setting_value')
221
			->setContent($value)
222
			->launch();
223
	}
224
225
	/**
226
	 * Deletes setting value.
227
	 *
228
	 * @return boolean
229
	 */
230
	protected function processDelete()
231
	{
232
		$setting_name = $this->io->getOption('delete');
233
234
		if ( $setting_name === null ) {
235
			return false;
236
		}
237
238
		$config_setting = $this->getConfigSetting($setting_name);
0 ignored issues
show
Bug introduced by
It seems like $setting_name can also be of type string[]; however, parameter $name of ConsoleHelpers\SVNBuddy\...and::getConfigSetting() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

238
		$config_setting = $this->getConfigSetting(/** @scrutinizer ignore-type */ $setting_name);
Loading history...
239
		$config_setting->setValue(null, $this->getScopeFilter());
240
		$this->io->writeln('Setting <info>' . $setting_name . '</info> was deleted.');
0 ignored issues
show
Bug introduced by
Are you sure $setting_name of type boolean|string|string[] can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

240
		$this->io->writeln('Setting <info>' . /** @scrutinizer ignore-type */ $setting_name . '</info> was deleted.');
Loading history...
241
242
		return true;
243
	}
244
245
	/**
246
	 * Lists values for every stored setting.
247
	 *
248
	 * @param string $setting_name Setting name.
249
	 *
250
	 * @return void
251
	 */
252
	protected function listSettings($setting_name = null)
253
	{
254
		if ( isset($setting_name) ) {
255
			$this->getConfigSetting($setting_name);
256
		}
257
258
		$extra_title = isset($setting_name) ? ' (filtered)' : '';
259
260
		if ( $this->isGlobal() ) {
261
			$this->io->writeln('Showing global settings' . $extra_title . ':');
262
		}
263
		else {
264
			$this->io->writeln(
265
				'Showing settings' . $extra_title . ' for <info>' . $this->getWorkingCopyUrl() . '</info> url:'
266
			);
267
		}
268
269
		$table = new Table($this->io->getOutput());
270
271
		$table->setHeaders(array(
272
			'Setting Name',
273
			'Setting Value',
274
		));
275
276
		$value_filter = $this->getValueFilter();
277
278
		foreach ( $this->getConfigSettingsByScope($this->getScopeFilter()) as $name => $config_setting ) {
279
			if ( isset($setting_name) && $name !== $setting_name ) {
280
				continue;
281
			}
282
283
			$table->addRow(array(
284
				$name,
285
				var_export($config_setting->getValue($value_filter), true),
286
			));
287
		}
288
289
		$table->render();
290
	}
291
292
	/**
293
	 * Returns config settings filtered by scope.
294
	 *
295
	 * @param integer $scope_filter Scope filter.
296
	 *
297
	 * @return AbstractConfigSetting[]
298
	 */
299
	protected function getConfigSettingsByScope($scope_filter)
300
	{
301
		$ret = array();
302
303
		foreach ( $this->configSettings as $name => $config_setting ) {
304
			if ( $config_setting->isWithinScope($scope_filter) ) {
305
				$ret[$name] = $config_setting;
306
			}
307
		}
308
309
		return $ret;
310
	}
311
312
	/**
313
	 * Validates setting name.
314
	 *
315
	 * @param string $name Setting name.
316
	 *
317
	 * @return AbstractConfigSetting
318
	 * @throws \InvalidArgumentException When non-existing/outside of scope setting given.
319
	 */
320
	protected function getConfigSetting($name)
321
	{
322
		if ( !array_key_exists($name, $this->configSettings) ) {
323
			throw new \InvalidArgumentException('The "' . $name . '" setting is unknown.');
324
		}
325
326
		$config_setting = $this->configSettings[$name];
327
328
		if ( !$config_setting->isWithinScope($this->getScopeFilter()) ) {
329
			throw new \InvalidArgumentException('The "' . $name . '" setting cannot be used in this scope.');
330
		}
331
332
		return $config_setting;
333
	}
334
335
	/**
336
	 * Returns scope filter for viewing config settings.
337
	 *
338
	 * @return integer
339
	 */
340
	protected function getScopeFilter()
341
	{
342
		return $this->isGlobal() ? AbstractConfigSetting::SCOPE_GLOBAL : AbstractConfigSetting::SCOPE_WORKING_COPY;
343
	}
344
345
	/**
346
	 * Returns value filter for editing config settings.
347
	 *
348
	 * @return integer
349
	 */
350
	protected function getValueFilter()
351
	{
352
		return $this->isGlobal() ? AbstractConfigSetting::SCOPE_GLOBAL : null;
353
	}
354
355
	/**
356
	 * Returns possible settings with their defaults.
357
	 *
358
	 * @return AbstractConfigSetting[]
359
	 */
360
	protected function getConfigSettings()
361
	{
362
		/** @var AbstractConfigSetting[] $config_settings */
363
		$config_settings = array();
364
365
		foreach ( $this->getApplication()->all() as $command ) {
366
			if ( $command instanceof IConfigAwareCommand ) {
367
				foreach ( $command->getConfigSettings() as $config_setting ) {
368
					$config_settings[$config_setting->getName()] = $config_setting;
369
				}
370
			}
371
		}
372
373
		// Allow to operate on global settings outside of working copy.
374
		$wc_url = $this->isGlobal() ? '' : $this->getWorkingCopyUrl();
375
376
		foreach ( $config_settings as $config_setting ) {
377
			$config_setting->setWorkingCopyUrl($wc_url);
378
			$config_setting->setEditor($this->_configEditor);
379
		}
380
381
		return $config_settings;
382
	}
383
384
	/**
385
	 * Determines if global only config settings should be used.
386
	 *
387
	 * @return boolean
388
	 */
389
	protected function isGlobal()
390
	{
391
		// During auto-complete the IO isn't set.
392
		if ( !isset($this->io) ) {
393
			return true;
394
		}
395
396
		return $this->io->getOption('global');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->io->getOption('global') also could return the type string|string[] which is incompatible with the documented return type boolean.
Loading history...
397
	}
398
399
	/**
400
	 * Returns option names, that makes sense to use in aggregation mode.
401
	 *
402
	 * @return array
403
	 */
404
	public function getAggregatedOptions()
405
	{
406
		return array();
407
	}
408
409
}
410