Passed
Push — master ( bba5a4...181b61 )
by Tom
03:44
created

Glob::expandBraceInnerMatch()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 26
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 40
ccs 25
cts 25
cp 1
crap 8
rs 8.4444
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines;
6
7
use UnexpectedValueException;
8
9
/**
10
 * Library for pattern matching
11
 *
12
 * @package Ktomk\Pipelines
13
 */
14
class Glob
15
{
16
    private static $map = array(
17
        '\\\\' => '[\\\\]', '\\*' => '[*]', '\\?' => '[?]', '\\' => '[\\\\]',
18
        '^' => '\\^', '$' => '[$]',
19
        '|' => '[|]', '+' => '[+]',
20
        '[' => '[[]', ']' => '[]]',
21
        '{' => '[{]', '}' => '[}]',
22
        '(' => '[(]', ')' => '[)]',
23
        '<' => '[<]', '>' => '[>]',
24
        '**' => '.*',
25
        '*' => '[^/]*', '?' => '[^/]?',
26
        '.' => '[.]',
27
        '=' => '[=]', '!' => '[!]',
28
        ':' => '[:]', '-' => '[-]',
29
        '~' => '\\~',
30
    );
31
32
    /**
33
     * glob pattern match
34
     *
35
     * @param string $pattern
36
     * @param string $subject
37
     * @throws \UnexpectedValueException
38
     * @return bool
39
     */
40 28
    public static function match($pattern, $subject) {
41 28
        $regex = implode(
42 28
            '|',
43 28
            array_map(array('Ktomk\Pipelines\Glob', 'map'), self::expandBrace($pattern))
44
        );
45
46 28
        return (bool)preg_match(
47 28
            '~^(' . $regex . ')$~',
48 28
            $subject
49
        );
50
    }
51
52
    /**
53
     * expand brace "{}" in pattern
54
     *
55
     * @param string $pattern
56
     * @throws \UnexpectedValueException
57
     * @return array of all patterns w/o braces, no duplicates
58
     */
59 42
    public static function expandBrace($pattern)
60
    {
61 42
        $stack = array($pattern);
62
63 42
        for ($i = 0; isset($stack[$i]); $i++) {
64 42
            $subject = $stack[$i];
65 42
            $result = self::expandBraceInnerMatch($subject, $matches);
66 42
            if (0 === $result) {
67 42
                continue;
68
            }
69
            // match
70 15
            $segments = preg_split('~\\\\.(*SKIP)(*FAIL)|,~', $matches[2]);
71 15
            $segments = array_unique(/** @scrutinizer ignore-type */ $segments);
72 15
            foreach ($segments as $segment) {
73 15
                $permutation = $matches[1] . $segment . $matches[3];
74 15
                in_array($permutation, $stack, true) || $stack[] = $permutation;
75
            }
76 15
            unset($stack[$i]);
77
        }
78
79
        // inline escaped brace characters
80 42
        $stack = array_map(function ($str) {
81 42
            return strtr($str, array(
82 42
                '\\\\' => '\\\\',
83
                '\\{' => '{', '\\}' => '}', '\\,' => ',',
84
            ));
85 42
        }, $stack);
86
87 42
        return array_values($stack);
88
    }
89
90
    /**
91
     * @param string $subject
92
     * @param array $matches
93
     * @throws UnexpectedValueException
94
     * @return false|int
95
     */
96 42
    private static function expandBraceInnerMatch($subject, &$matches)
97
    {
98 42
        $result = preg_match_all(
99 42
            '~(\\\\.(*SKIP)(*FAIL)|(?P<token>[,{}]))~',
100 42
            $subject,
101 42
            $lookup,
102 42
            PREG_OFFSET_CAPTURE
103
        );
104
105 42
        if (false === $result) {
106
            throw new UnexpectedValueException('regex pattern failure'); // @codeCoverageIgnore
107
        }
108
109 42
        if (0 === $result) {
110 37
            return $result;
111
        }
112
113 18
        $open = null;
114 18
        $comma = null;
115
116 18
        foreach ($lookup['token'] as $token) {
117 18
            list($type, $pos) = $token;
118 18
            if ('{' === $type) {
119 17
                $open = $token;
120 17
                $comma = null;
121 18
            } elseif (',' === $type) {
122 16
                $comma = $token;
123 18
            } elseif ($open && $comma) {
124
                $matches = array(
125 15
                    $subject,
126 15
                    substr($subject, 0, $open[1]),
127 15
                    substr($subject, $open[1] + 1, $pos - $open[1] - 1),
128 15
                    substr($subject, $pos + 1),
129
                );
130
131 18
                return 1;
132
            }
133
        }
134
135 5
        return 0;
136
    }
137
138 28
    private static function map($pattern)
139
    {
140 28
        return strtr($pattern, self::$map);
141
    }
142
}
143