CommandRunner::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Startwind\Forrest\Runner;
4
5
use Startwind\Forrest\History\HistoryHandler;
6
use Startwind\Forrest\Runner\Exception\ToolNotFoundException;
7
use Symfony\Component\Console\Command\Command as SymfonyCommand;
8
use Symfony\Component\Console\Output\OutputInterface;
9
use Symfony\Component\Process\Process;
10
11
class CommandRunner
12
{
13
    private static array $prefixCommands = [
14
        'sudo'
15
    ];
16
17
    private HistoryHandler $historyHandler;
18
19
    public function __construct(HistoryHandler $historyHandler)
20
    {
21
        $this->historyHandler = $historyHandler;
22
    }
23
24
    /**
25
     * Return a string array with all the commands. This is needed for multi line
26
     * commands.
27
     */
28
    public static function stringToMultilinePrompt(string $string): array
29
    {
30
        $commands = explode("\n", $string);
31
32
        if ($commands[count($commands) - 1] == '') {
33
            unset($commands[count($commands) - 1]);
34
        }
35
36
        return $commands;
37
    }
38
39
    /**
40
     * Run a single command line.
41
     */
42
    public function execute(OutputInterface $output, string $prompt, bool $checkForExistence = true, $storeInHistory = true): int
43
    {
44
        if ($checkForExistence && !self::isToolInstalled($prompt, $tool)) {
45
            throw new ToolNotFoundException($tool);
46
        }
47
48
        if ($storeInHistory) {
49
            $this->historyHandler->addEntry($prompt);
50
        }
51
52
        $process = Process::fromShellCommandline($prompt);
53
54
        $process->run(function (string $pipe, string $outputString) use ($output) {
55
            if ($pipe == Process::OUT) {
56
                $output->write($outputString);
57
            } elseif ($pipe == Process::ERR) {
58
                $output->write("<error>" . $outputString . "</error>");
59
            } else {
60
                $output->write("<info>" . $outputString . "</info>");
61
            }
62
        });
63
64
        return $process->getExitCode();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $process->getExitCode() could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
65
    }
66
67
    /**
68
     * Return true if the tool is installed.
69
     */
70
    public static function isToolInstalled(string $prompt, &$command): bool
71
    {
72
        $command = self::extractToolFromPrompt($prompt);
73
        exec('which ' . $command, $output, $resultCode);
74
        return $resultCode == SymfonyCommand::SUCCESS;
75
    }
76
77
    /**
78
     * Get the tool name from a prompt.
79
     *
80
     * It also removes sudo and other "prefix" tools.
81
     */
82
    public static function extractToolFromPrompt(string $prompt): string
83
    {
84
        $parts = explode(' ', $prompt);
85
86
        $command = array_shift($parts);
87
88
        while (in_array($command, self::$prefixCommands) && !empty($parts)) {
89
            $command = array_shift($parts);
90
        }
91
92
        return $command;
93
    }
94
}
95