Repository: phpactor/phpactor (php)

Issue:
Symofny: Container service not inferring types
in an SF KernelTestCase:

```
        $service = $this->getContainer()->get(RequestHandlerService::class);
        $out = $service->handle($content);
```

Pull request description:
Gh 2263
fixes #2263 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 731a4557..1c9f71b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,11 @@ Features:
   - Code action to suggest fixes for undefined variables (in case of typos) #2209
   - PHPUnit: code action for adding `setUp` / `tearDown` #2180 @mamazu
 
+Improvements:
+
+  - Symfony: show and consider non-public services by default (e.g. in tests it's
+    possible to retrieve non-public services) #2263
+
 Bug fixes:
 
   - Fix behavior when user cancels type selection on goto type #2270
diff --git a/doc/reference/configuration.rst b/doc/reference/configuration.rst
index 396f980e..d6a2e579 100644
--- a/doc/reference/configuration.rst
+++ b/doc/reference/configuration.rst
@@ -2022,6 +2022,19 @@ Enable/disable the Symfony completor - depends on Symfony extension being enable
 **Default**: ``true``
 
 
+.. _param_public_services_only:
+
+
+``public_services_only``
+""""""""""""""""""""""""
+
+
+Only consider public services when providing analysis for the service locator
+
+
+**Default**: ``false``
+
+
 .. _PHPUnitExtension:
 
 
diff --git a/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php b/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php
index 5ab3c77a..074751e9 100644
--- a/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php
+++ b/lib/Extension/Symfony/Adapter/Symfony/XmlSymfonyContainerInspector.php
@@ -17,7 +17,7 @@ class XmlSymfonyContainerInspector implements SymfonyContainerInspector
 
     private ?DOMXPath $cache = null;
 
-    public function __construct(private string $xmlPath)
+    public function __construct(private string $xmlPath, private bool $publicOnly = false)
     {
     }
 
@@ -121,7 +121,7 @@ class XmlSymfonyContainerInspector implements SymfonyContainerInspector
         $id = $serviceEl->getAttribute('id');
         $class = $serviceEl->getAttribute('class');
         $public = $serviceEl->getAttribute('public');
-        if ('true' !== $public) {
+        if (true === $this->publicOnly && 'true' !== $public) {
             return null;
         }
         if (empty($id) || empty($class)) {
diff --git a/lib/Extension/Symfony/SymfonyExtension.php b/lib/Extension/Symfony/SymfonyExtension.php
index 389d948d..7b23d6d7 100644
--- a/lib/Extension/Symfony/SymfonyExtension.php
+++ b/lib/Extension/Symfony/SymfonyExtension.php
@@ -12,24 +12,30 @@ use Phpactor\Extension\Symfony\Completor\SymfonyContainerCompletor;
 use Phpactor\Extension\Symfony\Model\SymfonyContainerInspector;
 use Phpactor\Extension\Symfony\WorseReflection\SymfonyContainerContextResolver;
 use Phpactor\Extension\WorseReflection\WorseReflectionExtension;
+use Phpactor\FilePathResolver\PathResolver;
 use Phpactor\MapResolver\Resolver;
+use Phpactor\WorseReflection\Reflector;
 
 class SymfonyExtension implements OptionalExtension
 {
     const XML_PATH = 'symfony.xml_path';
     const PARAM_COMPLETOR_ENABLED = 'completion_worse.completor.symfony.enabled';
     const PARAM_ENABLED = 'symfony.enabled';
+    public const PARAM_PUBLIC_SERVICES_ONLY = 'public_services_only';
 
     public function load(ContainerBuilder $container): void
     {
         $container->register(SymfonyContainerInspector::class, function (Container $container) {
-            $xmlPath = $container->get(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER)
-                ->resolve($container->getParameter(self::XML_PATH));
-            return new XmlSymfonyContainerInspector($xmlPath);
+            $xmlPath = $container->expect(FilePathResolverExtension::SERVICE_FILE_PATH_RESOLVER, PathResolver::class)
+                ->resolve($container->parameter(self::XML_PATH)->string());
+            return new XmlSymfonyContainerInspector(
+                $xmlPath,
+                $container->parameter(self::PARAM_PUBLIC_SERVICES_ONLY)->bool()
+            );
         });
         $container->register(SymfonyContainerCompletor::class, function (Container $container) {
             return new SymfonyContainerCompletor(
-                $container->get(WorseReflectionExtension::SERVICE_REFLECTOR),
+                $container->expect(WorseReflectionExtension::SERVICE_REFLECTOR, Reflector::class),
                 $container->get(SymfonyContainerInspector::class)
             );
         }, [
@@ -53,10 +59,12 @@ class SymfonyExtension implements OptionalExtension
         $schema->setDefaults([
             self::XML_PATH => '%project_root%/var/cache/dev/App_KernelDevDebugContainer.xml',
             self::PARAM_COMPLETOR_ENABLED => true,
+            self::PARAM_PUBLIC_SERVICES_ONLY => false,
         ]);
         $schema->setDescriptions([
             self::XML_PATH => 'Path to the Symfony container XML dump file',
             self::PARAM_COMPLETOR_ENABLED => 'Enable/disable the Symfony completor - depends on Symfony extension being enabled',
+            self::PARAM_PUBLIC_SERVICES_ONLY => 'Only consider public services when providing analysis for the service locator',
         ]);
     }
 
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index bce45949..49807325 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -5755,16 +5755,6 @@ parameters:
 			count: 1
 			path: lib/Extension/Symfony/Completor/SymfonyContainerCompletor.php
 
-		-
-			message: "#^Cannot call method resolve\\(\\) on mixed\\.$#"
-			count: 1
-			path: lib/Extension/Symfony/SymfonyExtension.php
-
-		-
-			message: "#^Parameter \\#1 \\$reflector of class Phpactor\\\\Extension\\\\Symfony\\\\Completor\\\\SymfonyContainerCompletor constructor expects Phpactor\\\\WorseReflection\\\\Reflector, mixed given\\.$#"
-			count: 1
-			path: lib/Extension/Symfony/SymfonyExtension.php
-
 		-
 			message: "#^Cannot access property \\$type on Phpactor\\\\Extension\\\\Symfony\\\\Model\\\\SymfonyContainerService\\|null\\.$#"
 			count: 2


Test patch:
diff --git a/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php b/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php
index 353ed723..2cfa1759 100644
--- a/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php
+++ b/lib/Extension/Symfony/Tests/Unit/Adapter/XmlSymfonyContainerInspectorTest.php
@@ -90,6 +90,32 @@ class XmlSymfonyContainerInspectorTest extends IntegrationTestCase
         );
     }
 
+    public function testRetrieveNonPublicServiceIfConfigured(): void
+    {
+        $this->workspace()->put(
+            'services.xml',
+            <<<'EOT'
+                <?xml version="1.0" encoding="utf-8"?>
+                <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
+                    <services>
+                        <service id="App\Component\Foo\Service\RequestHandlerService" class="App\Component\Foo\Service\RequestHandlerService" autowire="true" autoconfigure="true" public="true">
+                        </service>
+                    </services>
+                </container>
+                EOT
+        );
+        self::assertEquals(
+            new SymfonyContainerService(
+                'App\Component\Foo\Service\RequestHandlerService',
+                TypeFactory::class('App\Component\Foo\Service\RequestHandlerService')
+            ),
+            $this->inspect(
+                $this->workspace()->path('services.xml'),
+                publicOnly: false,
+            )->service('App\Component\Foo\Service\RequestHandlerService')
+        );
+    }
+
     public function testDefinitionWithNoAttributes(): void
     {
         $this->workspace()->put(
@@ -191,8 +217,8 @@ class XmlSymfonyContainerInspectorTest extends IntegrationTestCase
         self::assertNull($inspector->service('test'));
     }
 
-    private function inspect(string $xmlPath): XmlSymfonyContainerInspector
+    private function inspect(string $xmlPath, bool $publicOnly = true): XmlSymfonyContainerInspector
     {
-        return new XmlSymfonyContainerInspector($xmlPath);
+        return new XmlSymfonyContainerInspector($xmlPath, $publicOnly);
     }
 }