Skip to content

Commit

Permalink
[container] multiple container xml file support (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
seferov authored Jun 19, 2020
1 parent 7769787 commit 76ff9a0
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 40 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
<pluginClass class="Psalm\SymfonyPsalmPlugin\Plugin">
<containerXml>var/cache/dev/App_KernelDevDebugContainer.xml</containerXml>
<containerXml>var/cache/dev/App_KernelTestDebugContainer.xml</containerXml>
</pluginClass>
```

### Credits

- Plugin created by [@seferov](https://github.com/seferov)
Expand Down
1 change: 0 additions & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>

</projectFiles>

<plugins>
Expand Down
10 changes: 2 additions & 8 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Expand Down
71 changes: 42 additions & 29 deletions src/Symfony/ContainerMeta.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ');
}
}
14 changes: 12 additions & 2 deletions tests/unit/Symfony/ContainerMetaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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']);
}

/**
Expand All @@ -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());
}
}

0 comments on commit 76ff9a0

Please sign in to comment.