1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace N98\Magento\Command; |
4
|
|
|
|
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
use N98\Magento\Command\AbstractMagentoCommand; |
7
|
|
|
use N98\Util\BinaryString; |
8
|
|
|
use N98\Util\Exec; |
9
|
|
|
use RuntimeException; |
10
|
|
|
use Symfony\Component\Console\Helper\DialogHelper; |
11
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
12
|
|
|
use Symfony\Component\Console\Input\InputOption; |
13
|
|
|
use Symfony\Component\Console\Input\StringInput; |
14
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
15
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
16
|
|
|
|
17
|
|
|
class ScriptCommand extends AbstractMagentoCommand |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @var array |
21
|
|
|
*/ |
22
|
|
|
protected $scriptVars = array(); |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var string |
26
|
|
|
*/ |
27
|
|
|
protected $_scriptFilename = ''; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var bool |
31
|
|
|
*/ |
32
|
|
|
protected $_stopOnError = false; |
33
|
|
|
|
34
|
|
View Code Duplication |
protected function configure() |
|
|
|
|
35
|
|
|
{ |
36
|
|
|
$this |
37
|
|
|
->setName('script') |
38
|
|
|
->addArgument('filename', InputArgument::OPTIONAL, 'Script file') |
39
|
|
|
->addOption('define', 'd', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Defines a variable') |
40
|
|
|
->addOption('stop-on-error', null, InputOption::VALUE_NONE, 'Stops execution of script on error') |
41
|
|
|
->setDescription('Runs multiple n98-magerun commands') |
42
|
|
|
; |
43
|
|
|
|
44
|
|
|
$help = <<<HELP |
45
|
|
|
Example: |
46
|
|
|
|
47
|
|
|
# Set multiple config |
48
|
|
|
config:set "web/cookie/cookie_domain" example.com |
49
|
|
|
|
50
|
|
|
# Set with multiline values with "\n" |
51
|
|
|
config:set "general/store_information/address" "First line\nSecond line\nThird line" |
52
|
|
|
|
53
|
|
|
# This is a comment |
54
|
|
|
cache:flush |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
Optionally you can work with unix pipes. |
58
|
|
|
|
59
|
|
|
\$ echo "cache:flush" | n98-magerun-dev script |
60
|
|
|
|
61
|
|
|
\$ n98-magerun.phar script < filename |
62
|
|
|
|
63
|
|
|
It is even possible to create executable scripts: |
64
|
|
|
|
65
|
|
|
Create file `test.magerun` and make it executable (`chmod +x test.magerun`): |
66
|
|
|
|
67
|
|
|
#!/usr/bin/env n98-magerun.phar script |
68
|
|
|
|
69
|
|
|
config:set "web/cookie/cookie_domain" example.com |
70
|
|
|
cache:flush |
71
|
|
|
|
72
|
|
|
# Run a shell script with "!" as first char |
73
|
|
|
! ls -l |
74
|
|
|
|
75
|
|
|
# Register your own variable (only key = value currently supported) |
76
|
|
|
\${my.var}=bar |
77
|
|
|
|
78
|
|
|
# Let magerun ask for variable value - add a question mark |
79
|
|
|
\${my.var}=? |
80
|
|
|
|
81
|
|
|
! echo \${my.var} |
82
|
|
|
|
83
|
|
|
# Use resolved variables from n98-magerun in shell commands |
84
|
|
|
! ls -l \${magento.root}/code/local |
85
|
|
|
|
86
|
|
|
Pre-defined variables: |
87
|
|
|
|
88
|
|
|
* \${magento.root} -> Magento Root-Folder |
89
|
|
|
* \${magento.version} -> Magento Version i.e. 1.7.0.2 |
90
|
|
|
* \${magento.edition} -> Magento Edition -> Community or Enterprise |
91
|
|
|
* \${magerun.version} -> Magerun version i.e. 1.66.0 |
92
|
|
|
* \${php.version} -> PHP Version |
93
|
|
|
* \${script.file} -> Current script file path |
94
|
|
|
* \${script.dir} -> Current script file dir |
95
|
|
|
|
96
|
|
|
Variables can be passed to a script with "--define (-d)" option. |
97
|
|
|
|
98
|
|
|
Example: |
99
|
|
|
|
100
|
|
|
$ n98-magerun.phar script -d foo=bar filename |
101
|
|
|
|
102
|
|
|
# This will register the variable \${foo} with value bar. |
103
|
|
|
|
104
|
|
|
It's possible to define multiple values by passing more than one option. |
105
|
|
|
HELP; |
106
|
|
|
$this->setHelp($help); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* @return bool |
111
|
|
|
*/ |
112
|
|
|
public function isEnabled() |
113
|
|
|
{ |
114
|
|
|
return Exec::allowed(); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
118
|
|
|
{ |
119
|
|
|
$this->_scriptFilename = $input->getArgument('filename'); |
120
|
|
|
$this->_stopOnError = $input->getOption('stop-on-error'); |
121
|
|
|
$this->_initDefines($input); |
122
|
|
|
$script = $this->_getContent($this->_scriptFilename); |
123
|
|
|
$commands = explode("\n", $script); |
124
|
|
|
$this->initScriptVars(); |
125
|
|
|
|
126
|
|
|
foreach ($commands as $commandString) { |
127
|
|
|
$commandString = trim($commandString); |
128
|
|
|
if (empty($commandString)) { |
129
|
|
|
continue; |
130
|
|
|
} |
131
|
|
|
$firstChar = substr($commandString, 0, 1); |
132
|
|
|
|
133
|
|
|
switch ($firstChar) { |
134
|
|
|
|
135
|
|
|
// comment |
136
|
|
|
case '#': |
137
|
|
|
continue; |
138
|
|
|
break; |
|
|
|
|
139
|
|
|
|
140
|
|
|
// set var |
141
|
|
|
case '$': |
142
|
|
|
$this->registerVariable($output, $commandString); |
143
|
|
|
break; |
144
|
|
|
|
145
|
|
|
// run shell script |
146
|
|
|
case '!': |
147
|
|
|
$this->runShellCommand($output, $commandString); |
148
|
|
|
break; |
149
|
|
|
|
150
|
|
|
default: |
151
|
|
|
$this->runMagerunCommand($input, $output, $commandString); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* @param InputInterface $input |
158
|
|
|
* @throws InvalidArgumentException |
159
|
|
|
*/ |
160
|
|
|
protected function _initDefines(InputInterface $input) |
161
|
|
|
{ |
162
|
|
|
$defines = $input->getOption('define'); |
163
|
|
|
if (is_string($defines)) { |
164
|
|
|
$defines = array($defines); |
165
|
|
|
} |
166
|
|
|
if (count($defines) > 0) { |
167
|
|
|
foreach ($defines as $define) { |
|
|
|
|
168
|
|
|
if (!strstr($define, '=')) { |
169
|
|
|
throw new InvalidArgumentException('Invalid define'); |
170
|
|
|
} |
171
|
|
|
$parts = BinaryString::trimExplodeEmpty('=', $define); |
172
|
|
|
$variable = $parts[0]; |
173
|
|
|
$value = null; |
174
|
|
|
if (isset($parts[1])) { |
175
|
|
|
$value = $parts[1]; |
176
|
|
|
} |
177
|
|
|
$this->scriptVars['${' . $variable . '}'] = $value; |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @param string $filename |
184
|
|
|
* @throws RuntimeException |
185
|
|
|
* @internal param string $input |
186
|
|
|
* @return string |
187
|
|
|
*/ |
188
|
|
|
protected function _getContent($filename) |
189
|
|
|
{ |
190
|
|
|
if ($filename == '-' || empty($filename)) { |
191
|
|
|
$script = @\file_get_contents('php://stdin', 'r'); |
192
|
|
|
} else { |
193
|
|
|
$script = @\file_get_contents($filename); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
if (!$script) { |
197
|
|
|
throw new RuntimeException('Script file was not found'); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
return $script; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* @param OutputInterface $output |
205
|
|
|
* @param string $commandString |
206
|
|
|
* @throws RuntimeException |
207
|
|
|
* @return void |
208
|
|
|
*/ |
209
|
|
|
protected function registerVariable(OutputInterface $output, $commandString) |
210
|
|
|
{ |
211
|
|
|
if (preg_match('/^(\$\{[a-zA-Z0-9-_.]+\})=(.+)/', $commandString, $matches)) { |
212
|
|
|
if (isset($matches[2]) && $matches[2][0] == '?') { |
213
|
|
|
|
214
|
|
|
// Variable is already defined |
215
|
|
|
if (isset($this->scriptVars[$matches[1]])) { |
216
|
|
|
return $this->scriptVars[$matches[1]]; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/* @var $dialog DialogHelper */ |
220
|
|
|
$dialog = $this->getHelperSet()->get('dialog'); |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Check for select "?[" |
224
|
|
|
*/ |
225
|
|
|
if (isset($matches[2][1]) && $matches[2][1] == '[') { |
226
|
|
|
if (preg_match('/\[(.+)\]/', $matches[2], $choiceMatches)) { |
227
|
|
|
$choices = BinaryString::trimExplodeEmpty(',', $choiceMatches[1]); |
228
|
|
|
$selectedIndex = $dialog->select( |
229
|
|
|
$output, |
230
|
|
|
'<info>Please enter a value for <comment>' . $matches[1] . '</comment>:</info> ', |
231
|
|
|
$choices |
232
|
|
|
); |
233
|
|
|
$this->scriptVars[$matches[1]] = $choices[$selectedIndex]; |
234
|
|
|
|
235
|
|
|
} else { |
236
|
|
|
throw new RuntimeException('Invalid choices'); |
237
|
|
|
} |
238
|
|
|
} else { |
239
|
|
|
// normal input |
240
|
|
|
$this->scriptVars[$matches[1]] = $dialog->askAndValidate( |
241
|
|
|
$output, |
242
|
|
|
'<info>Please enter a value for <comment>' . $matches[1] . '</comment>:</info> ', |
243
|
|
|
function($value) { |
244
|
|
|
if ($value == '') { |
245
|
|
|
throw new RuntimeException('Please enter a value'); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $value; |
249
|
|
|
} |
250
|
|
|
); |
251
|
|
|
} |
252
|
|
|
} else { |
253
|
|
|
$this->scriptVars[$matches[1]] = $this->_replaceScriptVars($matches[2]); |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @param InputInterface $input |
260
|
|
|
* @param OutputInterface $output |
261
|
|
|
* @param string $commandString |
262
|
|
|
* @throws RuntimeException |
263
|
|
|
*/ |
264
|
|
|
protected function runMagerunCommand(InputInterface $input, OutputInterface $output, $commandString) |
|
|
|
|
265
|
|
|
{ |
266
|
|
|
$this->getApplication()->setAutoExit(false); |
267
|
|
|
$commandString = $this->_replaceScriptVars($commandString); |
268
|
|
|
$input = new StringInput($commandString); |
269
|
|
|
$exitCode = $this->getApplication()->run($input, $output); |
270
|
|
|
if ($exitCode !== 0 && $this->_stopOnError) { |
271
|
|
|
throw new RuntimeException('Script stopped with errors'); |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* @param string $commandString |
277
|
|
|
* @return string |
278
|
|
|
*/ |
279
|
|
|
protected function _prepareShellCommand($commandString) |
280
|
|
|
{ |
281
|
|
|
$commandString = ltrim($commandString, '!'); |
282
|
|
|
|
283
|
|
|
// @TODO find a better place |
284
|
|
|
if (strstr($commandString, '${magento.root}') |
285
|
|
|
|| strstr($commandString, '${magento.version}') |
286
|
|
|
|| strstr($commandString, '${magento.edition}') |
287
|
|
|
) { |
288
|
|
|
$this->initMagento(); |
289
|
|
|
} |
290
|
|
|
$this->initScriptVars(); |
291
|
|
|
$commandString = $this->_replaceScriptVars($commandString); |
292
|
|
|
|
293
|
|
|
return $commandString; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
protected function initScriptVars() |
297
|
|
|
{ |
298
|
|
|
if (class_exists('\Mage')) { |
299
|
|
|
$this->scriptVars['${magento.root}'] = $this->getApplication()->getMagentoRootFolder(); |
300
|
|
|
$this->scriptVars['${magento.version}'] = \Mage::getVersion(); |
301
|
|
|
$this->scriptVars['${magento.edition}'] = is_callable(array('\Mage', 'getEdition')) ? \Mage::getEdition() : 'Community'; |
|
|
|
|
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
$this->scriptVars['${php.version}'] = substr(phpversion(), 0, strpos(phpversion(), '-')); |
305
|
|
|
$this->scriptVars['${magerun.version}'] = $this->getApplication()->getVersion(); |
306
|
|
|
$this->scriptVars['${script.file}'] = $this->_scriptFilename; |
307
|
|
|
$this->scriptVars['${script.dir}'] = dirname($this->_scriptFilename); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @param OutputInterface $output |
312
|
|
|
* @param string $commandString |
313
|
|
|
* @internal param $returnValue |
314
|
|
|
*/ |
315
|
|
|
protected function runShellCommand(OutputInterface $output, $commandString) |
316
|
|
|
{ |
317
|
|
|
$commandString = $this->_prepareShellCommand($commandString); |
318
|
|
|
$returnValue = shell_exec($commandString); |
319
|
|
|
if (!empty($returnValue)) { |
320
|
|
|
$output->writeln($returnValue); |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @param string $commandString |
326
|
|
|
* |
327
|
|
|
* @return string |
328
|
|
|
*/ |
329
|
|
|
protected function _replaceScriptVars($commandString) |
330
|
|
|
{ |
331
|
|
|
$commandString = str_replace(array_keys($this->scriptVars), $this->scriptVars, $commandString); |
332
|
|
|
|
333
|
|
|
return $commandString; |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
|
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.