Passed
Push — master ( 1eafe3...862746 )
by Gaetano
07:42
created

SQLExecutingCommand   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 41
eloc 158
c 1
b 0
f 0
dl 0
loc 285
rs 9.1199

7 Methods

Rating   Name   Duplication   Size   Complexity  
A formatResults() 0 14 6
A __construct() 0 10 1
A addCommonOptions() 0 12 1
A writeResults() 0 16 6
A writeResultsToFile() 0 4 1
F executeSqlAction() 0 148 24
A parseCommonOptions() 0 18 2

How to fix   Complexity   

Complex Class

Complex classes like SQLExecutingCommand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SQLExecutingCommand, and based on these observations, apply Extract Interface, too.

1
<?php
2
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
namespace Db3v4l\Command;
4
5
use Db3v4l\API\Interfaces\SqlAction\CommandAction;
6
use Db3v4l\API\Interfaces\SqlAction\FileAction;
7
use Db3v4l\Core\DatabaseSchemaManager;
8
use Db3v4l\Service\DatabaseConfigurationManager;
9
use Db3v4l\Service\ProcessManager;
10
use Db3v4l\Service\SqlExecutorFactory;
11
use Db3v4l\Util\Process;
12
use Symfony\Component\Console\Input\InputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Symfony\Component\Console\Input\InputOption;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\InputOption was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Symfony\Component\Console\Output\OutputInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Output\OutputInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Symfony\Component\Yaml\Yaml;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Yaml\Yaml was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
abstract class SQLExecutingCommand extends BaseCommand
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class SQLExecutingCommand
Loading history...
18
{
19
    /** @var DatabaseConfigurationManager $dbConfigurationManager */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
20
    protected $dbConfigurationManager;
21
    /** @var SqlExecutorFactory $executorFactory */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
22
    protected $executorFactory;
23
    protected $processManager;
24
25
    const DEFAULT_OUTPUT_FORMAT = 'text';
26
    const DEFAULT_PARALLEL_PROCESSES = 16;
27
    const DEFAULT_PROCESS_TIMEOUT = 600;
28
    const DEFAULT_EXECUTOR_TYPE = 'NativeClient';
29
30
    protected $outputFormat;
31
    protected $outputFile;
32
    protected $maxParallelProcesses;
33
    protected $processTimeout;
34
    protected $executionStrategy;
35
    protected $executeInProcess;
36
37
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
38
        DatabaseConfigurationManager $dbConfigurationManager,
39
        SqlExecutorFactory $executorFactory,
40
        ProcessManager $processManager)
0 ignored issues
show
Coding Style introduced by
The closing parenthesis of a multi-line function declaration must be on a new line
Loading history...
41
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
42
        $this->dbConfigurationManager = $dbConfigurationManager;
43
        $this->executorFactory = $executorFactory;
44
        $this->processManager = $processManager;
45
46
        parent::__construct();
47
    }
48
49
    protected function addCommonOptions()
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function addCommonOptions()
Loading history...
50
    {
51
        $this
52
            ->addOption('only-instances', 'o', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Filter the database servers to run this command against. Usage of * and ? wildcards is allowed. To see all instances available, use `instance:list`', null)
53
            ->addOption('except-instances', 'x', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Filter the database servers to run this command against', null)
54
            ->addOption('output-type', null, InputOption::VALUE_REQUIRED, 'The format for the output: json, php, text or yml', self::DEFAULT_OUTPUT_FORMAT)
55
            ->addOption('output-file', null, InputOption::VALUE_REQUIRED, 'Save output to a file instead of writing it to stdout. NB: take care that dbconsole runs in a container, which has a different view of the filesystem. A good dir for output is ./shared')
56
            ->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'The maximum time to wait for subprocess execution (secs)', self::DEFAULT_PROCESS_TIMEOUT)
57
            ->addOption('max-parallel', null, InputOption::VALUE_REQUIRED, 'The maximum number of subprocesses to run in parallel', self::DEFAULT_PARALLEL_PROCESSES)
58
            ->addOption('dont-force-enabled-sigchild', null, InputOption::VALUE_NONE, "When using a separate process to run each sql command, do not force Symfony to believe that php was compiled with --enable-sigchild option")
59
            ->addOption('execution-strategy', null, InputOption::VALUE_REQUIRED, "EXPERIMENTAL. Internal usage", self::DEFAULT_EXECUTOR_TYPE)
60
            ->addOption('execute-in-process', null, InputOption::VALUE_NONE, "EXPERIMENTAL. Internal usage")
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
61
        ;
62
    }
63
64
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
65
     * @param InputInterface $input
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
66
     * @return string[][] the list of instances to use. key: name, value: connection spec
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
67
     */
68
    protected function parseCommonOptions(InputInterface $input)
69
    {
70
        $this->outputFormat = $input->getOption('output-type');
71
        $this->outputFile = $input->getOption('output-file');
72
        $this->processTimeout = $input->getOption('timeout');
73
        $this->maxParallelProcesses = $input->getOption('max-parallel');
74
        $this->executionStrategy = $input->getOption('execution-strategy');
75
        $this->executeInProcess = $input->getOption('execute-in-process');
76
77
        // On Debian, which we use by default, SF has troubles understanding that php was compiled with --enable-sigchild
78
        // We thus force it, but give end users an option to disable this
79
        // For more details, see comment 12 at https://bugs.launchpad.net/ubuntu/+source/php5/+bug/516061
80
        if (! $input->getOption('dont-force-enabled-sigchild')) {
81
            Process::forceSigchildEnabled(true);
82
        }
83
84
        return $this->dbConfigurationManager->getInstancesConfiguration(
85
            $this->dbConfigurationManager->listInstances($input->getOption('only-instances'), $input->getOption('except-instances'))
86
        );
87
    }
88
89
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
90
     * @param string[][] $instanceList list of instances to execute the action on. Key: instance name, value: connection spec
0 ignored issues
show
Coding Style introduced by
Expected 10 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
91
     * @param string $actionName used to build error messages
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 12 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
92
     * @param callable $getSqlActionCallable the method used to retrieve the desired SqlAction.
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
93
     *                                       It will be passed as arguments the SchemaManager and instance name, and should return a CommandAction or FileAction
94
     * @param bool $timed whether to use a timed executor
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 17 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
95
     * @param callable $onForkedProcessOutput a callback invoked when forked processes produce output
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
96
     * @return array 'succeeded': int, 'failed': int, 'data': mixed[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
97
     * @throws \Exception
0 ignored issues
show
Coding Style introduced by
Tag @throws cannot be grouped with parameter tags in a doc comment
Loading history...
98
     */
99
    protected function executeSqlAction($instanceList, $actionName, $getSqlActionCallable, $timed = false, $onForkedProcessOutput = null)
100
    {
101
        $processes = [];
102
        $callables = [];
103
        $outputFilters = [];
104
        $tempSQLFileNames = [];
105
        $executors = [];
106
107
        try {
108
109
            foreach ($instanceList as $instanceName => $dbConnectionSpec) {
110
111
                $schemaManager = new DatabaseSchemaManager($dbConnectionSpec);
112
113
                /** @var CommandAction|FileAction $sqlAction */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
114
                $sqlAction = call_user_func_array($getSqlActionCallable, [$schemaManager, $instanceName]);
115
116
                if ($sqlAction instanceof CommandAction) {
117
                    $filename = null;
118
                    $sql = $sqlAction->getCommand();
119
                } else if ($sqlAction instanceof FileAction) {
120
                    $filename = $sqlAction->getFilename();
121
                    $sql = null;
122
                } else {
123
                    // this is a coding error, not a sql execution error
124
                    throw new \Exception("Unsupported action type: " . get_class($sqlAction));
125
                }
126
                $filterCallable = $sqlAction->getResultsFilterCallable();
127
128
                if ($filename === null && $sql === null) {
129
                    // no sql to execute as forked process - we run the 'filter' functions in a separate loop
130
                    $callables[$instanceName] = $filterCallable;
131
                } else {
132
                    $outputFilters[$instanceName] = $filterCallable;
133
134
                    if ($filename === null && !$sqlAction->isSingleStatement()) {
0 ignored issues
show
Bug introduced by
The method isSingleStatement() does not exist on Db3v4l\API\Interfaces\SqlAction\FileAction. ( Ignorable by Annotation )

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

134
                    if ($filename === null && !$sqlAction->/** @scrutinizer ignore-call */ isSingleStatement()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
135
                        $filename = tempnam(sys_get_temp_dir(), 'db3v4l_') . '.sql';
136
                        file_put_contents($filename, $sql);
137
                        $tempSQLFileNames[] = $filename;
138
                    }
139
140
                    if ($this->executeInProcess) {
141
                        $executor = $this->executorFactory->createInProcessExecutor($instanceName, $dbConnectionSpec, $this->executionStrategy, $timed);
142
                        if ($filename === null) {
143
                            $callables[$instanceName] = $executor->getExecuteCommandCallable($sql);
144
                        } else {
145
                            $callables[$instanceName] = $executor->getExecuteFileCallable($filename);
0 ignored issues
show
Bug introduced by
The method getExecuteFileCallable() does not exist on Db3v4l\Core\SqlExecutor\InProcess\Doctrine. ( Ignorable by Annotation )

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

145
                            /** @scrutinizer ignore-call */ 
146
                            $callables[$instanceName] = $executor->getExecuteFileCallable($filename);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getExecuteFileCallable() does not exist on Db3v4l\Core\SqlExecutor\InProcess\PDO. ( Ignorable by Annotation )

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

145
                            /** @scrutinizer ignore-call */ 
146
                            $callables[$instanceName] = $executor->getExecuteFileCallable($filename);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
146
                        }
147
                    } else {
148
                        $executor = $this->executorFactory->createForkedExecutor($instanceName, $dbConnectionSpec, $this->executionStrategy, $timed);
149
                        $executors[$instanceName] = $executor;
150
151
                        if ($filename === null) {
152
                            $process = $executor->getExecuteStatementProcess($sql);
153
                        } else {
154
                            $process = $executor->getExecuteFileProcess($filename);
155
                        }
156
157
                        if ($this->outputFormat === 'text') {
158
                            $this->writeln('Command line: ' . $process->getCommandLine(), OutputInterface::VERBOSITY_VERY_VERBOSE);
159
                        }
160
161
                        $process->setTimeout($this->processTimeout);
162
163
                        $processes[$instanceName] = $process;
164
                    }
165
                }
166
            }
167
168
            /// @todo refactor the filtering loop so that filters can be applied as well to inProcess executors
169
170
            $succeeded = 0;
171
            $failed = 0;
172
            $results = [];
173
174
            foreach ($callables as $instanceName => $callable) {
175
                try {
176
                    $results[$instanceName] = call_user_func($callable);
177
                    $succeeded++;
178
                } catch (\Throwable $t) {
179
                    $failed++;
180
                    $this->writeErrorln("\n<error>$actionName in instance '$instanceName' failed! Reason: " . $t->getMessage() . "</error>\n", OutputInterface::VERBOSITY_NORMAL);
181
                }
182
            }
183
184
            if (count($processes)) {
185
                if ($this->outputFormat === 'text') {
186
                    $this->writeln('<info>Starting parallel execution...</info>', OutputInterface::VERBOSITY_VERY_VERBOSE);
187
                }
188
                $this->processManager->runParallel($processes, $this->maxParallelProcesses, 100, $onForkedProcessOutput);
189
190
                foreach ($processes as $instanceName => $process) {
191
                    if ($process->isSuccessful()) {
192
                        /// @todo is it necessary to have rtrim here ? shall we maybe move it to the executor ?
193
                        $output = rtrim($process->getOutput());
194
                        if (isset($outputFilters[$instanceName])) {
195
                            try {
196
                                $output = call_user_func_array($outputFilters[$instanceName], [$output, $executors[$instanceName]]);
197
                            } catch (\Throwable $t) {
198
                                /// @todo shall we reset $result to null or not?
199
                                //$result = null;
200
                                $failed++;
201
                                $succeeded--;
202
                                $this->writeErrorln("\n<error>$actionName in instance '$instanceName' failed! Reason: " . $t->getMessage() . "</error>\n", OutputInterface::VERBOSITY_NORMAL);
203
                            }
204
                        }
205
                        $results[$instanceName] = $output;
206
                        $succeeded++;
207
                    } else {
208
                        $results[$instanceName] = [
209
                            'stderr' => trim($process->getErrorOutput()),
210
                            'exitcode' => $process->getExitCode()
211
                        ];
212
                        $failed++;
213
                        $this->writeErrorln("\n<error>$actionName in instance '$instanceName' failed! Reason: " . $process->getErrorOutput() . "</error>\n", OutputInterface::VERBOSITY_NORMAL);
214
                    }
215
                }
216
            }
217
218
        } finally {
219
            // make sure that we clean up temp files, as they might contain sensitive data
220
            foreach($tempSQLFileNames as $tempSQLFileName) {
0 ignored issues
show
Coding Style introduced by
Expected "foreach (...) {\n"; found "foreach(...) {\n"
Loading history...
221
                unlink($tempSQLFileName);
222
            }
223
        }
224
225
        uksort($results, function ($a, $b) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
226
            $aParts = explode('_', $a, 2);
227
            $bParts = explode('_', $b, 2);
228
            $cmp = strcasecmp($aParts[0], $bParts[0]);
229
            if ($cmp !== 0) {
230
                return $cmp;
231
            }
232
            if (count($aParts) == 1) {
233
                return -1;
234
            }
235
            if (count($bParts) == 1) {
236
                return 1;
237
            }
238
            $aVersion = str_replace('_', '.', $aParts[1]);
239
            $bVersion = str_replace('_', '.', $bParts[1]);
240
            return version_compare($aVersion, $bVersion);
241
        });
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
242
243
        return [
244
            'succeeded' => $succeeded,
245
            'failed' => $failed,
246
            'data' => $results
247
        ];
248
    }
249
250
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
251
     * @param array $results
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
252
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
253
     * @throws \OutOfBoundsException for unsupported output formats
0 ignored issues
show
Coding Style introduced by
Tag @throws cannot be grouped with parameter tags in a doc comment
Loading history...
254
     */
255
    protected function formatResults(array $results)
256
    {
257
        switch ($this->outputFormat) {
258
            case 'json':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
259
                return json_encode($results['data'], JSON_PRETTY_PRINT);
260
            case 'php':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
261
                return var_export($results['data'], true);
262
            case 'text':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
263
            case 'yml':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
264
            case 'yaml':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
265
                return Yaml::dump($results['data'], 2, 4, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
266
            default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
267
                throw new \OutOfBoundsException("Unsupported output format: '{$this->outputFormat}'");
268
                break;
269
        }
270
    }
271
272
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
273
     * @param array $results should contain elements: succeeded(int) failed(int), data(mixed)
0 ignored issues
show
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
274
     * @param float $time execution time in seconds
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces after parameter name; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
275
     * @throws \Exception for unsupported formats
0 ignored issues
show
Coding Style introduced by
Tag @throws cannot be grouped with parameter tags in a doc comment
Loading history...
276
     * @todo since we use could be using forked processes, we can not measure total memory used... is it worth measuring just ours?
0 ignored issues
show
Coding Style introduced by
Tag @todo cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value for @todo tag indented incorrectly; expected 3 spaces but found 1
Loading history...
277
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
278
    protected function writeResults(array $results, $time = null)
279
    {
280
        if ($this->outputFile != null) {
281
            $this->writeResultsToFile($results);
282
        } else {
283
            $formattedResults = $this->formatResults($results);
284
            $this->writeln($formattedResults, OutputInterface::VERBOSITY_QUIET,  OutputInterface::OUTPUT_RAW);
285
        }
286
287
        if ($this->outputFormat === 'text' || $this->outputFile != null) {
288
            $this->writeln($results['succeeded'] . ' succeeded, ' . $results['failed'] . ' failed');
289
            if ($this->outputFile != null) {
290
                $this->writeln("Results saved to file {$this->outputFile}");
291
            }
292
            if ($time !== null) {
293
                $this->writeln("<info>Time taken: ".sprintf('%.2f', $time)." secs</info>");
294
            }
295
        }
296
    }
297
298
    protected function writeResultsToFile(array $results)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function writeResultsToFile()
Loading history...
299
    {
300
        $formattedResults = $this->formatResults($results);
301
        file_put_contents($this->outputFile, $formattedResults);
302
    }
303
}
304