Skip to content

Commit

Permalink
Switched to a better performing algorithm for Glob::getStaticPrefix()
Browse files Browse the repository at this point in the history
  • Loading branch information
webmozart committed Dec 23, 2015
1 parent daf2dce commit f6787fb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 68 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Changelog

* 4.0.0 (@release_date@)

* switched to a better-performing regex compilation algorithm
* switched to a better-performing algorithm for `Glob::toRegEx()`
* switched to a better-performing algorithm for `Glob::getStaticPrefix()`
* escaping is now always enabled

* 3.3.0 (2015-12-23)
Expand Down
66 changes: 41 additions & 25 deletions src/Glob.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,33 +420,49 @@ public static function getStaticPrefix($glob, $flags = 0)
));
}

$prefix = $glob;
$prefix = '';
$length = strlen($glob);

if ($flags & self::ESCAPE) {
// Read backslashes together with the next (the escaped) character
// up to the first non-escaped star/brace
if (preg_match('~^('.Symbol::BACKSLASH.'.|[^'.Symbol::BACKSLASH.Symbol::STAR.Symbol::L_BRACE.Symbol::QUESTION_MARK.Symbol::L_BRACKET.'])*~', $glob, $matches)) {
$prefix = $matches[0];
}
for ($i = 0; $i < $length; ++$i) {
$c = $glob[$i];

switch ($c) {
case '/':
$prefix .= '/';
if (isset($glob[$i + 3]) && '**/' === $glob[$i + 1].$glob[$i + 2].$glob[$i + 3]) {
break 2;
}
break;

case '*':
case '?':
case '{':
case '[':
break 2;

case '\\':
if (isset($glob[$i + 1])) {
switch ($glob[$i + 1]) {
case '*':
case '?':
case '{':
case '[':
case '\\':
$prefix .= $glob[$i + 1];
++$i;
break;

// Replace escaped characters by their unescaped equivalents
$prefix = str_replace(
array('\\\\', '\\*', '\\{', '\\}', '\\?', '\\[', '\\]', '\\^'),
array('\\', '*', '{', '}', '?', '[', ']', '^'),
$prefix
);
} else {
$pos1 = strpos($glob, '*');
$pos2 = strpos($glob, '{');
$pos3 = strpos($glob, '?');
$pos4 = strpos($glob, '[');

$positions = array_filter(array($pos1, $pos2, $pos3, $pos4), function ($v) {
return false !== $v;
});

if (!empty($positions)) {
$prefix = substr($glob, 0, min($positions));
default:
$prefix .= '\\';
}
} else {
$prefix .= '\\';
}
break;

default:
$prefix .= $c;
break;
}
}

Expand Down
68 changes: 26 additions & 42 deletions tests/GlobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -711,48 +711,36 @@ public function testToRegexFailsIfNotAbsolute()
/**
* @dataProvider provideStaticPrefixes
*/
public function testGetStaticPrefix($glob, $prefix, $flags = 0)
public function testGetStaticPrefix($glob, $prefix)
{
$this->assertSame($prefix, Glob::getStaticPrefix($glob, $flags));
$this->assertSame($prefix, Glob::getStaticPrefix($glob));
}

public function provideStaticPrefixes()
{
return array(
// The method assumes that the path is already consolidated
array('/foo/baz/../*/bar/*', '/foo/baz/../'),
array('/foo/baz/bar*', '/foo/baz/bar'),
array('/foo/baz/bar\\*', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\*', '/foo/baz/bar\\\\'),
array('/foo/baz/bar{a,b}', '/foo/baz/bar'),
array('/foo/baz/bar\\{a,b}', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\{a,b}', '/foo/baz/bar\\\\'),
array('/foo/baz/bar?', '/foo/baz/bar'),
array('/foo/baz/bar\\?', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\?', '/foo/baz/bar\\\\'),
array('/foo/baz/bar[ab]', '/foo/baz/bar'),
array('/foo/baz/bar\\[ab]', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\[ab]', '/foo/baz/bar\\\\'),
array('/foo/baz/bar\\*', '/foo/baz/bar*', Glob::ESCAPE),
array('/foo/baz/bar\\\\*', '/foo/baz/bar\\', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\*', '/foo/baz/bar\\*', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\\\*', '/foo/baz/bar\\\\', Glob::ESCAPE),
array('/foo/baz/bar\\*\\\\', '/foo/baz/bar*\\', Glob::ESCAPE),
array('/foo/baz/bar\\{a,b}', '/foo/baz/bar{a,b}', Glob::ESCAPE),
array('/foo/baz/bar\\\\{a,b}', '/foo/baz/bar\\', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\{a,b}', '/foo/baz/bar\\{a,b}', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\\\{a,b}', '/foo/baz/bar\\\\', Glob::ESCAPE),
array('/foo/baz/bar\\{a,b}\\\\', '/foo/baz/bar{a,b}\\', Glob::ESCAPE),
array('/foo/baz/bar\\?', '/foo/baz/bar?', Glob::ESCAPE),
array('/foo/baz/bar\\\\?', '/foo/baz/bar\\', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\?', '/foo/baz/bar\\?', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\\\?', '/foo/baz/bar\\\\', Glob::ESCAPE),
array('/foo/baz/bar\\?\\\\', '/foo/baz/bar?\\', Glob::ESCAPE),
array('/foo/baz/bar\\[ab]', '/foo/baz/bar[ab]', Glob::ESCAPE),
array('/foo/baz/bar\\\\[ab]', '/foo/baz/bar\\', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\[ab]', '/foo/baz/bar\\[ab]', Glob::ESCAPE),
array('/foo/baz/bar\\\\\\\\[ab]', '/foo/baz/bar\\\\', Glob::ESCAPE),
array('/foo/baz/bar\\[ab]\\\\', '/foo/baz/bar[ab]\\', Glob::ESCAPE),
array('/foo/baz/bar\\*', '/foo/baz/bar*'),
array('/foo/baz/bar\\\\*', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\\\*', '/foo/baz/bar\\*'),
array('/foo/baz/bar\\\\\\\\*', '/foo/baz/bar\\\\'),
array('/foo/baz/bar\\*\\\\', '/foo/baz/bar*\\'),
array('/foo/baz/bar\\{a,b}', '/foo/baz/bar{a,b}'),
array('/foo/baz/bar\\\\{a,b}', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\\\{a,b}', '/foo/baz/bar\\{a,b}'),
array('/foo/baz/bar\\\\\\\\{a,b}', '/foo/baz/bar\\\\'),
array('/foo/baz/bar\\{a,b}\\\\', '/foo/baz/bar{a,b}\\'),
array('/foo/baz/bar\\?', '/foo/baz/bar?'),
array('/foo/baz/bar\\\\?', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\\\?', '/foo/baz/bar\\?'),
array('/foo/baz/bar\\\\\\\\?', '/foo/baz/bar\\\\'),
array('/foo/baz/bar\\?\\\\', '/foo/baz/bar?\\'),
array('/foo/baz/bar\\[ab]', '/foo/baz/bar[ab]'),
array('/foo/baz/bar\\\\[ab]', '/foo/baz/bar\\'),
array('/foo/baz/bar\\\\\\[ab]', '/foo/baz/bar\\[ab]'),
array('/foo/baz/bar\\\\\\\\[ab]', '/foo/baz/bar\\\\'),
array('/foo/baz/bar\\[ab]\\\\', '/foo/baz/bar[ab]\\'),
);
}

Expand All @@ -776,9 +764,9 @@ public function testGetBasePath($glob, $basePath, $flags = 0)
/**
* @dataProvider provideBasePaths
*/
public function testGetBasePathStream($glob, $basePath, $flags = 0)
public function testGetBasePathStream($glob, $basePath)
{
$this->assertSame('globtest://'.$basePath, Glob::getBasePath('globtest://'.$glob, $flags));
$this->assertSame('globtest://'.$basePath, Glob::getBasePath('globtest://'.$glob));
}

public function provideBasePaths()
Expand All @@ -792,13 +780,9 @@ public function provideBasePaths()
array('/foo*', '/'),
array('/*', '/'),
array('/foo/baz*/bar', '/foo'),
array('/foo/baz\\*/bar', '/foo'),
array('/foo/baz\\*/bar', '/foo/baz*'),
array('/foo/baz\\\\*/bar', '/foo'),
array('/foo/baz\\\\\\*/bar', '/foo'),
array('/foo/baz*/bar', '/foo', Glob::ESCAPE),
array('/foo/baz\\*/bar', '/foo/baz*', Glob::ESCAPE),
array('/foo/baz\\\\*/bar', '/foo', Glob::ESCAPE),
array('/foo/baz\\\\\\*/bar', '/foo/baz\\*', Glob::ESCAPE),
array('/foo/baz\\\\\\*/bar', '/foo/baz\\*'),
);
}

Expand Down

0 comments on commit f6787fb

Please sign in to comment.