Skip to content

Commit

Permalink
Merge pull request #403 from Landerstraeten/improve-phpstan-configura…
Browse files Browse the repository at this point in the history
…tion

Improve phpstan configuration
  • Loading branch information
Landerstraeten authored Nov 17, 2017
2 parents 6af45c0 + 694994f commit 6badbe0
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 3 deletions.
14 changes: 14 additions & 0 deletions doc/tasks/phpstan.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ parameters:
autoload_file: ~
configuration: ~
level: 0
force_patterns: []
ignore_patterns: []
triggered_by: ['php']
```
Expand All @@ -38,6 +40,18 @@ With this parameter you can specify the path your project's configuration file.
With this parameter you can set the level of rule options - the higher the stricter.
**force_patterns**
*Default: []*
This is a list of patterns that will be forced for analysis even when the file or path is ignored.
**ignore_patterns**
*Default: []*
This is a list of patterns that will be ignored by phpstan. With this option you can skip files like tests. Leave this option blank to run phpstan for every php file.
**triggered_by**
*Default: [php]*
Expand Down
27 changes: 27 additions & 0 deletions spec/Collection/FilesCollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ function it_should_filter_by_not_path(SplFileInfo $file1, SplFileInfo $file2)
$files[0]->shouldBe($file1);
}

function it_should_filter_by_not_paths(SplFileInfo $file1, SplFileInfo $file2, SplFileInfo $file3)
{
$file1->getRelativePathname()->willReturn('path1/file.php');
$file2->getRelativePathname()->willReturn('path2/file.php');
$file3->getRelativePathname()->willReturn('path3/file.png');

$result = $this->notPaths(['path2', 'path3']);
$result->shouldBeAnInstanceOf(FilesCollection::class);
$result->count()->shouldBe(1);
$files = $result->toArray();
$files[0]->shouldBe($file1);
}

function it_should_filter_by_size(SplFileInfo $file1, SplFileInfo $file2)
{
$file1->isFile()->willReturn(true);
Expand Down Expand Up @@ -190,4 +203,18 @@ function it_should_return_an_empty_list_when_filtering_by_no_extension(SplFileIn
$result = $this->extensions([]);
$result->count()->shouldBe(0);
}

function it_should_combine_two_collections_with_ensured_files()
{
$file1 = new \SplFileInfo('path1/file1.php');
$file2 = new \SplFileInfo('path1/file2.php');
$file3 = new \SplFileInfo('path1/file3.php');

$this->beConstructedWith([$file2, $file3]);
$ensureFiles = new FilesCollection([$file1, $file2]);

$result = $this->ensureFiles($ensureFiles);
$result->shouldIterateAs([$file2, $file3, $file1]);
$result->shouldHaveCount(3);
}
}
55 changes: 54 additions & 1 deletion spec/Task/PhpStanSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ function it_should_have_configurable_options()
$options->shouldBeAnInstanceOf(OptionsResolver::class);
$options->getDefinedOptions()->shouldContain('autoload_file');
$options->getDefinedOptions()->shouldContain('configuration');
$options->getDefinedOptions()->shouldContain('ignore_patterns');
$options->getDefinedOptions()->shouldContain('force_patterns');
$options->getDefinedOptions()->shouldContain('level');
$options->getDefinedOptions()->shouldContain('triggered_by');
}
Expand All @@ -59,7 +61,6 @@ function it_should_run_in_run_context(RunContext $context)
function it_does_not_do_anything_if_there_are_no_files(ProcessBuilder $processBuilder, ContextInterface $context)
{
$processBuilder->buildProcess('phpstan')->shouldNotBeCalled();
$processBuilder->buildProcess()->shouldNotBeCalled();
$context->getFiles()->willReturn(new FilesCollection());

$result = $this->run($context);
Expand Down Expand Up @@ -87,6 +88,58 @@ function it_runs_the_suite(ProcessBuilder $processBuilder, Process $process, Con
$result->isPassed()->shouldBe(true);
}

function it_runs_the_suite_with_ignored_files(ProcessBuilder $processBuilder, Process $process, ContextInterface $context, GrumPHP $grumPHP)
{
$grumPHP->getTaskConfiguration('phpstan')->willReturn([
'ignore_patterns' => ['TaskResultCollection.php'],
]);

$context->getFiles()->willReturn(new FilesCollection([
new SplFileInfo('src/Collection/TaskResultCollection.php', 'src/Collection', 'TaskResultCollection.php'),
new SplFileInfo('src/Collection/TaskResultCollection.php', 'src/Collection', 'Passed.php'),
]));

$processBuilder->buildProcess('phpstan')->shouldNotBeCalled();

$arguments = new ProcessArgumentsCollection();
$processBuilder->createArgumentsForCommand('phpstan')->willReturn($arguments);
$processBuilder->buildProcess($arguments)->willReturn($process);

$process->run()->shouldBeCalled();
$process->isSuccessful()->willReturn(true);
$process->getErrorOutput()->willReturn('');
$process->getOutput()->willReturn('');

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->getResultCode()->shouldBe(TaskResult::PASSED);
}

function it_runs_the_suite_with_forced_files(ProcessBuilder $processBuilder, Process $process, ContextInterface $context, GrumPHP $grumPHP)
{
$grumPHP->getTaskConfiguration('phpstan')->willReturn([
'ignore_patterns' => ['TaskResultCollection.php'],
'force_patterns' => ['TaskResultCollection.php'],
]);

$arguments = new ProcessArgumentsCollection();
$processBuilder->createArgumentsForCommand('phpstan')->willReturn($arguments);
$processBuilder->buildProcess($arguments)->willReturn($process);

$process->run()->shouldBeCalled();
$process->isSuccessful()->willReturn(true);
$process->getErrorOutput()->willReturn('');
$process->getOutput()->willReturn('');

$context->getFiles()->willReturn(new FilesCollection([
new SplFileInfo('src/Collection/TaskResultCollection.php', 'src/Collection', 'TaskResultCollection.php')])
);

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->getResultCode()->shouldBe(TaskResult::PASSED);
}

function it_throws_exception_if_the_process_fails(ProcessBuilder $processBuilder, Process $process, ContextInterface $context)
{
$arguments = new ProcessArgumentsCollection();
Expand Down
36 changes: 35 additions & 1 deletion src/Collection/FilesCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,23 @@ public function paths(array $patterns)
*/
public function notPath($pattern)
{
$filter = new Iterator\PathFilterIterator($this->getIterator(), [], [$pattern]);
return $this->notPaths([$pattern]);
}

/**
* Adds rules that filenames must not match.
*
* You can use patterns (delimited with / sign) or simple strings.
*
* $collection->notPaths(['/^spec\/','/^src\/'])
*
* @param array $pattern
*
* @return FilesCollection
*/
public function notPaths(array $pattern)
{
$filter = new Iterator\PathFilterIterator($this->getIterator(), [], $pattern);

return new FilesCollection(iterator_to_array($filter));
}
Expand Down Expand Up @@ -211,4 +227,22 @@ public function filterByFileList(Traversable $fileList)
return in_array($file->getPathname(), $allowedFiles);
});
}

/**
* @param FilesCollection $files
*
* @return FilesCollection
*/
public function ensureFiles(FilesCollection $files)
{
$newFiles = new self($this->toArray());

foreach ($files as $file) {
if (!$newFiles->contains($file)) {
$newFiles->add($file);
}
}

return $newFiles;
}
}
15 changes: 14 additions & 1 deletion src/Task/PhpStan.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ public function getConfigurableOptions()
'autoload_file' => null,
'configuration' => null,
'level' => 0,
'ignore_patterns' => [],
'force_patterns' => [],
'triggered_by' => ['php'],
]);

$resolver->addAllowedTypes('autoload_file', ['null', 'string']);
$resolver->addAllowedTypes('configuration', ['null', 'string']);
$resolver->addAllowedTypes('level', ['int']);
$resolver->addAllowedTypes('ignore_patterns', ['array']);
$resolver->addAllowedTypes('force_patterns', ['array']);
$resolver->addAllowedTypes('triggered_by', ['array']);

return $resolver;
Expand All @@ -56,7 +60,16 @@ public function canRunInContext(ContextInterface $context)
public function run(ContextInterface $context)
{
$config = $this->getConfiguration();
$files = $context->getFiles()->extensions($config['triggered_by']);

$files = $context
->getFiles()
->notPaths($config['ignore_patterns'])
->extensions($config['triggered_by']);

if (!empty($config['force_patterns'])) {
$forcedFiles = $context->getFiles()->paths($config['force_patterns']);
$files = $files->ensureFiles($forcedFiles);
}

if (0 === count($files)) {
return TaskResult::createSkipped($this, $context);
Expand Down

0 comments on commit 6badbe0

Please sign in to comment.