PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,240 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
@trigger_error('The '.__NAMESPACE__.'\AbstractAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
/**
* Interface for finder engine implementations.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
*/
abstract class AbstractAdapter implements AdapterInterface
{
protected $followLinks = false;
protected $mode = 0;
protected $minDepth = 0;
protected $maxDepth = PHP_INT_MAX;
protected $exclude = array();
protected $names = array();
protected $notNames = array();
protected $contains = array();
protected $notContains = array();
protected $sizes = array();
protected $dates = array();
protected $filters = array();
protected $sort = false;
protected $paths = array();
protected $notPaths = array();
protected $ignoreUnreadableDirs = false;
private static $areSupported = array();
/**
* {@inheritdoc}
*/
public function isSupported()
{
$name = $this->getName();
if (!array_key_exists($name, self::$areSupported)) {
self::$areSupported[$name] = $this->canBeUsed();
}
return self::$areSupported[$name];
}
/**
* {@inheritdoc}
*/
public function setFollowLinks($followLinks)
{
$this->followLinks = $followLinks;
return $this;
}
/**
* {@inheritdoc}
*/
public function setMode($mode)
{
$this->mode = $mode;
return $this;
}
/**
* {@inheritdoc}
*/
public function setDepths(array $depths)
{
$this->minDepth = 0;
$this->maxDepth = PHP_INT_MAX;
foreach ($depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$this->minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$this->minDepth = $comparator->getTarget();
break;
case '<':
$this->maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$this->maxDepth = $comparator->getTarget();
break;
default:
$this->minDepth = $this->maxDepth = $comparator->getTarget();
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setExclude(array $exclude)
{
$this->exclude = $exclude;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNames(array $names)
{
$this->names = $names;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotNames(array $notNames)
{
$this->notNames = $notNames;
return $this;
}
/**
* {@inheritdoc}
*/
public function setContains(array $contains)
{
$this->contains = $contains;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotContains(array $notContains)
{
$this->notContains = $notContains;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSizes(array $sizes)
{
$this->sizes = $sizes;
return $this;
}
/**
* {@inheritdoc}
*/
public function setDates(array $dates)
{
$this->dates = $dates;
return $this;
}
/**
* {@inheritdoc}
*/
public function setFilters(array $filters)
{
$this->filters = $filters;
return $this;
}
/**
* {@inheritdoc}
*/
public function setSort($sort)
{
$this->sort = $sort;
return $this;
}
/**
* {@inheritdoc}
*/
public function setPath(array $paths)
{
$this->paths = $paths;
return $this;
}
/**
* {@inheritdoc}
*/
public function setNotPath(array $notPaths)
{
$this->notPaths = $notPaths;
return $this;
}
/**
* {@inheritdoc}
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (bool) $ignore;
return $this;
}
/**
* Returns whether the adapter is supported in the current environment.
*
* This method should be implemented in all adapters. Do not implement
* isSupported in the adapters as the generic implementation provides a cache
* layer.
*
* @see isSupported()
*
* @return bool Whether the adapter is supported
*/
abstract protected function canBeUsed();
}

View File

@ -0,0 +1,325 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
@trigger_error('The '.__NAMESPACE__.'\AbstractFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\Expression\Expression;
use Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Shell\Shell;
/**
* Shell engine implementation using GNU find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
*/
abstract class AbstractFindAdapter extends AbstractAdapter
{
protected $shell;
public function __construct()
{
$this->shell = new Shell();
}
/**
* {@inheritdoc}
*/
public function searchInDirectory($dir)
{
// having "/../" in path make find fail
$dir = realpath($dir);
// searching directories containing or not containing strings leads to no result
if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) {
return new Iterator\FilePathsIterator(array(), $dir);
}
$command = Command::create();
$find = $this->buildFindCommand($command, $dir);
if ($this->followLinks) {
$find->add('-follow');
}
$find->add('-mindepth')->add($this->minDepth + 1);
if (PHP_INT_MAX !== $this->maxDepth) {
$find->add('-maxdepth')->add($this->maxDepth + 1);
}
if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) {
$find->add('-type d');
} elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) {
$find->add('-type f');
}
$this->buildNamesFiltering($find, $this->names);
$this->buildNamesFiltering($find, $this->notNames, true);
$this->buildPathsFiltering($find, $dir, $this->paths);
$this->buildPathsFiltering($find, $dir, $this->notPaths, true);
$this->buildSizesFiltering($find, $this->sizes);
$this->buildDatesFiltering($find, $this->dates);
$useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs');
$useSort = \is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut');
if ($useGrep && ($this->contains || $this->notContains)) {
$grep = $command->ins('grep');
$this->buildContentFiltering($grep, $this->contains);
$this->buildContentFiltering($grep, $this->notContains, true);
}
if ($useSort) {
$this->buildSorting($command, $this->sort);
}
$command->setErrorHandler(
$this->ignoreUnreadableDirs
// If directory is unreadable and finder is set to ignore it, `stderr` is ignored.
? function ($stderr) { }
: function ($stderr) { throw new AccessDeniedException($stderr); }
);
$paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute());
$iterator = new Iterator\FilePathsIterator($paths, $dir);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
if (!$useGrep && ($this->contains || $this->notContains)) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if (!$useSort && $this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
}
return $iterator;
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return $this->shell->testCommand('find');
}
/**
* @param Command $command
* @param string $dir
*
* @return Command
*/
protected function buildFindCommand(Command $command, $dir)
{
return $command
->ins('find')
->add('find ')
->arg($dir)
->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions
}
/**
* @param Command $command
* @param string[] $names
* @param bool $not
*/
private function buildNamesFiltering(Command $command, array $names, $not = false)
{
if (0 === \count($names)) {
return;
}
$command->add($not ? '-not' : null)->cmd('(');
foreach ($names as $i => $name) {
$expr = Expression::create($name);
// Find does not support expandable globs ("*.{a,b}" syntax).
if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
$expr = Expression::create($expr->getGlob()->toRegex(false));
}
// Fixes 'not search' and 'full path matching' regex problems.
// - Jokers '.' are replaced by [^/].
// - We add '[^/]*' before and after regex (if no ^|$ flags are present).
if ($expr->isRegex()) {
$regex = $expr->getRegex();
$regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*')
->setStartFlag(false)
->setStartJoker(true)
->replaceJokers('[^/]');
if (!$regex->hasEndFlag() || $regex->hasEndJoker()) {
$regex->setEndJoker(false)->append('[^/]*');
}
}
$command
->add($i > 0 ? '-or' : null)
->add($expr->isRegex()
? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
: ($expr->isCaseSensitive() ? '-name' : '-iname')
)
->arg($expr->renderPattern());
}
$command->cmd(')');
}
/**
* @param Command $command
* @param string $dir
* @param string[] $paths
* @param bool $not
*/
private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false)
{
if (0 === \count($paths)) {
return;
}
$command->add($not ? '-not' : null)->cmd('(');
foreach ($paths as $i => $path) {
$expr = Expression::create($path);
// Find does not support expandable globs ("*.{a,b}" syntax).
if ($expr->isGlob() && $expr->getGlob()->isExpandable()) {
$expr = Expression::create($expr->getGlob()->toRegex(false));
}
// Fixes 'not search' regex problems.
if ($expr->isRegex()) {
$regex = $expr->getRegex();
$regex->prepend($regex->hasStartFlag() ? preg_quote($dir).\DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag());
} else {
$expr->prepend('*')->append('*');
}
$command
->add($i > 0 ? '-or' : null)
->add($expr->isRegex()
? ($expr->isCaseSensitive() ? '-regex' : '-iregex')
: ($expr->isCaseSensitive() ? '-path' : '-ipath')
)
->arg($expr->renderPattern());
}
$command->cmd(')');
}
/**
* @param Command $command
* @param NumberComparator[] $sizes
*/
private function buildSizesFiltering(Command $command, array $sizes)
{
foreach ($sizes as $i => $size) {
$command->add($i > 0 ? '-and' : null);
switch ($size->getOperator()) {
case '<=':
$command->add('-size -'.($size->getTarget() + 1).'c');
break;
case '>=':
$command->add('-size +'.($size->getTarget() - 1).'c');
break;
case '>':
$command->add('-size +'.$size->getTarget().'c');
break;
case '!=':
$command->add('-size -'.$size->getTarget().'c');
$command->add('-size +'.$size->getTarget().'c');
break;
case '<':
default:
$command->add('-size -'.$size->getTarget().'c');
}
}
}
/**
* @param Command $command
* @param DateComparator[] $dates
*/
private function buildDatesFiltering(Command $command, array $dates)
{
foreach ($dates as $i => $date) {
$command->add($i > 0 ? '-and' : null);
$mins = (int) round((time() - $date->getTarget()) / 60);
if (0 > $mins) {
// mtime is in the future
$command->add(' -mmin -0');
// we will have no result so we don't need to continue
return;
}
switch ($date->getOperator()) {
case '<=':
$command->add('-mmin +'.($mins - 1));
break;
case '>=':
$command->add('-mmin -'.($mins + 1));
break;
case '>':
$command->add('-mmin -'.$mins);
break;
case '!=':
$command->add('-mmin +'.$mins.' -or -mmin -'.$mins);
break;
case '<':
default:
$command->add('-mmin +'.$mins);
}
}
}
/**
* @param Command $command
* @param string $sort
*
* @throws \InvalidArgumentException
*/
private function buildSorting(Command $command, $sort)
{
$this->buildFormatSorting($command, $sort);
}
/**
* @param Command $command
* @param string $sort
*/
abstract protected function buildFormatSorting(Command $command, $sort);
/**
* @param Command $command
* @param array $contains
* @param bool $not
*/
abstract protected function buildContentFiltering(Command $command, array $contains, $not = false);
}

View File

@ -0,0 +1,124 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
interface AdapterInterface
{
/**
* @param bool $followLinks
*
* @return $this
*/
public function setFollowLinks($followLinks);
/**
* @param int $mode
*
* @return $this
*/
public function setMode($mode);
/**
* @return $this
*/
public function setExclude(array $exclude);
/**
* @return $this
*/
public function setDepths(array $depths);
/**
* @return $this
*/
public function setNames(array $names);
/**
* @return $this
*/
public function setNotNames(array $notNames);
/**
* @return $this
*/
public function setContains(array $contains);
/**
* @return $this
*/
public function setNotContains(array $notContains);
/**
* @return $this
*/
public function setSizes(array $sizes);
/**
* @return $this
*/
public function setDates(array $dates);
/**
* @return $this
*/
public function setFilters(array $filters);
/**
* @param \Closure|int $sort
*
* @return $this
*/
public function setSort($sort);
/**
* @return $this
*/
public function setPath(array $paths);
/**
* @return $this
*/
public function setNotPath(array $notPaths);
/**
* @param bool $ignore
*
* @return $this
*/
public function ignoreUnreadableDirs($ignore = true);
/**
* @param string $dir
*
* @return \Iterator Result iterator
*/
public function searchInDirectory($dir);
/**
* Tests adapter support for current platform.
*
* @return bool
*/
public function isSupported();
/**
* Returns adapter name.
*
* @return string
*/
public function getName();
}

View File

@ -0,0 +1,107 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
@trigger_error('The '.__NAMESPACE__.'\BsdFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Expression\Expression;
use Symfony\Component\Finder\Iterator\SortableIterator;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Shell\Shell;
/**
* Shell engine implementation using BSD find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
*/
class BsdFindAdapter extends AbstractFindAdapter
{
/**
* {@inheritdoc}
*/
public function getName()
{
return 'bsd_find';
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return \in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed();
}
/**
* {@inheritdoc}
*/
protected function buildFormatSorting(Command $command, $sort)
{
switch ($sort) {
case SortableIterator::SORT_BY_NAME:
$command->ins('sort')->add('| sort');
return;
case SortableIterator::SORT_BY_TYPE:
$format = '%HT';
break;
case SortableIterator::SORT_BY_ACCESSED_TIME:
$format = '%a';
break;
case SortableIterator::SORT_BY_CHANGED_TIME:
$format = '%c';
break;
case SortableIterator::SORT_BY_MODIFIED_TIME:
$format = '%m';
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
}
$command
->add('-print0 | xargs -0 stat -f')
->arg($format.'%t%N')
->add('| sort | cut -f 2');
}
/**
* {@inheritdoc}
*/
protected function buildFindCommand(Command $command, $dir)
{
parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1);
return $command;
}
/**
* {@inheritdoc}
*/
protected function buildContentFiltering(Command $command, array $contains, $not = false)
{
foreach ($contains as $contain) {
$expr = Expression::create($contain);
// todo: avoid forking process for each $pattern by using multiple -e options
$command
->add('| grep -v \'^$\'')
->add('| xargs -I{} grep -I')
->add($expr->isCaseSensitive() ? null : '-i')
->add($not ? '-L' : '-l')
->add('-Ee')->arg($expr->renderPattern())
->add('{}')
;
}
}
}

View File

@ -0,0 +1,108 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
@trigger_error('The '.__NAMESPACE__.'\GnuFindAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Expression\Expression;
use Symfony\Component\Finder\Iterator\SortableIterator;
use Symfony\Component\Finder\Shell\Command;
use Symfony\Component\Finder\Shell\Shell;
/**
* Shell engine implementation using GNU find command.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
*/
class GnuFindAdapter extends AbstractFindAdapter
{
/**
* {@inheritdoc}
*/
public function getName()
{
return 'gnu_find';
}
/**
* {@inheritdoc}
*/
protected function buildFormatSorting(Command $command, $sort)
{
switch ($sort) {
case SortableIterator::SORT_BY_NAME:
$command->ins('sort')->add('| sort');
return;
case SortableIterator::SORT_BY_TYPE:
$format = '%y';
break;
case SortableIterator::SORT_BY_ACCESSED_TIME:
$format = '%A@';
break;
case SortableIterator::SORT_BY_CHANGED_TIME:
$format = '%C@';
break;
case SortableIterator::SORT_BY_MODIFIED_TIME:
$format = '%T@';
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort));
}
$command
->get('find')
->add('-printf')
->arg($format.' %h/%f\\n')
->add('| sort | cut')
->arg('-d ')
->arg('-f2-')
;
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return Shell::TYPE_UNIX === $this->shell->getType() && parent::canBeUsed();
}
/**
* {@inheritdoc}
*/
protected function buildFindCommand(Command $command, $dir)
{
return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended');
}
/**
* {@inheritdoc}
*/
protected function buildContentFiltering(Command $command, array $contains, $not = false)
{
foreach ($contains as $contain) {
$expr = Expression::create($contain);
// todo: avoid forking process for each $pattern by using multiple -e options
$command
->add('| xargs -I{} -r grep -I')
->add($expr->isCaseSensitive() ? null : '-i')
->add($not ? '-L' : '-l')
->add('-Ee')->arg($expr->renderPattern())
->add('{}')
;
}
}
}

View File

@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Adapter;
@trigger_error('The '.__NAMESPACE__.'\PhpAdapter class is deprecated since Symfony 2.8 and will be removed in 3.0. Use directly the Finder class instead.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Iterator;
/**
* PHP finder engine implementation.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0. Use Finder instead.
*/
class PhpAdapter extends AbstractAdapter
{
/**
* {@inheritdoc}
*/
public function searchInDirectory($dir)
{
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth);
}
if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $this->notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
}
if ($this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
}
return $iterator;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'php';
}
/**
* {@inheritdoc}
*/
protected function canBeUsed()
{
return true;
}
}

View File

@ -0,0 +1,98 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* Comparator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Comparator
{
private $target;
private $operator = '==';
/**
* Gets the target value.
*
* @return string The target value
*/
public function getTarget()
{
return $this->target;
}
/**
* Sets the target value.
*
* @param string $target The target value
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Gets the comparison operator.
*
* @return string The operator
*/
public function getOperator()
{
return $this->operator;
}
/**
* Sets the comparison operator.
*
* @param string $operator A valid operator
*
* @throws \InvalidArgumentException
*/
public function setOperator($operator)
{
if (!$operator) {
$operator = '==';
}
if (!\in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
}
$this->operator = $operator;
}
/**
* Tests against the target.
*
* @param mixed $test A test value
*
* @return bool
*/
public function test($test)
{
switch ($this->operator) {
case '>':
return $test > $this->target;
case '>=':
return $test >= $this->target;
case '<':
return $test < $this->target;
case '<=':
return $test <= $this->target;
case '!=':
return $test != $this->target;
}
return $test == $this->target;
}
}

View File

@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* DateCompare compiles date comparisons.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateComparator extends Comparator
{
/**
* @param string $test A comparison string
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
}
try {
$date = new \DateTime($matches[2]);
$target = $date->format('U');
} catch (\Exception $e) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
}
$operator = isset($matches[1]) ? $matches[1] : '==';
if ('since' === $operator || 'after' === $operator) {
$operator = '>';
}
if ('until' === $operator || 'before' === $operator) {
$operator = '<';
}
$this->setOperator($operator);
$this->setTarget($target);
}
}

View File

@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Comparator;
/**
* NumberComparator compiles a simple comparison to an anonymous
* subroutine, which you can call with a value to be tested again.
*
* Now this would be very pointless, if NumberCompare didn't understand
* magnitudes.
*
* The target value may use magnitudes of kilobytes (k, ki),
* megabytes (m, mi), or gigabytes (g, gi). Those suffixed
* with an i use the appropriate 2**n version in accordance with the
* IEC standard: http://physics.nist.gov/cuu/Units/binary.html
*
* Based on the Perl Number::Compare module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*
* @see http://physics.nist.gov/cuu/Units/binary.html
*/
class NumberComparator extends Comparator
{
/**
* @param string|int $test A comparison string or an integer
*
* @throws \InvalidArgumentException If the test is not understood
*/
public function __construct($test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
}
$target = $matches[2];
if (!is_numeric($target)) {
throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
}
if (isset($matches[3])) {
// magnitude
switch (strtolower($matches[3])) {
case 'k':
$target *= 1000;
break;
case 'ki':
$target *= 1024;
break;
case 'm':
$target *= 1000000;
break;
case 'mi':
$target *= 1024 * 1024;
break;
case 'g':
$target *= 1000000000;
break;
case 'gi':
$target *= 1024 * 1024 * 1024;
break;
}
}
$this->setTarget($target);
$this->setOperator(isset($matches[1]) ? $matches[1] : '==');
}
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class AccessDeniedException extends \UnexpectedValueException
{
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
@trigger_error('The '.__NAMESPACE__.'\AdapterFailureException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Adapter\AdapterInterface;
/**
* Base exception for all adapter failures.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class AdapterFailureException extends \RuntimeException implements ExceptionInterface
{
private $adapter;
/**
* @param AdapterInterface $adapter
* @param string|null $message
* @param \Exception|null $previous
*/
public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null)
{
$this->adapter = $adapter;
parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous);
}
/**
* {@inheritdoc}
*/
public function getAdapter()
{
return $this->adapter;
}
}

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface ExceptionInterface
{
/**
* @return \Symfony\Component\Finder\Adapter\AdapterInterface
*/
public function getAdapter();
}

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
@trigger_error('The '.__NAMESPACE__.'\OperationNotPermitedException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class OperationNotPermitedException extends AdapterFailureException
{
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Exception;
@trigger_error('The '.__NAMESPACE__.'\ShellCommandFailureException class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Adapter\AdapterInterface;
use Symfony\Component\Finder\Shell\Command;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class ShellCommandFailureException extends AdapterFailureException
{
private $command;
public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null)
{
$this->command = $command;
parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous);
}
/**
* @return Command
*/
public function getCommand()
{
return $this->command;
}
}

View File

@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Expression;
@trigger_error('The '.__NAMESPACE__.'\Expression class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Expression implements ValueInterface
{
const TYPE_REGEX = 1;
const TYPE_GLOB = 2;
/**
* @var ValueInterface
*/
private $value;
/**
* @param string $expr
*
* @return self
*/
public static function create($expr)
{
return new self($expr);
}
/**
* @param string $expr
*/
public function __construct($expr)
{
try {
$this->value = Regex::create($expr);
} catch (\InvalidArgumentException $e) {
$this->value = new Glob($expr);
}
}
/**
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* {@inheritdoc}
*/
public function render()
{
return $this->value->render();
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return $this->value->renderPattern();
}
/**
* @return bool
*/
public function isCaseSensitive()
{
return $this->value->isCaseSensitive();
}
/**
* @return int
*/
public function getType()
{
return $this->value->getType();
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->value->prepend($expr);
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->value->append($expr);
return $this;
}
/**
* @return bool
*/
public function isRegex()
{
return self::TYPE_REGEX === $this->value->getType();
}
/**
* @return bool
*/
public function isGlob()
{
return self::TYPE_GLOB === $this->value->getType();
}
/**
* @return Glob
*
* @throws \LogicException
*/
public function getGlob()
{
if (self::TYPE_GLOB !== $this->value->getType()) {
throw new \LogicException('Regex can\'t be transformed to glob.');
}
return $this->value;
}
/**
* @return Regex
*/
public function getRegex()
{
return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex();
}
}

View File

@ -0,0 +1,108 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Expression;
@trigger_error('The '.__NAMESPACE__.'\Glob class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
use Symfony\Component\Finder\Glob as FinderGlob;
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Glob implements ValueInterface
{
private $pattern;
/**
* @param string $pattern
*/
public function __construct($pattern)
{
$this->pattern = $pattern;
}
/**
* {@inheritdoc}
*/
public function render()
{
return $this->pattern;
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return $this->pattern;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return Expression::TYPE_GLOB;
}
/**
* {@inheritdoc}
*/
public function isCaseSensitive()
{
return true;
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->pattern = $expr.$this->pattern;
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->pattern .= $expr;
return $this;
}
/**
* Tests if glob is expandable ("*.{a,b}" syntax).
*
* @return bool
*/
public function isExpandable()
{
return false !== strpos($this->pattern, '{')
&& false !== strpos($this->pattern, '}');
}
/**
* @param bool $strictLeadingDot
* @param bool $strictWildcardSlash
*
* @return Regex
*/
public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true)
{
$regex = FinderGlob::toRegex($this->pattern, $strictLeadingDot, $strictWildcardSlash, '');
return new Regex($regex);
}
}

View File

@ -0,0 +1,321 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Expression;
@trigger_error('The '.__NAMESPACE__.'\Regex class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
class Regex implements ValueInterface
{
const START_FLAG = '^';
const END_FLAG = '$';
const BOUNDARY = '~';
const JOKER = '.*';
const ESCAPING = '\\';
/**
* @var string
*/
private $pattern;
/**
* @var string
*/
private $options;
/**
* @var bool
*/
private $startFlag;
/**
* @var bool
*/
private $endFlag;
/**
* @var bool
*/
private $startJoker;
/**
* @var bool
*/
private $endJoker;
/**
* @param string $expr
*
* @return self
*
* @throws \InvalidArgumentException
*/
public static function create($expr)
{
if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
if (
($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start))
|| ('{' === $start && '}' === $end)
|| ('(' === $start && ')' === $end)
) {
return new self(substr($m[1], 1, -1), $m[2], $end);
}
}
throw new \InvalidArgumentException('Given expression is not a regex.');
}
/**
* @param string $pattern
* @param string $options
* @param string $delimiter
*/
public function __construct($pattern, $options = '', $delimiter = null)
{
if (null !== $delimiter) {
// removes delimiter escaping
$pattern = str_replace('\\'.$delimiter, $delimiter, $pattern);
}
$this->parsePattern($pattern);
$this->options = $options;
}
/**
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* {@inheritdoc}
*/
public function render()
{
return self::BOUNDARY
.$this->renderPattern()
.self::BOUNDARY
.$this->options;
}
/**
* {@inheritdoc}
*/
public function renderPattern()
{
return ($this->startFlag ? self::START_FLAG : '')
.($this->startJoker ? self::JOKER : '')
.str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern)
.($this->endJoker ? self::JOKER : '')
.($this->endFlag ? self::END_FLAG : '');
}
/**
* {@inheritdoc}
*/
public function isCaseSensitive()
{
return !$this->hasOption('i');
}
/**
* {@inheritdoc}
*/
public function getType()
{
return Expression::TYPE_REGEX;
}
/**
* {@inheritdoc}
*/
public function prepend($expr)
{
$this->pattern = $expr.$this->pattern;
return $this;
}
/**
* {@inheritdoc}
*/
public function append($expr)
{
$this->pattern .= $expr;
return $this;
}
/**
* @param string $option
*
* @return bool
*/
public function hasOption($option)
{
return false !== strpos($this->options, $option);
}
/**
* @param string $option
*
* @return $this
*/
public function addOption($option)
{
if (!$this->hasOption($option)) {
$this->options .= $option;
}
return $this;
}
/**
* @param string $option
*
* @return $this
*/
public function removeOption($option)
{
$this->options = str_replace($option, '', $this->options);
return $this;
}
/**
* @param bool $startFlag
*
* @return $this
*/
public function setStartFlag($startFlag)
{
$this->startFlag = $startFlag;
return $this;
}
/**
* @return bool
*/
public function hasStartFlag()
{
return $this->startFlag;
}
/**
* @param bool $endFlag
*
* @return $this
*/
public function setEndFlag($endFlag)
{
$this->endFlag = (bool) $endFlag;
return $this;
}
/**
* @return bool
*/
public function hasEndFlag()
{
return $this->endFlag;
}
/**
* @param bool $startJoker
*
* @return $this
*/
public function setStartJoker($startJoker)
{
$this->startJoker = $startJoker;
return $this;
}
/**
* @return bool
*/
public function hasStartJoker()
{
return $this->startJoker;
}
/**
* @param bool $endJoker
*
* @return $this
*/
public function setEndJoker($endJoker)
{
$this->endJoker = (bool) $endJoker;
return $this;
}
/**
* @return bool
*/
public function hasEndJoker()
{
return $this->endJoker;
}
/**
* @return $this
*/
public function replaceJokers($replacement)
{
$replace = function ($subject) use ($replacement) {
$subject = $subject[0];
$replace = 0 === substr_count($subject, '\\') % 2;
return $replace ? str_replace('.', $replacement, $subject) : $subject;
};
$this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern);
return $this;
}
/**
* @param string $pattern
*/
private function parsePattern($pattern)
{
if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) {
$pattern = substr($pattern, 1);
}
if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) {
$pattern = substr($pattern, 2);
}
if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) {
$pattern = substr($pattern, 0, -1);
}
if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) {
$pattern = substr($pattern, 0, -2);
}
$this->pattern = $pattern;
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Expression;
@trigger_error('The '.__NAMESPACE__.'\ValueInterface interface is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*/
interface ValueInterface
{
/**
* Renders string representation of expression.
*
* @return string
*/
public function render();
/**
* Renders string representation of pattern.
*
* @return string
*/
public function renderPattern();
/**
* Returns value case sensitivity.
*
* @return bool
*/
public function isCaseSensitive();
/**
* Returns expression type.
*
* @return int
*/
public function getType();
/**
* @param string $expr
*
* @return $this
*/
public function prepend($expr);
/**
* @param string $expr
*
* @return $this
*/
public function append($expr);
}

View File

@ -0,0 +1,916 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
use Symfony\Component\Finder\Adapter\AdapterInterface;
use Symfony\Component\Finder\Adapter\BsdFindAdapter;
use Symfony\Component\Finder\Adapter\GnuFindAdapter;
use Symfony\Component\Finder\Adapter\PhpAdapter;
use Symfony\Component\Finder\Comparator\DateComparator;
use Symfony\Component\Finder\Comparator\NumberComparator;
use Symfony\Component\Finder\Exception\ExceptionInterface;
use Symfony\Component\Finder\Iterator\CustomFilterIterator;
use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
use Symfony\Component\Finder\Iterator\SortableIterator;
/**
* Finder allows to build rules to find files and directories.
*
* It is a thin wrapper around several specialized iterator classes.
*
* All rules may be invoked several times.
*
* All methods return the current Finder object to allow easy chaining:
*
* $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Finder implements \IteratorAggregate, \Countable
{
const IGNORE_VCS_FILES = 1;
const IGNORE_DOT_FILES = 2;
private $mode = 0;
private $names = array();
private $notNames = array();
private $exclude = array();
private $filters = array();
private $depths = array();
private $sizes = array();
private $followLinks = false;
private $sort = false;
private $ignore = 0;
private $dirs = array();
private $dates = array();
private $iterators = array();
private $contains = array();
private $notContains = array();
private $adapters = null;
private $paths = array();
private $notPaths = array();
private $ignoreUnreadableDirs = false;
private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
public function __construct()
{
$this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
}
/**
* Creates a new Finder.
*
* @return static
*/
public static function create()
{
return new static();
}
/**
* Registers a finder engine implementation.
*
* @param AdapterInterface $adapter An adapter instance
* @param int $priority Highest is selected first
*
* @return $this
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function addAdapter(AdapterInterface $adapter, $priority = 0)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->initDefaultAdapters();
$this->adapters[$adapter->getName()] = array(
'adapter' => $adapter,
'priority' => $priority,
'selected' => false,
);
return $this->sortAdapters();
}
/**
* Sets the selected adapter to the best one according to the current platform the code is run on.
*
* @return $this
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function useBestAdapter()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->initDefaultAdapters();
$this->resetAdapterSelection();
return $this->sortAdapters();
}
/**
* Selects the adapter to use.
*
* @param string $name
*
* @return $this
*
* @throws \InvalidArgumentException
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function setAdapter($name)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->initDefaultAdapters();
if (!isset($this->adapters[$name])) {
throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name));
}
$this->resetAdapterSelection();
$this->adapters[$name]['selected'] = true;
return $this->sortAdapters();
}
/**
* Removes all adapters registered in the finder.
*
* @return $this
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function removeAdapters()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->adapters = array();
return $this;
}
/**
* Returns registered adapters ordered by priority without extra information.
*
* @return AdapterInterface[]
*
* @deprecated since 2.8, to be removed in 3.0.
*/
public function getAdapters()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
$this->initDefaultAdapters();
return array_values(array_map(function (array $adapter) {
return $adapter['adapter'];
}, $this->adapters));
}
/**
* Restricts the matching to directories only.
*
* @return $this
*/
public function directories()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
return $this;
}
/**
* Restricts the matching to files only.
*
* @return $this
*/
public function files()
{
$this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
return $this;
}
/**
* Adds tests for the directory depth.
*
* Usage:
*
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
*
* @param string|int $level The depth level expression
*
* @return $this
*
* @see DepthRangeFilterIterator
* @see NumberComparator
*/
public function depth($level)
{
$this->depths[] = new Comparator\NumberComparator($level);
return $this;
}
/**
* Adds tests for file dates (last modified).
*
* The date must be something that strtotime() is able to parse:
*
* $finder->date('since yesterday');
* $finder->date('until 2 days ago');
* $finder->date('> now - 2 hours');
* $finder->date('>= 2005-10-15');
*
* @param string $date A date range string
*
* @return $this
*
* @see strtotime
* @see DateRangeFilterIterator
* @see DateComparator
*/
public function date($date)
{
$this->dates[] = new Comparator\DateComparator($date);
return $this;
}
/**
* Adds rules that files must match.
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('test.php')
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function name($pattern)
{
$this->names[] = $pattern;
return $this;
}
/**
* Adds rules that files must not match.
*
* @param string $pattern A pattern (a regexp, a glob, or a string)
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notName($pattern)
{
$this->notNames[] = $pattern;
return $this;
}
/**
* Adds tests that file contents must match.
*
* Strings or PCRE patterns can be used:
*
* $finder->contains('Lorem ipsum')
* $finder->contains('/Lorem ipsum/i')
*
* @param string $pattern A pattern (string or regexp)
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function contains($pattern)
{
$this->contains[] = $pattern;
return $this;
}
/**
* Adds tests that file contents must not match.
*
* Strings or PCRE patterns can be used:
*
* $finder->notContains('Lorem ipsum')
* $finder->notContains('/Lorem ipsum/i')
*
* @param string $pattern A pattern (string or regexp)
*
* @return $this
*
* @see FilecontentFilterIterator
*/
public function notContains($pattern)
{
$this->notContains[] = $pattern;
return $this;
}
/**
* Adds rules that filenames must match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->path('some/special/dir')
* $finder->path('/some\/special\/dir/') // same as above
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function path($pattern)
{
$this->paths[] = $pattern;
return $this;
}
/**
* Adds rules that filenames must not match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $finder->notPath('some/special/dir')
* $finder->notPath('/some\/special\/dir/') // same as above
*
* Use only / as dirname separator.
*
* @param string $pattern A pattern (a regexp or a string)
*
* @return $this
*
* @see FilenameFilterIterator
*/
public function notPath($pattern)
{
$this->notPaths[] = $pattern;
return $this;
}
/**
* Adds tests for file sizes.
*
* $finder->size('> 10K');
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param string|int $size A size range string or an integer
*
* @return $this
*
* @see SizeRangeFilterIterator
* @see NumberComparator
*/
public function size($size)
{
$this->sizes[] = new Comparator\NumberComparator($size);
return $this;
}
/**
* Excludes directories.
*
* Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
*
* $finder->in(__DIR__)->exclude('ruby');
*
* @param string|array $dirs A directory path or an array of directories
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function exclude($dirs)
{
$this->exclude = array_merge($this->exclude, (array) $dirs);
return $this;
}
/**
* Excludes "hidden" directories and files (starting with a dot).
*
* This option is enabled by default.
*
* @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreDotFiles($ignoreDotFiles)
{
if ($ignoreDotFiles) {
$this->ignore |= static::IGNORE_DOT_FILES;
} else {
$this->ignore &= ~static::IGNORE_DOT_FILES;
}
return $this;
}
/**
* Forces the finder to ignore version control directories.
*
* This option is enabled by default.
*
* @param bool $ignoreVCS Whether to exclude VCS files or not
*
* @return $this
*
* @see ExcludeDirectoryFilterIterator
*/
public function ignoreVCS($ignoreVCS)
{
if ($ignoreVCS) {
$this->ignore |= static::IGNORE_VCS_FILES;
} else {
$this->ignore &= ~static::IGNORE_VCS_FILES;
}
return $this;
}
/**
* Adds VCS patterns.
*
* @see ignoreVCS()
*
* @param string|string[] $pattern VCS patterns to ignore
*/
public static function addVCSPattern($pattern)
{
foreach ((array) $pattern as $p) {
self::$vcsPatterns[] = $p;
}
self::$vcsPatterns = array_unique(self::$vcsPatterns);
}
/**
* Sorts files and directories by an anonymous function.
*
* The anonymous function receives two \SplFileInfo instances to compare.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sort(\Closure $closure)
{
$this->sort = $closure;
return $this;
}
/**
* Sorts files and directories by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByName()
{
$this->sort = Iterator\SortableIterator::SORT_BY_NAME;
return $this;
}
/**
* Sorts files and directories by type (directories before files), then by name.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByType()
{
$this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
return $this;
}
/**
* Sorts files and directories by the last accessed time.
*
* This is the time that the file was last accessed, read or written to.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByAccessedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
return $this;
}
/**
* Sorts files and directories by the last inode changed time.
*
* This is the time that the inode information was last modified (permissions, owner, group or other metadata).
*
* On Windows, since inode is not available, changed time is actually the file creation time.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByChangedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
return $this;
}
/**
* Sorts files and directories by the last modified time.
*
* This is the last time the actual contents of the file were last modified.
*
* This can be slow as all the matching files and directories must be retrieved for comparison.
*
* @return $this
*
* @see SortableIterator
*/
public function sortByModifiedTime()
{
$this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
return $this;
}
/**
* Filters the iterator with an anonymous function.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @return $this
*
* @see CustomFilterIterator
*/
public function filter(\Closure $closure)
{
$this->filters[] = $closure;
return $this;
}
/**
* Forces the following of symlinks.
*
* @return $this
*/
public function followLinks()
{
$this->followLinks = true;
return $this;
}
/**
* Tells finder to ignore unreadable directories.
*
* By default, scanning unreadable directories content throws an AccessDeniedException.
*
* @param bool $ignore
*
* @return $this
*/
public function ignoreUnreadableDirs($ignore = true)
{
$this->ignoreUnreadableDirs = (bool) $ignore;
return $this;
}
/**
* Searches files and directories which match defined rules.
*
* @param string|array $dirs A directory path or an array of directories
*
* @return $this
*
* @throws \InvalidArgumentException if one of the directories does not exist
*/
public function in($dirs)
{
$resolvedDirs = array();
foreach ((array) $dirs as $dir) {
if (is_dir($dir)) {
$resolvedDirs[] = $this->normalizeDir($dir);
} elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
$resolvedDirs = array_merge($resolvedDirs, array_map(array($this, 'normalizeDir'), $glob));
} else {
throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
}
}
$this->dirs = array_merge($this->dirs, $resolvedDirs);
return $this;
}
/**
* Returns an Iterator for the current Finder configuration.
*
* This method implements the IteratorAggregate interface.
*
* @return \Iterator|SplFileInfo[] An iterator
*
* @throws \LogicException if the in() method has not been called
*/
public function getIterator()
{
if (0 === \count($this->dirs) && 0 === \count($this->iterators)) {
throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
}
if (1 === \count($this->dirs) && 0 === \count($this->iterators)) {
return $this->searchInDirectory($this->dirs[0]);
}
$iterator = new \AppendIterator();
foreach ($this->dirs as $dir) {
$iterator->append($this->searchInDirectory($dir));
}
foreach ($this->iterators as $it) {
$iterator->append($it);
}
return $iterator;
}
/**
* Appends an existing set of files/directories to the finder.
*
* The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
*
* @param iterable $iterator
*
* @return $this
*
* @throws \InvalidArgumentException when the given argument is not iterable
*/
public function append($iterator)
{
if ($iterator instanceof \IteratorAggregate) {
$this->iterators[] = $iterator->getIterator();
} elseif ($iterator instanceof \Iterator) {
$this->iterators[] = $iterator;
} elseif ($iterator instanceof \Traversable || \is_array($iterator)) {
$it = new \ArrayIterator();
foreach ($iterator as $file) {
$it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
}
$this->iterators[] = $it;
} else {
throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
}
return $this;
}
/**
* Counts all the results collected by the iterators.
*
* @return int
*/
public function count()
{
return iterator_count($this->getIterator());
}
/**
* @return $this
*/
private function sortAdapters()
{
uasort($this->adapters, function (array $a, array $b) {
if ($a['selected'] || $b['selected']) {
return $a['selected'] ? -1 : 1;
}
return $a['priority'] > $b['priority'] ? -1 : 1;
});
return $this;
}
/**
* @param string $dir
*
* @return \Iterator
*/
private function searchInDirectory($dir)
{
if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
$this->exclude = array_merge($this->exclude, self::$vcsPatterns);
}
if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
$this->notPaths[] = '#(^|/)\..+(/|$)#';
}
if ($this->adapters) {
foreach ($this->adapters as $adapter) {
if ($adapter['adapter']->isSupported()) {
try {
return $this
->buildAdapter($adapter['adapter'])
->searchInDirectory($dir);
} catch (ExceptionInterface $e) {
}
}
}
}
$minDepth = 0;
$maxDepth = PHP_INT_MAX;
foreach ($this->depths as $comparator) {
switch ($comparator->getOperator()) {
case '>':
$minDepth = $comparator->getTarget() + 1;
break;
case '>=':
$minDepth = $comparator->getTarget();
break;
case '<':
$maxDepth = $comparator->getTarget() - 1;
break;
case '<=':
$maxDepth = $comparator->getTarget();
break;
default:
$minDepth = $maxDepth = $comparator->getTarget();
}
}
$flags = \RecursiveDirectoryIterator::SKIP_DOTS;
if ($this->followLinks) {
$flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
}
$iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
if ($this->exclude) {
$iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
}
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
$iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
}
if ($this->mode) {
$iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
}
if ($this->names || $this->notNames) {
$iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
}
if ($this->contains || $this->notContains) {
$iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
}
if ($this->sizes) {
$iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
}
if ($this->dates) {
$iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
}
if ($this->filters) {
$iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
}
if ($this->paths || $this->notPaths) {
$iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
}
if ($this->sort) {
$iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
$iterator = $iteratorAggregate->getIterator();
}
return $iterator;
}
/**
* @return AdapterInterface
*/
private function buildAdapter(AdapterInterface $adapter)
{
return $adapter
->setFollowLinks($this->followLinks)
->setDepths($this->depths)
->setMode($this->mode)
->setExclude($this->exclude)
->setNames($this->names)
->setNotNames($this->notNames)
->setContains($this->contains)
->setNotContains($this->notContains)
->setSizes($this->sizes)
->setDates($this->dates)
->setFilters($this->filters)
->setSort($this->sort)
->setPath($this->paths)
->setNotPath($this->notPaths)
->ignoreUnreadableDirs($this->ignoreUnreadableDirs);
}
/**
* Unselects all adapters.
*/
private function resetAdapterSelection()
{
$this->adapters = array_map(function (array $properties) {
$properties['selected'] = false;
return $properties;
}, $this->adapters);
}
private function initDefaultAdapters()
{
if (null === $this->adapters) {
$this->adapters = array();
$this
->addAdapter(new GnuFindAdapter())
->addAdapter(new BsdFindAdapter())
->addAdapter(new PhpAdapter(), -50)
->setAdapter('php')
;
}
}
/**
* Normalizes given directory names by removing trailing slashes.
*
* @param string $dir
*
* @return string
*/
private function normalizeDir($dir)
{
return rtrim($dir, '/'.\DIRECTORY_SEPARATOR);
}
}

View File

@ -0,0 +1,104 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
/**
* Glob matches globbing patterns against text.
*
* if match_glob("foo.*", "foo.bar") echo "matched\n";
*
* // prints foo.bar and foo.baz
* $regex = glob_to_regex("foo.*");
* for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
* {
* if (/$regex/) echo "matched: $car\n";
* }
*
* Glob implements glob(3) style matching that can be used to match
* against text, rather than fetching names from a filesystem.
*
* Based on the Perl Text::Glob module.
*
* @author Fabien Potencier <fabien@symfony.com> PHP port
* @author Richard Clamp <richardc@unixbeard.net> Perl version
* @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
* @copyright 2002 Richard Clamp <richardc@unixbeard.net>
*/
class Glob
{
/**
* Returns a regexp which is the equivalent of the glob pattern.
*
* @param string $glob The glob pattern
* @param bool $strictLeadingDot
* @param bool $strictWildcardSlash
* @param string $delimiter Optional delimiter
*
* @return string regex The regexp
*/
public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#')
{
$firstByte = true;
$escaping = false;
$inCurlies = 0;
$regex = '';
$sizeGlob = \strlen($glob);
for ($i = 0; $i < $sizeGlob; ++$i) {
$car = $glob[$i];
if ($firstByte) {
if ($strictLeadingDot && '.' !== $car) {
$regex .= '(?=[^\.])';
}
$firstByte = false;
}
if ('/' === $car) {
$firstByte = true;
}
if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
$regex .= "\\$car";
} elseif ('*' === $car) {
$regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
} elseif ('?' === $car) {
$regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
} elseif ('{' === $car) {
$regex .= $escaping ? '\\{' : '(';
if (!$escaping) {
++$inCurlies;
}
} elseif ('}' === $car && $inCurlies) {
$regex .= $escaping ? '}' : ')';
if (!$escaping) {
--$inCurlies;
}
} elseif (',' === $car && $inCurlies) {
$regex .= $escaping ? ',' : '|';
} elseif ('\\' === $car) {
if ($escaping) {
$regex .= '\\\\';
$escaping = false;
} else {
$escaping = true;
}
continue;
} else {
$regex .= $car;
}
$escaping = false;
}
return $delimiter.'^'.$regex.'$'.$delimiter;
}
}

View File

@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* CustomFilterIterator filters files by applying anonymous functions.
*
* The anonymous function receives a \SplFileInfo and must return false
* to remove files.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CustomFilterIterator extends FilterIterator
{
private $filters = array();
/**
* @param \Iterator $iterator The Iterator to filter
* @param callable[] $filters An array of PHP callbacks
*
* @throws \InvalidArgumentException
*/
public function __construct(\Iterator $iterator, array $filters)
{
foreach ($filters as $filter) {
if (!\is_callable($filter)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
}
$this->filters = $filters;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
foreach ($this->filters as $filter) {
if (false === \call_user_func($filter, $fileinfo)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\DateComparator;
/**
* DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DateRangeFilterIterator extends FilterIterator
{
private $comparators = array();
/**
* @param \Iterator $iterator The Iterator to filter
* @param DateComparator[] $comparators An array of DateComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!file_exists($fileinfo->getPathname())) {
return false;
}
$filedate = $fileinfo->getMTime();
foreach ($this->comparators as $compare) {
if (!$compare->test($filedate)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* DepthRangeFilterIterator limits the directory depth.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DepthRangeFilterIterator extends FilterIterator
{
private $minDepth = 0;
/**
* @param \RecursiveIteratorIterator $iterator The Iterator to filter
* @param int $minDepth The min depth
* @param int $maxDepth The max depth
*/
public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
{
$this->minDepth = $minDepth;
$iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
return $this->getInnerIterator()->getDepth() >= $this->minDepth;
}
}

View File

@ -0,0 +1,84 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* ExcludeDirectoryFilterIterator filters out directories.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator
{
private $iterator;
private $isRecursive;
private $excludedDirs = array();
private $excludedPattern;
/**
* @param \Iterator $iterator The Iterator to filter
* @param array $directories An array of directories to exclude
*/
public function __construct(\Iterator $iterator, array $directories)
{
$this->iterator = $iterator;
$this->isRecursive = $iterator instanceof \RecursiveIterator;
$patterns = array();
foreach ($directories as $directory) {
$directory = rtrim($directory, '/');
if (!$this->isRecursive || false !== strpos($directory, '/')) {
$patterns[] = preg_quote($directory, '#');
} else {
$this->excludedDirs[$directory] = true;
}
}
if ($patterns) {
$this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
}
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool True if the value should be kept, false otherwise
*/
public function accept()
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
return false;
}
if ($this->excludedPattern) {
$path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
$path = str_replace('\\', '/', $path);
return !preg_match($this->excludedPattern, $path);
}
return true;
}
public function hasChildren()
{
return $this->isRecursive && $this->iterator->hasChildren();
}
public function getChildren()
{
$children = new self($this->iterator->getChildren(), array());
$children->excludedDirs = $this->excludedDirs;
$children->excludedPattern = $this->excludedPattern;
return $children;
}
}

View File

@ -0,0 +1,135 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
@trigger_error('The '.__NAMESPACE__.'\FilePathsIterator class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
use Symfony\Component\Finder\SplFileInfo;
/**
* Iterate over shell command result.
*
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class FilePathsIterator extends \ArrayIterator
{
/**
* @var string
*/
private $baseDir;
/**
* @var int
*/
private $baseDirLength;
/**
* @var string
*/
private $subPath;
/**
* @var string
*/
private $subPathname;
/**
* @var SplFileInfo
*/
private $current;
/**
* @param array $paths List of paths returned by shell command
* @param string $baseDir Base dir for relative path building
*/
public function __construct(array $paths, $baseDir)
{
$this->baseDir = $baseDir;
$this->baseDirLength = \strlen($baseDir);
parent::__construct($paths);
}
/**
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public function __call($name, array $arguments)
{
return \call_user_func_array(array($this->current(), $name), $arguments);
}
/**
* Return an instance of SplFileInfo with support for relative paths.
*
* @return SplFileInfo File information
*/
public function current()
{
return $this->current;
}
/**
* @return string
*/
public function key()
{
return $this->current->getPathname();
}
public function next()
{
parent::next();
$this->buildProperties();
}
public function rewind()
{
parent::rewind();
$this->buildProperties();
}
/**
* @return string
*/
public function getSubPath()
{
return $this->subPath;
}
/**
* @return string
*/
public function getSubPathname()
{
return $this->subPathname;
}
private function buildProperties()
{
$absolutePath = parent::current();
if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) {
$this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\');
$dir = \dirname($this->subPathname);
$this->subPath = '.' === $dir ? '' : $dir;
} else {
$this->subPath = $this->subPathname = '';
}
$this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname);
}
}

View File

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* FileTypeFilterIterator only keeps files, directories, or both.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileTypeFilterIterator extends FilterIterator
{
const ONLY_FILES = 1;
const ONLY_DIRECTORIES = 2;
private $mode;
/**
* @param \Iterator $iterator The Iterator to filter
* @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
*/
public function __construct(\Iterator $iterator, $mode)
{
$this->mode = $mode;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
return false;
} elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class FilecontentFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
if (!$this->matchRegexps && !$this->noMatchRegexps) {
return true;
}
$fileinfo = $this->current();
if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
return false;
}
$content = $fileinfo->getContents();
if (!$content) {
return false;
}
return $this->isAccepted($content);
}
/**
* Converts string to regexp if necessary.
*
* @param string $str Pattern: string or regexp
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Glob;
/**
* FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilenameFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
return $this->isAccepted($this->current()->getFilename());
}
/**
* Converts glob to regexp.
*
* PCRE patterns are left unchanged.
* Glob strings are transformed with Glob::toRegex().
*
* @param string $str Pattern: glob or regexp
*
* @return string regexp corresponding to a given glob or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : Glob::toRegex($str);
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* This iterator just overrides the rewind method in order to correct a PHP bug,
* which existed before version 5.5.23/5.6.7.
*
* @see https://bugs.php.net/68557
*
* @author Alex Bogomazov
*/
abstract class FilterIterator extends \FilterIterator
{
/**
* This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
* rewind in some cases.
*
* @see FilterIterator::rewind()
*/
public function rewind()
{
if (\PHP_VERSION_ID > 50607 || (\PHP_VERSION_ID > 50523 && \PHP_VERSION_ID < 50600)) {
parent::rewind();
return;
}
$iterator = $this;
while ($iterator instanceof \OuterIterator) {
$innerIterator = $iterator->getInnerIterator();
if ($innerIterator instanceof RecursiveDirectoryIterator) {
// this condition is necessary for iterators to work properly with non-local filesystems like ftp
if ($innerIterator->isRewindable()) {
$innerIterator->next();
$innerIterator->rewind();
}
} elseif ($innerIterator instanceof \FilesystemIterator) {
$innerIterator->next();
$innerIterator->rewind();
}
$iterator = $innerIterator;
}
parent::rewind();
}
}

View File

@ -0,0 +1,112 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class MultiplePcreFilterIterator extends FilterIterator
{
protected $matchRegexps = array();
protected $noMatchRegexps = array();
/**
* @param \Iterator $iterator The Iterator to filter
* @param array $matchPatterns An array of patterns that need to match
* @param array $noMatchPatterns An array of patterns that need to not match
*/
public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
{
foreach ($matchPatterns as $pattern) {
$this->matchRegexps[] = $this->toRegex($pattern);
}
foreach ($noMatchPatterns as $pattern) {
$this->noMatchRegexps[] = $this->toRegex($pattern);
}
parent::__construct($iterator);
}
/**
* Checks whether the string is accepted by the regex filters.
*
* If there is no regexps defined in the class, this method will accept the string.
* Such case can be handled by child classes before calling the method if they want to
* apply a different behavior.
*
* @param string $string The string to be matched against filters
*
* @return bool
*/
protected function isAccepted($string)
{
// should at least not match one rule to exclude
foreach ($this->noMatchRegexps as $regex) {
if (preg_match($regex, $string)) {
return false;
}
}
// should at least match one rule
if ($this->matchRegexps) {
foreach ($this->matchRegexps as $regex) {
if (preg_match($regex, $string)) {
return true;
}
}
return false;
}
// If there is no match rules, the file is accepted
return true;
}
/**
* Checks whether the string is a regex.
*
* @param string $str
*
* @return bool Whether the given string is a regex
*/
protected function isRegex($str)
{
if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
$start = substr($m[1], 0, 1);
$end = substr($m[1], -1);
if ($start === $end) {
return !preg_match('/[*?[:alnum:] \\\\]/', $start);
}
foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) {
if ($start === $delimiters[0] && $end === $delimiters[1]) {
return true;
}
}
}
return false;
}
/**
* Converts string into regexp.
*
* @param string $str Pattern
*
* @return string regexp corresponding to a given string
*/
abstract protected function toRegex($str);
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* PathFilterIterator filters files by path patterns (e.g. some/special/dir).
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
*/
class PathFilterIterator extends MultiplePcreFilterIterator
{
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$filename = $this->current()->getRelativePathname();
if ('\\' === \DIRECTORY_SEPARATOR) {
$filename = str_replace('\\', '/', $filename);
}
return $this->isAccepted($filename);
}
/**
* Converts strings to regexp.
*
* PCRE patterns are left unchanged.
*
* Default conversion:
* 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
*
* Use only / as directory separator (on Windows also).
*
* @param string $str Pattern: regexp or dirname
*
* @return string regexp corresponding to a given string or regexp
*/
protected function toRegex($str)
{
return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
}
}

View File

@ -0,0 +1,154 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Symfony\Component\Finder\SplFileInfo;
/**
* Extends the \RecursiveDirectoryIterator to support relative paths.
*
* @author Victor Berchet <victor@suumit.com>
*/
class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
{
/**
* @var bool
*/
private $ignoreUnreadableDirs;
/**
* @var bool
*/
private $rewindable;
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
private $rootPath;
private $subPath;
private $directorySeparator = '/';
/**
* @param string $path
* @param int $flags
* @param bool $ignoreUnreadableDirs
*
* @throws \RuntimeException
*/
public function __construct($path, $flags, $ignoreUnreadableDirs = false)
{
if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
throw new \RuntimeException('This iterator only support returning current as fileinfo.');
}
parent::__construct($path, $flags);
$this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
$this->rootPath = (string) $path;
if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
$this->directorySeparator = \DIRECTORY_SEPARATOR;
}
}
/**
* Return an instance of SplFileInfo with support for relative paths.
*
* @return SplFileInfo File information
*/
public function current()
{
// the logic here avoids redoing the same work in all iterations
if (null === $subPathname = $this->subPath) {
$subPathname = $this->subPath = (string) $this->getSubPath();
}
if ('' !== $subPathname) {
$subPathname .= $this->directorySeparator;
}
$subPathname .= $this->getFilename();
return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
}
/**
* @return \RecursiveIterator
*
* @throws AccessDeniedException
*/
public function getChildren()
{
try {
$children = parent::getChildren();
if ($children instanceof self) {
// parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
// performance optimization to avoid redoing the same work in all children
$children->rewindable = &$this->rewindable;
$children->rootPath = $this->rootPath;
}
return $children;
} catch (\UnexpectedValueException $e) {
if ($this->ignoreUnreadableDirs) {
// If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
return new \RecursiveArrayIterator(array());
} else {
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
}
/**
* Do nothing for non rewindable stream.
*/
public function rewind()
{
if (false === $this->isRewindable()) {
return;
}
// @see https://bugs.php.net/68557
if (\PHP_VERSION_ID < 50523 || \PHP_VERSION_ID >= 50600 && \PHP_VERSION_ID < 50607) {
parent::next();
}
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*
* @return bool true when the stream is rewindable, false otherwise
*/
public function isRewindable()
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
// workaround for an HHVM bug, should be removed when https://github.com/facebook/hhvm/issues/7281 is fixed
if ('' === $this->getPath()) {
return $this->rewindable = false;
}
if (false !== $stream = @opendir($this->getPath())) {
$infos = stream_get_meta_data($stream);
closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = true;
}
}
return $this->rewindable = false;
}
}

View File

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
use Symfony\Component\Finder\Comparator\NumberComparator;
/**
* SizeRangeFilterIterator filters out files that are not in the given size range.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SizeRangeFilterIterator extends FilterIterator
{
private $comparators = array();
/**
* @param \Iterator $iterator The Iterator to filter
* @param NumberComparator[] $comparators An array of NumberComparator instances
*/
public function __construct(\Iterator $iterator, array $comparators)
{
$this->comparators = $comparators;
parent::__construct($iterator);
}
/**
* Filters the iterator values.
*
* @return bool true if the value should be kept, false otherwise
*/
public function accept()
{
$fileinfo = $this->current();
if (!$fileinfo->isFile()) {
return true;
}
$filesize = $fileinfo->getSize();
foreach ($this->comparators as $compare) {
if (!$compare->test($filesize)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Iterator;
/**
* SortableIterator applies a sort on a given Iterator.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SortableIterator implements \IteratorAggregate
{
const SORT_BY_NAME = 1;
const SORT_BY_TYPE = 2;
const SORT_BY_ACCESSED_TIME = 3;
const SORT_BY_CHANGED_TIME = 4;
const SORT_BY_MODIFIED_TIME = 5;
private $iterator;
private $sort;
/**
* @param \Traversable $iterator The Iterator to filter
* @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
*
* @throws \InvalidArgumentException
*/
public function __construct(\Traversable $iterator, $sort)
{
$this->iterator = $iterator;
if (self::SORT_BY_NAME === $sort) {
$this->sort = function ($a, $b) {
return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_TYPE === $sort) {
$this->sort = function ($a, $b) {
if ($a->isDir() && $b->isFile()) {
return -1;
} elseif ($a->isFile() && $b->isDir()) {
return 1;
}
return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
};
} elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getATime() - $b->getATime();
};
} elseif (self::SORT_BY_CHANGED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getCTime() - $b->getCTime();
};
} elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
$this->sort = function ($a, $b) {
return $a->getMTime() - $b->getMTime();
};
} elseif (\is_callable($sort)) {
$this->sort = $sort;
} else {
throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
}
}
public function getIterator()
{
$array = iterator_to_array($this->iterator, true);
uasort($array, $this->sort);
return new \ArrayIterator($array);
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2004-2018 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,278 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Shell;
@trigger_error('The '.__NAMESPACE__.'\Command class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class Command
{
private $parent;
private $bits = array();
private $labels = array();
/**
* @var \Closure|null
*/
private $errorHandler;
public function __construct(Command $parent = null)
{
$this->parent = $parent;
}
/**
* Returns command as string.
*
* @return string
*/
public function __toString()
{
return $this->join();
}
/**
* Creates a new Command instance.
*
* @return self
*/
public static function create(Command $parent = null)
{
return new self($parent);
}
/**
* Escapes special chars from input.
*
* @param string $input A string to escape
*
* @return string The escaped string
*/
public static function escape($input)
{
return escapeshellcmd($input);
}
/**
* Quotes input.
*
* @param string $input An argument string
*
* @return string The quoted string
*/
public static function quote($input)
{
return escapeshellarg($input);
}
/**
* Appends a string or a Command instance.
*
* @param string|Command $bit
*
* @return $this
*/
public function add($bit)
{
$this->bits[] = $bit;
return $this;
}
/**
* Prepends a string or a command instance.
*
* @param string|Command $bit
*
* @return $this
*/
public function top($bit)
{
array_unshift($this->bits, $bit);
foreach ($this->labels as $label => $index) {
++$this->labels[$label];
}
return $this;
}
/**
* Appends an argument, will be quoted.
*
* @param string $arg
*
* @return $this
*/
public function arg($arg)
{
$this->bits[] = self::quote($arg);
return $this;
}
/**
* Appends escaped special command chars.
*
* @param string $esc
*
* @return $this
*/
public function cmd($esc)
{
$this->bits[] = self::escape($esc);
return $this;
}
/**
* Inserts a labeled command to feed later.
*
* @param string $label The unique label
*
* @return self|string
*
* @throws \RuntimeException If label already exists
*/
public function ins($label)
{
if (isset($this->labels[$label])) {
throw new \RuntimeException(sprintf('Label "%s" already exists.', $label));
}
$this->bits[] = self::create($this);
$this->labels[$label] = \count($this->bits) - 1;
return $this->bits[$this->labels[$label]];
}
/**
* Retrieves a previously labeled command.
*
* @param string $label
*
* @return self|string
*
* @throws \RuntimeException
*/
public function get($label)
{
if (!isset($this->labels[$label])) {
throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label));
}
return $this->bits[$this->labels[$label]];
}
/**
* Returns parent command (if any).
*
* @return self
*
* @throws \RuntimeException If command has no parent
*/
public function end()
{
if (null === $this->parent) {
throw new \RuntimeException('Calling end on root command doesn\'t make sense.');
}
return $this->parent;
}
/**
* Counts bits stored in command.
*
* @return int The bits count
*/
public function length()
{
return \count($this->bits);
}
/**
* @return $this
*/
public function setErrorHandler(\Closure $errorHandler)
{
$this->errorHandler = $errorHandler;
return $this;
}
/**
* @return \Closure|null
*/
public function getErrorHandler()
{
return $this->errorHandler;
}
/**
* Executes current command.
*
* @return array The command result
*
* @throws \RuntimeException
*/
public function execute()
{
if (null === $errorHandler = $this->errorHandler) {
exec($this->join(), $output);
} else {
$process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes);
$output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY);
if ($error = stream_get_contents($pipes[2])) {
$errorHandler($error);
}
proc_close($process);
}
return $output ?: array();
}
/**
* Joins bits.
*
* @return string
*/
public function join()
{
return implode(' ', array_filter(
array_map(function ($bit) {
return $bit instanceof Command ? $bit->join() : ($bit ?: null);
}, $this->bits),
function ($bit) { return null !== $bit; }
));
}
/**
* Insert a string or a Command instance before the bit at given position $index (index starts from 0).
*
* @param string|Command $bit
* @param int $index
*
* @return $this
*/
public function addAtIndex($bit, $index)
{
array_splice($this->bits, $index, 0, $bit instanceof self ? array($bit) : $bit);
return $this;
}
}

View File

@ -0,0 +1,101 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder\Shell;
@trigger_error('The '.__NAMESPACE__.'\Shell class is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Jean-François Simon <contact@jfsimon.fr>
*
* @deprecated since 2.8, to be removed in 3.0.
*/
class Shell
{
const TYPE_UNIX = 1;
const TYPE_DARWIN = 2;
const TYPE_CYGWIN = 3;
const TYPE_WINDOWS = 4;
const TYPE_BSD = 5;
/**
* @var string|null
*/
private $type;
/**
* Returns guessed OS type.
*
* @return int
*/
public function getType()
{
if (null === $this->type) {
$this->type = $this->guessType();
}
return $this->type;
}
/**
* Tests if a command is available.
*
* @param string $command
*
* @return bool
*/
public function testCommand($command)
{
if (!\function_exists('exec')) {
return false;
}
// todo: find a better way (command could not be available)
$testCommand = 'which ';
if (self::TYPE_WINDOWS === $this->type) {
$testCommand = 'where ';
}
$command = escapeshellcmd($command);
exec($testCommand.$command, $output, $code);
return 0 === $code && \count($output) > 0;
}
/**
* Guesses OS type.
*
* @return int
*/
private function guessType()
{
$os = strtolower(PHP_OS);
if (false !== strpos($os, 'cygwin')) {
return self::TYPE_CYGWIN;
}
if (false !== strpos($os, 'darwin')) {
return self::TYPE_DARWIN;
}
if (false !== strpos($os, 'bsd')) {
return self::TYPE_BSD;
}
if (0 === strpos($os, 'win')) {
return self::TYPE_WINDOWS;
}
return self::TYPE_UNIX;
}
}

View File

@ -0,0 +1,78 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Finder;
/**
* Extends \SplFileInfo to support relative paths.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SplFileInfo extends \SplFileInfo
{
private $relativePath;
private $relativePathname;
/**
* @param string $file The file name
* @param string $relativePath The relative path
* @param string $relativePathname The relative path name
*/
public function __construct($file, $relativePath, $relativePathname)
{
parent::__construct($file);
$this->relativePath = $relativePath;
$this->relativePathname = $relativePathname;
}
/**
* Returns the relative path.
*
* This path does not contain the file name.
*
* @return string the relative path
*/
public function getRelativePath()
{
return $this->relativePath;
}
/**
* Returns the relative path name.
*
* This path contains the file name.
*
* @return string the relative path name
*/
public function getRelativePathname()
{
return $this->relativePathname;
}
/**
* Returns the contents of the file.
*
* @return string the contents of the file
*
* @throws \RuntimeException
*/
public function getContents()
{
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$content = file_get_contents($this->getPathname());
restore_error_handler();
if (false === $content) {
throw new \RuntimeException($error);
}
return $content;
}
}

View File

@ -0,0 +1,33 @@
{
"name": "symfony/finder",
"type": "library",
"description": "Symfony Finder Component",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=5.3.9"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Finder\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
}
}