Browse Source

Merge branch 'master' into 3.0

Dariusz Ruminski 6 years ago
parent
commit
c9f6bc1064

+ 1 - 2
.travis.yml

@@ -114,7 +114,7 @@ jobs:
                 # Composer: boost installation
                 - composer global show hirak/prestissimo -q || travis_retry composer global require $DEFAULT_COMPOSER_FLAGS hirak/prestissimo
             script:
-                - phpdbg -qrr vendor/bin/phpunit --exclude-group covers-nothing --coverage-clover build/logs/clover.xml || travis_terminate 1
+                - phpdbg -qrr vendor/bin/phpunit --testsuite coverage --exclude-group covers-nothing --coverage-clover build/logs/clover.xml || travis_terminate 1
                 - php vendor/bin/php-coveralls -v
 
         -
@@ -148,4 +148,3 @@ jobs:
 
     allow_failures:
         - php: nightly
-        - env: COLLECT_COVERAGE=1

+ 7 - 2
README.rst

@@ -797,14 +797,19 @@ Choose from the list of available rules:
 
 * **native_function_invocation**
 
-  Add leading ``\`` before function invocation of internal function to speed
-  up resolving.
+  Add leading ``\`` before function invocation to speed up resolving.
 
   *Risky rule: risky when any of the functions are overridden.*
 
   Configuration options:
 
   - ``exclude`` (``array``): list of functions to ignore; defaults to ``[]``
+  - ``include`` (``array``): list of function names or sets to fix. Defined sets are
+    ``@internal`` (all native functions), ``@all`` (all global functions) and
+    ``@compiler_optimized`` (functions that are specially optimized by Zend);
+    defaults to ``['@internal']``
+  - ``scope`` (``'all'``, ``'namespaced'``): only fix function calls that are made
+    within a namespace or fix all; defaults to ``'all'``
 
 * **new_with_braces** [@Symfony]
 

+ 6 - 1
phpunit.xml.dist

@@ -21,9 +21,14 @@
     verbose="true"
 >
     <testsuites>
-        <testsuite>
+        <testsuite name="all">
             <directory>./tests</directory>
         </testsuite>
+        <testsuite name="coverage">
+            <directory>./tests</directory>
+            <exclude>./tests/AutoReview</exclude>
+            <exclude>./tests/Smoke</exclude>
+        </testsuite>
     </testsuites>
 
     <filter>

+ 311 - 52
src/Fixer/FunctionNotation/NativeFunctionInvocationFixer.php

@@ -24,16 +24,51 @@ use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
 
 /**
  * @author Andreas Möller <am@localheinz.com>
+ * @author SpacePossum
  */
 final class NativeFunctionInvocationFixer extends AbstractFixer implements ConfigurableFixerInterface
 {
+    /**
+     * @internal
+     */
+    const SET_ALL = '@all';
+
+    /**
+     * Subset of SET_INTERNAL.
+     *
+     * Change function call to functions known to be optimized by the Zend engine.
+     * For details:
+     * - @see https://github.com/php/php-src/blob/php-7.2.6/Zend/zend_compile.c "zend_try_compile_special_func"
+     * - @see https://github.com/php/php-src/blob/php-7.2.6/ext/opcache/Optimizer/pass1_5.c
+     *
+     * @internal
+     */
+    const SET_COMPILER_OPTIMIZED = '@compiler_optimized';
+
+    /**
+     * @internal
+     */
+    const SET_INTERNAL = '@internal';
+
+    /**
+     * @var callable
+     */
+    private $functionFilter;
+
+    public function configure(array $configuration = null)
+    {
+        parent::configure($configuration);
+
+        $this->functionFilter = $this->getFunctionFilter();
+    }
+
     /**
      * {@inheritdoc}
      */
     public function getDefinition()
     {
         return new FixerDefinition(
-            'Add leading `\` before function invocation of internal function to speed up resolving.',
+            'Add leading `\` before function invocation to speed up resolving.',
             [
                 new CodeSample(
 '<?php
@@ -66,6 +101,56 @@ function baz($options)
                         ],
                     ]
                 ),
+                new CodeSample(
+                    '<?php
+namespace space1 {
+    echo count([1]);
+}
+namespace {
+    echo count([1]);
+}
+',
+                    ['scope' => 'all']
+                ),
+                new CodeSample(
+                    '<?php
+namespace space1 {
+    echo count([1]);
+}
+namespace {
+    echo count([1]);
+}
+',
+                    ['scope' => 'namespaced']
+                ),
+                new CodeSample(
+                    '<?php
+myGlobalFunction();
+count();
+',
+                    ['include' => ['myGlobalFunction']]
+                ),
+                new CodeSample(
+                    '<?php
+myGlobalFunction();
+count();
+',
+                    ['include' => ['@all']]
+                ),
+                new CodeSample(
+                    '<?php
+myGlobalFunction();
+count();
+',
+                    ['include' => ['@internal']]
+                ),
+                new CodeSample(
+                    '<?php
+$a .= str_repeat($a, 4);
+$c = get_class($d);
+',
+                    ['include' => ['@compiler_optimized']]
+                ),
             ],
             null,
             'Risky when any of the functions are overridden.'
@@ -93,22 +178,89 @@ function baz($options)
      */
     protected function applyFix(\SplFileInfo $file, Tokens $tokens)
     {
-        $functionNames = $this->getFunctionNames();
+        if ('all' === $this->configuration['scope']) {
+            $this->fixFunctionCalls($tokens, $this->functionFilter, 0, \count($tokens) - 1);
+
+            return;
+        }
+
+        // 'scope' is 'namespaced' here
+        foreach (\array_reverse($this->getUserDefinedNamespaces($tokens)) as $namespace) {
+            $this->fixFunctionCalls($tokens, $this->functionFilter, $namespace['open'], $namespace['close']);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function createConfigurationDefinition()
+    {
+        return new FixerConfigurationResolver([
+            (new FixerOptionBuilder('exclude', 'List of functions to ignore.'))
+                ->setAllowedTypes(['array'])
+                ->setAllowedValues([static function (array $value) {
+                    foreach ($value as $functionName) {
+                        if (!\is_string($functionName) || '' === \trim($functionName) || \trim($functionName) !== $functionName) {
+                            throw new InvalidOptionsException(\sprintf(
+                                'Each element must be a non-empty, trimmed string, got "%s" instead.',
+                                \is_object($functionName) ? \get_class($functionName) : \gettype($functionName)
+                            ));
+                        }
+                    }
+
+                    return true;
+                }])
+                ->setDefault([])
+                ->getOption(),
+            (new FixerOptionBuilder('include', 'List of function names or sets to fix. Defined sets are `@internal` (all native functions), `@all` (all global functions) and `@compiler_optimized` (functions that are specially optimized by Zend).'))
+                ->setAllowedTypes(['array'])
+                ->setAllowedValues([static function (array $value) {
+                    foreach ($value as $functionName) {
+                        if (!\is_string($functionName) || '' === \trim($functionName) || \trim($functionName) !== $functionName) {
+                            throw new InvalidOptionsException(\sprintf(
+                                'Each element must be a non-empty, trimmed string, got "%s" instead.',
+                                \is_object($functionName) ? \get_class($functionName) : \gettype($functionName)
+                            ));
+                        }
 
-        $indexes = [];
+                        $sets = [
+                            self::SET_ALL,
+                            self::SET_INTERNAL,
+                            self::SET_COMPILER_OPTIMIZED,
+                        ];
 
-        for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
-            $token = $tokens[$index];
+                        if ('@' === $value[0] && !\in_array($value, $sets, true)) {
+                            throw new InvalidOptionsException(\sprintf('Unknown set "%s", known sets are "%s".', $value, \implode('", "', $sets)));
+                        }
+                    }
 
-            $tokenContent = $token->getContent();
+                    return true;
+                }])
+                ->setDefault([self::SET_INTERNAL])
+                ->getOption(),
+            (new FixerOptionBuilder('scope', 'Only fix function calls that are made within a namespace or fix all.'))
+                ->setAllowedValues(['all', 'namespaced'])
+                ->setDefault('all')
+                ->getOption(),
+        ]);
+    }
 
+    /**
+     * @param Tokens   $tokens
+     * @param callable $functionFilter
+     * @param int      $start
+     * @param int      $end
+     */
+    private function fixFunctionCalls(Tokens $tokens, callable $functionFilter, $start, $end)
+    {
+        $insertAtIndexes = [];
+        for ($index = $start; $index < $end; ++$index) {
             // test if we are at a function call
-            if (!$token->isGivenKind(T_STRING)) {
+            if (!$tokens[$index]->isGivenKind(T_STRING)) {
                 continue;
             }
 
-            $next = $tokens->getNextMeaningfulToken($index);
-            if (!$tokens[$next]->equals('(')) {
+            if (!$tokens[$tokens->getNextMeaningfulToken($index)]->equals('(')) {
                 continue;
             }
 
@@ -117,81 +269,188 @@ function baz($options)
                 continue;
             }
 
-            if ($tokens[$functionNamePrefix]->isGivenKind(T_NS_SEPARATOR)) {
-                // skip if the call is to a constructor or to a function in a namespace other than the default
-                $prev = $tokens->getPrevMeaningfulToken($functionNamePrefix);
-                if ($tokens[$prev]->isGivenKind([T_STRING, T_NEW])) {
-                    continue;
-                }
+            if (
+                $tokens[$functionNamePrefix]->isGivenKind(T_NS_SEPARATOR)
+                && $tokens[$tokens->getPrevMeaningfulToken($functionNamePrefix)]->isGivenKind([T_STRING, T_NEW])
+            ) {
+                continue; // skip if the call is to a constructor or to a function in a namespace other than the default
             }
 
-            $lowerFunctionName = \strtolower($tokenContent);
-
-            if (!\in_array($lowerFunctionName, $functionNames, true)) {
+            if (!$functionFilter($tokens[$index]->getContent())) {
                 continue;
             }
 
-            // do not bother if previous token is already namespace separator
             if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NS_SEPARATOR)) {
-                continue;
+                continue; // do not bother if previous token is already namespace separator
             }
 
-            $indexes[] = $index;
+            $insertAtIndexes[] = $index;
         }
 
-        $indexes = \array_reverse($indexes);
-        foreach ($indexes as $index) {
+        foreach (\array_reverse($insertAtIndexes) as $index) {
             $tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
         }
     }
 
     /**
-     * {@inheritdoc}
+     * @return callable
      */
-    protected function createConfigurationDefinition()
+    private function getFunctionFilter()
     {
-        return new FixerConfigurationResolver([
-            (new FixerOptionBuilder('exclude', 'List of functions to ignore.'))
-                ->setAllowedTypes(['array'])
-                ->setAllowedValues([static function ($value) {
-                    foreach ($value as $functionName) {
-                        if (!\is_string($functionName) || '' === \trim($functionName) || \trim($functionName) !== $functionName) {
-                            throw new InvalidOptionsException(\sprintf(
-                                'Each element must be a non-empty, trimmed string, got "%s" instead.',
-                                \is_object($functionName) ? \get_class($functionName) : \gettype($functionName)
-                            ));
-                        }
-                    }
+        $exclude = $this->normalizeFunctionNames($this->configuration['exclude']);
 
-                    return true;
-                }])
-                ->setDefault([])
-                ->getOption(),
+        if (\in_array(self::SET_ALL, $this->configuration['include'], true)) {
+            if (\count($exclude) > 0) {
+                return static function ($functionName) use ($exclude) {
+                    return !isset($exclude[\strtolower($functionName)]);
+                };
+            }
+
+            return static function () {
+                return true;
+            };
+        }
+
+        $include = [];
+        if (\in_array(self::SET_INTERNAL, $this->configuration['include'], true)) {
+            $include = $this->getAllInternalFunctionsNormalized();
+        } elseif (\in_array(self::SET_COMPILER_OPTIMIZED, $this->configuration['include'], true)) {
+            $include = $this->getAllCompilerOptimizedFunctionsNormalized(); // if `@internal` is set all compiler optimized function are already loaded
+        }
+
+        foreach ($this->configuration['include'] as $additional) {
+            if ('@' !== $additional[0]) {
+                $include[\strtolower($additional)] = true;
+            }
+        }
+
+        if (\count($exclude) > 0) {
+            return static function ($functionName) use ($include, $exclude) {
+                return isset($include[\strtolower($functionName)]) && !isset($exclude[\strtolower($functionName)]);
+            };
+        }
+
+        return static function ($functionName) use ($include) {
+            return isset($include[\strtolower($functionName)]);
+        };
+    }
+
+    /**
+     * @return array<string, true> normalized function names of which the PHP compiler optimizes
+     */
+    private function getAllCompilerOptimizedFunctionsNormalized()
+    {
+        return $this->normalizeFunctionNames([
+            // @see https://github.com/php/php-src/blob/php-7.2.6/Zend/zend_compile.c "zend_try_compile_special_func"
+            'array_slice',
+            'assert',
+            'boolval',
+            'call_user_func',
+            'call_user_func_array',
+            'chr',
+            'count',
+            'defined',
+            'doubleval',
+            'floatval',
+            'func_get_args',
+            'func_num_args',
+            'get_called_class',
+            'get_class',
+            'gettype',
+            'in_array',
+            'intval',
+            'is_array',
+            'is_bool',
+            'is_double',
+            'is_float',
+            'is_int',
+            'is_integer',
+            'is_long',
+            'is_null',
+            'is_object',
+            'is_real',
+            'is_resource',
+            'is_string',
+            'ord',
+            'strlen',
+            'strval',
+            // @see https://github.com/php/php-src/blob/php-7.2.6/ext/opcache/Optimizer/pass1_5.c
+            'constant',
+            'define',
+            'dirname',
+            'extension_loaded',
+            'function_exists',
+            'is_callable',
         ]);
     }
 
     /**
-     * @return string[]
+     * @return array<string, true> normalized function names of all internal defined functions
      */
-    private function getFunctionNames()
+    private function getAllInternalFunctionsNormalized()
     {
-        $definedFunctions = \get_defined_functions();
+        return $this->normalizeFunctionNames(\get_defined_functions()['internal']);
+    }
 
-        return \array_diff(
-            $this->normalizeFunctionNames($definedFunctions['internal']),
-            \array_unique($this->normalizeFunctionNames($this->configuration['exclude']))
-        );
+    /**
+     * Returns array<'open'|'close', int>[].
+     *
+     * @param Tokens $tokens
+     *
+     * @return array
+     */
+    private function getUserDefinedNamespaces(Tokens $tokens)
+    {
+        $namespaces = [];
+        for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
+            if (!$tokens[$index]->isGivenKind(T_NAMESPACE)) {
+                continue;
+            }
+
+            $index = $tokens->getNextMeaningfulToken($index);
+            if ($tokens[$index]->equals('{')) { // global namespace
+                $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
+
+                continue;
+            }
+
+            while (!$tokens[++$index]->equalsAny(['{', ';', [T_CLOSE_TAG]])) {
+                // no-op
+            }
+
+            if ($tokens[$index]->equals('{')) {
+                // namespace ends at block end of `{`
+                $namespaces[] = ['open' => $index, 'close' => $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index)];
+
+                continue;
+            }
+
+            // namespace ends at next T_NAMESPACE or EOF
+            $close = $tokens->getNextTokenOfKind($index, [[T_NAMESPACE]], false);
+            if (null === $close) {
+                $namespaces[] = ['open' => $index, 'close' => \count($tokens) - 1];
+
+                break;
+            }
+
+            $namespaces[] = ['open' => $index, 'close' => $close];
+        }
+
+        return $namespaces;
     }
 
     /**
      * @param string[] $functionNames
      *
-     * @return string[]
+     * @return array<string, true> all function names lower cased
      */
     private function normalizeFunctionNames(array $functionNames)
     {
-        return \array_map(static function ($functionName) {
-            return \strtolower($functionName);
-        }, $functionNames);
+        foreach ($functionNames as $index => $functionName) {
+            $functionNames[\strtolower($functionName)] = true;
+            unset($functionNames[$index]);
+        }
+
+        return $functionNames;
     }
 }

+ 40 - 52
tests/AutoReview/ProjectCodeTest.php

@@ -235,18 +235,25 @@ final class ProjectCodeTest extends TestCase
     }
 
     /**
-     * @dataProvider provideDataProviderMethodNameCases
+     * @dataProvider provideTestClassCases
      *
      * @param string $testClassName
-     * @param string $dataProviderMethodName
      */
-    public function testThatDataProvidersAreCorrectlyNamed($testClassName, $dataProviderMethodName)
+    public function testThatDataProvidersAreCorrectlyNamed($testClassName)
     {
-        $this->assertRegExp('/^provide[A-Z]\S+Cases$/', $dataProviderMethodName, sprintf(
-            'Data provider in "%s" with name "%s" is not correctly named.',
-            $testClassName,
-            $dataProviderMethodName
-        ));
+        $dataProviderMethodNames = $this->getDataProviderMethodNames($testClassName);
+
+        if (empty($dataProviderMethodNames)) {
+            $this->addToAssertionCount(1); // no data providers to test, all good!
+        }
+
+        foreach ($dataProviderMethodNames as $dataProviderMethodName) {
+            $this->assertRegExp('/^provide[A-Z]\S+Cases$/', $dataProviderMethodName, sprintf(
+                'Data provider in "%s" with name "%s" is not correctly named.',
+                $testClassName,
+                $dataProviderMethodName
+            ));
+        }
     }
 
     /**
@@ -350,52 +357,10 @@ final class ProjectCodeTest extends TestCase
         );
     }
 
-    public function provideDataProviderMethodNameCases()
-    {
-        if (extension_loaded('xdebug') && false === getenv('CI')) {
-            $this->markTestSkipped('Data provider too slow when Xdebug is loaded.');
-        }
-
-        $data = [];
-
-        $testClassNames = $this->getTestClasses();
-
-        foreach ($testClassNames as $testClassName) {
-            $dataProviderMethodNames = [];
-            $tokens = Tokens::fromCode(file_get_contents(
-                str_replace('\\', DIRECTORY_SEPARATOR, preg_replace('#^PhpCsFixer\\\Tests#', 'tests', $testClassName)).'.php'
-            ));
-
-            foreach ($tokens as $token) {
-                if ($token->isGivenKind(T_DOC_COMMENT)) {
-                    $docBlock = new DocBlock($token->getContent());
-                    $dataProviderAnnotations = $docBlock->getAnnotationsOfType('dataProvider');
-
-                    foreach ($dataProviderAnnotations as $dataProviderAnnotation) {
-                        if (1 === preg_match('/@dataProvider\s+(?P<methodName>\w+)/', $dataProviderAnnotation->getContent(), $matches)) {
-                            $dataProviderMethodNames[] = $matches['methodName'];
-                        }
-                    }
-                }
-            }
-
-            $dataProviderMethodNames = array_unique($dataProviderMethodNames);
-
-            foreach ($dataProviderMethodNames as $dataProviderMethodName) {
-                $data[] = [
-                    $testClassName,
-                    $dataProviderMethodName,
-                ];
-            }
-        }
-
-        return $data;
-    }
-
     public function provideClassesWherePregFunctionsAreForbiddenCases()
     {
-        if (extension_loaded('xdebug') && false === getenv('CI')) {
-            $this->markTestSkipped('Test too slow when Xdebug is loaded.');
+        if ((extension_loaded('xdebug') || 'phpdbg' === PHP_SAPI) && false === getenv('CI')) {
+            $this->markTestSkipped('Data provider too slow when Xdebug is loaded or running under phpdbg.');
         }
 
         return array_map(
@@ -411,6 +376,29 @@ final class ProjectCodeTest extends TestCase
         );
     }
 
+    private function getDataProviderMethodNames($testClassName)
+    {
+        $dataProviderMethodNames = [];
+        $tokens = Tokens::fromCode(file_get_contents(
+            str_replace('\\', DIRECTORY_SEPARATOR, preg_replace('#^PhpCsFixer\\\Tests#', 'tests', $testClassName)).'.php'
+        ));
+
+        foreach ($tokens as $token) {
+            if ($token->isGivenKind(T_DOC_COMMENT)) {
+                $docBlock = new DocBlock($token->getContent());
+                $dataProviderAnnotations = $docBlock->getAnnotationsOfType('dataProvider');
+
+                foreach ($dataProviderAnnotations as $dataProviderAnnotation) {
+                    if (1 === preg_match('/@dataProvider\s+(?P<methodName>\w+)/', $dataProviderAnnotation->getContent(), $matches)) {
+                        $dataProviderMethodNames[] = $matches['methodName'];
+                    }
+                }
+            }
+        }
+
+        return array_unique($dataProviderMethodNames);
+    }
+
     private function getSrcClasses()
     {
         static $classes;

+ 252 - 0
tests/Fixer/FunctionNotation/NativeFunctionInvocationFixerTest.php

@@ -16,6 +16,7 @@ use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
 
 /**
  * @author Andreas Möller <am@localheinz.com>
+ * @author SpacePossum
  *
  * @internal
  *
@@ -260,4 +261,255 @@ class Foo
             ],
         ];
     }
+
+    /**
+     * @dataProvider provideFixWithNamespaceConfigurationCases
+     *
+     * @param string      $expected
+     * @param null|string $input
+     * @param string      $scope
+     */
+    public function testFixWithNamespaceConfiguration($expected, $input = null, $scope = 'namespaced')
+    {
+        $this->fixer->configure(['scope' => $scope]);
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithNamespaceConfigurationCases()
+    {
+        return [
+            [
+                '<?php echo count([1]);',
+            ],
+            [
+                '<?php
+namespace space1 { ?>
+<?php echo \count([2]) ?>
+<?php }namespace {echo count([1]);}
+',
+                '<?php
+namespace space1 { ?>
+<?php echo count([2]) ?>
+<?php }namespace {echo count([1]);}
+',
+            ],
+            [
+                '<?php
+namespace Bar {
+    echo \strtoLOWER("in 1");
+}
+
+namespace {
+    echo strtolower("out 1");
+}
+
+namespace {
+    echo strtolower("out 2");
+}
+
+namespace Bar{
+    echo \strtolower("in 2");
+}
+
+namespace {
+    echo strtolower("out 3");
+}
+',
+                '<?php
+namespace Bar {
+    echo strtoLOWER("in 1");
+}
+
+namespace {
+    echo strtolower("out 1");
+}
+
+namespace {
+    echo strtolower("out 2");
+}
+
+namespace Bar{
+    echo strtolower("in 2");
+}
+
+namespace {
+    echo strtolower("out 3");
+}
+',
+            ],
+            [
+                '<?php
+namespace space11 ?>
+
+    <?php
+echo \strtolower(__NAMESPACE__);
+namespace space2;
+echo \strtolower(__NAMESPACE__);
+',
+                '<?php
+namespace space11 ?>
+
+    <?php
+echo strtolower(__NAMESPACE__);
+namespace space2;
+echo strtolower(__NAMESPACE__);
+',
+            ],
+            [
+                '<?php namespace PhpCsFixer\Tests\Fixer\Casing;\count([1]);',
+                '<?php namespace PhpCsFixer\Tests\Fixer\Casing;count([1]);',
+            ],
+            [
+                '<?php
+namespace Space12;
+
+echo \count([1]);
+
+namespace Space2;
+
+echo \count([1]);
+?>
+',
+                '<?php
+namespace Space12;
+
+echo count([1]);
+
+namespace Space2;
+
+echo count([1]);
+?>
+',
+            ],
+            [
+                '<?php namespace {echo strtolower("out 2");}',
+            ],
+            [
+                '<?php
+namespace space13 {
+    echo \strtolower("in 1");
+}
+
+namespace space2 {
+    echo \strtolower("in 2");
+}
+
+namespace { // global
+    echo strtolower("global 1");
+}
+',
+                '<?php
+namespace space13 {
+    echo strtolower("in 1");
+}
+
+namespace space2 {
+    echo strtolower("in 2");
+}
+
+namespace { // global
+    echo strtolower("global 1");
+}
+',
+            ],
+            [
+                '<?php
+namespace space1 {
+    echo \count([1]);
+}
+namespace {
+    echo \count([1]);
+}
+',
+                '<?php
+namespace space1 {
+    echo count([1]);
+}
+namespace {
+    echo count([1]);
+}
+',
+                'all',
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider provideFixWithConfiguredIncludeCases
+     *
+     * @param string      $expected
+     * @param null|string $input
+     * @param array       $configuration
+     */
+    public function testFixWithConfiguredInclude($expected, $input = null, array $configuration = [])
+    {
+        $this->fixer->configure($configuration);
+
+        $this->doTest($expected, $input);
+    }
+
+    /**
+     * @return array
+     */
+    public function provideFixWithConfiguredIncludeCases()
+    {
+        return [
+            'include set + 1, exclude 1' => [
+                '<?php
+                    echo \count([1]);
+                    \some_other($a, 3);
+                    echo strlen($a);
+                    not_me();
+                ',
+                '<?php
+                    echo count([1]);
+                    some_other($a, 3);
+                    echo strlen($a);
+                    not_me();
+                ',
+                [
+                    'include' => ['@internal', 'some_other'],
+                    'exclude' => ['strlen'],
+                ],
+            ],
+            'include @all' => [
+                '<?php
+                    echo \count([1]);
+                    \some_other($a, 3);
+                    echo \strlen($a);
+                    \me_as_well();
+                ',
+                '<?php
+                    echo count([1]);
+                    some_other($a, 3);
+                    echo strlen($a);
+                    me_as_well();
+                ',
+                [
+                    'include' => ['@all'],
+                ],
+            ],
+            'include @compiler_optimized' => [
+                '<?php
+                    // do not fix
+                    $a = strrev($a);
+                    $a .= str_repeat($a, 4);
+                    // fix
+                    $c = \get_class($d);
+                    $e = \intval($f);
+                ',
+                '<?php
+                    // do not fix
+                    $a = strrev($a);
+                    $a .= str_repeat($a, 4);
+                    // fix
+                    $c = get_class($d);
+                    $e = intval($f);
+                ',
+                [
+                    'include' => ['@compiler_optimized'],
+                ],
+            ],
+        ];
+    }
 }

+ 1 - 1
tests/Smoke/StdinTest.php

@@ -45,7 +45,7 @@ final class StdinTest extends TestCase
                     $fileResult->getError()
                 ),
                 'output' => str_ireplace(
-                    str_replace('/', DIRECTORY_SEPARATOR, 'PHP-CS-Fixer/tests/Fixtures/Integration/set/@PSR2.test-in.php'),
+                    str_replace('/', DIRECTORY_SEPARATOR, basename(realpath($cwd)).'/'.$inputFile),
                     'php://stdin',
                     $this->unifyFooter($fileResult->getOutput())
                 ),