Repository: symfony/ux
Pull request #2267 (2024-10-12 10:31:35)

[Autocomplete] Remove symfony/string dependency
* remove Symfony\String dependency (used only to get form short name)
* fix Attribute methods missing internal tag


Patch:
diff --git a/src/Autocomplete/composer.json b/src/Autocomplete/composer.json
index 94b94e39..8698cd4d 100644
--- a/src/Autocomplete/composer.json
+++ b/src/Autocomplete/composer.json
@@ -29,8 +29,7 @@
         "symfony/deprecation-contracts": "^2.5|^3",
         "symfony/http-foundation": "^6.3|^7.0",
         "symfony/http-kernel": "^6.3|^7.0",
-        "symfony/property-access": "^6.3|^7.0",
-        "symfony/string": "^6.3|^7.0"
+        "symfony/property-access": "^6.3|^7.0"
     },
     "require-dev": {
         "doctrine/collections": "^1.6.8|^2.0",
diff --git a/src/Autocomplete/src/AutocompleteBundle.php b/src/Autocomplete/src/AutocompleteBundle.php
index a8add0a4..adb471a1 100644
--- a/src/Autocomplete/src/AutocompleteBundle.php
+++ b/src/Autocomplete/src/AutocompleteBundle.php
@@ -20,7 +20,7 @@ use Symfony\UX\Autocomplete\DependencyInjection\AutocompleteFormTypePass;
  */
 final class AutocompleteBundle extends Bundle
 {
-    public function build(ContainerBuilder $container): void
+    public function build(ContainerBuilder $container)
     {
         $container->addCompilerPass(new AutocompleteFormTypePass());
     }
diff --git a/src/Autocomplete/src/AutocompleterRegistry.php b/src/Autocomplete/src/AutocompleterRegistry.php
index c3c65865..31f81c21 100644
--- a/src/Autocomplete/src/AutocompleterRegistry.php
+++ b/src/Autocomplete/src/AutocompleterRegistry.php
@@ -28,9 +28,6 @@ final class AutocompleterRegistry
         return $this->autocompletersLocator->has($alias) ? $this->autocompletersLocator->get($alias) : null;
     }
 
-    /**
-     * @return list<string>
-     */
     public function getAutocompleterNames(): array
     {
         return array_keys($this->autocompletersLocator->getProvidedServices());
diff --git a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php
index 3dec572a..c44103c8 100644
--- a/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php
+++ b/src/Autocomplete/src/DependencyInjection/AutocompleteExtension.php
@@ -40,7 +40,7 @@ use function Symfony\Component\DependencyInjection\Loader\Configurator\abstract_
  */
 final class AutocompleteExtension extends Extension implements PrependExtensionInterface
 {
-    public function prepend(ContainerBuilder $container): void
+    public function prepend(ContainerBuilder $container)
     {
         $bundles = $container->getParameter('kernel.bundles');
 
@@ -61,7 +61,7 @@ final class AutocompleteExtension extends Extension implements PrependExtensionI
         }
     }
 
-    public function load(array $configs, ContainerBuilder $container): void
+    public function load(array $configs, ContainerBuilder $container)
     {
         $this->registerBasicServices($container);
         if (ContainerBuilder::willBeAvailable('symfony/form', Form::class, ['symfony/framework-bundle'])) {
diff --git a/src/Autocomplete/src/DependencyInjection/AutocompleteFormTypePass.php b/src/Autocomplete/src/DependencyInjection/AutocompleteFormTypePass.php
index fd6cb19d..b07c23fc 100644
--- a/src/Autocomplete/src/DependencyInjection/AutocompleteFormTypePass.php
+++ b/src/Autocomplete/src/DependencyInjection/AutocompleteFormTypePass.php
@@ -29,13 +29,13 @@ class AutocompleteFormTypePass implements CompilerPassInterface
     /** @var string Tag applied to EntityAutocompleterInterface classes */
     public const ENTITY_AUTOCOMPLETER_TAG = 'ux.entity_autocompleter';
 
-    public function process(ContainerBuilder $container): void
+    public function process(ContainerBuilder $container)
     {
         $this->processEntityAutocompleteFieldTag($container);
         $this->processEntityAutocompleterTag($container);
     }
 
-    private function processEntityAutocompleteFieldTag(ContainerBuilder $container): void
+    private function processEntityAutocompleteFieldTag(ContainerBuilder $container)
     {
         foreach ($container->findTaggedServiceIds(self::ENTITY_AUTOCOMPLETE_FIELD_TAG, true) as $serviceId => $tag) {
             $serviceDefinition = $container->getDefinition($serviceId);
@@ -68,7 +68,7 @@ class AutocompleteFormTypePass implements CompilerPassInterface
         return $attribute->getAlias() ?: AsEntityAutocompleteField::shortName($class);
     }
 
-    private function processEntityAutocompleterTag(ContainerBuilder $container): void
+    private function processEntityAutocompleterTag(ContainerBuilder $container)
     {
         $servicesMap = [];
         foreach ($container->findTaggedServiceIds(self::ENTITY_AUTOCOMPLETER_TAG, true) as $serviceId => $tag) {
diff --git a/src/Autocomplete/src/Form/AsEntityAutocompleteField.php b/src/Autocomplete/src/Form/AsEntityAutocompleteField.php
index d707c4b7..94119fba 100644
--- a/src/Autocomplete/src/Form/AsEntityAutocompleteField.php
+++ b/src/Autocomplete/src/Form/AsEntityAutocompleteField.php
@@ -11,8 +11,6 @@
 
 namespace Symfony\UX\Autocomplete\Form;
 
-use Symfony\Component\String\UnicodeString;
-
 /**
  * All form types that want to expose autocomplete functionality should have this.
  *
@@ -37,13 +35,25 @@ class AsEntityAutocompleteField
         return $this->route;
     }
 
+    /**
+     * @internal
+     *
+     * @param class-string $class
+     */
     public static function shortName(string $class): string
     {
-        $string = new UnicodeString($class);
+        if ($pos = (int) strrpos($class, '\\')) {
+            $class = substr($class, $pos + 1);
+        }
 
-        return $string->afterLast('\\')->snake()->toString();
+        return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $class));
     }
 
+    /**
+     * @internal
+     *
+     * @param class-string $class
+     */
     public static function getInstance(string $class): ?self
     {
         $reflectionClass = new \ReflectionClass($class);
diff --git a/src/Autocomplete/src/Maker/MakeAutocompleteField.php b/src/Autocomplete/src/Maker/MakeAutocompleteField.php
index 1588b2cb..41ef37a4 100644
--- a/src/Autocomplete/src/Maker/MakeAutocompleteField.php
+++ b/src/Autocomplete/src/Maker/MakeAutocompleteField.php
@@ -53,7 +53,7 @@ class MakeAutocompleteField extends AbstractMaker
         return 'Generates an Ajax-autocomplete form field class for symfony/ux-autocomplete.';
     }
 
-    public function configureCommand(Command $command, InputConfiguration $inputConfig): void
+    public function configureCommand(Command $command, InputConfiguration $inputConfig)
     {
         $command
              ->setHelp(<<<EOF
@@ -66,12 +66,12 @@ EOF)
         ;
     }
 
-    public function configureDependencies(DependencyBuilder $dependencies): void
+    public function configureDependencies(DependencyBuilder $dependencies)
     {
         $dependencies->addClassDependency(FormInterface::class, 'symfony/form');
     }
 
-    public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
+    public function interact(InputInterface $input, ConsoleStyle $io, Command $command)
     {
         if (null === $this->doctrineHelper) {
             throw new \LogicException('Somehow the DoctrineHelper service is missing from MakerBundle.');
@@ -94,7 +94,7 @@ EOF)
         );
     }
 
-    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
+    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
     {
         if (null === $this->doctrineHelper) {
             throw new \LogicException('Somehow the DoctrineHelper service is missing from MakerBundle.');


Test patch:
diff --git a/src/Autocomplete/tests/Unit/Form/AsEntityAutocompleteFieldTest.php b/src/Autocomplete/tests/Unit/Form/AsEntityAutocompleteFieldTest.php
new file mode 100644
index 00000000..c7fabeaf
--- /dev/null
+++ b/src/Autocomplete/tests/Unit/Form/AsEntityAutocompleteFieldTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\UX\Autocomplete\Tests\Unit\Form;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\UX\Autocomplete\Form\AsEntityAutocompleteField;
+use Symfony\UX\Autocomplete\Tests\Fixtures\Form\ProductType;
+
+class AsEntityAutocompleteFieldTest extends TestCase
+{
+    /**
+     * @dataProvider provideClassNames
+     */
+    public function testShortName(string $shortName, string $className): void
+    {
+        $this->assertEquals($shortName, AsEntityAutocompleteField::shortName($className));
+    }
+
+    /**
+     * @return iterable<{string, string}>
+     */
+    public static function provideClassNames(): iterable
+    {
+        yield from [
+            ['as_entity_autocomplete_field', AsEntityAutocompleteField::class],
+            ['product_type', ProductType::class],
+            ['bar', 'Bar'],
+            ['foo_bar', 'FooBar'],
+            ['foo_bar', 'Foo\FooBar'],
+            ['foo_bar', 'Foo\Bar\FooBar'],
+        ];
+    }
+}