Completed
Push — master ( 5834ca...7116ff )
by
unknown
13s queued 11s
created

ShellBuilder::createGroup()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 2
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPSu\ShellCommandBuilder;
6
7
use PHPSu\ShellCommandBuilder\Collection\CollectionTuple;
8
use PHPSu\ShellCommandBuilder\Collection\Pipeline;
9
use PHPSu\ShellCommandBuilder\Collection\Redirection;
10
use PHPSu\ShellCommandBuilder\Collection\ShellList;
11
use PHPSu\ShellCommandBuilder\Conditional\BasicExpression;
12
use PHPSu\ShellCommandBuilder\Definition\ControlOperator;
13
use PHPSu\ShellCommandBuilder\Definition\GroupType;
14
use PHPSu\ShellCommandBuilder\Exception\ShellBuilderException;
15
use TypeError;
16
17
final class ShellBuilder implements ShellInterface, \JsonSerializable
18
{
19
    /** @var array<ShellInterface|CollectionTuple>  */
20
    private $commandList = [];
21
    /** @var int */
22
    private $groupType;
23
    /**
24
     * name of the coprocess - empty string means anonymous
25
     * @var null|string
26
     */
27
    private $asynchronously;
28
    /** @var bool */
29
    private $processSubstitution = false;
30
    /** @var bool */
31
    private $commandSubstitution = false;
32
33
    /**
34
     * This is a shortcut for quicker fluid access to the shell builder
35
     * @return static
36
     */
37
    public static function new(): self
38
    {
39
        return new ShellBuilder();
40
    }
41
42
    /**
43
     * This is a shortcut for quicker fluid access to the command api
44
     * @param string $executable
45
     * @return ShellCommand
46
     */
47
    public static function command(string $executable): ShellCommand
48
    {
49
        return new ShellCommand($executable, new self());
50
    }
51
52
    public function __construct(int $groupType = GroupType::NO_GROUP)
53
    {
54
        $this->groupType = $groupType;
55
    }
56
57
    public function createCommand(string $name, bool $withNewBuilder = false): ShellCommand
58
    {
59
        return new ShellCommand($name, $withNewBuilder ? new self() : $this);
60
    }
61
62
    public function runAsynchronously(bool $isAsync = true, string $name = ''): self
63
    {
64
        $this->asynchronously = $isAsync ? $name : null;
65
        return $this;
66
    }
67
68
    /**
69
     * @param string|ShellInterface $command
70
     * @return $this
71
     * @throws ShellBuilderException
72
     */
73
    public function add($command): self
74
    {
75
        $command = $this->parseCommand($command, true);
76
        if (empty($this->commandList)) {
77
            $this->commandList[] = $command;
78
            return $this;
79
        }
80
        $this->commandList[] = ShellList::add($command);
81
        return $this;
82
    }
83
84
    /**
85
     * @param string|ShellInterface $command
86
     * @return $this
87
     * @throws ShellBuilderException
88
     */
89
    public function and($command): self
90
    {
91
        $this->commandList[] = ShellList::addAnd($this->parseCommand($command));
92
        return $this;
93
    }
94
95
    /**
96
     * @param string|ShellInterface $command
97
     * @return $this
98
     * @throws ShellBuilderException
99
     */
100
    public function or($command): self
101
    {
102
        $this->commandList[] = ShellList::addOr($this->parseCommand($command));
103
        return $this;
104
    }
105
106
    /**
107
     * @param string|ShellInterface $command
108
     * @return $this
109
     * @throws ShellBuilderException
110
     */
111
    public function async($command = ''): self
112
    {
113
        $this->commandList[] = ShellList::async($this->parseCommand($command));
114
        return $this;
115
    }
116
117
    /**
118
     * @param string|ShellInterface $command
119
     * @return $this
120
     * @throws ShellBuilderException
121
     */
122
    public function pipe($command): self
123
    {
124
        $this->commandList[] = Pipeline::pipe($this->parseCommand($command));
125
        return $this;
126
    }
127
128
    /**
129
     * @param string|ShellInterface $command
130
     * @return $this
131
     * @throws ShellBuilderException
132
     */
133
    public function pipeWithForward($command): self
134
    {
135
        $this->commandList[] = Pipeline::pipeErrorForward($this->parseCommand($command));
136
        return $this;
137
    }
138
139
    /**
140
     * @param string|ShellInterface $command
141
     * @param bool $append
142
     * @return $this
143
     * @throws ShellBuilderException
144
     */
145
    public function redirectOutput($command, bool $append = false): self
146
    {
147
        $command = $this->parseCommand($command);
148
        $this->commandList[] = Redirection::redirectOutput($command, $append);
149
        return $this;
150
    }
151
152
    /**
153
     * @param string|ShellInterface $command
154
     * @return $this
155
     * @throws ShellBuilderException
156
     */
157
    public function redirectInput($command): self
158
    {
159
        $command = $this->parseCommand($command);
160
        $this->commandList[] = Redirection::redirectInput($command);
161
        return $this;
162
    }
163
164
    /**
165
     * @param string|ShellInterface $command
166
     * @return $this
167
     * @throws ShellBuilderException
168
     */
169
    public function redirectError($command): self
170
    {
171
        $command = $this->parseCommand($command);
172
        $this->commandList[] = Redirection::redirectError($command);
173
        return $this;
174
    }
175
176
    /**
177
     * @param string|ShellInterface $command
178
     * @param bool $toLeft
179
     * @return $this
180
     * @throws ShellBuilderException
181
     */
182
    public function redirect($command, bool $toLeft = true): self
183
    {
184
        $command = $this->parseCommand($command);
185
        $this->commandList[] = Redirection::redirectBetweenFiles($command, $toLeft);
186
        return $this;
187
    }
188
189
    /**
190
     * @param ShellInterface|string $command
191
     * @param bool $toLeft
192
     * @param null|int $firstDescriptor
193
     * @param null|int $secondDescriptor
194
     * @return static
195
     * @throws ShellBuilderException
196
     */
197
    public function redirectDescriptor($command, bool $toLeft, int $firstDescriptor = null, int $secondDescriptor = null): self
198
    {
199
        $command = $this->parseCommand($command);
200
        $this->commandList[] = Redirection::redirectBetweenDescriptors($command, $toLeft, $firstDescriptor, $secondDescriptor);
201
        return $this;
202
    }
203
204
    public function redirectErrorToOutput(): self
205
    {
206
        $this->commandList[] = Redirection::redirectErrorToOutput();
207
        return $this;
208
    }
209
210
    public function addCondition(BasicExpression $condition): self
211
    {
212
        $this->commandList[] = $condition;
213
        return $this;
214
    }
215
216
    /**
217
     * @param string|ShellInterface $fileEnding
218
     * @return ShellBuilder
219
     * @throws ShellBuilderException
220
     */
221
    public function addFileEnding($fileEnding): self
222
    {
223
        $tuple = CollectionTuple::create($fileEnding, '.');
224
        $tuple
225
            ->noSpaceAfterJoin(true)
226
            ->noSpaceBeforeJoin(true);
227
        $this->commandList[] = $tuple;
228
        return $this;
229
    }
230
231
    public function createGroup(bool $inSameShell = false): self
232
    {
233
        return new self($inSameShell ? GroupType::SAMESHELL_GROUP : GroupType::SUBSHELL_GROUP);
234
    }
235
236
    public function createProcessSubstition(): self
237
    {
238
        $builder = new self(GroupType::SUBSHELL_GROUP);
239
        $builder->processSubstitution = true;
240
        return $builder;
241
    }
242
243
    public function createCommandSubstition(): self
244
    {
245
        $builder = new self(GroupType::SUBSHELL_GROUP);
246
        $builder->commandSubstitution = true;
247
        return $builder;
248
    }
249
250
    /**
251
     * @param string|ShellInterface $command
252
     * @param bool $allowEmpty
253
     * @return ShellInterface
254
     * @throws ShellBuilderException
255
     */
256
    private function parseCommand($command, bool $allowEmpty = false): ShellInterface
257
    {
258
        if (is_string($command)) {
259
            $command = $this->createCommand($command);
260
        }
261
        try {
262
            $this->validateCommand($command, $allowEmpty);
263
        } catch (TypeError $typeError) {
264
            throw new ShellBuilderException('Provided the wrong type - only ShellCommand and ShellBuilder allowed');
265
        }
266
        return $command;
267
    }
268
269
    /** @noinspection PhpUnusedParameterInspection */
270
    private function validateCommand(ShellInterface $command, bool $allowEmpty): void
0 ignored issues
show
Unused Code introduced by
The parameter $command is not used and could be removed. ( Ignorable by Annotation )

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

270
    private function validateCommand(/** @scrutinizer ignore-unused */ ShellInterface $command, bool $allowEmpty): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
271
    {
272
        if (!$allowEmpty && empty($this->commandList)) {
273
            throw new ShellBuilderException('You have to first add a command before you can combine it');
274
        }
275
    }
276
277
    public function jsonSerialize(): array
278
    {
279
        return $this->__toArray();
280
    }
281
282
    /**
283
     * @return array<mixed>
284
     */
285
    public function __toArray(): array
286
    {
287
        $commands = [];
288
        foreach ($this->commandList as $item) {
289
            $commands[] = $item instanceof ShellInterface ? $item->__toArray() : $item;
290
        }
291
        return $commands;
292
    }
293
294
    public function __toString(): string
295
    {
296
        $result = '';
297
        if ($this->asynchronously !== null) {
298
            $result = sprintf('coproc %s%s', $this->asynchronously, $this->asynchronously !== '' ? ' ' : '');
299
        }
300
        foreach ($this->commandList as $command) {
301
            $result .= $command;
302
        }
303
        if ($this->groupType === GroupType::SAMESHELL_GROUP) {
304
            return sprintf(
305
                '%s%s;%s',
306
                ControlOperator::CURLY_BLOCK_DEFINITON_OPEN,
307
                $result,
308
                ControlOperator::CURLY_BLOCK_DEFINITON_CLOSE
309
            );
310
        }
311
        if ($this->groupType === GroupType::SUBSHELL_GROUP) {
312
            $substitionType = '';
313
            if ($this->commandSubstitution) {
314
                $substitionType = '$';
315
            }
316
            if ($this->processSubstitution) {
317
                $substitionType = '<';
318
            }
319
            return sprintf(
320
                '%s%s%s%s',
321
                $substitionType,
322
                ControlOperator::BLOCK_DEFINITON_OPEN,
323
                $result,
324
                ControlOperator::BLOCK_DEFINITON_CLOSE
325
            );
326
        }
327
        return rtrim($result);
328
    }
329
}
330