From cb23986d9309a10eaa284242f2169723af4e4a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 13 Feb 2022 16:17:47 +0100 Subject: [PATCH] Add support for regexes for excluded symbols (#660) --- docs/configuration.md | 6 +- docs/further-reading.md | 4 - fixtures/set020-infection/scoper.inc.php | 3 - scoper.inc.php | 3 - src/Configuration/SymbolsConfiguration.php | 80 ++++++------------- .../SymbolsConfigurationFactory.php | 53 ++++++------ src/Symbol/EnrichedReflectorFactory.php | 8 +- src/Symbol/Reflector.php | 19 ++--- src/Symbol/SymbolRegistry.php | 15 ++-- src/scoper.inc.php.tpl | 2 - .../AddPrefixCommandIntegrationTest.php | 2 +- tests/Scoper/PhpScoperSpecTest.php | 14 ++-- tests/Symbol/ConstantSymbolRegistryTest.php | 8 +- tests/Symbol/EnrichedReflectorFactoryTest.php | 14 ++-- .../Reflector/UserSymbolsReflectorTest.php | 60 +++++++++----- tests/Symbol/SymbolRegistryTest.php | 8 +- tests/functions.php | 2 +- 17 files changed, 136 insertions(+), 165 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 1d20c4ca..ec6bc670 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -186,9 +186,9 @@ Symbols can be marked as excluded as follows: return [ 'exclude-namespaces' => [ 'WP', '/regex/' ], - 'exclude-classes' => ['Stringeable'], - 'exclude-functions' => ['str_contains'], - 'exclude-constants' => ['PHP_EOL'], + 'exclude-classes' => ['Stringeable', '/regex/'], + 'exclude-functions' => ['str_contains', '/regex/'], + 'exclude-constants' => ['PHP_EOL', '/regex/'], ]; ``` diff --git a/docs/further-reading.md b/docs/further-reading.md index 63db6346..7d3e9a46 100644 --- a/docs/further-reading.md +++ b/docs/further-reading.md @@ -47,11 +47,7 @@ return [ ], 'exclude-constants' => [ // Symfony global constants - // TODO: switch to the following regex once regexes are supported here - // https://github.com/humbug/php-scoper/issues/634 '/^SYMFONY\_[\p{L}_]+$/', - // Meanwhile: - 'SYMFONY_GRAPHEME_CLUSTER_RX', ], 'exclude-files' => [ ...$polyfillsBootstraps, diff --git a/fixtures/set020-infection/scoper.inc.php b/fixtures/set020-infection/scoper.inc.php index d102af88..12d4d062 100644 --- a/fixtures/set020-infection/scoper.inc.php +++ b/fixtures/set020-infection/scoper.inc.php @@ -33,10 +33,7 @@ ], 'exclude-constants' => [ // Symfony global constants - // TODO: switch to the following regex once regexes are supported here - // https://github.com/humbug/php-scoper/issues/634 '/^SYMFONY\_[\p{L}_]+$/', - 'SYMFONY_GRAPHEME_CLUSTER_RX', ], 'exclude-files' => [ ...$polyfillsBootstraps, diff --git a/scoper.inc.php b/scoper.inc.php index fa87ff71..f8cb1884 100644 --- a/scoper.inc.php +++ b/scoper.inc.php @@ -77,10 +77,7 @@ ], 'exclude-constants' => [ // Symfony global constants - // TODO: switch to the following regex once regexes are supported here - // https://github.com/humbug/php-scoper/issues/634 '/^SYMFONY\_[\p{L}_]+$/', - 'SYMFONY_GRAPHEME_CLUSTER_RX', ], 'exclude-files' => [ ...$jetBrainStubs, diff --git a/src/Configuration/SymbolsConfiguration.php b/src/Configuration/SymbolsConfiguration.php index bbb7be67..f0eef1ac 100644 --- a/src/Configuration/SymbolsConfiguration.php +++ b/src/Configuration/SymbolsConfiguration.php @@ -30,26 +30,10 @@ final class SymbolsConfiguration private SymbolRegistry $exposedFunctions; private SymbolRegistry $exposedConstants; - /** - * @var string[] - */ - private array $excludedClassNames; - - /** - * @var string[] - */ - private array $excludedFunctionNames; - - /** - * @var string[] - */ - private array $excludedConstantNames; - - /** - * @param string[] $excludedClassNames - * @param string[] $excludedFunctionNames - * @param string[] $excludedConstantNames - */ + private SymbolRegistry $excludedClasses; + private SymbolRegistry $excludedFunctions; + private SymbolRegistry $excludedConstants; + public static function create( bool $exposeGlobalConstants = false, bool $exposeGlobalClasses = false, @@ -58,12 +42,12 @@ public static function create( // Does not contain the list of excluded symbols which go to the // Reflector (which has no notion of namespaces) ?NamespaceRegistry $exposedNamespaces = null, - SymbolRegistry $exposedClasses = null, - SymbolRegistry $exposedFunctions = null, - SymbolRegistry $exposedConstants = null, - array $excludedClassNames = [], - array $excludedFunctionNames = [], - array $excludedConstantNames = [] + ?SymbolRegistry $exposedClasses = null, + ?SymbolRegistry $exposedFunctions = null, + ?SymbolRegistry $exposedConstants = null, + ?SymbolRegistry $excludedClasses = null, + ?SymbolRegistry $excludedFunctions = null, + ?SymbolRegistry $excludedConstants = null ): self { return new self( $exposeGlobalConstants, @@ -74,17 +58,12 @@ public static function create( $exposedClasses ?? SymbolRegistry::create(), $exposedFunctions ?? SymbolRegistry::create(), $exposedConstants ?? SymbolRegistry::createForConstants(), - $excludedClassNames, - $excludedFunctionNames, - $excludedConstantNames, + $excludedClasses ?? SymbolRegistry::create(), + $excludedFunctions ?? SymbolRegistry::create(), + $excludedConstants ?? SymbolRegistry::createForConstants(), ); } - /** - * @param string[] $excludedClassNames - * @param string[] $excludedFunctionNames - * @param string[] $excludedConstantNames - */ private function __construct( bool $exposeGlobalConstants, bool $exposeGlobalClasses, @@ -94,9 +73,9 @@ private function __construct( SymbolRegistry $exposedClasses, SymbolRegistry $exposedFunctions, SymbolRegistry $exposedConstants, - array $excludedClassNames, - array $excludedFunctionNames, - array $excludedConstantNames + SymbolRegistry $excludedClasses, + SymbolRegistry $excludedFunctions, + SymbolRegistry $excludedConstants ) { $this->exposeGlobalConstants = $exposeGlobalConstants; $this->exposeGlobalClasses = $exposeGlobalClasses; @@ -106,9 +85,9 @@ private function __construct( $this->exposedClasses = $exposedClasses; $this->exposedFunctions = $exposedFunctions; $this->exposedConstants = $exposedConstants; - $this->excludedClassNames = $excludedClassNames; - $this->excludedFunctionNames = $excludedFunctionNames; - $this->excludedConstantNames = $excludedConstantNames; + $this->excludedClasses = $excludedClasses; + $this->excludedFunctions = $excludedFunctions; + $this->excludedConstants = $excludedConstants; } public function shouldExposeGlobalConstants(): bool @@ -151,27 +130,18 @@ public function getExposedConstants(): SymbolRegistry return $this->exposedConstants; } - /** - * @return string[] - */ - public function getExcludedClassNames(): array + public function getExcludedClasses(): SymbolRegistry { - return $this->excludedClassNames; + return $this->excludedClasses; } - /** - * @return string[] - */ - public function getExcludedFunctionNames(): array + public function getExcludedFunctions(): SymbolRegistry { - return $this->excludedFunctionNames; + return $this->excludedFunctions; } - /** - * @return string[] - */ - public function getExcludedConstantNames(): array + public function getExcludedConstants(): SymbolRegistry { - return $this->excludedConstantNames; + return $this->excludedConstants; } } diff --git a/src/Configuration/SymbolsConfigurationFactory.php b/src/Configuration/SymbolsConfigurationFactory.php index 70a97d06..b96c143d 100644 --- a/src/Configuration/SymbolsConfigurationFactory.php +++ b/src/Configuration/SymbolsConfigurationFactory.php @@ -93,6 +93,27 @@ public function createSymbolsConfiguration(array $config): SymbolsConfiguration ConfigurationKeys::EXPOSE_CONSTANTS_SYMBOLS_KEYWORD, ); + $excludedClasses = SymbolRegistry::create( + ...$this->retrieveElements( + $config, + ConfigurationKeys::CLASSES_INTERNAL_SYMBOLS_KEYWORD, + ), + ); + + $excludedFunctions = SymbolRegistry::create( + ...$this->retrieveElements( + $config, + ConfigurationKeys::FUNCTIONS_INTERNAL_SYMBOLS_KEYWORD, + ), + ); + + $excludedConstants = SymbolRegistry::createForConstants( + ...$this->retrieveElements( + $config, + ConfigurationKeys::CONSTANTS_INTERNAL_SYMBOLS_KEYWORD, + ), + ); + return SymbolsConfiguration::create( $exposeGlobalConstants, $exposeGlobalClasses, @@ -135,7 +156,9 @@ public function createSymbolsConfiguration(array $config): SymbolsConfiguration $legacyExposedSymbolsPatterns, ), ), - ...self::retrieveAllExcludedSymbols($config), + $excludedClasses, + $excludedFunctions, + $excludedConstants, ); } @@ -253,18 +276,6 @@ private function retrieveElements(array $config, string $key): array ]; } - /** - * @return array{string[], string[], string[]} - */ - private static function retrieveAllExcludedSymbols(array $config): array - { - return [ - self::retrieveExcludedSymbols($config, ConfigurationKeys::CLASSES_INTERNAL_SYMBOLS_KEYWORD), - self::retrieveExcludedSymbols($config, ConfigurationKeys::FUNCTIONS_INTERNAL_SYMBOLS_KEYWORD), - self::retrieveExcludedSymbols($config, ConfigurationKeys::CONSTANTS_INTERNAL_SYMBOLS_KEYWORD), - ]; - } - /** * @deprecated * @@ -421,20 +432,4 @@ private static function lowerCaseConstantName(string $name): string return implode('\\', $parts); } - - /** - * @return string[] - */ - private static function retrieveExcludedSymbols(array $config, string $key): array - { - if (!array_key_exists($key, $config)) { - return []; - } - - $symbols = $config[$key]; - - self::assertIsArrayOfStrings($symbols, $key); - - return $symbols; - } } diff --git a/src/Symbol/EnrichedReflectorFactory.php b/src/Symbol/EnrichedReflectorFactory.php index 0948e3b3..51bee4c3 100644 --- a/src/Symbol/EnrichedReflectorFactory.php +++ b/src/Symbol/EnrichedReflectorFactory.php @@ -17,10 +17,10 @@ public function __construct(Reflector $reflector) public function create(SymbolsConfiguration $symbolsConfiguration): EnrichedReflector { - $configuredReflector = $this->reflector->withSymbols( - $symbolsConfiguration->getExcludedClassNames(), - $symbolsConfiguration->getExcludedFunctionNames(), - $symbolsConfiguration->getExcludedConstantNames(), + $configuredReflector = $this->reflector->withAdditionalSymbols( + $symbolsConfiguration->getExcludedClasses(), + $symbolsConfiguration->getExcludedFunctions(), + $symbolsConfiguration->getExcludedConstants(), ); return new EnrichedReflector( diff --git a/src/Symbol/Reflector.php b/src/Symbol/Reflector.php index b1f405bd..eebe1116 100644 --- a/src/Symbol/Reflector.php +++ b/src/Symbol/Reflector.php @@ -633,21 +633,16 @@ private function __construct( $this->constants = $constants; } - /** - * @param string[] $classNames - * @param string[] $functionNames - * @param string[] $constantNames - */ - public function withSymbols( - array $classNames, - array $functionNames, - array $constantNames + public function withAdditionalSymbols( + SymbolRegistry $classNames, + SymbolRegistry $functionNames, + SymbolRegistry $constantNames ): self { return new self( - $this->classes->withAdditionalSymbols($classNames), - $this->functions->withAdditionalSymbols($functionNames), - $this->constants->withAdditionalSymbols($constantNames), + $this->classes->merge($classNames), + $this->functions->merge($functionNames), + $this->constants->merge($constantNames), ); } diff --git a/src/Symbol/SymbolRegistry.php b/src/Symbol/SymbolRegistry.php index f15e3ca6..90bb4864 100644 --- a/src/Symbol/SymbolRegistry.php +++ b/src/Symbol/SymbolRegistry.php @@ -4,6 +4,7 @@ namespace Humbug\PhpScoper\Symbol; +use InvalidArgumentException; use function array_key_exists; use function array_keys; use function array_map; @@ -107,20 +108,20 @@ public function matches(string $symbol): bool return false; } - /** - * @param string[] $names - * @param string[] $regexes - */ - public function withAdditionalSymbols(array $names = [], array $regexes = []): self + public function merge(SymbolRegistry $registry): self { + if ($this->constants !== $registry->constants) { + throw new InvalidArgumentException('Cannot merge registries of different symbol types'); + } + $args = [ [ ...$this->getNames(), - ...$names, + ...$registry->getNames(), ], [ ...$this->getRegexes(), - ...$regexes, + ...$registry->getRegexes(), ], ]; diff --git a/src/scoper.inc.php.tpl b/src/scoper.inc.php.tpl index 3b10c2d4..07663cbe 100644 --- a/src/scoper.inc.php.tpl +++ b/src/scoper.inc.php.tpl @@ -76,8 +76,6 @@ return [ // '~^$~', // The root namespace only // '', // Any namespace ], - // Warning: regexes are not supported there (yet) - // See https://github.com/humbug/php-scoper/issues/634 'exclude-classes' => [ // 'ReflectionClassConstant', ], diff --git a/tests/Console/Command/AddPrefixCommandIntegrationTest.php b/tests/Console/Command/AddPrefixCommandIntegrationTest.php index 1d18b36d..84bb13b9 100644 --- a/tests/Console/Command/AddPrefixCommandIntegrationTest.php +++ b/tests/Console/Command/AddPrefixCommandIntegrationTest.php @@ -29,8 +29,8 @@ use function Safe\file_put_contents; use function Safe\preg_replace; use function Safe\realpath; -use function str_replace; use function Safe\sprintf; +use function str_replace; use const DIRECTORY_SEPARATOR; /** diff --git a/tests/Scoper/PhpScoperSpecTest.php b/tests/Scoper/PhpScoperSpecTest.php index 0b8dabe2..eda0ae56 100644 --- a/tests/Scoper/PhpScoperSpecTest.php +++ b/tests/Scoper/PhpScoperSpecTest.php @@ -280,10 +280,10 @@ private static function createScoper( $reflector = Reflector ::createWithPhpStormStubs() - ->withSymbols( - $symbolsConfiguration->getExcludedClassNames(), - $symbolsConfiguration->getExcludedFunctionNames(), - $symbolsConfiguration->getExcludedConstantNames(), + ->withAdditionalSymbols( + $symbolsConfiguration->getExcludedClasses(), + $symbolsConfiguration->getExcludedFunctions(), + $symbolsConfiguration->getExcludedConstants(), ); $enrichedReflector = new EnrichedReflector( @@ -451,9 +451,9 @@ private static function createSpecMessage( $formattedFunctionsToExpose = self::formatSymbolRegistry($symbolsConfiguration->getExposedFunctions()); $formattedConstantsToExpose = self::formatSymbolRegistry($symbolsConfiguration->getExposedConstants()); - $formattedInternalClasses = self::formatSimpleList($symbolsConfiguration->getExcludedClassNames()); - $formattedInternalFunctions = self::formatSimpleList($symbolsConfiguration->getExcludedFunctionNames()); - $formattedInternalConstants = self::formatSimpleList($symbolsConfiguration->getExcludedConstantNames()); + $formattedInternalClasses = self::formatSymbolRegistry($symbolsConfiguration->getExcludedClasses()); + $formattedInternalFunctions = self::formatSymbolRegistry($symbolsConfiguration->getExcludedFunctions()); + $formattedInternalConstants = self::formatSymbolRegistry($symbolsConfiguration->getExcludedConstants()); $formattedExpectedRegisteredClasses = self::formatTupleList($expectedRegisteredClasses); $formattedExpectedRegisteredFunctions = self::formatTupleList($expectedRegisteredFunctions); diff --git a/tests/Symbol/ConstantSymbolRegistryTest.php b/tests/Symbol/ConstantSymbolRegistryTest.php index 4998f98b..70a09389 100644 --- a/tests/Symbol/ConstantSymbolRegistryTest.php +++ b/tests/Symbol/ConstantSymbolRegistryTest.php @@ -89,9 +89,11 @@ public function test_it_can_create_an_augmented_instance(): void ['/^Acme\\\\Foo$/'], ); - $augmentedRegistry = $registry->withAdditionalSymbols( - ['Acme\Bar'], - ['/^Acme\\\\Bar/'], + $augmentedRegistry = $registry->merge( + SymbolRegistry::createForConstants( + ['Acme\Bar'], + ['/^Acme\\\\Bar/'], + ), ); SymbolRegistryAssertions::assertStateIs( diff --git a/tests/Symbol/EnrichedReflectorFactoryTest.php b/tests/Symbol/EnrichedReflectorFactoryTest.php index fdb9385d..01878014 100644 --- a/tests/Symbol/EnrichedReflectorFactoryTest.php +++ b/tests/Symbol/EnrichedReflectorFactoryTest.php @@ -24,18 +24,18 @@ public function test_it_can_create_an_enriched_reflector(): void null, null, null, - ['Acme\Foo'], - ['Acme\main'], - ['Acme\BAR'], + SymbolRegistry::create(['Acme\Foo']), + SymbolRegistry::create(['Acme\main']), + SymbolRegistry::createForConstants(['Acme\BAR']), ); $factory = new EnrichedReflectorFactory(Reflector::createEmpty()); $expected = new EnrichedReflector( - Reflector::createEmpty()->withSymbols( - ['Acme\Foo'], - ['Acme\main'], - ['Acme\BAR'], + Reflector::createEmpty()->withAdditionalSymbols( + SymbolRegistry::create(['Acme\Foo']), + SymbolRegistry::create(['Acme\main']), + SymbolRegistry::createForConstants(['Acme\BAR']), ), $symbolsConfiguration, ); diff --git a/tests/Symbol/Reflector/UserSymbolsReflectorTest.php b/tests/Symbol/Reflector/UserSymbolsReflectorTest.php index 78cdda64..5694e079 100644 --- a/tests/Symbol/Reflector/UserSymbolsReflectorTest.php +++ b/tests/Symbol/Reflector/UserSymbolsReflectorTest.php @@ -15,6 +15,7 @@ namespace Humbug\PhpScoper\Symbol\Reflector; use Humbug\PhpScoper\Symbol\Reflector; +use Humbug\PhpScoper\Symbol\SymbolRegistry; use PHPUnit\Framework\TestCase; /** @@ -26,26 +27,26 @@ class UserSymbolsReflectorTest extends TestCase * @dataProvider symbolsProvider */ public function test_it_can_be_enriched_with_arbitrary_symbols( - array $classNames, - array $functionNames, - array $constantNames + SymbolRegistry $classes, + SymbolRegistry $functions, + SymbolRegistry $constants ): void { - $reflector = Reflector::createEmpty()->withSymbols( - $classNames, - $functionNames, - $constantNames, + $reflector = Reflector::createEmpty()->withAdditionalSymbols( + $classes, + $functions, + $constants, ); - foreach ($classNames as $className) { + foreach ($classes->getNames() as $className) { self::assertTrue($reflector->isClassInternal($className)); } - foreach ($functionNames as $functionName) { + foreach ($functions->getNames() as $functionName) { self::assertTrue($reflector->isFunctionInternal($functionName)); } - foreach ($constantNames as $constantName) { + foreach ($constants->getNames() as $constantName) { self::assertTrue($reflector->isConstantInternal($constantName)); } } @@ -61,14 +62,22 @@ public function test_it_can_be_enriched_multiple_times(): void self::assertFalse($emptyReflector->isClassInternal($classA)); self::assertFalse($emptyReflector->isClassInternal($classB)); - $reflectorWithA = $emptyReflector->withSymbols([$classA], [], []); + $reflectorWithA = $emptyReflector->withAdditionalSymbols( + SymbolRegistry::create([$classA]), + SymbolRegistry::create(), + SymbolRegistry::createForConstants(), + ); self::assertFalse($emptyReflector->isClassInternal($classA)); self::assertFalse($emptyReflector->isClassInternal($classB)); self::assertTrue($reflectorWithA->isClassInternal($classA)); self::assertFalse($reflectorWithA->isClassInternal($classB)); - $reflectorWithAandB = $reflectorWithA->withSymbols([$classB], [], []); + $reflectorWithAandB = $reflectorWithA->withAdditionalSymbols( + SymbolRegistry::create([$classB]), + SymbolRegistry::create(), + SymbolRegistry::createForConstants(), + ); self::assertFalse($emptyReflector->isClassInternal($classA)); self::assertFalse($emptyReflector->isClassInternal($classB)); @@ -80,25 +89,34 @@ public function test_it_can_be_enriched_multiple_times(): void public static function symbolsProvider(): iterable { - $classNames = ['PHPUnit\Framework\TestCase', 'Symfony\Component\Finder\Finder']; - $functionNames = ['PHPUnit\main', 'Symfony\dump']; - $constantNames = ['PHPUnit\VERSION', 'Symfony\VERSION']; + $classNames = SymbolRegistry::create([ + 'PHPUnit\Framework\TestCase', + 'Symfony\Component\Finder\Finder', + ]); + $functionNames = SymbolRegistry::create([ + 'PHPUnit\main', + 'Symfony\dump', + ]); + $constantNames = SymbolRegistry::createForConstants([ + 'PHPUnit\VERSION', + 'Symfony\VERSION', + ]); yield 'classes' => [ $classNames, - [], - [], + SymbolRegistry::create(), + SymbolRegistry::createForConstants(), ]; yield 'functions' => [ - [], + SymbolRegistry::create(), $functionNames, - [], + SymbolRegistry::createForConstants(), ]; yield 'constants' => [ - [], - [], + SymbolRegistry::create(), + SymbolRegistry::create(), $constantNames, ]; diff --git a/tests/Symbol/SymbolRegistryTest.php b/tests/Symbol/SymbolRegistryTest.php index 93749854..e97b2a0e 100644 --- a/tests/Symbol/SymbolRegistryTest.php +++ b/tests/Symbol/SymbolRegistryTest.php @@ -89,9 +89,11 @@ public function test_it_can_create_an_augmented_instance(): void ['/^Acme\\\\Foo$/'], ); - $augmentedRegistry = $registry->withAdditionalSymbols( - ['Acme\Bar'], - ['/^Acme\\\\Bar/'], + $augmentedRegistry = $registry->merge( + SymbolRegistry::create( + ['Acme\Bar'], + ['/^Acme\\\\Bar/'], + ), ); SymbolRegistryAssertions::assertStateIs( diff --git a/tests/functions.php b/tests/functions.php index 7e4b6d2b..5bd733ad 100644 --- a/tests/functions.php +++ b/tests/functions.php @@ -17,8 +17,8 @@ use PhpParser\Parser; use RuntimeException; use Symfony\Component\Filesystem\Filesystem; -use function rand; use function mkdir as native_mkdir; +use function rand; use function Safe\realpath; use function Safe\sprintf; use function Safe\substr;