Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • AbstractAdapter
  • AbstractFindAdapter
  • BsdFindAdapter
  • GnuFindAdapter
  • PhpAdapter

Interfaces

  • AdapterInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: /*
  4:  * This file is part of the Symfony package.
  5:  *
  6:  * (c) Fabien Potencier <fabien@symfony.com>
  7:  *
  8:  * For the full copyright and license information, please view the LICENSE
  9:  * file that was distributed with this source code.
 10:  */
 11: 
 12: namespace Symfony\Component\Finder\Adapter;
 13: 
 14: use Symfony\Component\Finder\Iterator;
 15: use Symfony\Component\Finder\Shell\Shell;
 16: use Symfony\Component\Finder\Expression\Expression;
 17: use Symfony\Component\Finder\Shell\Command;
 18: use Symfony\Component\Finder\Iterator\SortableIterator;
 19: use Symfony\Component\Finder\Comparator\NumberComparator;
 20: use Symfony\Component\Finder\Comparator\DateComparator;
 21: 
 22: /**
 23:  * Shell engine implementation using GNU find command.
 24:  *
 25:  * @author Jean-François Simon <contact@jfsimon.fr>
 26:  */
 27: abstract class AbstractFindAdapter extends AbstractAdapter
 28: {
 29:     /**
 30:      * @var Shell
 31:      */
 32:     protected $shell;
 33: 
 34:     /**
 35:      * Constructor.
 36:      */
 37:     public function __construct()
 38:     {
 39:         $this->shell = new Shell();
 40:     }
 41: 
 42:     /**
 43:      * {@inheritdoc}
 44:      */
 45:     public function searchInDirectory($dir)
 46:     {
 47:         // having "/../" in path make find fail
 48:         $dir = realpath($dir);
 49: 
 50:         // searching directories containing or not containing strings leads to no result
 51:         if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
 52:             return new Iterator\FilePathsIterator(array(), $dir);
 53:         }
 54: 
 55:         $command = Command::create();
 56:         $find = $this->buildFindCommand($command, $dir);
 57: 
 58:         if ($this->followLinks) {
 59:             $find->add('-follow');
 60:         }
 61: 
 62:         $find->add('-mindepth')->add($this->minDepth + 1);
 63: 
 64:         if (PHP_INT_MAX !== $this->maxDepth) {
 65:             $find->add('-maxdepth')->add($this->maxDepth + 1);
 66:         }
 67: 
 68:         if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
 69:             $find->add('-type d');
 70:         } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
 71:             $find->add('-type f');
 72:         }
 73: 
 74:         $this->buildNamesFiltering($find, $this->names);
 75:         $this->buildNamesFiltering($find, $this->notNames, true);
 76:         $this->buildPathsFiltering($find, $dir, $this->paths);
 77:         $this->buildPathsFiltering($find, $dir, $this->notPaths, true);
 78:         $this->buildSizesFiltering($find, $this->sizes);
 79:         $this->buildDatesFiltering($find, $this->dates);
 80: 
 81:         $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
 82:         $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
 83: 
 84:         if ($useGrep && ($this->contains || $this->notContains)) {
 85:             $grep = $command->ins('grep');
 86:             $this->buildContentFiltering($grep, $this->contains);
 87:             $this->buildContentFiltering($grep, $this->notContains, true);
 88:         }
 89: 
 90:         if ($useSort) {
 91:             $this->buildSorting($command, $this->sort);
 92:         }
 93: 
 94:         $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
 95:         $iterator = new Iterator\FilePathsIterator($paths, $dir);
 96: 
 97:         if ($this->exclude) {
 98:             $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
 99:         }
100: 
101:         if (!$useGrep && ($this->contains || $this->notContains)) {
102:             $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
103:         }
104: 
105:         if ($this->filters) {
106:             $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
107:         }
108: 
109:         if (!$useSort && $this->sort) {
110:             $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
111:             $iterator = $iteratorAggregate->getIterator();
112:         }
113: 
114:         return $iterator;
115:     }
116: 
117:     /**
118:      * {@inheritdoc}
119:      */
120:     protected function canBeUsed()
121:     {
122:         return $this->shell->testCommand('find');
123:     }
124: 
125:     /**
126:      * @param Command $command
127:      * @param string  $dir
128:      *
129:      * @return Command
130:      */
131:     protected function buildFindCommand(Command $command, $dir)
132:     {
133:         return $command
134:             ->ins('find')
135:             ->add('find ')
136:             ->arg($dir)
137:             ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions
138:     }
139: 
140:     /**
141:      * @param Command  $command
142:      * @param string[] $names
143:      * @param Boolean  $not
144:      */
145:     private function buildNamesFiltering(Command $command, array $names, $not = false)
146:     {
147:         if (0 === count($names)) {
148:             return;
149:         }
150: 
151:         $command->add($not ? '-not' : null)->cmd('(');
152: 
153:         foreach ($names as $i => $name) {
154:             $expr = Expression::create($name);
155: 
156:             // Find does not support expandable globs ("*.{a,b}" syntax).
157:             if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
158:                 $expr = Expression::create($expr->getGlob()->toRegex(false));
159:             }
160: 
161:             // Fixes 'not search' and 'full path matching' regex problems.
162:             // - Jokers '.' are replaced by [^/].
163:             // - We add '[^/]*' before and after regex (if no ^|$ flags are present).
164:             if ($expr->isRegex()) {
165:                 $regex = $expr->getRegex();
166:                 $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
167:                     ->setStartFlag(false)
168:                     ->setStartJoker(true)
169:                     ->replaceJokers('[^/]');
170:                 if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
171:                     $regex->setEndJoker(false)->append('[^/]*');
172:                 }
173:             }
174: 
175:             $command
176:                 ->add($i > 0 ? '-or' : null)
177:                 ->add($expr->isRegex()
178:                     ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
179:                     : ($expr->isCaseSensitive() ? '-name' : '-iname')
180:                 )
181:                 ->arg($expr->renderPattern());
182:         }
183: 
184:         $command->cmd(')');
185:     }
186: 
187:     /**
188:      * @param Command  $command
189:      * @param string   $dir
190:      * @param string[] $paths
191:      * @param Boolean  $not
192:      */
193:     private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
194:     {
195:         if (0 === count($paths)) {
196:             return;
197:         }
198: 
199:         $command->add($not ? '-not' : null)->cmd('(');
200: 
201:         foreach ($paths as $i => $path) {
202:             $expr = Expression::create($path);
203: 
204:             // Find does not support expandable globs ("*.{a,b}" syntax).
205:             if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
206:                 $expr = Expression::create($expr->getGlob()->toRegex(false));
207:             }
208: 
209:             // Fixes 'not search' regex problems.
210:             if ($expr->isRegex()) {
211:                 $regex = $expr->getRegex();
212:                 $regex->prepend($regex->hasStartFlag() ? $dir.DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
213:             } else {
214:                 $expr->prepend('*')->append('*');
215:             }
216: 
217:             $command
218:                 ->add($i > 0 ? '-or' : null)
219:                 ->add($expr->isRegex()
220:                     ? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
221:                     : ($expr->isCaseSensitive() ? '-path' : '-ipath')
222:                 )
223:                 ->arg($expr->renderPattern());
224:         }
225: 
226:         $command->cmd(')');
227:     }
228: 
229:     /**
230:      * @param Command            $command
231:      * @param NumberComparator[] $sizes
232:      */
233:     private function buildSizesFiltering(Command $command, array $sizes)
234:     {
235:         foreach ($sizes as $i => $size) {
236:             $command->add($i > 0 ? '-and' : null);
237: 
238:             switch ($size->getOperator()) {
239:                 case '<=':
240:                     $command->add('-size -' . ($size->getTarget() + 1) . 'c');
241:                     break;
242:                 case '>=':
243:                     $command->add('-size +'. ($size->getTarget() - 1) . 'c');
244:                     break;
245:                 case '>':
246:                     $command->add('-size +' . $size->getTarget() . 'c');
247:                     break;
248:                 case '!=':
249:                     $command->add('-size -' . $size->getTarget() . 'c');
250:                     $command->add('-size +' . $size->getTarget() . 'c');
251:                 case '<':
252:                 default:
253:                     $command->add('-size -' . $size->getTarget() . 'c');
254:             }
255:         }
256:     }
257: 
258:     /**
259:      * @param Command          $command
260:      * @param DateComparator[] $dates
261:      */
262:     private function buildDatesFiltering(Command $command, array $dates)
263:     {
264:         foreach ($dates as $i => $date) {
265:             $command->add($i > 0 ? '-and' : null);
266: 
267:             $mins = (int) round((time()-$date->getTarget()) / 60);
268: 
269:             if (0 > $mins) {
270:                 // mtime is in the future
271:                 $command->add(' -mmin -0');
272:                 // we will have no result so we don't need to continue
273:                 return;
274:             }
275: 
276:             switch ($date->getOperator()) {
277:                 case '<=':
278:                     $command->add('-mmin +' . ($mins - 1));
279:                     break;
280:                 case '>=':
281:                     $command->add('-mmin -' . ($mins + 1));
282:                     break;
283:                 case '>':
284:                     $command->add('-mmin -' . $mins);
285:                     break;
286:                 case '!=':
287:                     $command->add('-mmin +' . $mins.' -or -mmin -' . $mins);
288:                     break;
289:                 case '<':
290:                 default:
291:                     $command->add('-mmin +' . $mins);
292:             }
293:         }
294:     }
295: 
296:     /**
297:      * @param Command $command
298:      * @param string  $sort
299:      *
300:      * @throws \InvalidArgumentException
301:      */
302:     private function buildSorting(Command $command, $sort)
303:     {
304:         $this->buildFormatSorting($command, $sort);
305:     }
306: 
307:     /**
308:      * @param Command $command
309:      * @param string  $sort
310:      */
311:     abstract protected function buildFormatSorting(Command $command, $sort);
312: 
313:     /**
314:      * @param Command $command
315:      * @param array   $contains
316:      * @param Boolean $not
317:      */
318:     abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
319: }
320: 
php-coveralls API documentation generated by ApiGen 2.8.0