Yaffs site version 1.1
[yaffs-website] / vendor / symfony / finder / Finder.php
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;
13
14 use Symfony\Component\Finder\Adapter\AdapterInterface;
15 use Symfony\Component\Finder\Adapter\GnuFindAdapter;
16 use Symfony\Component\Finder\Adapter\BsdFindAdapter;
17 use Symfony\Component\Finder\Adapter\PhpAdapter;
18 use Symfony\Component\Finder\Comparator\DateComparator;
19 use Symfony\Component\Finder\Comparator\NumberComparator;
20 use Symfony\Component\Finder\Exception\ExceptionInterface;
21 use Symfony\Component\Finder\Iterator\CustomFilterIterator;
22 use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
23 use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
24 use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
25 use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
26 use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
27 use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
28 use Symfony\Component\Finder\Iterator\SortableIterator;
29
30 /**
31  * Finder allows to build rules to find files and directories.
32  *
33  * It is a thin wrapper around several specialized iterator classes.
34  *
35  * All rules may be invoked several times.
36  *
37  * All methods return the current Finder object to allow easy chaining:
38  *
39  * $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
40  *
41  * @author Fabien Potencier <fabien@symfony.com>
42  */
43 class Finder implements \IteratorAggregate, \Countable
44 {
45     const IGNORE_VCS_FILES = 1;
46     const IGNORE_DOT_FILES = 2;
47
48     private $mode = 0;
49     private $names = array();
50     private $notNames = array();
51     private $exclude = array();
52     private $filters = array();
53     private $depths = array();
54     private $sizes = array();
55     private $followLinks = false;
56     private $sort = false;
57     private $ignore = 0;
58     private $dirs = array();
59     private $dates = array();
60     private $iterators = array();
61     private $contains = array();
62     private $notContains = array();
63     private $adapters = null;
64     private $paths = array();
65     private $notPaths = array();
66     private $ignoreUnreadableDirs = false;
67
68     private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
69
70     /**
71      * Constructor.
72      */
73     public function __construct()
74     {
75         $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
76     }
77
78     /**
79      * Creates a new Finder.
80      *
81      * @return static
82      */
83     public static function create()
84     {
85         return new static();
86     }
87
88     /**
89      * Registers a finder engine implementation.
90      *
91      * @param AdapterInterface $adapter  An adapter instance
92      * @param int              $priority Highest is selected first
93      *
94      * @return $this
95      *
96      * @deprecated since 2.8, to be removed in 3.0.
97      */
98     public function addAdapter(AdapterInterface $adapter, $priority = 0)
99     {
100         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
101
102         $this->initDefaultAdapters();
103
104         $this->adapters[$adapter->getName()] = array(
105             'adapter' => $adapter,
106             'priority' => $priority,
107             'selected' => false,
108         );
109
110         return $this->sortAdapters();
111     }
112
113     /**
114      * Sets the selected adapter to the best one according to the current platform the code is run on.
115      *
116      * @return $this
117      *
118      * @deprecated since 2.8, to be removed in 3.0.
119      */
120     public function useBestAdapter()
121     {
122         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
123
124         $this->initDefaultAdapters();
125
126         $this->resetAdapterSelection();
127
128         return $this->sortAdapters();
129     }
130
131     /**
132      * Selects the adapter to use.
133      *
134      * @param string $name
135      *
136      * @return $this
137      *
138      * @throws \InvalidArgumentException
139      *
140      * @deprecated since 2.8, to be removed in 3.0.
141      */
142     public function setAdapter($name)
143     {
144         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
145
146         $this->initDefaultAdapters();
147
148         if (!isset($this->adapters[$name])) {
149             throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
150         }
151
152         $this->resetAdapterSelection();
153         $this->adapters[$name]['selected'] = true;
154
155         return $this->sortAdapters();
156     }
157
158     /**
159      * Removes all adapters registered in the finder.
160      *
161      * @return $this
162      *
163      * @deprecated since 2.8, to be removed in 3.0.
164      */
165     public function removeAdapters()
166     {
167         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
168
169         $this->adapters = array();
170
171         return $this;
172     }
173
174     /**
175      * Returns registered adapters ordered by priority without extra information.
176      *
177      * @return AdapterInterface[]
178      *
179      * @deprecated since 2.8, to be removed in 3.0.
180      */
181     public function getAdapters()
182     {
183         @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
184
185         $this->initDefaultAdapters();
186
187         return array_values(array_map(function (array $adapter) {
188             return $adapter['adapter'];
189         }, $this->adapters));
190     }
191
192     /**
193      * Restricts the matching to directories only.
194      *
195      * @return $this
196      */
197     public function directories()
198     {
199         $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
200
201         return $this;
202     }
203
204     /**
205      * Restricts the matching to files only.
206      *
207      * @return $this
208      */
209     public function files()
210     {
211         $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
212
213         return $this;
214     }
215
216     /**
217      * Adds tests for the directory depth.
218      *
219      * Usage:
220      *
221      *   $finder->depth('> 1') // the Finder will start matching at level 1.
222      *   $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
223      *
224      * @param string|int $level The depth level expression
225      *
226      * @return $this
227      *
228      * @see DepthRangeFilterIterator
229      * @see NumberComparator
230      */
231     public function depth($level)
232     {
233         $this->depths[] = new Comparator\NumberComparator($level);
234
235         return $this;
236     }
237
238     /**
239      * Adds tests for file dates (last modified).
240      *
241      * The date must be something that strtotime() is able to parse:
242      *
243      *   $finder->date('since yesterday');
244      *   $finder->date('until 2 days ago');
245      *   $finder->date('> now - 2 hours');
246      *   $finder->date('>= 2005-10-15');
247      *
248      * @param string $date A date range string
249      *
250      * @return $this
251      *
252      * @see strtotime
253      * @see DateRangeFilterIterator
254      * @see DateComparator
255      */
256     public function date($date)
257     {
258         $this->dates[] = new Comparator\DateComparator($date);
259
260         return $this;
261     }
262
263     /**
264      * Adds rules that files must match.
265      *
266      * You can use patterns (delimited with / sign), globs or simple strings.
267      *
268      * $finder->name('*.php')
269      * $finder->name('/\.php$/') // same as above
270      * $finder->name('test.php')
271      *
272      * @param string $pattern A pattern (a regexp, a glob, or a string)
273      *
274      * @return $this
275      *
276      * @see FilenameFilterIterator
277      */
278     public function name($pattern)
279     {
280         $this->names[] = $pattern;
281
282         return $this;
283     }
284
285     /**
286      * Adds rules that files must not match.
287      *
288      * @param string $pattern A pattern (a regexp, a glob, or a string)
289      *
290      * @return $this
291      *
292      * @see FilenameFilterIterator
293      */
294     public function notName($pattern)
295     {
296         $this->notNames[] = $pattern;
297
298         return $this;
299     }
300
301     /**
302      * Adds tests that file contents must match.
303      *
304      * Strings or PCRE patterns can be used:
305      *
306      * $finder->contains('Lorem ipsum')
307      * $finder->contains('/Lorem ipsum/i')
308      *
309      * @param string $pattern A pattern (string or regexp)
310      *
311      * @return $this
312      *
313      * @see FilecontentFilterIterator
314      */
315     public function contains($pattern)
316     {
317         $this->contains[] = $pattern;
318
319         return $this;
320     }
321
322     /**
323      * Adds tests that file contents must not match.
324      *
325      * Strings or PCRE patterns can be used:
326      *
327      * $finder->notContains('Lorem ipsum')
328      * $finder->notContains('/Lorem ipsum/i')
329      *
330      * @param string $pattern A pattern (string or regexp)
331      *
332      * @return $this
333      *
334      * @see FilecontentFilterIterator
335      */
336     public function notContains($pattern)
337     {
338         $this->notContains[] = $pattern;
339
340         return $this;
341     }
342
343     /**
344      * Adds rules that filenames must match.
345      *
346      * You can use patterns (delimited with / sign) or simple strings.
347      *
348      * $finder->path('some/special/dir')
349      * $finder->path('/some\/special\/dir/') // same as above
350      *
351      * Use only / as dirname separator.
352      *
353      * @param string $pattern A pattern (a regexp or a string)
354      *
355      * @return $this
356      *
357      * @see FilenameFilterIterator
358      */
359     public function path($pattern)
360     {
361         $this->paths[] = $pattern;
362
363         return $this;
364     }
365
366     /**
367      * Adds rules that filenames must not match.
368      *
369      * You can use patterns (delimited with / sign) or simple strings.
370      *
371      * $finder->notPath('some/special/dir')
372      * $finder->notPath('/some\/special\/dir/') // same as above
373      *
374      * Use only / as dirname separator.
375      *
376      * @param string $pattern A pattern (a regexp or a string)
377      *
378      * @return $this
379      *
380      * @see FilenameFilterIterator
381      */
382     public function notPath($pattern)
383     {
384         $this->notPaths[] = $pattern;
385
386         return $this;
387     }
388
389     /**
390      * Adds tests for file sizes.
391      *
392      * $finder->size('> 10K');
393      * $finder->size('<= 1Ki');
394      * $finder->size(4);
395      *
396      * @param string|int $size A size range string or an integer
397      *
398      * @return $this
399      *
400      * @see SizeRangeFilterIterator
401      * @see NumberComparator
402      */
403     public function size($size)
404     {
405         $this->sizes[] = new Comparator\NumberComparator($size);
406
407         return $this;
408     }
409
410     /**
411      * Excludes directories.
412      *
413      * @param string|array $dirs A directory path or an array of directories
414      *
415      * @return $this
416      *
417      * @see ExcludeDirectoryFilterIterator
418      */
419     public function exclude($dirs)
420     {
421         $this->exclude = array_merge($this->exclude, (array) $dirs);
422
423         return $this;
424     }
425
426     /**
427      * Excludes "hidden" directories and files (starting with a dot).
428      *
429      * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
430      *
431      * @return $this
432      *
433      * @see ExcludeDirectoryFilterIterator
434      */
435     public function ignoreDotFiles($ignoreDotFiles)
436     {
437         if ($ignoreDotFiles) {
438             $this->ignore |= static::IGNORE_DOT_FILES;
439         } else {
440             $this->ignore &= ~static::IGNORE_DOT_FILES;
441         }
442
443         return $this;
444     }
445
446     /**
447      * Forces the finder to ignore version control directories.
448      *
449      * @param bool $ignoreVCS Whether to exclude VCS files or not
450      *
451      * @return $this
452      *
453      * @see ExcludeDirectoryFilterIterator
454      */
455     public function ignoreVCS($ignoreVCS)
456     {
457         if ($ignoreVCS) {
458             $this->ignore |= static::IGNORE_VCS_FILES;
459         } else {
460             $this->ignore &= ~static::IGNORE_VCS_FILES;
461         }
462
463         return $this;
464     }
465
466     /**
467      * Adds VCS patterns.
468      *
469      * @see ignoreVCS()
470      *
471      * @param string|string[] $pattern VCS patterns to ignore
472      */
473     public static function addVCSPattern($pattern)
474     {
475         foreach ((array) $pattern as $p) {
476             self::$vcsPatterns[] = $p;
477         }
478
479         self::$vcsPatterns = array_unique(self::$vcsPatterns);
480     }
481
482     /**
483      * Sorts files and directories by an anonymous function.
484      *
485      * The anonymous function receives two \SplFileInfo instances to compare.
486      *
487      * This can be slow as all the matching files and directories must be retrieved for comparison.
488      *
489      * @param \Closure $closure An anonymous function
490      *
491      * @return $this
492      *
493      * @see SortableIterator
494      */
495     public function sort(\Closure $closure)
496     {
497         $this->sort = $closure;
498
499         return $this;
500     }
501
502     /**
503      * Sorts files and directories by name.
504      *
505      * This can be slow as all the matching files and directories must be retrieved for comparison.
506      *
507      * @return $this
508      *
509      * @see SortableIterator
510      */
511     public function sortByName()
512     {
513         $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
514
515         return $this;
516     }
517
518     /**
519      * Sorts files and directories by type (directories before files), then by name.
520      *
521      * This can be slow as all the matching files and directories must be retrieved for comparison.
522      *
523      * @return $this
524      *
525      * @see SortableIterator
526      */
527     public function sortByType()
528     {
529         $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
530
531         return $this;
532     }
533
534     /**
535      * Sorts files and directories by the last accessed time.
536      *
537      * This is the time that the file was last accessed, read or written to.
538      *
539      * This can be slow as all the matching files and directories must be retrieved for comparison.
540      *
541      * @return $this
542      *
543      * @see SortableIterator
544      */
545     public function sortByAccessedTime()
546     {
547         $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
548
549         return $this;
550     }
551
552     /**
553      * Sorts files and directories by the last inode changed time.
554      *
555      * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
556      *
557      * On Windows, since inode is not available, changed time is actually the file creation time.
558      *
559      * This can be slow as all the matching files and directories must be retrieved for comparison.
560      *
561      * @return $this
562      *
563      * @see SortableIterator
564      */
565     public function sortByChangedTime()
566     {
567         $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
568
569         return $this;
570     }
571
572     /**
573      * Sorts files and directories by the last modified time.
574      *
575      * This is the last time the actual contents of the file were last modified.
576      *
577      * This can be slow as all the matching files and directories must be retrieved for comparison.
578      *
579      * @return $this
580      *
581      * @see SortableIterator
582      */
583     public function sortByModifiedTime()
584     {
585         $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
586
587         return $this;
588     }
589
590     /**
591      * Filters the iterator with an anonymous function.
592      *
593      * The anonymous function receives a \SplFileInfo and must return false
594      * to remove files.
595      *
596      * @param \Closure $closure An anonymous function
597      *
598      * @return $this
599      *
600      * @see CustomFilterIterator
601      */
602     public function filter(\Closure $closure)
603     {
604         $this->filters[] = $closure;
605
606         return $this;
607     }
608
609     /**
610      * Forces the following of symlinks.
611      *
612      * @return $this
613      */
614     public function followLinks()
615     {
616         $this->followLinks = true;
617
618         return $this;
619     }
620
621     /**
622      * Tells finder to ignore unreadable directories.
623      *
624      * By default, scanning unreadable directories content throws an AccessDeniedException.
625      *
626      * @param bool $ignore
627      *
628      * @return $this
629      */
630     public function ignoreUnreadableDirs($ignore = true)
631     {
632         $this->ignoreUnreadableDirs = (bool) $ignore;
633
634         return $this;
635     }
636
637     /**
638      * Searches files and directories which match defined rules.
639      *
640      * @param string|array $dirs A directory path or an array of directories
641      *
642      * @return $this
643      *
644      * @throws \InvalidArgumentException if one of the directories does not exist
645      */
646     public function in($dirs)
647     {
648         $resolvedDirs = array();
649
650         foreach ((array) $dirs as $dir) {
651             if (is_dir($dir)) {
652                 $resolvedDirs[] = $dir;
653             } elseif ($glob = glob($dir, (defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
654                 $resolvedDirs = array_merge($resolvedDirs, $glob);
655             } else {
656                 throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
657             }
658         }
659
660         $this->dirs = array_merge($this->dirs, $resolvedDirs);
661
662         return $this;
663     }
664
665     /**
666      * Returns an Iterator for the current Finder configuration.
667      *
668      * This method implements the IteratorAggregate interface.
669      *
670      * @return \Iterator|SplFileInfo[] An iterator
671      *
672      * @throws \LogicException if the in() method has not been called
673      */
674     public function getIterator()
675     {
676         if (0 === count($this->dirs) && 0 === count($this->iterators)) {
677             throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
678         }
679
680         if (1 === count($this->dirs) && 0 === count($this->iterators)) {
681             return $this->searchInDirectory($this->dirs[0]);
682         }
683
684         $iterator = new \AppendIterator();
685         foreach ($this->dirs as $dir) {
686             $iterator->append($this->searchInDirectory($dir));
687         }
688
689         foreach ($this->iterators as $it) {
690             $iterator->append($it);
691         }
692
693         return $iterator;
694     }
695
696     /**
697      * Appends an existing set of files/directories to the finder.
698      *
699      * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
700      *
701      * @param mixed $iterator
702      *
703      * @return $this
704      *
705      * @throws \InvalidArgumentException When the given argument is not iterable.
706      */
707     public function append($iterator)
708     {
709         if ($iterator instanceof \IteratorAggregate) {
710             $this->iterators[] = $iterator->getIterator();
711         } elseif ($iterator instanceof \Iterator) {
712             $this->iterators[] = $iterator;
713         } elseif ($iterator instanceof \Traversable || is_array($iterator)) {
714             $it = new \ArrayIterator();
715             foreach ($iterator as $file) {
716                 $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
717             }
718             $this->iterators[] = $it;
719         } else {
720             throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
721         }
722
723         return $this;
724     }
725
726     /**
727      * Counts all the results collected by the iterators.
728      *
729      * @return int
730      */
731     public function count()
732     {
733         return iterator_count($this->getIterator());
734     }
735
736     /**
737      * @return $this
738      */
739     private function sortAdapters()
740     {
741         uasort($this->adapters, function (array $a, array $b) {
742             if ($a['selected'] || $b['selected']) {
743                 return $a['selected'] ? -1 : 1;
744             }
745
746             return $a['priority'] > $b['priority'] ? -1 : 1;
747         });
748
749         return $this;
750     }
751
752     /**
753      * @param $dir
754      *
755      * @return \Iterator
756      */
757     private function searchInDirectory($dir)
758     {
759         if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
760             $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
761         }
762
763         if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
764             $this->notPaths[] = '#(^|/)\..+(/|$)#';
765         }
766
767         if ($this->adapters) {
768             foreach ($this->adapters as $adapter) {
769                 if ($adapter['adapter']->isSupported()) {
770                     try {
771                         return $this
772                             ->buildAdapter($adapter['adapter'])
773                             ->searchInDirectory($dir);
774                     } catch (ExceptionInterface $e) {
775                     }
776                 }
777             }
778         }
779
780         $minDepth = 0;
781         $maxDepth = PHP_INT_MAX;
782
783         foreach ($this->depths as $comparator) {
784             switch ($comparator->getOperator()) {
785                 case '>':
786                     $minDepth = $comparator->getTarget() + 1;
787                     break;
788                 case '>=':
789                     $minDepth = $comparator->getTarget();
790                     break;
791                 case '<':
792                     $maxDepth = $comparator->getTarget() - 1;
793                     break;
794                 case '<=':
795                     $maxDepth = $comparator->getTarget();
796                     break;
797                 default:
798                     $minDepth = $maxDepth = $comparator->getTarget();
799             }
800         }
801
802         $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
803
804         if ($this->followLinks) {
805             $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
806         }
807
808         $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
809
810         if ($this->exclude) {
811             $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
812         }
813
814         $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
815
816         if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
817             $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
818         }
819
820         if ($this->mode) {
821             $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
822         }
823
824         if ($this->names || $this->notNames) {
825             $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
826         }
827
828         if ($this->contains || $this->notContains) {
829             $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
830         }
831
832         if ($this->sizes) {
833             $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
834         }
835
836         if ($this->dates) {
837             $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
838         }
839
840         if ($this->filters) {
841             $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
842         }
843
844         if ($this->paths || $this->notPaths) {
845             $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
846         }
847
848         if ($this->sort) {
849             $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
850             $iterator = $iteratorAggregate->getIterator();
851         }
852
853         return $iterator;
854     }
855
856     /**
857      * @param AdapterInterface $adapter
858      *
859      * @return AdapterInterface
860      */
861     private function buildAdapter(AdapterInterface $adapter)
862     {
863         return $adapter
864             ->setFollowLinks($this->followLinks)
865             ->setDepths($this->depths)
866             ->setMode($this->mode)
867             ->setExclude($this->exclude)
868             ->setNames($this->names)
869             ->setNotNames($this->notNames)
870             ->setContains($this->contains)
871             ->setNotContains($this->notContains)
872             ->setSizes($this->sizes)
873             ->setDates($this->dates)
874             ->setFilters($this->filters)
875             ->setSort($this->sort)
876             ->setPath($this->paths)
877             ->setNotPath($this->notPaths)
878             ->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
879     }
880
881     /**
882      * Unselects all adapters.
883      */
884     private function resetAdapterSelection()
885     {
886         $this->adapters = array_map(function (array $properties) {
887             $properties['selected'] = false;
888
889             return $properties;
890         }, $this->adapters);
891     }
892
893     private function initDefaultAdapters()
894     {
895         if (null === $this->adapters) {
896             $this->adapters = array();
897             $this
898                 ->addAdapter(new GnuFindAdapter())
899                 ->addAdapter(new BsdFindAdapter())
900                 ->addAdapter(new PhpAdapter(), -50)
901                 ->setAdapter('php')
902             ;
903         }
904     }
905 }