1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @package: chapi |
4
|
|
|
* |
5
|
|
|
* @author: msiebeneicher |
6
|
|
|
* @since: 2015-07-28 |
7
|
|
|
* |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Chapi\Commands; |
11
|
|
|
|
12
|
|
|
use Chapi\Component\DependencyInjection\Loader\YamChapiConfigLoader; |
13
|
|
|
use Symfony\Component\Config\FileLocator; |
14
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
15
|
|
|
use Symfony\Component\Console\Input\InputOption; |
16
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
17
|
|
|
use Symfony\Component\Console\Question\Question; |
18
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
19
|
|
|
use Symfony\Component\Yaml\Dumper; |
20
|
|
|
use Symfony\Component\Yaml\Parser; |
21
|
|
|
|
22
|
|
|
class ConfigureCommand extends AbstractCommand |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* Configures the current command. |
26
|
|
|
*/ |
27
|
4 |
|
protected function configure() |
28
|
|
|
{ |
29
|
4 |
|
$this->setName('configure') |
30
|
4 |
|
->setDescription('Configure application and add necessary configs') |
31
|
4 |
|
->addOption('cache_dir', 'd', InputOption::VALUE_OPTIONAL, 'Path to cache directory') |
32
|
|
|
|
33
|
4 |
|
->addOption('chronos_url', 'u', InputOption::VALUE_OPTIONAL, 'The chronos url (inclusive port)', '') |
34
|
4 |
|
->addOption('chronos_http_username', 'un', InputOption::VALUE_OPTIONAL, 'The chronos username (HTTP credentials)', '') |
35
|
4 |
|
->addOption('chronos_http_password', 'p', InputOption::VALUE_OPTIONAL, 'The chronos password (HTTP credentials)', '') |
36
|
4 |
|
->addOption('repository_dir', 'r', InputOption::VALUE_OPTIONAL, 'Root path to your job files', '') |
37
|
|
|
|
38
|
4 |
|
->addOption('marathon_url', 'mu', InputOption::VALUE_OPTIONAL, 'The marathon url (inclusive port)', '') |
39
|
4 |
|
->addOption('marathon_http_username', 'mun', InputOption::VALUE_OPTIONAL, 'The marathon username (HTTP credentials)', '') |
40
|
4 |
|
->addOption('marathon_http_password', 'mp', InputOption::VALUE_OPTIONAL, 'The marathon password (HTTP credentials)', '') |
41
|
4 |
|
->addOption('repository_dir_marathon', 'mr', InputOption::VALUE_OPTIONAL, 'Root path to the app files', '') |
42
|
|
|
; |
43
|
4 |
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @param InputInterface $oInput |
47
|
|
|
* @param OutputInterface $oOutput |
48
|
|
|
* @return int |
49
|
|
|
*/ |
50
|
4 |
|
protected function execute(InputInterface $oInput, OutputInterface $oOutput) |
51
|
|
|
{ |
52
|
4 |
|
$this->oInput = $oInput; |
53
|
4 |
|
$this->oOutput = $oOutput; |
54
|
|
|
|
55
|
4 |
|
return $this->process(); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @return int |
60
|
|
|
*/ |
61
|
4 |
|
protected function process() |
62
|
|
|
{ |
63
|
4 |
|
$_aParams = $this->getInputValues(); |
64
|
|
|
|
65
|
4 |
|
if ($this->hasValidateUserInput($_aParams)) |
66
|
|
|
{ |
67
|
3 |
|
$this->saveParameters($_aParams); |
68
|
3 |
|
return 0; |
69
|
|
|
} |
70
|
|
|
|
71
|
1 |
|
return 1; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @return array<string,array<string,string|boolean>> |
76
|
|
|
*/ |
77
|
4 |
|
private function getInputValues() |
78
|
|
|
{ |
79
|
4 |
|
$_aResult = []; |
80
|
|
|
|
81
|
4 |
|
$_aResult['cache_dir'] = [ |
82
|
4 |
|
'value' => $this->getInputValue('cache_dir', '[GLOBAL] Please enter a cache directory'), |
83
|
|
|
'required' => true |
84
|
|
|
]; |
85
|
|
|
|
86
|
4 |
|
$_aResult['chronos_url'] = [ |
87
|
4 |
|
'value' => $this->getInputValue('chronos_url', '[CHRONOS] Please enter the chronos url (inclusive port)'), |
88
|
|
|
'required' => false |
89
|
|
|
]; |
90
|
|
|
|
91
|
4 |
|
$_aResult['chronos_http_username'] = [ |
92
|
4 |
|
'value' => $this->getInputValue('chronos_http_username', '[CHRONOS] Please enter the username to access your chronos instance'), |
93
|
|
|
'required' => false |
94
|
|
|
]; |
95
|
|
|
|
96
|
4 |
|
$_aResult['chronos_http_password'] = [ |
97
|
4 |
|
'value' => $this->getInputValue('chronos_http_password', '[CHRONOS] Please enter the password to access your chronos instance', true), |
98
|
|
|
'required' => false |
99
|
|
|
]; |
100
|
|
|
|
101
|
4 |
|
$_aResult['repository_dir'] = [ |
102
|
4 |
|
'value' => $this->getInputValue('repository_dir', '[CHRONOS] Please enter absolute path to your local chronos jobs configurations'), |
103
|
|
|
'required' => false |
104
|
|
|
]; |
105
|
|
|
|
106
|
4 |
|
$_aResult['marathon_url'] = [ |
107
|
4 |
|
'value' => $this->getInputValue('marathon_url', '[MARATHON] Please enter the marathon url (inclusive port)'), |
108
|
|
|
'required' => false |
109
|
|
|
]; |
110
|
|
|
|
111
|
4 |
|
$_aResult['marathon_http_username'] = [ |
112
|
4 |
|
'value' => $this->getInputValue('marathon_http_username', '[MARATHON] Please enter the username to access marathon instance'), |
113
|
|
|
'required' => false |
114
|
|
|
]; |
115
|
|
|
|
116
|
4 |
|
$_aResult['marathon_http_password'] = [ |
117
|
4 |
|
'value' => $this->getInputValue('marathon_http_password', '[MARATHON] Please enter the password to access marathon instance', true), |
118
|
|
|
'required' => false |
119
|
|
|
]; |
120
|
|
|
|
121
|
4 |
|
$_aResult['repository_dir_marathon'] = [ |
122
|
4 |
|
'value' => $this->getInputValue('repository_dir_marathon', '[MARATHON] Please enter absolute path to your local marathon tasks configurations'), |
123
|
|
|
'required' => false |
124
|
|
|
]; |
125
|
|
|
|
126
|
4 |
|
return $_aResult; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* @param string $sValueKey |
131
|
|
|
* @param string $sQuestion |
132
|
|
|
* @param boolean $bHiddenAnswer |
133
|
|
|
* @return string |
134
|
|
|
*/ |
135
|
4 |
|
private function getInputValue($sValueKey, $sQuestion, $bHiddenAnswer = false) |
136
|
|
|
{ |
137
|
4 |
|
$_sValue = $this->oInput->getOption($sValueKey); |
138
|
4 |
|
if (empty($_sValue)) |
139
|
|
|
{ |
140
|
3 |
|
$_sValue = $this->printQuestion( |
141
|
|
|
$sQuestion, |
142
|
3 |
|
$this->getParameterValue($sValueKey), |
143
|
|
|
$bHiddenAnswer |
144
|
|
|
); |
145
|
|
|
} |
146
|
|
|
|
147
|
4 |
|
return $_sValue; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* @param array $aUserInput |
152
|
|
|
*/ |
153
|
3 |
|
private function saveParameters(array $aUserInput) |
154
|
|
|
{ |
155
|
|
|
// We implemented an additional level of information |
156
|
|
|
// into the user input array: Is this field required or not? |
157
|
|
|
// To be backwards compatible we only store the value of |
158
|
|
|
// the question in the dump file. |
159
|
|
|
// With this loop we get rid of the "required" information |
160
|
|
|
// from getInputValues(). |
161
|
3 |
|
$aToStore = []; |
162
|
3 |
|
foreach ($aUserInput as $key => $value) |
163
|
|
|
{ |
164
|
3 |
|
$aToStore[$key] = ('null' === $value['value']) ? null : $value['value']; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$_aConfigToSave = [ |
168
|
3 |
|
$this->getProfileName() => [ |
169
|
3 |
|
'parameters' => $aToStore |
170
|
|
|
] |
171
|
|
|
]; |
172
|
|
|
|
173
|
3 |
|
$_sPath = $this->getHomeDir() . DIRECTORY_SEPARATOR . $this->getParameterFileName(); |
174
|
|
|
|
175
|
|
|
// load exiting config to merge |
176
|
3 |
|
$_aConfig = $this->loadConfigFile(['profiles' => []]); |
177
|
|
|
|
178
|
|
|
$_aFinalConfig = [ |
179
|
3 |
|
'profiles' => array_merge($_aConfig['profiles'], $_aConfigToSave) |
180
|
|
|
]; |
181
|
|
|
|
182
|
|
|
|
183
|
|
|
// dump final config |
184
|
3 |
|
$_oDumper = new Dumper(); |
185
|
3 |
|
$_sYaml = $_oDumper->dump($_aFinalConfig, 4); |
186
|
|
|
|
187
|
3 |
|
$_oFileSystem = new Filesystem(); |
188
|
3 |
|
$_oFileSystem->dumpFile( |
189
|
|
|
$_sPath, |
190
|
|
|
$_sYaml |
191
|
|
|
); |
192
|
3 |
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param array $aUserInput |
196
|
|
|
* @return bool |
197
|
|
|
*/ |
198
|
4 |
|
private function hasValidateUserInput(array $aUserInput) |
199
|
|
|
{ |
200
|
4 |
|
foreach ($aUserInput as $_sKey => $_sValue) |
201
|
|
|
{ |
202
|
4 |
|
if ($_sValue['required'] == true && empty($_sValue['value'])) |
203
|
|
|
{ |
204
|
1 |
|
$this->oOutput->writeln(sprintf('<error>Please add a valid value for parameter "%s"</error>', $_sKey)); |
205
|
4 |
|
return false; |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
|
209
|
3 |
|
return true; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @param string $sKey |
214
|
|
|
* @param mixed $mDefaultValue |
215
|
|
|
* @return mixed |
216
|
|
|
*/ |
217
|
3 |
|
private function getParameterValue($sKey, $mDefaultValue = null) |
218
|
|
|
{ |
219
|
3 |
|
$_aParameters = $this->getParameters(); |
220
|
|
|
|
221
|
3 |
|
if (isset($_aParameters['parameters']) && isset($_aParameters['parameters'][$sKey])) |
222
|
|
|
{ |
223
|
|
|
return $_aParameters['parameters'][$sKey]; |
224
|
|
|
} |
225
|
|
|
|
226
|
3 |
|
return $mDefaultValue; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @return array |
231
|
|
|
*/ |
232
|
3 |
|
private function getParameters() |
233
|
|
|
{ |
234
|
3 |
|
$_sProfile = $this->getProfileName(); |
235
|
3 |
|
$_aParameters = $this->loadConfigFile(); |
236
|
|
|
|
237
|
3 |
|
return (isset($_aParameters['profiles']) && isset($_aParameters['profiles'][$_sProfile])) |
238
|
|
|
? $_aParameters['profiles'][$_sProfile] |
239
|
3 |
|
: ['profiles' => []]; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param mixed $mDefaultValue |
244
|
|
|
* @return array |
245
|
|
|
*/ |
246
|
4 |
|
private function loadConfigFile($mDefaultValue = []) |
247
|
|
|
{ |
248
|
4 |
|
$_sParameterFile = $this->getHomeDir() . DIRECTORY_SEPARATOR . $this->getParameterFileName(); |
249
|
|
|
|
250
|
4 |
|
if (!file_exists($_sParameterFile)) { |
251
|
4 |
|
return $mDefaultValue; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
$_oParser = new Parser(); |
255
|
|
|
|
256
|
|
|
$_aParameters = $_oParser->parse( |
257
|
|
|
file_get_contents($_sParameterFile) |
258
|
|
|
); |
259
|
|
|
|
260
|
|
|
return $_aParameters; |
|
|
|
|
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @param string $sQuestion |
266
|
|
|
* @param null|mixed $mDefaultValue |
267
|
|
|
* @param boolean $bHiddenAnswer |
268
|
|
|
* @return mixed |
269
|
|
|
*/ |
270
|
3 |
|
private function printQuestion($sQuestion, $mDefaultValue = null, $bHiddenAnswer = false) |
271
|
|
|
{ |
272
|
3 |
|
$_oHelper = $this->getHelper('question'); |
273
|
|
|
|
274
|
|
|
// If we have a hidden answer and the default value is not empty |
275
|
|
|
// the we will set it as empty, because we don`t want to show |
276
|
|
|
// the default value on the terminal. |
277
|
|
|
// We know that the user has to enter the password again |
278
|
|
|
// if he / she want to reconfigure something. But this |
279
|
|
|
// is an acceptable tradeoff. |
280
|
3 |
|
if ($bHiddenAnswer === true && !empty($mDefaultValue)) |
281
|
|
|
{ |
282
|
|
|
$mDefaultValue = null; |
|
|
|
|
283
|
|
|
} |
284
|
|
|
|
285
|
3 |
|
$_sFormat = (!empty($mDefaultValue)) ? '<comment>%s (default: %s):</comment>' : '<comment>%s:</comment>'; |
286
|
3 |
|
$_oQuestion = new Question(sprintf($_sFormat, $sQuestion, $mDefaultValue), $mDefaultValue); |
287
|
|
|
|
288
|
|
|
// Sensitive information (like passwords) should not be |
289
|
|
|
// visible during the configuration wizard |
290
|
3 |
|
if ($bHiddenAnswer === true) |
291
|
|
|
{ |
292
|
3 |
|
$_oQuestion->setHidden(true); |
293
|
3 |
|
$_oQuestion->setHiddenFallback(false); |
294
|
|
|
} |
295
|
|
|
|
296
|
3 |
|
return $_oHelper->ask($this->oInput, $this->oOutput, $_oQuestion); |
297
|
|
|
} |
298
|
|
|
} |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.