diff --git a/.gitignore b/.gitignore
index 3a5cdf3..8576584 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,15 @@
-vendor/*
+# IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make
+# it easy to contribute
.idea
+/nbproject/private/
+.buildpath
+.project
+.settings
+
+# Build folder and vendor folder are generated code; no need to version this
+build/*
+vendor/*
+composer.phar
+
+# By default the phpunit.xml.dist is provided; you can override this using a local config file
+phpunit.xml
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..8b22db8
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,43 @@
+before_commands:
+ - "composer install --no-dev --prefer-source"
+
+tools:
+ external_code_coverage:
+ enabled: true
+ timeout: 300
+ filter:
+ excluded_paths: ["examples", "tests", "vendor"]
+ php_code_sniffer:
+ enabled: true
+ config:
+ standard: PSR2
+ filter:
+ paths: ["src/*", "tests/*"]
+ excluded_paths: []
+ php_cpd:
+ enabled: true
+ excluded_dirs: ["examples", "tests", "vendor"]
+ php_cs_fixer:
+ enabled: true
+ config:
+ level: all
+ filter:
+ paths: ["src/*", "tests/*"]
+ php_loc:
+ enabled: true
+ excluded_dirs: ["examples", "tests", "vendor"]
+ php_mess_detector:
+ enabled: true
+ config:
+ ruleset: phpmd.xml.dist
+ design_rules: { eval_expression: false }
+ filter:
+ paths: ["src/*"]
+ php_pdepend:
+ enabled: true
+ excluded_dirs: ["examples", "tests", "vendor"]
+ php_analyzer:
+ enabled: true
+ filter:
+ paths: ["src/*", "tests/*"]
+ sensiolabs_security_checker: true
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fa8e816
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,39 @@
+language: php
+php: [7.0, 7.1, 7.2, nightly ]
+sudo: false
+
+env:
+ global:
+ - VERSION=$(echo $TRAVIS_TAG | cut -c 2-10)
+
+install:
+- composer install --no-interaction --prefer-dist -o
+
+jobs:
+ include:
+ - stage: Test
+ script:
+ - vendor/bin/phpunit
+
+ - stage: Coverage
+ php: 7.1
+ script:
+ - vendor/bin/phpunit
+ after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
+
+ allow_failures:
+ - php: nightly
+
+cache:
+ directories:
+ - $HOME/.composer/cache/files
+
+notifications:
+ irc: irc.freenode.org#phpdocumentor
+ slack:
+ secure: fjumM0h+4w3EYM4dpgqvpiCug7m4sSIC5+HATgwga/Nrc6IjlbWvGOv3JPgD3kQUhi18VmZfUYPmCv916SIbMnv8JWcrSaJXnPCgmxidvYkuzQDIw1HDJbVppGnkmwQA/qjIrM3sIEMfnu/arLRJQLI363aStZzGPxwIa4PDKcg=
+ email:
+ - me@mikevanriel.com
+ - ashnazg@php.net
diff --git a/README.md b/README.md
index 34f79ba..fe9b033 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,19 @@
+[](https://travis-ci.org/phpDocumentor/FlyFinder)
+[](https://ci.appveyor.com/project/ashnazg/flyfinder/branch/master)
+[](https://scrutinizer-ci.com/g/phpDocumentor/FlyFinder/?branch=master)
+[](https://scrutinizer-ci.com/g/phpDocumentor/FlyFinder/?branch=master)
+
+
FlyFinder
================================================================================================================
FlyFinder is a plugin for [Flysystem](http://flysystem.thephpleague.com/) that will enable you to find files
based on certain criteria.
-FlyFinder can search for files and directories that are hidden, that have a certain extension or that exist in a
-certain path.
+FlyFinder can search for files that are hidden (either because they are hidden files themselves, or because they are
+inside a hidden directory), that have a certain extension, or that exist in a certain path.
+
+Flyfinder does *not* return directories themselves... only files.
## Installation
@@ -38,6 +46,7 @@ FlyFinder will need specifications to know what to look for. The following speci
- IsHidden (this specification will return `true` when a file or directory is hidden,
- HasExtension (this specification will return `true` when a file or directory has the specified extension),
- InPath (this specification will return `true` when a file is in the given path. Wildcards are allowed.)
+ - note that this path should be considered relative to the `$filesystem`'s path
Specifications can be instantiated as follows:
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..4484630
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,56 @@
+build: false
+clone_folder: c:\flyfinder
+max_jobs: 3
+platform: x86
+pull_requests:
+ do_not_increment_build_number: true
+version: '{build}.{branch}'
+skip_tags: true
+branches:
+ only:
+ - master
+
+environment:
+ matrix:
+ - PHP_VERSION: '7.0.27'
+ VC_VERSION: 'VC14'
+ - PHP_VERSION: '7.1.13'
+ VC_VERSION: 'VC14'
+ - PHP_VERSION: '7.2.1'
+ VC_VERSION: 'VC15'
+matrix:
+ fast_finish: false
+
+cache:
+ - c:\php -> appveyor.yml
+ - '%LOCALAPPDATA%\Composer\files'
+
+init:
+ - SET PATH=c:\php\%PHP_VERSION%;%PATH%
+
+install:
+ - IF NOT EXIST c:\php mkdir c:\php
+ - IF NOT EXIST c:\php\%PHP_VERSION% mkdir c:\php\%PHP_VERSION%
+ - cd c:\php\%PHP_VERSION%
+ - IF NOT EXIST php-installed.txt appveyor DownloadFile http://windows.php.net/downloads/releases/php-%PHP_VERSION%-Win32-%VC_VERSION%-x86.zip
+ - IF NOT EXIST php-installed.txt 7z x php-%PHP_VERSION%-Win32-%VC_VERSION%-x86.zip -y >nul
+ - IF NOT EXIST php-installed.txt del /Q *.zip
+ - IF NOT EXIST php-installed.txt copy /Y php.ini-development php.ini
+ - IF NOT EXIST php-installed.txt echo max_execution_time=1200 >> php.ini
+ - IF NOT EXIST php-installed.txt echo date.timezone="UTC" >> php.ini
+ - IF NOT EXIST php-installed.txt echo extension_dir=ext >> php.ini
+ - IF NOT EXIST php-installed.txt echo extension=php_curl.dll >> php.ini
+ - IF NOT EXIST php-installed.txt echo extension=php_openssl.dll >> php.ini
+ - IF NOT EXIST php-installed.txt echo extension=php_mbstring.dll >> php.ini
+ - IF NOT EXIST php-installed.txt echo extension=php_fileinfo.dll >> php.ini
+ - IF NOT EXIST php-installed.txt echo zend.assertions=1 >> php.ini
+ - IF NOT EXIST php-installed.txt echo assert.exception=On >> php.ini
+ - IF NOT EXIST php-installed.txt appveyor DownloadFile https://getcomposer.org/composer.phar
+ - IF NOT EXIST php-installed.txt echo @php %%~dp0composer.phar %%* > composer.bat
+ - IF NOT EXIST php-installed.txt type nul >> php-installed.txt
+ - cd c:\flyfinder
+ - composer install --no-interaction --prefer-dist --no-progress
+
+test_script:
+ - cd c:\flyfinder
+ - vendor/bin/phpunit
diff --git a/examples/03-sample-files/src/Cilex/Provider/JmsSerializerServiceProvider.php b/examples/03-sample-files/src/Cilex/Provider/JmsSerializerServiceProvider.php
new file mode 100644
index 0000000..ec080b8
--- /dev/null
+++ b/examples/03-sample-files/src/Cilex/Provider/JmsSerializerServiceProvider.php
@@ -0,0 +1,2 @@
+addPlugin(new Finder());
+
+/*
+ * "phpdoc -d src -i src/phpDocumentor/DomainModel"
+ * should result in src/Cilex and src/phpDocumentor/. files being found,
+ * but src/phpDocumentor/DomainModel files being left out
+ */
+$dashDirectoryPath = new InPath(new Path('src'));
+$dashIgnorePath = new InPath(new Path('src/phpDocumentor/DomainModel'));
+$isHidden = new IsHidden();
+$isPhpFile = new HasExtension(['php']);
+$spec = new AndSpecification($dashDirectoryPath, $dashIgnorePath->notSpecification());
+$spec->andSpecification($isHidden->notSpecification());
+$spec->andSpecification($isPhpFile);
+
+$generator = $filesystem->find($spec);
+$result = [];
+foreach($generator as $value) {
+ $result[] = $value;
+}
diff --git a/phpmd.xml.dist b/phpmd.xml.dist
new file mode 100644
index 0000000..9abf85c
--- /dev/null
+++ b/phpmd.xml.dist
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 40
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 2a4dc6b..688582d 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -11,6 +11,9 @@
./tests/unit/
+
+ ./tests/integration/
+
diff --git a/src/Finder.php b/src/Finder.php
index d98199e..8c528ab 100644
--- a/src/Finder.php
+++ b/src/Finder.php
@@ -18,7 +18,9 @@
use Flyfinder\Specification\SpecificationInterface;
/**
- * Flysystem plugin to add file finding capabilities to the filesystem entity
+ * Flysystem plugin to add file finding capabilities to the filesystem entity.
+ *
+ * Note that found *directories* are **not** returned... only found *files*.
*/
class Finder implements PluginInterface
{
@@ -49,19 +51,29 @@ public function setFilesystem(FilesystemInterface $filesystem)
/**
* Find the specified files
*
+ * Note that only found *files* are yielded at this level,
+ * which go back to the caller.
+ *
* @param SpecificationInterface $specification
* @return Generator
*/
public function handle(SpecificationInterface $specification)
{
foreach ($this->yieldFilesInPath($specification, '') as $path) {
- yield $path;
+ if (isset($path['type']) && $path['type'] === 'file') {
+ yield $path;
+ }
}
}
/**
* Recursively yield files that meet the specification
*
+ * Note that directories are also yielded at this level,
+ * since they have to be recursed into. Yielded directories
+ * will not make their way back to the caller, as they are filtered out
+ * by {@link handle()}.
+ *
* @param SpecificationInterface $specification
* @param string $path
* @return Generator
diff --git a/src/Specification/InPath.php b/src/Specification/InPath.php
index e3f5392..9f0bb2f 100644
--- a/src/Specification/InPath.php
+++ b/src/Specification/InPath.php
@@ -16,7 +16,10 @@
/**
* Class InPath
- * Files and directories meet the specification if they are in the given path
+ *
+ * Files *and directories* meet the specification if they are in the given path.
+ * Note this behavior is different than in Finder, in that directories *can* meet the spec,
+ * whereas Finder would never return a directory as "found".
*/
class InPath extends CompositeSpecification implements SpecificationInterface
{
@@ -43,43 +46,46 @@ public function __construct(Path $path)
*/
public function isSatisfiedBy(array $value)
{
- if (isset($value['dirname'])) {
- $path = $this->cleanPath((string) $this->path);
- $validChars = '[a-zA-Z0-9\\\/\.\<\>\,\|\:\(\)\&\;\#]';
+ if (in_array($this->path, ['', '.', './'])) {
+ /*
+ * since flysystem stuff is always relative to the filesystem object's root,
+ * a spec of "current" dir should always be a match anything being considered
+ */
+ return true;
+ }
+ $path = (string) $this->path;
+ $validChars = '[a-zA-Z0-9\\\/\.\<\>\,\|\:\(\)\&\;\#]';
+
+ /*
+ * a FILE spec would have to match on 'path',
+ * e.g. value path 'src/Cilex/Provider/MonologServiceProvider.php' should match FILE spec of same path...
+ * this should also hit a perfect DIR=DIR_SPEC match,
+ * e.g. value path 'src/Cilex/Provider' should match DIR spec of 'src/Cilex/Provider'
+ */
+ if (isset($value['path'])) {
$pattern = '(^(?!\/)'
. str_replace(['?', '*'], [$validChars . '{1}', $validChars . '*'], $path)
- . $validChars . '*)';
-
- if (preg_match($pattern, $value['dirname'] . '/')) {
+ . $validChars . '*)'
+ ;
+ if (preg_match($pattern, $value['path'])) {
return true;
}
- return false;
- }
- return false;
- }
-
- /**
- * If a path is given with a leading ./ this will be removed
- * If a path doesn't have a trailing /, a slash will be added
- *
- * @param string $path
- * @return string
- */
- private function cleanPath($path)
- {
- if ($path === '.' || $path === './') {
- return '';
}
- if (substr($path, 0, 2) === './') {
- $path = substr($path, 1);
- }
-
- if (substr($path, -1) !== '/') {
- $path = $path . '/';
+ /* a DIR spec that wasn't an exact match should be able to match on dirname,
+ * e.g. value dirname 'src' of path 'src/Cilex' should match DIR spec of 'src'
+ */
+ if (isset($value['dirname'])) {
+ $pattern = '(^(?!\/)'
+ . str_replace(['?', '*'], [$validChars . '{1}', $validChars . '*'], $path . '/')
+ . $validChars . '*)'
+ ;
+ if (preg_match($pattern, $value['dirname'] . '/')) {
+ return true;
+ }
}
- return $path;
+ return false;
}
}
diff --git a/tests/integration/FindHiddenFilesTest.php b/tests/integration/FindHiddenFilesTest.php
index 8153d1c..12e0711 100644
--- a/tests/integration/FindHiddenFilesTest.php
+++ b/tests/integration/FindHiddenFilesTest.php
@@ -25,7 +25,7 @@ public function testFindingHiddenFiles()
{
include(__DIR__ . '/../../examples/01-find-hidden-files.php');
- $this->assertEquals(2, count($result));
- $this->assertEquals(".test.txt", $result[1]['basename']);
+ $this->assertEquals(1, count($result));
+ $this->assertEquals(".test.txt", $result[0]['basename']);
}
}
diff --git a/tests/integration/FindOnSamplePhpdocLayout.php b/tests/integration/FindOnSamplePhpdocLayout.php
new file mode 100644
index 0000000..14c46a0
--- /dev/null
+++ b/tests/integration/FindOnSamplePhpdocLayout.php
@@ -0,0 +1,34 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace Flyfinder;
+
+/**
+ * Integration test against examples/03-sample-phpdoc-layout.php
+ * @coversNothing
+ */
+class FindOnSamplePhpdocLayout extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var string[] $result
+ */
+ public function testFindingOnSamplePhpdocLayout()
+ {
+ include(__DIR__ . '/../../examples/03-sample-phpdoc-layout.php');
+
+ $this->assertEquals(4, count($result));
+ $this->assertEquals("JmsSerializerServiceProvider.php", $result[0]['basename']);
+ $this->assertEquals("MonologServiceProvider.php", $result[1]['basename']);
+ $this->assertEquals("Application.php", $result[2]['basename']);
+ $this->assertEquals("Bootstrap.php", $result[3]['basename']);
+ }
+}
diff --git a/tests/unit/FinderTest.php b/tests/unit/FinderTest.php
index e1fddfc..5c1974e 100644
--- a/tests/unit/FinderTest.php
+++ b/tests/unit/FinderTest.php
@@ -55,21 +55,26 @@ public function testIfCorrectFilesAreBeingYielded()
0 => [
"type" => "dir",
"path" => ".hiddendir",
- "basename" => ".hiddendir"
- ],
+ "dirname" => "",
+ "basename" => ".hiddendir",
+ "filename" => ".hiddendir",
+ ],
1 => [
"type" => "file",
"path" => "test.txt",
"basename" => "test.txt"
- ]
- ];
+ ],
+ ];
$listContents2 = [
- 0 => [
+ 0 => [
"type" => "file",
"path" => ".hiddendir/.test.txt",
- "basename" => ".test.txt"
- ]
+ "dirname" => ".hiddendir",
+ "basename" => ".test.txt",
+ "filename" => ".test",
+ "extension" => "txt",
+ ],
];
$filesystem->shouldReceive('listContents')
@@ -103,14 +108,12 @@ public function testIfCorrectFilesAreBeingYielded()
$expected = [
0 => [
- "type" => "dir",
- "path" => ".hiddendir",
- "basename" => ".hiddendir"
- ],
- 1 => [
"type" => "file",
"path" => ".hiddendir/.test.txt",
- "basename" => ".test.txt"
+ "dirname" => ".hiddendir",
+ "basename" => ".test.txt",
+ "filename" => ".test",
+ "extension" => "txt",
]
];
diff --git a/tests/unit/Specification/InPathTest.php b/tests/unit/Specification/InPathTest.php
index bc7fa3d..43fab1f 100644
--- a/tests/unit/Specification/InPathTest.php
+++ b/tests/unit/Specification/InPathTest.php
@@ -18,6 +18,7 @@
/**
* Test case for InPath
* @coversDefaultClass Flyfinder\Specification\InPath
+ * @covers ::
*/
class InPathTest extends \PHPUnit_Framework_TestCase
{
@@ -25,9 +26,26 @@ class InPathTest extends \PHPUnit_Framework_TestCase
private $fixture;
/**
- * Initializes the fixture for this test.
+ * @covers ::__construct
+ * @covers ::isSatisfiedBy
+ * @dataProvider validDirnames
+ * @uses Flyfinder\Path
*/
- public function setUp()
+ public function testExactMatch()
+ {
+ $absolutePath = 'absolute/path/to/file.txt';
+ $spec = new InPath(new Path($absolutePath));
+ $this->assertTrue($spec->isSatisfiedBy([
+ 'type' => 'file',
+ 'path' => $absolutePath,
+ 'dirname' => $absolutePath,
+ 'filename' => 'file',
+ 'extension' => 'txt',
+ 'basename' => 'file.txt'
+ ]));
+ }
+
+ private function useWildcardPath()
{
$this->fixture = new InPath(new Path('*dden?ir/n'));
}
@@ -35,19 +53,18 @@ public function setUp()
/**
* @covers ::__construct
* @covers ::isSatisfiedBy
- * @covers ::
* @dataProvider validDirnames
* @uses Flyfinder\Path
*/
public function testIfSpecificationIsSatisfied($dirname)
{
+ $this->useWildcardPath();
$this->assertTrue($this->fixture->isSatisfiedBy(['dirname' => $dirname]));
}
/**
* @covers ::__construct
* @covers ::isSatisfiedBy
- * @covers ::
* @dataProvider validDirnames
* @uses Flyfinder\Path
*/
@@ -60,7 +77,6 @@ public function testWithSingleDotSpec($dirname)
/**
* @covers ::__construct
* @covers ::isSatisfiedBy
- * @covers ::
* @dataProvider validDirnames
* @uses Flyfinder\Path
*/
@@ -89,12 +105,12 @@ public function validDirnames()
/**
* @covers ::__construct
* @covers ::isSatisfiedBy
- * @covers ::
* @dataProvider invalidDirnames
* @uses Flyfinder\Path
*/
public function testIfSpecificationIsNotSatisfied($dirname)
{
+ $this->useWildcardPath();
$this->assertFalse($this->fixture->isSatisfiedBy(['dirname' => $dirname]));
}