From 2f67e4c9941aff25f04f087f63281324b8b0ccc6 Mon Sep 17 00:00:00 2001 From: Marieke Bednarczyk Date: Sun, 16 Aug 2015 12:44:39 +0200 Subject: [PATCH 1/3] corrections in specifications, added examples and integration tests --- composer.json | 3 +- composer.lock | 51 ++++++++++++++++++- examples/01-find-hidden-files.php | 31 +++++++++++ examples/02-find-on-multiple-criteria.php | 42 +++++++++++++++ src/Finder.php | 4 ++ src/Specification/AndSpecification.php | 2 + src/Specification/CompositeSpecification.php | 9 ++++ src/Specification/InPath.php | 27 +++++++++- src/Specification/NotSpecification.php | 2 + src/Specification/OrSpecification.php | 2 + tests/integration/FindHiddenFilesTest.php | 31 +++++++++++ .../FindOnMultipleCriteriaTest.php | 32 ++++++++++++ tests/unit/Specification/InPathTest.php | 6 ++- 13 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 examples/01-find-hidden-files.php create mode 100644 examples/02-find-on-multiple-criteria.php create mode 100644 tests/integration/FindHiddenFilesTest.php create mode 100644 tests/integration/FindOnMultipleCriteriaTest.php diff --git a/composer.json b/composer.json index f837421..d573a4b 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "minimum-stability": "stable", "require-dev": { "phpunit/phpunit": "^4.0", - "mockery/mockery": "~0.9@dev" + "mockery/mockery": "~0.9@dev", + "league/flysystem-memory": "^1.0" } } diff --git a/composer.lock b/composer.lock index 13b75cd..36179e5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "8bcd2cc2f11c6f63d6eaaad1d3e6b92c", + "hash": "4a2aca29399bcf9460b78d5e7afa4f77", "packages": [ { "name": "league/flysystem", @@ -188,6 +188,55 @@ ], "time": "2015-05-11 14:41:42" }, + { + "name": "league/flysystem-memory", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-memory.git", + "reference": "46df9a22e2854ea919ccafb50c3306bdf309fbe8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-memory/zipball/46df9a22e2854ea919ccafb50c3306bdf309fbe8", + "reference": "46df9a22e2854ea919ccafb50c3306bdf309fbe8", + "shasum": "" + }, + "require": { + "league/flysystem": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\Memory\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Leppanen", + "email": "chris.leppanen@gmail.com", + "role": "Developer" + } + ], + "description": "An in-memory adapter for Flysystem.", + "homepage": "http://github.com/twistor/flysystem-memory-adapter", + "keywords": [ + "league" + ], + "time": "2015-06-26 18:25:54" + }, { "name": "mockery/mockery", "version": "0.9.4", diff --git a/examples/01-find-hidden-files.php b/examples/01-find-hidden-files.php new file mode 100644 index 0000000..0bfd896 --- /dev/null +++ b/examples/01-find-hidden-files.php @@ -0,0 +1,31 @@ +addPlugin(new Finder()); + +// Create some demo files +$filesystem->write('test.txt', 'test'); +$filesystem->write('.hiddendir/.test.txt', 'test'); + +//In order to tell FlyFinder what to find, you need to give it a specification +//In this example the specification will be satisfied by files and directories that are hidden +$specification = new IsHidden(); + +//FlyFinder will yield a generator object with the files that are found +$generator = $filesystem->find($specification); + +$result = []; + +foreach ($generator as $value) { + $result[] = $value; +} diff --git a/examples/02-find-on-multiple-criteria.php b/examples/02-find-on-multiple-criteria.php new file mode 100644 index 0000000..1e884ea --- /dev/null +++ b/examples/02-find-on-multiple-criteria.php @@ -0,0 +1,42 @@ +addPlugin(new Finder()); + +// Create some demo files +$filesystem->write('test.txt', 'test'); +$filesystem->write('.hiddendir/.test.txt', 'test'); +$filesystem->write('.hiddendir/found.txt', 'test'); +$filesystem->write('.hiddendir/normaldir/example.txt', 'test'); + +/* + * In order to tell FlyFinder what to find, you need to give it a specification + * In this example the specification will be satisfied by *.txt files + * within the .hidden directory and its subdirectories that are not hidden + */ +$isHidden = new IsHidden(); +$hasExtension = new HasExtension(['txt']); +$inPath = new InPath(new Path('.hiddendir')); +$specification = $inPath->andSpecification($hasExtension)->andSpecification($isHidden->notSpecification()); + +//FlyFinder will yield a generator object with the files that are found +$generator = $filesystem->find($specification); + +$result = []; + +foreach ($generator as $value) { + $result[] = $value; +} diff --git a/src/Finder.php b/src/Finder.php index de9d000..d98199e 100644 --- a/src/Finder.php +++ b/src/Finder.php @@ -47,6 +47,8 @@ public function setFilesystem(FilesystemInterface $filesystem) } /** + * Find the specified files + * * @param SpecificationInterface $specification * @return Generator */ @@ -58,6 +60,8 @@ public function handle(SpecificationInterface $specification) } /** + * Recursively yield files that meet the specification + * * @param SpecificationInterface $specification * @param string $path * @return Generator diff --git a/src/Specification/AndSpecification.php b/src/Specification/AndSpecification.php index f06aa73..a19096e 100644 --- a/src/Specification/AndSpecification.php +++ b/src/Specification/AndSpecification.php @@ -28,6 +28,8 @@ final class AndSpecification extends CompositeSpecification implements Specifica private $other; /** + * Initializes the AndSpecification object + * * @param CompositeSpecification $one * @param CompositeSpecification $other */ diff --git a/src/Specification/CompositeSpecification.php b/src/Specification/CompositeSpecification.php index 322602d..1b63d88 100644 --- a/src/Specification/CompositeSpecification.php +++ b/src/Specification/CompositeSpecification.php @@ -19,6 +19,9 @@ abstract class CompositeSpecification { /** + * Returns a specification that satisfies the original specification + * as well as the other specification + * * @param CompositeSpecification $other * @return AndSpecification */ @@ -28,6 +31,9 @@ public function andSpecification(CompositeSpecification $other) } /** + * Returns a specification that satisfies the original specification + * or the other specification + * * @param CompositeSpecification $other * @return OrSpecification */ @@ -37,6 +43,9 @@ public function orSpecification(CompositeSpecification $other) } /** + * Returns a specification that is the inverse of the original specification + * i.e. does not meet the original criteria + * * @return NotSpecification */ public function notSpecification() diff --git a/src/Specification/InPath.php b/src/Specification/InPath.php index ffbe7fa..1fe12b0 100644 --- a/src/Specification/InPath.php +++ b/src/Specification/InPath.php @@ -26,11 +26,12 @@ class InPath extends CompositeSpecification implements SpecificationInterface private $path; /** + * Initializes the InPath specification + * * @param Path $path */ public function __construct(Path $path) { - $this->path = $path; } @@ -42,6 +43,28 @@ public function __construct(Path $path) */ public function isSatisfiedBy(array $value) { - return isset($value['dirname']) && $value['dirname'] === (string)$this->path ? true : false; + if (isset($value['dirname'])) { + $path = $this->removeDotSlash((string) $this->path); + + if (substr($value['dirname'], 0, strlen($path)) === $path) { + return true; + } + return false; + } + return false; + } + + /** + * If a path is given with a leading ./ this will be removed + * + * @param string $dirname + * @return string + */ + private function removeDotSlash($dirname) + { + if (substr($dirname, 0, 2) === './') { + $dirname = substr($dirname, 1); + } + return $dirname; } } diff --git a/src/Specification/NotSpecification.php b/src/Specification/NotSpecification.php index b06e81f..42c1e7d 100644 --- a/src/Specification/NotSpecification.php +++ b/src/Specification/NotSpecification.php @@ -23,6 +23,8 @@ final class NotSpecification extends CompositeSpecification implements Specifica private $wrapped; /** + * Initializes the NotSpecification object + * * @param CompositeSpecification $wrapped */ public function __construct(CompositeSpecification $wrapped) diff --git a/src/Specification/OrSpecification.php b/src/Specification/OrSpecification.php index 9af673a..bd940f5 100644 --- a/src/Specification/OrSpecification.php +++ b/src/Specification/OrSpecification.php @@ -28,6 +28,8 @@ final class OrSpecification extends CompositeSpecification implements Specificat private $other; /** + * Initializes the OrSpecification object + * * @param CompositeSpecification $one * @param CompositeSpecification $other */ diff --git a/tests/integration/FindHiddenFilesTest.php b/tests/integration/FindHiddenFilesTest.php new file mode 100644 index 0000000..8153d1c --- /dev/null +++ b/tests/integration/FindHiddenFilesTest.php @@ -0,0 +1,31 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace Flyfinder; + +/** + * Integration test against examples/01-find-hidden-files.php + * @coversNothing + */ +class FindHiddenFilesTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string[] $result + */ + public function testFindingHiddenFiles() + { + include(__DIR__ . '/../../examples/01-find-hidden-files.php'); + + $this->assertEquals(2, count($result)); + $this->assertEquals(".test.txt", $result[1]['basename']); + } +} diff --git a/tests/integration/FindOnMultipleCriteriaTest.php b/tests/integration/FindOnMultipleCriteriaTest.php new file mode 100644 index 0000000..66cb5f3 --- /dev/null +++ b/tests/integration/FindOnMultipleCriteriaTest.php @@ -0,0 +1,32 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace Flyfinder; + +/** + * Integration test against examples/02-find-on-multiple-criteria.php + * @coversNothing + */ +class FindOnMultipleCriteriaTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string[] $result + */ + public function testFindingFilesOnMultipleCriteria() + { + include(__DIR__ . '/../../examples/02-find-on-multiple-criteria.php'); + + $this->assertEquals(2, count($result)); + $this->assertEquals("found.txt", $result[0]['basename']); + $this->assertEquals("example.txt", $result[1]['basename']); + } +} diff --git a/tests/unit/Specification/InPathTest.php b/tests/unit/Specification/InPathTest.php index f16c6a6..be9f97a 100644 --- a/tests/unit/Specification/InPathTest.php +++ b/tests/unit/Specification/InPathTest.php @@ -29,22 +29,24 @@ class InPathTest extends \PHPUnit_Framework_TestCase */ public function setUp() { - $this->fixture = new InPath(new Path('/tmp/test')); + $this->fixture = new InPath(new Path('.hiddendir')); } /** * @covers ::__construct * @covers ::isSatisfiedBy + * @covers :: * @uses Flyfinder\Path */ public function testIfSpecificationIsSatisfied() { - $this->assertTrue($this->fixture->isSatisfiedBy(['dirname' => '/tmp/test'])); + $this->assertTrue($this->fixture->isSatisfiedBy(['dirname' => '.hiddendir/normaldir'])); } /** * @covers ::__construct * @covers ::isSatisfiedBy + * @covers :: * @uses Flyfinder\Path */ public function testIfSpecificationIsNotSatisfied() From 0a99d5befb7e3ded9e41e5b5f5b6a85adaabe8a4 Mon Sep 17 00:00:00 2001 From: Marieke Bednarczyk Date: Sun, 16 Aug 2015 18:42:41 +0200 Subject: [PATCH 2/3] Added functionality to use wildcards in InPath --- src/Specification/InPath.php | 26 +++++++++++----- tests/unit/Specification/InPathTest.php | 41 ++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/Specification/InPath.php b/src/Specification/InPath.php index 1fe12b0..7cb2f6a 100644 --- a/src/Specification/InPath.php +++ b/src/Specification/InPath.php @@ -44,9 +44,15 @@ public function __construct(Path $path) public function isSatisfiedBy(array $value) { if (isset($value['dirname'])) { - $path = $this->removeDotSlash((string) $this->path); + $path = $this->cleanPath((string) $this->path); - if (substr($value['dirname'], 0, strlen($path)) === $path) { + $validChars = '[a-zA-Z0-9\\\/\.\<\>\,\|\:\(\)\&\;\#]'; + + $pattern = '(^(?!\/)' + . str_replace(['?', '*'], [$validChars . '?', $validChars . '*'], $path) + . $validChars . '*)'; + + if (preg_match($pattern, $value['dirname'] . '/')) { return true; } return false; @@ -56,15 +62,21 @@ public function isSatisfiedBy(array $value) /** * 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 $dirname + * @param string $path * @return string */ - private function removeDotSlash($dirname) + private function cleanPath($path) { - if (substr($dirname, 0, 2) === './') { - $dirname = substr($dirname, 1); + if (substr($path, 0, 2) === './') { + $path = substr($path, 1); } - return $dirname; + + if (substr($path, -1) !== '/') { + $path = $path . '/'; + } + + return $path; } } diff --git a/tests/unit/Specification/InPathTest.php b/tests/unit/Specification/InPathTest.php index be9f97a..af5709e 100644 --- a/tests/unit/Specification/InPathTest.php +++ b/tests/unit/Specification/InPathTest.php @@ -29,28 +29,59 @@ class InPathTest extends \PHPUnit_Framework_TestCase */ public function setUp() { - $this->fixture = new InPath(new Path('.hiddendir')); + $this->fixture = new InPath(new Path('*dden?ir/n')); } /** * @covers ::__construct * @covers ::isSatisfiedBy * @covers :: + * @dataProvider validDirnames * @uses Flyfinder\Path */ - public function testIfSpecificationIsSatisfied() + public function testIfSpecificationIsSatisfied($dirname) { - $this->assertTrue($this->fixture->isSatisfiedBy(['dirname' => '.hiddendir/normaldir'])); + $this->assertTrue($this->fixture->isSatisfiedBy(['dirname' => $dirname])); + } + + /** + * Data provider for testIfSpecificationIsSatisfied. Contains a few valid directory names + * + * @return array + */ + public function validDirnames() + { + return [ + ['.hiddendir/n'], + ['.hiddendir/n/'], + ['.hiddendir/n/somedir'], + ['.hiddendir/n/somedir.txt'] + ]; } /** * @covers ::__construct * @covers ::isSatisfiedBy * @covers :: + * @dataProvider InvalidDirnames * @uses Flyfinder\Path */ - public function testIfSpecificationIsNotSatisfied() + public function testIfSpecificationIsNotSatisfied($dirname) + { + $this->assertFalse($this->fixture->isSatisfiedBy(['dirname' => $dirname])); + } + + /** + * Data provider for testIfSpecificationIsNotSatisfied. Contains a few valid directory names + * + * @return array + */ + public function InvalidDirnames() { - $this->assertFalse($this->fixture->isSatisfiedBy(['dirname' => '/home'])); + return [ + ['/hiddendir/n'], + ['.hiddendir/normaldir'], + ['.hiddendir.ext/n'] + ]; } } From abe889ad79066f468af89468592421164f75a501 Mon Sep 17 00:00:00 2001 From: Marieke Bednarczyk Date: Sun, 16 Aug 2015 18:58:24 +0200 Subject: [PATCH 3/3] small correction in regex --- src/Specification/InPath.php | 2 +- tests/unit/Specification/InPathTest.php | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Specification/InPath.php b/src/Specification/InPath.php index 7cb2f6a..d62c542 100644 --- a/src/Specification/InPath.php +++ b/src/Specification/InPath.php @@ -49,7 +49,7 @@ public function isSatisfiedBy(array $value) $validChars = '[a-zA-Z0-9\\\/\.\<\>\,\|\:\(\)\&\;\#]'; $pattern = '(^(?!\/)' - . str_replace(['?', '*'], [$validChars . '?', $validChars . '*'], $path) + . str_replace(['?', '*'], [$validChars . '{1}', $validChars . '*'], $path) . $validChars . '*)'; if (preg_match($pattern, $value['dirname'] . '/')) { diff --git a/tests/unit/Specification/InPathTest.php b/tests/unit/Specification/InPathTest.php index af5709e..24a7e48 100644 --- a/tests/unit/Specification/InPathTest.php +++ b/tests/unit/Specification/InPathTest.php @@ -55,7 +55,8 @@ public function validDirnames() ['.hiddendir/n'], ['.hiddendir/n/'], ['.hiddendir/n/somedir'], - ['.hiddendir/n/somedir.txt'] + ['.hiddendir/n/somedir.txt'], + ['ddenxir/n'] ]; } @@ -63,7 +64,7 @@ public function validDirnames() * @covers ::__construct * @covers ::isSatisfiedBy * @covers :: - * @dataProvider InvalidDirnames + * @dataProvider invalidDirnames * @uses Flyfinder\Path */ public function testIfSpecificationIsNotSatisfied($dirname) @@ -76,12 +77,14 @@ public function testIfSpecificationIsNotSatisfied($dirname) * * @return array */ - public function InvalidDirnames() + public function invalidDirnames() { return [ ['/hiddendir/n'], ['.hiddendir/normaldir'], - ['.hiddendir.ext/n'] + ['.hiddendir.ext/n'], + ['.hiddenxxir/n'], + ['.hiddenir/n'] ]; } }