ScriptCommand   B
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 308
Duplicated Lines 24.03 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 36
c 2
b 1
f 0
lcom 1
cbo 5
dl 74
loc 308
rs 8.8

11 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 73 73 1
A _initDefines() 0 13 3
A _getContent() 0 13 4
C registerVariable() 0 47 9
A runMagerunCommand() 0 10 3
A _prepareShellCommand() 0 16 4
A initScriptVars() 0 14 2
A runShellCommand() 0 8 2
A isEnabled() 0 4 1
B execute() 0 38 6
A _replaceScriptVars() 0 6 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace N98\Magento\Command;
4
5
use N98\Util\BinaryString;
6
use RuntimeException;
7
use Symfony\Component\Console\Helper\DialogHelper;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Input\StringInput;
12
use Symfony\Component\Console\Output\OutputInterface;
13
14
class ScriptCommand extends AbstractMagentoCommand
15
{
16
    /**
17
     * @var array
18
     */
19
    protected $scriptVars = array();
20
21
    /**
22
     * @var string
23
     */
24
    protected $_scriptFilename = '';
25
26
    /**
27
     * @var bool
28
     */
29
    protected $_stopOnError = false;
30
31 View Code Duplication
    protected function configure()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
32
    {
33
        $this
34
            ->setName('script')
35
            ->addArgument('filename', InputArgument::OPTIONAL, 'Script file')
36
            ->addOption('define', 'd', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Defines a variable')
37
            ->addOption('stop-on-error', null, InputOption::VALUE_NONE, 'Stops execution of script on error')
38
            ->setDescription('Runs multiple n98-magerun commands');
39
40
        $help = <<<HELP
41
Example:
42
43
   # Set multiple config
44
   config:set "web/cookie/cookie_domain" example.com
45
46
   # Set with multiline values with "\n"
47
   config:set "general/store_information/address" "First line\nSecond line\nThird line"
48
49
   # This is a comment
50
   cache:flush
51
52
53
Optionally you can work with unix pipes.
54
55
   \$ echo "cache:flush" | n98-magerun-dev script
56
57
   \$ n98-magerun.phar script < filename
58
59
It is even possible to create executable scripts:
60
61
Create file `test.magerun` and make it executable (`chmod +x test.magerun`):
62
63
   #!/usr/bin/env n98-magerun.phar script
64
65
   config:set "web/cookie/cookie_domain" example.com
66
   cache:flush
67
68
   # Run a shell script with "!" as first char
69
   ! ls -l
70
71
   # Register your own variable (only key = value currently supported)
72
   \${my.var}=bar
73
74
   # Let magerun ask for variable value - add a question mark
75
   \${my.var}=?
76
77
   ! echo \${my.var}
78
79
   # Use resolved variables from n98-magerun in shell commands
80
   ! ls -l \${magento.root}/code/local
81
82
Pre-defined variables:
83
84
* \${magento.root}    -> Magento Root-Folder
85
* \${magento.version} -> Magento Version i.e. 1.7.0.2
86
* \${magento.edition} -> Magento Edition -> Community or Enterprise
87
* \${magerun.version} -> Magerun version i.e. 1.66.0
88
* \${php.version}     -> PHP Version
89
* \${script.file}     -> Current script file path
90
* \${script.dir}      -> Current script file dir
91
92
Variables can be passed to a script with "--define (-d)" option.
93
94
Example:
95
96
   $ n98-magerun.phar script -d foo=bar filename
97
98
   # This will register the variable \${foo} with value bar.
99
100
It's possible to define multiple values by passing more than one option.
101
HELP;
102
        $this->setHelp($help);
103
    }
104
105
    /**
106
     * @return bool
107
     */
108
    public function isEnabled()
109
    {
110
        return function_exists('exec');
111
    }
112
113
    protected function execute(InputInterface $input, OutputInterface $output)
114
    {
115
        $this->_scriptFilename = $input->getArgument('filename');
116
        $this->_stopOnError = $input->getOption('stop-on-error');
117
        $this->_initDefines($input);
118
        $script = $this->_getContent($this->_scriptFilename);
119
        $commands = explode("\n", $script);
120
        $this->initScriptVars();
121
122
        foreach ($commands as $commandString) {
123
            $commandString = trim($commandString);
124
            if (empty($commandString)) {
125
                continue;
126
            }
127
            $firstChar = substr($commandString, 0, 1);
128
129
            switch ($firstChar) {
130
131
                // comment
132
                case '#':
133
                    continue;
134
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
135
136
                // set var
137
                case '$':
138
                    $this->registerVariable($output, $commandString);
139
                    break;
140
141
                // run shell script
142
                case '!':
143
                    $this->runShellCommand($output, $commandString);
144
                    break;
145
146
                default:
147
                    $this->runMagerunCommand($input, $output, $commandString);
148
            }
149
        }
150
    }
151
152
    /**
153
     * @param InputInterface $input
154
     * @throws \InvalidArgumentException
155
     */
156
    protected function _initDefines(InputInterface $input)
157
    {
158
        $defines = (array) $input->getOption('define');
159
160
        foreach ($defines as $define) {
161
            if (!strstr($define, '=')) {
162
                throw new \InvalidArgumentException('Invalid define');
163
            }
164
            $parts = BinaryString::trimExplodeEmpty('=', $define);
165
            list($variable, $value) = $parts + [1 => null];
166
            $this->scriptVars['${' . $variable . '}'] = $value;
167
        }
168
    }
169
170
    /**
171
     * @param string $filename
172
     * @throws RuntimeException
173
     * @internal param string $input
174
     * @return string
175
     */
176
    protected function _getContent($filename)
177
    {
178
        if ($filename === '-' || empty($filename)) {
179
            $filename = 'php://stdin';
180
        }
181
        $script = @\file_get_contents($filename);
182
183
        if (!$script) {
184
            throw new RuntimeException('Script file was not found');
185
        }
186
187
        return $script;
188
    }
189
190
    /**
191
     * @param OutputInterface $output
192
     * @param string $commandString
193
     * @throws RuntimeException
194
     * @return void
195
     */
196
    protected function registerVariable(OutputInterface $output, $commandString)
197
    {
198
        if (preg_match('/^(\$\{[a-zA-Z0-9-_.]+\})=(.+)/', $commandString, $matches)) {
199
            if (isset($matches[2]) && $matches[2][0] == '?') {
200
201
                // Variable is already defined
202
                if (isset($this->scriptVars[$matches[1]])) {
203
                    return $this->scriptVars[$matches[1]];
204
                }
205
206
                /* @var $dialog DialogHelper */
207
                $dialog = $this->getHelper('dialog');
208
209
                /**
210
                 * Check for select "?["
211
                 */
212
                if (isset($matches[2][1]) && $matches[2][1] == '[') {
213
                    if (preg_match('/\[(.+)\]/', $matches[2], $choiceMatches)) {
214
                        $choices = BinaryString::trimExplodeEmpty(',', $choiceMatches[1]);
215
                        $selectedIndex = $dialog->select(
216
                            $output,
217
                            '<info>Please enter a value for <comment>' . $matches[1] . '</comment>:</info> ',
218
                            $choices
219
                        );
220
                        $this->scriptVars[$matches[1]] = $choices[$selectedIndex];
221
                    } else {
222
                        throw new RuntimeException('Invalid choices');
223
                    }
224
                } else {
225
                    // normal input
226
                    $this->scriptVars[$matches[1]] = $dialog->askAndValidate(
227
                        $output,
228
                        '<info>Please enter a value for <comment>' . $matches[1] . '</comment>:</info> ',
229
                        function ($value) {
230
                            if ($value == '') {
231
                                throw new \Exception('Please enter a value');
232
                            }
233
234
                            return $value;
235
                        }
236
                    );
237
                }
238
            } else {
239
                $this->scriptVars[$matches[1]] = $this->_replaceScriptVars($matches[2]);
240
            }
241
        }
242
    }
243
244
    /**
245
     * @param InputInterface $input
246
     * @param OutputInterface $output
247
     * @param string $commandString
248
     * @throws RuntimeException
249
     */
250
    protected function runMagerunCommand(InputInterface $input, OutputInterface $output, $commandString)
0 ignored issues
show
Unused Code introduced by
The parameter $input 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...
251
    {
252
        $this->getApplication()->setAutoExit(false);
253
        $commandString = $this->_replaceScriptVars($commandString);
254
        $input = new StringInput($commandString);
255
        $exitCode = $this->getApplication()->run($input, $output);
256
        if ($exitCode !== 0 && $this->_stopOnError) {
257
            throw new RuntimeException('Script stopped with errors');
258
        }
259
    }
260
261
    /**
262
     * @param string $commandString
263
     * @return string
264
     */
265
    protected function _prepareShellCommand($commandString)
266
    {
267
        $commandString = ltrim($commandString, '!');
268
269
        // @TODO find a better place
270
        if (strstr($commandString, '${magento.root}')
271
            || strstr($commandString, '${magento.version}')
272
            || strstr($commandString, '${magento.edition}')
273
        ) {
274
            $this->initMagento();
275
        }
276
        $this->initScriptVars();
277
        $commandString = $this->_replaceScriptVars($commandString);
278
279
        return $commandString;
280
    }
281
282
    protected function initScriptVars()
283
    {
284
        $rootFolder = $this->getApplication()->getMagentoRootFolder();
285
        if (!empty($rootFolder)) {
286
            $this->scriptVars['${magento.root}'] = $rootFolder;
287
            $this->scriptVars['${magento.version}'] = \Magento\Framework\AppInterface::VERSION;
288
            $this->scriptVars['${magento.edition}'] = 'Community'; // @TODO replace this if EE is available
289
        }
290
291
        $this->scriptVars['${php.version}'] = substr(phpversion(), 0, strpos(phpversion(), '-'));
292
        $this->scriptVars['${magerun.version}'] = $this->getApplication()->getVersion();
293
        $this->scriptVars['${script.file}'] = $this->_scriptFilename;
294
        $this->scriptVars['${script.dir}'] = dirname($this->_scriptFilename);
295
    }
296
297
    /**
298
     * @param OutputInterface $output
299
     * @param string $commandString
300
     * @internal param $returnValue
301
     */
302
    protected function runShellCommand(OutputInterface $output, $commandString)
303
    {
304
        $commandString = $this->_prepareShellCommand($commandString);
305
        $returnValue = shell_exec($commandString);
306
        if (!empty($returnValue)) {
307
            $output->writeln($returnValue);
308
        }
309
    }
310
311
    /**
312
     * @param string $commandString
313
     * @return string
314
     */
315
    protected function _replaceScriptVars($commandString)
316
    {
317
        $commandString = str_replace(array_keys($this->scriptVars), $this->scriptVars, $commandString);
318
319
        return $commandString;
320
    }
321
}
322