Glob   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 67
c 1
b 0
f 0
dl 0
loc 144
ccs 44
cts 44
cp 1
rs 10
wmc 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
A map() 0 3 1
A expandBrace() 0 29 5
B expandBraceInnerMatch() 0 40 8
A match() 0 10 1
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
     *
38
     * @throws UnexpectedValueException
39
     *
40
     * @return bool
41
     */
42 28
    public static function match($pattern, $subject)
43
    {
44 28
        $regex = implode(
45
            '|',
46 28
            array_map(array('Ktomk\Pipelines\Glob', 'map'), self::expandBrace($pattern))
47
        );
48
49 28
        return (bool)preg_match(
50 28
            '~^(' . $regex . ')$~',
51
            $subject
52
        );
53
    }
54
55
    /**
56
     * expand brace "{}" in pattern
57
     *
58
     * @param string $pattern
59
     *
60
     * @throws UnexpectedValueException
61
     *
62
     * @return array of all patterns w/o braces, no duplicates
63
     */
64 45
    public static function expandBrace($pattern)
65
    {
66 45
        $stack = array($pattern);
67
68 45
        for ($i = 0; isset($stack[$i]); $i++) {
69 45
            $subject = $stack[$i];
70 45
            $result = self::expandBraceInnerMatch($subject, $matches);
71 45
            if (0 === $result) {
72 45
                continue;
73
            }
74
            // match
75 18
            $segments = preg_split('~\\\\.(*SKIP)(*FAIL)|,~', $matches[2]);
76 18
            $segments = array_unique(/** @scrutinizer ignore-type */ $segments);
77 18
            foreach ($segments as $segment) {
78 18
                $permutation = $matches[1] . $segment . $matches[3];
79 18
                in_array($permutation, $stack, true) || $stack[] = $permutation;
80
            }
81 18
            unset($stack[$i]);
82
        }
83
84
        // inline escaped brace characters
85 45
        $stack = array_map(function ($str) {
86 45
            return strtr($str, array(
87
                '\\\\' => '\\\\',
88
                '\\{' => '{', '\\}' => '}', '\\,' => ',',
89
            ));
90
        }, $stack);
91
92 45
        return array_values($stack);
93
    }
94
95
    /**
96
     * @param string $subject
97
     * @param array $matches
98
     *
99
     * @throws UnexpectedValueException
100
     *
101
     * @return int
102
     */
103 45
    private static function expandBraceInnerMatch($subject, &$matches)
104
    {
105 45
        $result = preg_match_all(
106
            '~(\\\\.(*SKIP)(*FAIL)|(?P<token>[,{}]))~',
107
            $subject,
108
            $lookup,
109
            PREG_OFFSET_CAPTURE
110
        );
111
112 45
        if (false === $result) {
113
            throw new UnexpectedValueException('regex pattern failure'); // @codeCoverageIgnore
114
        }
115
116 45
        if (0 === $result) {
117 40
            return $result;
118
        }
119
120 21
        $open = null;
121 21
        $comma = null;
122
123 21
        foreach ($lookup['token'] as $token) {
124 21
            list($type, $pos) = $token;
125 21
            if ('{' === $type) {
126 20
                $open = $token;
127 20
                $comma = null;
128 21
            } elseif (',' === $type) {
129 19
                $comma = $token;
130 21
            } elseif ($open && $comma) {
131 18
                $matches = array(
132
                    $subject,
133 18
                    substr($subject, 0, $open[1]),
134 18
                    substr($subject, $open[1] + 1, $pos - $open[1] - 1),
135 18
                    substr($subject, $pos + 1),
136
                );
137
138 18
                return 1;
139
            }
140
        }
141
142 5
        return 0;
143
    }
144
145
    /**
146
     * map pattern
147
     *
148
     * via translation map to preserve glob and standard characters for pcre
149
     * pattern
150
     *
151
     * @param string $pattern
152
     *
153
     * @return string
154
     */
155 28
    private static function map($pattern)
156
    {
157 28
        return strtr($pattern, self::$map);
158
    }
159
}
160