From f1d7b2b5c4e153ed7e2cbbca7865dbe175296ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Mon, 9 Apr 2018 21:35:53 +0200 Subject: [PATCH 1/6] adding option to manually register psr0/psr4-like dependency autoloading might be a workaround for some of #55's issues --- .../Cli/CheckCommand.php | 28 +++++++++++++++++-- ...erPackageDirectDependenciesSourceFiles.php | 7 +++-- .../LocateComposerPackageSourceFiles.php | 6 +++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/ComposerRequireChecker/Cli/CheckCommand.php b/src/ComposerRequireChecker/Cli/CheckCommand.php index 3cc4255f..3699e650 100644 --- a/src/ComposerRequireChecker/Cli/CheckCommand.php +++ b/src/ComposerRequireChecker/Cli/CheckCommand.php @@ -40,12 +40,18 @@ protected function configure() 'the composer.json of your package, that should be checked', './composer.json' ) - ->addOption( + ->addOption( 'ignore-parse-errors', null, InputOption::VALUE_NONE, 'this will cause ComposerRequireChecker to ignore errors when files cannot be parsed, otherwise' . ' errors will be thrown' + ) + ->addOption( + 'register-namespace', + null, + InputOption::VALUE_IS_ARRAY|InputOption::VALUE_OPTIONAL, + 'vendor/package:namespace:path as if it was psr4' ); } @@ -64,13 +70,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $options = $this->getCheckOptions($input); $getPackageSourceFiles = new LocateComposerPackageSourceFiles(); + $manualRegistered = $this->buildManualList($input, $composerJson); $sourcesASTs = $this->getASTFromFilesLocator($input); $definedVendorSymbols = (new LocateDefinedSymbolsFromASTRoots())->__invoke($sourcesASTs( (new ComposeGenerators())->__invoke( $getPackageSourceFiles($composerJson), - (new LocateComposerPackageDirectDependenciesSourceFiles())->__invoke($composerJson) + (new LocateComposerPackageDirectDependenciesSourceFiles())->__invoke($composerJson, $manualRegistered) ) )); @@ -145,4 +152,21 @@ private function getASTFromFilesLocator(InputInterface $input): LocateASTFromFil return $sourcesASTs; } + /** + * @return array + */ + private function buildManualList(InputInterface $input, $composerJson) + { + if(!$input->hasOption('register-namespace')) { + return []; + } + $packageDir = dirname($composerJson); + $namespaces = []; + foreach($input->getOption('register-namespace') as $option) { + if(preg_match('!^([^/:]+(/[^/:]+)+):([^:]+):([^:]+)$!i', $option, $matches)) { + $namespaces[$packageDir . '/vendor/' . $matches[1]][$matches[3]] = $matches[4]; + } + } + return $namespaces; + } } diff --git a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php index dacd871b..7cb8d3f3 100644 --- a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php +++ b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageDirectDependenciesSourceFiles.php @@ -6,7 +6,7 @@ final class LocateComposerPackageDirectDependenciesSourceFiles { - public function __invoke(string $composerJsonPath): Generator + public function __invoke(string $composerJsonPath, array $manual = []): Generator { $packageDir = dirname($composerJsonPath); @@ -22,7 +22,10 @@ function (string $vendorName) use ($packageDir) { continue; } - yield from (new LocateComposerPackageSourceFiles())->__invoke($vendorDir . '/composer.json'); + yield from (new LocateComposerPackageSourceFiles())->__invoke( + $vendorDir . '/composer.json', + $manual[$vendorDir] ?? [] + ); } } } diff --git a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php index 874a54e4..0bdf1c8a 100644 --- a/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php +++ b/src/ComposerRequireChecker/FileLocator/LocateComposerPackageSourceFiles.php @@ -6,7 +6,7 @@ final class LocateComposerPackageSourceFiles { - public function __invoke(string $composerJsonPath): Generator + public function __invoke(string $composerJsonPath, array $manual = []): Generator { $packageDir = dirname($composerJsonPath); $composerData = json_decode(file_get_contents($composerJsonPath), true); @@ -29,6 +29,10 @@ public function __invoke(string $composerJsonPath): Generator $this->getFilePaths($composerData['autoload']['psr-4'] ?? [], $packageDir), $blacklist ); + yield from $this->locateFilesInFilesInFilesDefinitions( + $this->getFilePaths($manual, $packageDir), + $blacklist + ); } private function getFilePaths(array $sourceDirs, string $packageDir): array From 29cb9cab79d1b299880d3190ad790a9b42743d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Mon, 9 Apr 2018 22:25:52 +0200 Subject: [PATCH 2/6] adding simple test for manual "autoloading" input --- .../Cli/CheckCommandGetManualListTest.php | 60 +++++++++++++++++++ .../Cli/CheckCommandTest.php | 3 +- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php new file mode 100644 index 00000000..91bec063 --- /dev/null +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php @@ -0,0 +1,60 @@ +getMethod('buildManualList'); + $method->setAccessible(true); + return [ + [$this->getInputMockForManualList(), $method, []], + [$this->getInputMockForManualList( + ['abc', 'nope', 'abc/hi:Abc\\:src', 'abc/def/qoi:Qui\\:src/lib/ext', 'abc/def/:Qui\\:src/lib/ext']), + $method, + ['abc/def/qoi', 'abc/hi'] + ], + ]; + } + + /** + * @param array $return + * @return InputInterface + */ + private function getInputMockForManualList(array $return = []) + { + $input = $this->getMockBuilder(InputInterface::class)->getMock(); + $hasReturn = count($return)>0; + $input->expects($this->once()) + ->method('hasOption') + ->with('register-namespace') + ->willReturn($hasReturn); + $input->expects($hasReturn?$this->once():$this->never()) + ->method('getOption') + ->with('register-namespace') + ->willReturn($return); + return $input; + } + + /** + * Since the command does so much, there's no reasonable way to supply test data + * @test + */ + public function testBuildManualList(InputInterface $input, ReflectionMethod $method, array $expected) + { + $instance = new CheckCommand(); + $result = $method->invoke($instance, $input, __FILE__); + $this->assertCount(count($expected), $result); + $this->assertCount(0, array_diff(array_keys($result), $expected)); + } +} \ No newline at end of file diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php index c955f5ba..788ae501 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandTest.php @@ -3,6 +3,7 @@ namespace ComposerRequireCheckerTest\Cli; use ComposerRequireChecker\Cli\Application; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; @@ -24,7 +25,7 @@ public function setUp() public function testExceptionIfComposerJsonNotFound() { - self::expectException(\InvalidArgumentException::class); + self::expectException(InvalidArgumentException::class); $this->commandTester->execute([ 'composer-json' => 'this-will-not-be-found.json' From 910845af7d7d974f5b293823d7b29b6b16622d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Mon, 9 Apr 2018 22:27:42 +0200 Subject: [PATCH 3/6] adding forgotten dataprovider --- .../Cli/CheckCommandGetManualListTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php index 91bec063..e519b0f9 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php @@ -28,6 +28,7 @@ public function provideBuildManualList():array } /** + * @dataProvider provideBuildManualList * @param array $return * @return InputInterface */ From d7a0c26d5125095c5c2c22cbd10c496f84af36df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Mon, 9 Apr 2018 22:30:57 +0200 Subject: [PATCH 4/6] adding forgotten dataprovider to correct method --- .../Cli/CheckCommandGetManualListTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php index e519b0f9..bc4ea649 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php @@ -28,7 +28,6 @@ public function provideBuildManualList():array } /** - * @dataProvider provideBuildManualList * @param array $return * @return InputInterface */ @@ -48,6 +47,7 @@ private function getInputMockForManualList(array $return = []) } /** + * @dataProvider provideBuildManualList * Since the command does so much, there's no reasonable way to supply test data * @test */ From 29c543091ad8577cabe33a9dd81e555e1dce18eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Mon, 9 Apr 2018 22:59:28 +0200 Subject: [PATCH 5/6] correcting folders to be tested --- .../Cli/CheckCommandGetManualListTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php index bc4ea649..a9094ba8 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php @@ -22,7 +22,7 @@ public function provideBuildManualList():array [$this->getInputMockForManualList( ['abc', 'nope', 'abc/hi:Abc\\:src', 'abc/def/qoi:Qui\\:src/lib/ext', 'abc/def/:Qui\\:src/lib/ext']), $method, - ['abc/def/qoi', 'abc/hi'] + [__DIR__.'/vendor/abc/def/qoi', __DIR__.'/vendor/abc/hi'] ], ]; } From 18cbd0a6c23f693a57211f8902c3b7f66907ac27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20B=C3=BCttner?= Date: Tue, 10 Apr 2018 00:01:23 +0200 Subject: [PATCH 6/6] including some fixes for scrutinizer --- .gitignore | 2 +- src/ComposerRequireChecker/Cli/CheckCommand.php | 8 ++++---- .../Cli/CheckCommandGetManualListTest.php | 13 +++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 1b2bc847..553c78fe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ phpdox.xml clover.xml example/test-data phing-latest.phar -build/ +build/ \ No newline at end of file diff --git a/src/ComposerRequireChecker/Cli/CheckCommand.php b/src/ComposerRequireChecker/Cli/CheckCommand.php index 3699e650..41ee9b7b 100644 --- a/src/ComposerRequireChecker/Cli/CheckCommand.php +++ b/src/ComposerRequireChecker/Cli/CheckCommand.php @@ -50,7 +50,7 @@ protected function configure() ->addOption( 'register-namespace', null, - InputOption::VALUE_IS_ARRAY|InputOption::VALUE_OPTIONAL, + InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'vendor/package:namespace:path as if it was psr4' ); } @@ -157,13 +157,13 @@ private function getASTFromFilesLocator(InputInterface $input): LocateASTFromFil */ private function buildManualList(InputInterface $input, $composerJson) { - if(!$input->hasOption('register-namespace')) { + if (!$input->hasOption('register-namespace')) { return []; } $packageDir = dirname($composerJson); $namespaces = []; - foreach($input->getOption('register-namespace') as $option) { - if(preg_match('!^([^/:]+(/[^/:]+)+):([^:]+):([^:]+)$!i', $option, $matches)) { + foreach ($input->getOption('register-namespace') as $option) { + if (preg_match('!^([^/:]+(/[^/:]+)+):([^:]+):([^:]+)$!i', $option, $matches)) { $namespaces[$packageDir . '/vendor/' . $matches[1]][$matches[3]] = $matches[4]; } } diff --git a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php index a9094ba8..5fdd76d2 100644 --- a/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php +++ b/test/ComposerRequireCheckerTest/Cli/CheckCommandGetManualListTest.php @@ -22,7 +22,7 @@ public function provideBuildManualList():array [$this->getInputMockForManualList( ['abc', 'nope', 'abc/hi:Abc\\:src', 'abc/def/qoi:Qui\\:src/lib/ext', 'abc/def/:Qui\\:src/lib/ext']), $method, - [__DIR__.'/vendor/abc/def/qoi', __DIR__.'/vendor/abc/hi'] + [__DIR__ . '/vendor/abc/def/qoi', __DIR__ . '/vendor/abc/hi'] ], ]; } @@ -31,17 +31,17 @@ public function provideBuildManualList():array * @param array $return * @return InputInterface */ - private function getInputMockForManualList(array $return = []) + private function getInputMockForManualList(array $return = []):InputInterface { $input = $this->getMockBuilder(InputInterface::class)->getMock(); - $hasReturn = count($return)>0; + $hasReturn = count($return) > 0; $input->expects($this->once()) ->method('hasOption') - ->with('register-namespace') + ->with(/** @scrutinizer ignore-type */'register-namespace') ->willReturn($hasReturn); - $input->expects($hasReturn?$this->once():$this->never()) + $input->expects($hasReturn ? $this->once() : $this->never()) ->method('getOption') - ->with('register-namespace') + ->with(/** @scrutinizer ignore-type */'register-namespace') ->willReturn($return); return $input; } @@ -55,6 +55,7 @@ public function testBuildManualList(InputInterface $input, ReflectionMethod $met { $instance = new CheckCommand(); $result = $method->invoke($instance, $input, __FILE__); + $this->assertInternalType('array', $result); $this->assertCount(count($expected), $result); $this->assertCount(0, array_diff(array_keys($result), $expected)); }