Passed
Pull Request — master (#7)
by
unknown
02:04
created

ShellCommand::environmentVariablesToString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPSu\ShellCommandBuilder;
6
7
use PHPSu\ShellCommandBuilder\Exception\ShellBuilderException;
8
use PHPSu\ShellCommandBuilder\Literal\ShellArgument;
9
use PHPSu\ShellCommandBuilder\Literal\ShellEnvironmentVariable;
10
use PHPSu\ShellCommandBuilder\Literal\ShellExecutable;
11
use PHPSu\ShellCommandBuilder\Literal\ShellOption;
12
use PHPSu\ShellCommandBuilder\Literal\ShellShortOption;
13
use PHPSu\ShellCommandBuilder\Literal\ShellWord;
14
15
final class ShellCommand implements ShellInterface
16
{
17
    /**
18
     * @var ShellWord
19
     * @psalm-readonly
20
     */
21
    private $executable;
22
    /** @var array<ShellWord> */
23
    private $arguments = [];
24
    /** @var array<ShellWord> */
25
    private $environmentVariables = [];
26
    /** @var bool  */
27
    private $isCommandSubstitution = false;
28
    /** @var bool  */
29
    private $isProcessSubstitution = false;
30
    /** @var ShellBuilder|null */
31
    private $parentBuilder;
32
    /** @var bool */
33
    private $invertOutput = false;
34
35
    public function __construct(string $name, ShellBuilder $builder = null)
36
    {
37
        $this->executable = new ShellExecutable($name);
38
        $this->parentBuilder = $builder;
39
    }
40
41
    public function addToBuilder(): ShellBuilder
42
    {
43
        if ($this->parentBuilder === null) {
44
            throw new ShellBuilderException('You need to create a ShellBuilder first before you can use it within a command');
45
        }
46
        return $this->parentBuilder->add($this);
47
    }
48
49
    public function toggleCommandSubstitution(): self
50
    {
51
        $this->isCommandSubstitution = !$this->isCommandSubstitution;
52
        return $this;
53
    }
54
55
    public function isProcessSubstitution(bool $enable = true): self
56
    {
57
        $this->isProcessSubstitution = $enable;
58
        return $this;
59
    }
60
61
    public function invert(bool $invert = true): self
62
    {
63
        $this->invertOutput = $invert;
64
        return $this;
65
    }
66
67
    /**
68
     * @param string $option
69
     * @param ShellInterface|string|mixed $value
70
     * @param bool $escapeArgument
71
     * @param bool $withAssignOperator
72
     * @return self
73
     * @throws ShellBuilderException
74
     */
75
    public function addShortOption(string $option, $value = '', bool $escapeArgument = true, bool $withAssignOperator = false): self
76
    {
77
        if (!($value instanceof ShellInterface || is_string($value))) {
78
            throw new ShellBuilderException('Provided the wrong type - only ShellCommand and ShellBuilder allowed');
79
        }
80
        $word = new ShellShortOption($option, $value);
81
        return $this->add($word, $escapeArgument, $withAssignOperator);
82
    }
83
84
    /**
85
     * @param string $option
86
     * @param ShellInterface|string|mixed $value
87
     * @param bool $escapeArgument
88
     * @param bool $withAssignOperator
89
     * @return self
90
     * @throws ShellBuilderException
91
     */
92
    public function addOption(string $option, $value = '', bool $escapeArgument = true, bool $withAssignOperator = false): self
93
    {
94
        if (!($value instanceof ShellInterface || is_string($value))) {
95
            throw new ShellBuilderException('Provided the wrong type - only ShellCommand and ShellBuilder allowed');
96
        }
97
        $word = new ShellOption($option, $value);
98
        return $this->add($word, $escapeArgument, $withAssignOperator);
99
    }
100
101
    /**
102
     * @param ShellInterface|string|mixed $argument
103
     * @param bool $escapeArgument
104
     * @return self
105
     * @throws ShellBuilderException
106
     */
107
    public function addArgument($argument, bool $escapeArgument = true): self
108
    {
109
        if (!($argument instanceof ShellInterface || is_string($argument))) {
110
            throw new ShellBuilderException('Provided the wrong type - only ShellCommand and ShellBuilder allowed');
111
        }
112
        $word = new ShellArgument($argument);
113
        return $this->add($word, $escapeArgument);
114
    }
115
116
    /**
117
     * This is an alias for argument, that automatically escapes the argument.
118
     * It does in the end does not provide any additional functionality
119
     *
120
     * @param ShellInterface $argument
121
     * @return $this
122
     * @throws ShellBuilderException
123
     */
124
    public function addSubCommand(ShellInterface $argument): self
125
    {
126
        return $this->addArgument($argument, true);
127
    }
128
129
    /**
130
     * @param ShellInterface|string|mixed $argument
131
     * @return self
132
     * @throws ShellBuilderException
133
     */
134
    public function addNoSpaceArgument($argument): self
135
    {
136
        if (!($argument instanceof ShellInterface || is_string($argument))) {
137
            throw new ShellBuilderException('Provided the wrong type - only ShellCommand and ShellBuilder allowed');
138
        }
139
        $word = new ShellArgument($argument);
140
        $word->setSpaceAfterValue(false);
141
        return $this->add($word, false);
142
    }
143
144
    private function add(ShellWord $word, bool $escapeArgument, bool $withAssignOperator = false): self
145
    {
146
        $word->setEscape($escapeArgument);
147
        $word->setAssignOperator($withAssignOperator);
148
        $this->arguments[] = $word;
149
        return $this;
150
    }
151
152
    public function addEnv(string $name, string $value): self
153
    {
154
        $word = new ShellEnvironmentVariable($name, $value);
155
        $this->environmentVariables[$name] = $word;
156
        return $this;
157
    }
158
159
    private function argumentsToString(): string
160
    {
161
        $result = [];
162
        foreach ($this->arguments as $part) {
163
            $result[] = $part;
164
        }
165
        return trim(implode('', $result));
166
    }
167
168
    private function environmentVariablesToString(): string
169
    {
170
        $result = [];
171
        foreach ($this->environmentVariables as $part) {
172
            $result[] = $part;
173
        }
174
        return implode('', $result);
175
    }
176
177
    /**
178
     * @return array<string, mixed>
179
     */
180
    public function __toArray(): array
181
    {
182
        $commands = [];
183
        foreach ($this->arguments as $item) {
184
            $commands[] = $item->__toArray();
185
        }
186
        $envs = [];
187
        foreach ($this->environmentVariables as $item) {
188
            $envs[] = $item->__toArray();
189
        }
190
        return [
191
            'executable' => $this->executable->__toString(),
192
            'arguments' => $commands,
193
            'isCommandSubstitution' => $this->isCommandSubstitution,
194
            'environmentVariables' => $envs,
195
        ];
196
    }
197
198
    public function __toString(): string
199
    {
200
        /** @psalm-suppress ImplicitToStringCast */
201
        $result = (sprintf(
202
            '%s%s%s%s',
203
            $this->invertOutput ? '! ' : '',
204
            empty($this->environmentVariables) ? '' : $this->environmentVariablesToString(),
205
            $this->executable,
206
            empty($this->arguments) ? '' : ' ' . $this->argumentsToString()
207
        ));
208
        if ($this->isCommandSubstitution && !$this->isProcessSubstitution) {
209
            return sprintf("\$(%s)", $result);
210
        }
211
        if ($this->isProcessSubstitution && !$this->isCommandSubstitution) {
212
            return sprintf("<(%s)", $result);
213
        }
214
        return $result;
215
    }
216
}
217