Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modular config files #2635

Merged
merged 1 commit into from Jan 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/running_psalm/configuration.md
Expand Up @@ -11,6 +11,29 @@ Psalm uses an XML config file (by default, `psalm.xml`). A barebones example loo
</psalm>
```

Configuration file may be split into several files using [XInclude](https://www.w3.org/TR/xinclude/) tags (c.f. previous example):
#### psalm.xml
```xml
<?xml version="1.0"?>
<psalm
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<xi:include href="files.xml"/>
</psalm>
```
#### files.xml
```xml
<?xml version="1.0" encoding="UTF-8"?>
<projectFiles xmlns="https://getpsalm.org/schema/config">
<file name="Bar.php" />
<file name="Bat.php" />
</projectFiles>
```


## Optional `<psalm />` attributes

### Coding style
Expand Down
35 changes: 28 additions & 7 deletions src/Psalm/Config.php
Expand Up @@ -7,6 +7,8 @@
use function array_unique;
use function class_exists;
use Composer\Autoload\ClassLoader;
use DOMDocument;

use function count;
use const DIRECTORY_SEPARATOR;
use function dirname;
Expand Down Expand Up @@ -65,6 +67,10 @@
use function trigger_error;
use function unlink;
use function version_compare;
use function getcwd;
use function chdir;
use function simplexml_import_dom;
use const LIBXML_NONET;

class Config
{
Expand Down Expand Up @@ -605,25 +611,39 @@ public static function loadFromXML($base_dir, $file_contents, $current_dir = nul
$current_dir = $base_dir;
}

self::validateXmlConfig($file_contents);
self::validateXmlConfig($base_dir, $file_contents);

return self::fromXmlAndPaths($base_dir, $file_contents, $current_dir);
}

private static function loadDomDocument(string $base_dir, string $file_contents): DOMDocument
{
$dom_document = new DOMDocument();

// there's no obvious way to set xml:base for a document when loading it from string
// so instead we're changing the current directory instead to be able to process XIncludes
$oldpwd = getcwd();
chdir($base_dir);

$dom_document->loadXML($file_contents, LIBXML_NONET);
$dom_document->xinclude(LIBXML_NONET);

chdir($oldpwd);
return $dom_document;
}

/**
* @throws ConfigException
*/
private static function validateXmlConfig(string $file_contents): void
private static function validateXmlConfig(string $base_dir, string $file_contents): void
{
$schema_path = dirname(dirname(__DIR__)) . '/config.xsd';

if (!file_exists($schema_path)) {
throw new ConfigException('Cannot locate config schema');
}

$dom_document = new \DOMDocument();
$dom_document->loadXML($file_contents);
$dom_document = self::loadDomDocument($base_dir, $file_contents);

$psalm_nodes = $dom_document->getElementsByTagName('psalm');

Expand All @@ -640,8 +660,7 @@ private static function validateXmlConfig(string $file_contents): void
$psalm_node->setAttribute('xmlns', 'https://getpsalm.org/schema/config');

$old_dom_document = $dom_document;
$dom_document = new \DOMDocument();
$dom_document->loadXML($old_dom_document->saveXML());
$dom_document = self::loadDomDocument($base_dir, $old_dom_document->saveXML());
}

// Enable user error handling
Expand Down Expand Up @@ -674,7 +693,9 @@ private static function fromXmlAndPaths(string $base_dir, string $file_contents,
{
$config = new static();

$config_xml = new SimpleXMLElement($file_contents);
$dom_document = self::loadDomDocument($base_dir, $file_contents);

$config_xml = simplexml_import_dom($dom_document);

$booleanAttributes = [
'useDocblockTypes' => 'use_docblock_types',
Expand Down
14 changes: 14 additions & 0 deletions tests/Config/ConfigTest.php
Expand Up @@ -1195,6 +1195,20 @@ public function testTemplatedFiles()
}
}

/** @return void */
public function testModularConfig()
{
$root = __DIR__ . '/../fixtures/ModularConfig';
$config = Config::loadFromXMLFile($root . '/psalm.xml', $root);
$this->assertEquals(
[
realpath($root . '/Bar.php'),
realpath($root . '/Bat.php')
],
$config->getProjectFiles()
);
}

public function tearDown(): void
{
parent::tearDown();
Expand Down
23 changes: 23 additions & 0 deletions tests/fixtures/ModularConfig/Bar.php
@@ -0,0 +1,23 @@
<?php
namespace Vimeo\Test\DummyProject;

class Bar
{
use SomeTrait;

/** @var string */
public $x;

public function __construct()
{
$this->x = 'hello';
}
}

/**
* @return void
*/
function someFunction()
{
echo 'here';
}
13 changes: 13 additions & 0 deletions tests/fixtures/ModularConfig/Bat.php
@@ -0,0 +1,13 @@
<?php
namespace Vimeo\Test\DummyProject;

class Bat
{
public function __construct()
{
$a = new Bar();

someFunction();
someOtherFunction();
}
}
14 changes: 14 additions & 0 deletions tests/fixtures/ModularConfig/SomeTrait.php
@@ -0,0 +1,14 @@
<?php
namespace Vimeo\Test\DummyProject;

trait SomeTrait
{
}

/**
* @return void
*/
function someOtherFunction()
{
echo 'here';
}
5 changes: 5 additions & 0 deletions tests/fixtures/ModularConfig/files.xml
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectFiles xmlns="https://getpsalm.org/schema/config">
<file name="Bar.php" />
<file name="Bat.php" />
</projectFiles>
10 changes: 10 additions & 0 deletions tests/fixtures/ModularConfig/psalm.xml
@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<psalm
totallyTyped="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
xmlns:xi="http://www.w3.org/2001/XInclude"
>
<xi:include href="files.xml"/>
</psalm>