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

Pattern   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 48
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 6
eloc 26
c 1
b 0
f 0
dl 0
loc 48
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
B split() 0 28 6
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