diff --git a/README.md b/README.md
index 275dfe2b..5e118e82 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@ vendor/bin/psalm-plugin enable psalm/plugin-symfony
- Detect `ContainerInterface::get()` result type. Works better if you [configure](#configuration) compiled container XML file.
- Detect return type of console arguments (`InputInterface::getArgument()`) and options (`InputInterface::getOption()`). Enforces
to use InputArgument and InputOption constants as a part of best practise.
+- Detects correct Doctrine repository class if entities are configured with annotations.
- Fixes `PossiblyInvalidArgument` for `Symfony\Component\HttpFoundation\Request::getContent`.
The plugin calculates real return type by checking the given argument and marks return type as either string or resource.
- Detect return type of `Symfony\Component\HttpFoundation\HeaderBag::get` (by checking third argument for < Symfony 4.4)
@@ -49,6 +50,17 @@ Default file for Symfony versions:
- Symfony 4: var/cache/dev/srcApp_KernelDevDebugContainer.xml
- Symfony 5: var/cache/dev/App_KernelDevDebugContainer.xml
+Multiple container files can be configured. In this case, first valid file is taken into account.
+If none of the given files is valid, configuration exception is thrown.
+Example:
+
+```xml
+
+ var/cache/dev/App_KernelDevDebugContainer.xml
+ var/cache/dev/App_KernelTestDebugContainer.xml
+
+```
+
### Credits
- Plugin created by [@seferov](https://github.com/seferov)
diff --git a/psalm.xml b/psalm.xml
index 34bee50c..70f87f4c 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -11,7 +11,6 @@
-
diff --git a/src/Plugin.php b/src/Plugin.php
index 6b3b42d3..477cd1f3 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -3,7 +3,6 @@
namespace Psalm\SymfonyPsalmPlugin;
use Doctrine\Common\Annotations\AnnotationRegistry;
-use Psalm\Exception\ConfigException;
use Psalm\Plugin\PluginEntryPointInterface;
use Psalm\Plugin\RegistrationInterface;
use Psalm\SymfonyPsalmPlugin\Handler\ClassHandler;
@@ -35,17 +34,12 @@ public function __invoke(RegistrationInterface $api, SimpleXMLElement $config =
}
if (isset($config->containerXml)) {
- $containerXmlPath = realpath((string) $config->containerXml);
- if (!$containerXmlPath) {
- throw new ConfigException(sprintf('Container XML file (%s) does not exits', $containerXmlPath));
- }
-
- ContainerHandler::init(new ContainerMeta($containerXmlPath));
+ ContainerHandler::init(new ContainerMeta((array) $config->containerXml));
}
$api->registerHooksFromClass(ContainerHandler::class);
- foreach (glob(__DIR__ . '/Stubs/*.stubphp') as $stubFilePath) {
+ foreach (glob(__DIR__.'/Stubs/*.stubphp') as $stubFilePath) {
$api->addStubFile($stubFilePath);
}
}
diff --git a/src/Symfony/ContainerMeta.php b/src/Symfony/ContainerMeta.php
index 62d5edb6..f2c7d081 100644
--- a/src/Symfony/ContainerMeta.php
+++ b/src/Symfony/ContainerMeta.php
@@ -18,36 +18,9 @@ class ContainerMeta
*/
private $classNames = [];
- public function __construct(string $containerXmlPath)
+ public function __construct(array $containerXmlPaths)
{
- if (!file_exists($containerXmlPath)) {
- throw new ConfigException('Container xml file not found at '.$containerXmlPath);
- }
-
- $xml = simplexml_load_file($containerXmlPath);
- if (!$xml->services instanceof \SimpleXMLElement) {
- throw new ConfigException('Not a valid container xml file');
- }
-
- /** @psalm-var \SimpleXMLElement $serviceXml */
- foreach ($xml->services->service as $serviceXml) {
- /** @psalm-var \SimpleXMLElement $serviceAttributes */
- $serviceAttributes = $serviceXml->attributes();
-
- $className = (string) $serviceAttributes->class;
-
- if ($className) {
- $this->classNames[] = $className;
- }
-
- $service = new Service((string) $serviceAttributes->id, $className);
- if (isset($serviceAttributes->alias)) {
- $service->setAlias((string) $serviceAttributes->alias);
- }
- $service->setIsPublic('false' !== (string) $serviceAttributes->public);
-
- $this->add($service);
- }
+ $this->init($containerXmlPaths);
}
public function get(string $id): ?Service
@@ -76,4 +49,44 @@ public function getClassNames(): array
{
return $this->classNames;
}
+
+ private function init(array $containerXmlPaths): void
+ {
+ /** @var string $containerXmlPath */
+ foreach ($containerXmlPaths as $containerXmlPath) {
+ $xmlPath = realpath((string) $containerXmlPath);
+ if (!$xmlPath || !file_exists($xmlPath)) {
+ continue;
+ }
+
+ $xml = simplexml_load_file($xmlPath);
+ if (!$xml->services instanceof \SimpleXMLElement) {
+ throw new ConfigException($xmlPath.' is not a valid container xml file');
+ }
+
+ /** @psalm-var \SimpleXMLElement $serviceXml */
+ foreach ($xml->services->service as $serviceXml) {
+ /** @psalm-var \SimpleXMLElement $serviceAttributes */
+ $serviceAttributes = $serviceXml->attributes();
+
+ $className = (string) $serviceAttributes->class;
+
+ if ($className) {
+ $this->classNames[] = $className;
+ }
+
+ $service = new Service((string) $serviceAttributes->id, $className);
+ if (isset($serviceAttributes->alias)) {
+ $service->setAlias((string) $serviceAttributes->alias);
+ }
+ $service->setIsPublic('false' !== (string) $serviceAttributes->public);
+
+ $this->add($service);
+ }
+
+ return;
+ }
+
+ throw new ConfigException('Container xml file(s) not found at ');
+ }
}
diff --git a/tests/unit/Symfony/ContainerMetaTest.php b/tests/unit/Symfony/ContainerMetaTest.php
index dc73f998..f7ef7335 100644
--- a/tests/unit/Symfony/ContainerMetaTest.php
+++ b/tests/unit/Symfony/ContainerMetaTest.php
@@ -19,7 +19,7 @@ class ContainerMetaTest extends TestCase
public function setUp()
{
- $this->containerMeta = new ContainerMeta(__DIR__.'/../../acceptance/container.xml');
+ $this->containerMeta = new ContainerMeta([__DIR__.'/../../acceptance/container.xml']);
}
public function tearDown()
@@ -71,7 +71,7 @@ public function publicServices()
public function testInvalidFile()
{
$this->expectException(ConfigException::class);
- $this->containerMeta = new ContainerMeta('non-existent-file.xml');
+ $this->containerMeta = new ContainerMeta(['non-existent-file.xml']);
}
/**
@@ -81,4 +81,14 @@ public function testNonExistentService()
{
$this->assertNull($this->containerMeta->get('non-existent-service'));
}
+
+ /**
+ * @testdox one valid, one invalid file should not raise an issue
+ */
+ public function testBothValidAndInvalidArray()
+ {
+ $containerMeta = new ContainerMeta(['non-existent-file.xml', __DIR__.'/../../acceptance/container.xml']);
+ $service = $containerMeta->get('service_container');
+ $this->assertSame('Symfony\Component\DependencyInjection\ContainerInterface', $service->getClassName());
+ }
}