1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 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: 24: 25: 26:
27: abstract class AbstractFindAdapter extends AbstractAdapter
28: {
29: 30: 31:
32: protected $shell;
33:
34: 35: 36:
37: public function __construct()
38: {
39: $this->shell = new Shell();
40: }
41:
42: 43: 44:
45: public function searchInDirectory($dir)
46: {
47:
48: $dir = realpath($dir);
49:
50:
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: 119:
120: protected function canBeUsed()
121: {
122: return $this->shell->testCommand('find');
123: }
124:
125: 126: 127: 128: 129: 130:
131: protected function buildFindCommand(Command $command, $dir)
132: {
133: return $command
134: ->ins('find')
135: ->add('find ')
136: ->arg($dir)
137: ->add('-noleaf');
138: }
139:
140: 141: 142: 143: 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:
157: if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
158: $expr = Expression::create($expr->getGlob()->toRegex(false));
159: }
160:
161:
162:
163:
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: 189: 190: 191: 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:
205: if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
206: $expr = Expression::create($expr->getGlob()->toRegex(false));
207: }
208:
209:
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: 231: 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: 260: 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:
271: $command->add(' -mmin -0');
272:
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: 298: 299: 300: 301:
302: private function buildSorting(Command $command, $sort)
303: {
304: $this->buildFormatSorting($command, $sort);
305: }
306:
307: 308: 309: 310:
311: abstract protected function buildFormatSorting(Command $command, $sort);
312:
313: 314: 315: 316: 317:
318: abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
319: }
320: