Completed
Push — master ( 7bf58d...9f8ca5 )
by
unknown
20s queued 12s
created

Pattern::split()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 20
c 1
b 0
f 0
dl 0
loc 28
rs 8.9777
cc 6
nc 10
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPSu\ShellCommandBuilder\Definition;
6
7
use PHPSu\ShellCommandBuilder\Exception\ShellBuilderException;
8
9
final class Pattern
10
{
11
    // @see https://github.com/ruby/ruby/blob/master/lib/shellwords.rb#L82
12
    public const SHELLWORD_PATTERN = <<<REGEXP
13
/\G\s*(?>([^\s\\\\\'\"]+)|'([^\']*)'|"((?:[^\"\\\\]|\\\\.)*)"|(\\\\.?)|(\S))(\s|\z)?/m
14
REGEXP;
15
    // @see https://github.com/jimmycuadra/rust-shellwords/blob/master/src/lib.rs#L104
16
    public const METACHAR_PATTERN = /** @lang PhpRegExp */ '/\\\\([$`"\\\\\n])/';
17
    public const ESCAPE_PATTERN = /** @lang PhpRegExp */ '/\\\\(.)/';
18
19
    /**
20
     * Splitting an input into an array of shell words.
21
     * The pattern being used is based on a combination of the ruby and rust implementation of the same functionality
22
     * It derives from the original UNIX Bourne documentation
23
     *
24
     * @psalm-pure
25
     * @param string $input
26
     * @return array<string>
27
     * @throws ShellBuilderException
28
     */
29
    public static function split(string $input): array
30
    {
31
        $words = [];
32
        $field = '';
33
        $matches = [];
34
        preg_match_all(self::SHELLWORD_PATTERN, $input . ' ', $matches, PREG_SET_ORDER | PREG_UNMATCHED_AS_NULL);
35
        /** @var array<int, null|string> $match */
36
        foreach ($matches as $match) {
37
            if ($match[5] ?? false) {
38
                throw new ShellBuilderException('The given input has mismatching Quotes');
39
            }
40
            $doubleQuoted = '';
41
            if (isset($match[3])) {
42
                $doubleQuoted = preg_replace(self::METACHAR_PATTERN, '$1', $match[3]);
43
            }
44
            $escaped = '';
45
            if (isset($match[4])) {
46
                $escaped = preg_replace(self::ESCAPE_PATTERN, '$1', $match[4]);
47
                $escaped .= '';
48
            }
49
            $field .= implode('', [$match[1], $match[2] ?? '', $doubleQuoted, $escaped]);
50
            $seperator = $match[6] ?? '';
51
            if ($seperator !== '') {
52
                $words[] = $field;
53
                $field = '';
54
            }
55
        }
56
        return $words;
57
    }
58
}
59