Repository: humbug/php-scoper (php)

Issue:
Add support for regexes for excluded symbols
At the moment `exclude-*` settings, at the exception of `exclude-namespaces`, do not support regexes.

Pull request description:
Add support for regexes for excluded symbols
Closes #634

Patch:
diff --git a/docs/configuration.md b/docs/configuration.md
index 1d20c4c..ec6bc67 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 63db634..7d3e9a4 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 d102af8..12d4d06 100644
--- a/fixtures/set020-infection/scoper.inc.php
+++ b/fixtures/set020-infection/scoper.inc.php
@@ -33,10 +33,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}_]+$/',
-        'SYMFONY_GRAPHEME_CLUSTER_RX',
     ],
     'exclude-files' => [
         ...$polyfillsBootstraps,
diff --git a/scoper.inc.php b/scoper.inc.php
index fa87ff7..f8cb188 100644
--- a/scoper.inc.php
+++ b/scoper.inc.php
@@ -77,10 +77,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}_]+$/',
-        'SYMFONY_GRAPHEME_CLUSTER_RX',
     ],
     'exclude-files' => [
         ...$jetBrainStubs,
diff --git a/src/Configuration/SymbolsConfiguration.php b/src/Configuration/SymbolsConfiguration.php
index bbb7be6..f0eef1a 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 @@ final class SymbolsConfiguration
         // 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 @@ final class SymbolsConfiguration
             $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 @@ final class SymbolsConfiguration
         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 @@ final class SymbolsConfiguration
         $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 @@ final class SymbolsConfiguration
         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 70a97d0..b96c143 100644
--- a/src/Configuration/SymbolsConfigurationFactory.php
+++ b/src/Configuration/SymbolsConfigurationFactory.php
@@ -93,6 +93,27 @@ final class SymbolsConfigurationFactory
             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 @@ final class SymbolsConfigurationFactory
                     $legacyExposedSymbolsPatterns,
                 ),
             ),
-            ...self::retrieveAllExcludedSymbols($config),
+            $excludedClasses,
+            $excludedFunctions,
+            $excludedConstants,
         );
     }
 
@@ -253,18 +276,6 @@ final class SymbolsConfigurationFactory
         ];
     }
 
-    /**
-     * @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 @@ final class SymbolsConfigurationFactory
 
         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 0948e3b..51bee4c 100644
--- a/src/Symbol/EnrichedReflectorFactory.php
+++ b/src/Symbol/EnrichedReflectorFactory.php
@@ -17,10 +17,10 @@ final class EnrichedReflectorFactory
 
     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 b1f405b..eebe111 100644
--- a/src/Symbol/Reflector.php
+++ b/src/Symbol/Reflector.php
@@ -633,21 +633,16 @@ final class Reflector
         $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 f15e3ca..90bb486 100644
--- a/src/Symbol/SymbolRegistry.php
+++ b/src/Symbol/SymbolRegistry.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Humbug\PhpScoper\Symbol;
 
+use InvalidArgumentException;
 use function array_key_exists;
 use function array_keys;
 use function array_map;
@@ -107,20 +108,20 @@ final class SymbolRegistry
         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 3b10c2d..07663cb 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',
     ],


Test patch:
diff --git a/tests/Console/Command/AddPrefixCommandIntegrationTest.php b/tests/Console/Command/AddPrefixCommandIntegrationTest.php
index 1d18b36..84bb13b 100644
--- a/tests/Console/Command/AddPrefixCommandIntegrationTest.php
+++ b/tests/Console/Command/AddPrefixCommandIntegrationTest.php
@@ -29,8 +29,8 @@ use function Safe\file_get_contents;
 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 0b8dabe..eda0ae5 100644
--- a/tests/Scoper/PhpScoperSpecTest.php
+++ b/tests/Scoper/PhpScoperSpecTest.php
@@ -280,10 +280,10 @@ class PhpScoperSpecTest extends TestCase
 
         $reflector = Reflector
             ::createWithPhpStormStubs()
-            ->withSymbols(
-                $symbolsConfiguration->getExcludedClassNames(),
-                $symbolsConfiguration->getExcludedFunctionNames(),
-                $symbolsConfiguration->getExcludedConstantNames(),
+            ->withAdditionalSymbols(
+                $symbolsConfiguration->getExcludedClasses(),
+                $symbolsConfiguration->getExcludedFunctions(),
+                $symbolsConfiguration->getExcludedConstants(),
             );
 
         $enrichedReflector = new EnrichedReflector(
@@ -451,9 +451,9 @@ class PhpScoperSpecTest extends TestCase
         $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 4998f98..70a0938 100644
--- a/tests/Symbol/ConstantSymbolRegistryTest.php
+++ b/tests/Symbol/ConstantSymbolRegistryTest.php
@@ -89,9 +89,11 @@ class ConstantSymbolRegistryTest extends TestCase
             ['/^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 fdb9385..0187801 100644
--- a/tests/Symbol/EnrichedReflectorFactoryTest.php
+++ b/tests/Symbol/EnrichedReflectorFactoryTest.php
@@ -24,18 +24,18 @@ final class EnrichedReflectorFactoryTest extends TestCase
             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 78cdda6..5694e07 100644
--- a/tests/Symbol/Reflector/UserSymbolsReflectorTest.php
+++ b/tests/Symbol/Reflector/UserSymbolsReflectorTest.php
@@ -15,6 +15,7 @@ declare(strict_types=1);
 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 @@ class UserSymbolsReflectorTest extends TestCase
         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 @@ class UserSymbolsReflectorTest extends TestCase
 
     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 9374985..e97b2a0 100644
--- a/tests/Symbol/SymbolRegistryTest.php
+++ b/tests/Symbol/SymbolRegistryTest.php
@@ -89,9 +89,11 @@ class SymbolRegistryTest extends TestCase
             ['/^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 7e4b6d2..5bd733a 100644
--- a/tests/functions.php
+++ b/tests/functions.php
@@ -17,8 +17,8 @@ namespace Humbug\PhpScoper;
 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;