ShellWord::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 3
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPSu\ShellCommandBuilder\Literal;
6
7
use PHPSu\ShellCommandBuilder\Definition\Pattern;
8
use PHPSu\ShellCommandBuilder\Exception\ShellBuilderException;
9
use PHPSu\ShellCommandBuilder\ShellInterface;
10
11
/**
12
 * Representing the basic element of a Shell Command, a single literal aka "word"
13
 * @internal
14
 * @psalm-internal PHPSu\ShellCommandBuilder\Literal
15
 * @package PHPSu\ShellCommandBuilder\Literal
16
 */
17
class ShellWord implements ShellInterface
18
{
19
    protected const OPTION_CONTROL = '--';
20
    protected const SHORT_OPTION_CONTROL = '-';
21
    protected const EQUAL_CONTROL = '=';
22
23
    /**
24
     * @var bool
25
     * @psalm-readonly
26
     */
27
    protected $isShortOption = false;
28
    /**
29
     * @var bool
30
     * @psalm-readonly
31
     */
32
    protected $isOption = false;
33
    /**
34
     * @var bool
35
     * @psalm-readonly
36
     */
37
    protected $isArgument = false;
38
    /**
39
     * @var bool
40
     * @psalm-readonly
41
     */
42
    protected $isEnvironmentVariable = false;
43
    /**
44
     * @var bool
45
     * @psalm-readonly
46
     */
47
    protected $isVariable = false;
48
    /** @var bool */
49
    protected $isEscaped = true;
50
    /** @var bool */
51
    protected $spaceAfterValue = true;
52
    /** @var bool */
53
    protected $useAssignOperator = false;
54
    /** @var bool */
55
    protected $nameUpperCase = false;
56
    /** @var bool */
57
    protected $wrapAsSubcommand = false;
58
    /** @var bool */
59
    protected $wrapWithBacktricks = false;
60
    /** @var string */
61
    protected $prefix = '';
62
    /** @var string */
63
    protected $suffix = ' ';
64
    /** @var string */
65
    protected $delimiter = ' ';
66
    /** @var string */
67
    protected $argument;
68
    /** @var string|ShellInterface */
69
    protected $value;
70
71
    /**
72
     * The constructor is protected, you must choose one of the children
73
     * @param string $argument
74
     * @param string|ShellInterface $value
75
     * @throws ShellBuilderException
76
     */
77
    protected function __construct(string $argument, $value = '')
78
    {
79
        if (!empty($argument) && !$this->validShellWord($argument)) {
80
            throw new ShellBuilderException(
81
                'A Shell Argument has to be a valid Shell word and cannot contain e.g whitespace'
82
            );
83
        }
84
        $this->argument = $argument;
85
        $this->value = $value;
86
    }
87
88
    public function setEscape(bool $isEscaped): self
89
    {
90
        $this->isEscaped = $isEscaped;
91
        return $this;
92
    }
93
94
    public function setSpaceAfterValue(bool $spaceAfterValue): self
95
    {
96
        $this->spaceAfterValue = $spaceAfterValue;
97
        return $this;
98
    }
99
100
    public function setAssignOperator(bool $useAssignOperator): self
101
    {
102
        $this->useAssignOperator = $useAssignOperator;
103
        return $this;
104
    }
105
106
    public function setNameUppercase(bool $uppercaseName): self
107
    {
108
        $this->nameUpperCase = $uppercaseName;
109
        return $this;
110
    }
111
112
    protected function validate(): void
113
    {
114
        /** @psalm-suppress DocblockTypeContradiction */
115
        if (!(is_string($this->value) || $this->value instanceof ShellInterface)) {
0 ignored issues
show
introduced by
$this->value is always a sub-type of PHPSu\ShellCommandBuilder\ShellInterface.
Loading history...
116
            throw new ShellBuilderException('Value must be an instance of ShellInterface or a string');
117
        }
118
    }
119
120
    /**
121
     * @psalm-pure
122
     * @param string $word
123
     * @return bool
124
     * @throws ShellBuilderException
125
     */
126
    private function validShellWord(string $word): bool
127
    {
128
        return count(Pattern::split($word)) === 1;
129
    }
130
131
    private function prepare(): void
132
    {
133
        $this->validate();
134
        if (!$this->spaceAfterValue) {
135
            $this->suffix = '';
136
        }
137
        if ($this->useAssignOperator) {
138
            $this->delimiter = self::EQUAL_CONTROL;
139
        }
140
        if (!empty($this->argument) && $this->nameUpperCase) {
141
            $this->argument = strtoupper($this->argument);
142
        }
143
    }
144
145
    /**
146
     * @param bool $debug
147
     * @return array<mixed>|string
148
     */
149
    private function getValue(bool $debug = false)
150
    {
151
        $word = $this->value;
152
        if ($word instanceof ShellInterface) {
153
            if ($debug) {
154
                return $word->__toArray();
155
            }
156
            $word = (string)$word;
157
        }
158
        if ($this->isEscaped && !empty($word)) {
159
            $word = escapeshellarg($word);
160
        }
161
        return $word;
162
    }
163
164
    /**
165
     * @return array<string, mixed>
166
     */
167
    public function __toArray(): array
168
    {
169
        $this->prepare();
170
        return [
171
            'isArgument' => $this->isArgument,
172
            'isShortOption' => $this->isShortOption,
173
            'isOption' => $this->isOption,
174
            'isEnvironmentVariable' => $this->isEnvironmentVariable,
175
            'isVariable' => $this->isVariable,
176
            'escaped' => $this->isEscaped,
177
            'withAssign' => $this->useAssignOperator,
178
            'spaceAfterValue' => $this->spaceAfterValue,
179
            'value' => $this->getValue(true),
180
            'argument' => $this->argument,
181
        ];
182
    }
183
184
    public function __toString(): string
185
    {
186
        $this->prepare();
187
        /** @var string $value */
188
        $value = $this->getValue();
189
        if ($this->value instanceof ShellInterface && $this->wrapAsSubcommand) {
190
            $value = $this->wrapWithBacktricks ? "`$value`" : "$($value)";
191
        }
192
        return sprintf(
193
            '%s%s%s%s%s',
194
            $this->prefix,
195
            $this->argument,
196
            $this->delimiter,
197
            $value,
198
            $this->suffix
199
        );
200
    }
201
}
202