From f6787fb16935ab5d3695459a7b54b5c35a308b19 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Wed, 23 Dec 2015 14:04:48 +0100 Subject: [PATCH] Switched to a better performing algorithm for Glob::getStaticPrefix() --- CHANGELOG.md | 3 +- src/Glob.php | 66 +++++++++++++++++++++++++++----------------- tests/GlobTest.php | 68 ++++++++++++++++++---------------------------- 3 files changed, 69 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17980f1..82016cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/Glob.php b/src/Glob.php index 15e0957..34de929 100644 --- a/src/Glob.php +++ b/src/Glob.php @@ -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; } } diff --git a/tests/GlobTest.php b/tests/GlobTest.php index 904a94e..3b8311b 100644 --- a/tests/GlobTest.php +++ b/tests/GlobTest.php @@ -711,9 +711,9 @@ 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() @@ -721,38 +721,26 @@ 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]\\'), ); } @@ -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() @@ -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\\*'), ); }