| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 | <?php
/*
 * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace JMS\DiExtraBundle\Finder;
use JMS\DiExtraBundle\Exception\RuntimeException;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\ExecutableFinder;
class PatternFinder
{
    const METHOD_GREP = 1;
    const METHOD_FINDSTR = 2;
    const METHOD_FINDER = 3;
    private static $method;
    private static $grepPath;
    private $pattern;
    private $filePattern;
    private $recursive = true;
    private $regexPattern = false;
    public function __construct($pattern, $filePattern = '*.php')
    {
        if (null === self::$method) {
            self::determineMethod();
        }
        $this->pattern = $pattern;
        $this->filePattern = $filePattern;
    }
    public function setRecursive($bool)
    {
        $this->recursive = (Boolean) $bool;
    }
    public function setRegexPattern($bool)
    {
        $this->regexPattern = (Boolean) $bool;
    }
    public function findFiles(array $dirs)
    {
        // check for grep availability
        if (self::METHOD_GREP === self::$method) {
            return $this->findUsingGrep($dirs);
        }
        // use FINDSTR on Windows
        if (self::METHOD_FINDSTR === self::$method) {
            return $this->findUsingFindstr($dirs);
        }
        // this should really be avoided at all costs since it is damn slow
        return $this->findUsingFinder($dirs);
    }
    private function findUsingFindstr(array $dirs)
    {
        $cmd = 'FINDSTR /M /S /P';
        if (!$this->recursive) {
            $cmd .= ' /L';
        }
        $cmd .= ' /D:'.escapeshellarg(implode(';', $dirs));
        $cmd .= ' '.escapeshellarg($this->pattern);
        $cmd .= ' '.$this->filePattern;
        exec($cmd, $lines, $exitCode);
        if (1 === $exitCode) {
            return array();
        }
        if (0 !== $exitCode) {
            throw new RuntimeException(sprintf('Command "%s" exited with non-successful status code. "%d".', $cmd, $exitCode));
        }
        // Looks like FINDSTR has different versions with different output formats.
        //
        // Supported format #1:
        //     C:\matched\dir1:
        // Relative\Path\To\File1.php
        // Relative\Path\To\File2.php
        //     C:\matched\dir2:
        // Relative\Path\To\File3.php
        // Relative\Path\To\File4.php
        //
        // Supported format #2:
        // C:\matched\dir1\Relative\Path\To\File1.php
        // C:\matched\dir1\Relative\Path\To\File2.php
        // C:\matched\dir2\Relative\Path\To\File3.php
        // C:\matched\dir2\Relative\Path\To\File4.php
        $files = array();
        $currentDir = '';
        foreach ($lines as $line) {
            if (':' === substr($line, -1)) {
                $currentDir = trim($line, ' :/').'/';
                continue;
            }
            $files[] = $currentDir.$line;
        }
        return $files;
    }
    private function findUsingGrep(array $dirs)
    {
        $cmd = self::$grepPath;
        if (!$this->regexPattern) {
            $cmd .= ' --fixed-strings';
        } else {
            $cmd .= ' --extended-regexp';
        }
        if ($this->recursive) {
            $cmd .= ' --directories=recurse';
        } else {
            $cmd .= ' --directories=skip';
        }
        $cmd .= ' --devices=skip --files-with-matches --with-filename --color=never --include='.$this->filePattern;
        $cmd .= ' '.escapeshellarg($this->pattern);
        foreach ($dirs as $dir) {
            $cmd .= ' '.escapeshellarg($dir);
        }
        exec($cmd, $files, $exitCode);
        if (1 === $exitCode) {
            return array();
        }
        if (0 !== $exitCode) {
            throw new RuntimeException(sprintf('Command "%s" exited with non-successful status code "%d".', $cmd, $exitCode));
        }
        return $files;
    }
    private function findUsingFinder(array $dirs)
    {
        $finder = new Finder();
        $pattern = $this->pattern;
        $regex = $this->regexPattern;
        $finder
            ->files()
            ->name($this->filePattern)
            ->in($dirs)
            ->ignoreVCS(true)
            ->filter(function($file) use ($pattern, $regex) {
                if (!$regex) {
                    return false !== strpos(file_get_contents($file->getPathName()), $pattern);
                }
                return 0 < preg_match('#'.$pattern.'#', file_get_contents($file->getPathName()));
            })
        ;
        if (!$this->recursive) {
            $finder->depth('<= 0');
        }
        return array_keys(iterator_to_array($finder));
    }
    private static function determineMethod()
    {
        $finder = new ExecutableFinder();
        $isWindows = 0 === stripos(PHP_OS, 'win');
        $execAvailable = function_exists('exec');
        if (!$isWindows && $execAvailable && self::$grepPath = $finder->find('grep')) {
            self::$method = self::METHOD_GREP;
        } else if ($isWindows && $execAvailable) {
            self::$method = self::METHOD_FINDSTR;
        } else {
            self::$method = self::METHOD_FINDER;
        }
    }
}
 |