1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-27 04:45:20 +01:00

Improve signature of DOMDocument::loadXML()

This commit is contained in:
Fabien Villepinte 2022-01-17 22:52:58 +00:00
parent a99c433f3a
commit c9eafa15ad
8 changed files with 65 additions and 47 deletions

View File

@ -2109,9 +2109,9 @@ return [
'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespaceuri'=>'string', 'localname'=>'string'], 'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespaceuri'=>'string', 'localname'=>'string'],
'DOMDocument::importNode' => ['DOMNode|false', 'importednode'=>'DOMNode', 'deep='=>'bool'], 'DOMDocument::importNode' => ['DOMNode|false', 'importednode'=>'DOMNode', 'deep='=>'bool'],
'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'], 'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadHTML' => ['bool', 'source'=>'string', 'options='=>'int'], 'DOMDocument::loadHTML' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'], 'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'string', 'options='=>'int'], 'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::normalizeDocument' => ['void'], 'DOMDocument::normalizeDocument' => ['void'],
'DOMDocument::registerNodeClass' => ['bool', 'baseclass'=>'string', 'extendedclass'=>'string'], 'DOMDocument::registerNodeClass' => ['bool', 'baseclass'=>'string', 'extendedclass'=>'string'],
'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'], 'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'],

View File

@ -939,9 +939,9 @@ return [
'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespaceuri'=>'string', 'localname'=>'string'], 'DOMDocument::getElementsByTagNameNS' => ['DOMNodeList', 'namespaceuri'=>'string', 'localname'=>'string'],
'DOMDocument::importNode' => ['DOMNode|false', 'importednode'=>'DOMNode', 'deep='=>'bool'], 'DOMDocument::importNode' => ['DOMNode|false', 'importednode'=>'DOMNode', 'deep='=>'bool'],
'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'], 'DOMDocument::load' => ['DOMDocument|bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadHTML' => ['bool', 'source'=>'string', 'options='=>'int'], 'DOMDocument::loadHTML' => ['bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'], 'DOMDocument::loadHTMLFile' => ['bool', 'filename'=>'string', 'options='=>'int'],
'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'string', 'options='=>'int'], 'DOMDocument::loadXML' => ['DOMDocument|bool', 'source'=>'non-empty-string', 'options='=>'int'],
'DOMDocument::normalizeDocument' => ['void'], 'DOMDocument::normalizeDocument' => ['void'],
'DOMDocument::registerNodeClass' => ['bool', 'baseclass'=>'string', 'extendedclass'=>'string'], 'DOMDocument::registerNodeClass' => ['bool', 'baseclass'=>'string', 'extendedclass'=>'string'],
'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'], 'DOMDocument::relaxNGValidate' => ['bool', 'filename'=>'string'],

View File

@ -646,6 +646,10 @@ class Config
throw new InvalidArgumentException('Cannot open ' . $file_path); throw new InvalidArgumentException('Cannot open ' . $file_path);
} }
if ($file_contents === '') {
throw new InvalidArgumentException('Invalid empty file ' . $file_path);
}
try { try {
$config = self::loadFromXML($base_dir, $file_contents, $current_dir, $file_path); $config = self::loadFromXML($base_dir, $file_contents, $current_dir, $file_path);
$config->hash = sha1($file_contents . PSALM_VERSION); $config->hash = sha1($file_contents . PSALM_VERSION);
@ -669,6 +673,7 @@ class Config
/** /**
* Creates a new config object from an XML string * Creates a new config object from an XML string
* @param string|null $current_dir Current working directory, if different to $base_dir * @param string|null $current_dir Current working directory, if different to $base_dir
* @param non-empty-string $file_contents
* *
* @throws ConfigException * @throws ConfigException
*/ */
@ -687,6 +692,9 @@ class Config
return self::fromXmlAndPaths($base_dir, $file_contents, $current_dir, $file_path); return self::fromXmlAndPaths($base_dir, $file_contents, $current_dir, $file_path);
} }
/**
* @param non-empty-string $file_contents
*/
private static function loadDomDocument(string $base_dir, string $file_contents): DOMDocument private static function loadDomDocument(string $base_dir, string $file_contents): DOMDocument
{ {
$dom_document = new DOMDocument(); $dom_document = new DOMDocument();
@ -704,6 +712,8 @@ class Config
} }
/** /**
* @param non-empty-string $file_contents
*
* @throws ConfigException * @throws ConfigException
*/ */
private static function validateXmlConfig(string $base_dir, string $file_contents): void private static function validateXmlConfig(string $base_dir, string $file_contents): void
@ -731,7 +741,9 @@ class Config
$psalm_node->setAttribute('xmlns', self::CONFIG_NAMESPACE); $psalm_node->setAttribute('xmlns', self::CONFIG_NAMESPACE);
$old_dom_document = $dom_document; $old_dom_document = $dom_document;
$dom_document = self::loadDomDocument($base_dir, $old_dom_document->saveXML()); $old_file_contents = $old_dom_document->saveXML();
assert($old_file_contents !== false && $old_file_contents !== '');
$dom_document = self::loadDomDocument($base_dir, $old_file_contents);
} }
// Enable user error handling // Enable user error handling
@ -857,6 +869,8 @@ class Config
} }
/** /**
* @param non-empty-string $file_contents
*
* @psalm-suppress MixedMethodCall * @psalm-suppress MixedMethodCall
* @psalm-suppress MixedAssignment * @psalm-suppress MixedAssignment
* @psalm-suppress MixedArgument * @psalm-suppress MixedArgument

View File

@ -52,6 +52,9 @@ class Creator
</psalm> </psalm>
'; ';
/**
* @return non-empty-string
*/
public static function getContents( public static function getContents(
string $current_dir, string $current_dir,
?string $suggested_dir, ?string $suggested_dir,
@ -80,6 +83,7 @@ class Creator
); );
} }
/** @var non-empty-string */
return str_replace( return str_replace(
'errorLevel="1"', 'errorLevel="1"',
'errorLevel="' . $level . '"', 'errorLevel="' . $level . '"',

View File

@ -7,6 +7,7 @@ use DomElement;
use Psalm\Config; use Psalm\Config;
use RuntimeException; use RuntimeException;
use function assert;
use function file_get_contents; use function file_get_contents;
use function file_put_contents; use function file_put_contents;
use function strpos; use function strpos;
@ -120,6 +121,7 @@ class ConfigFile
} }
} }
assert($file_contents !== '');
$doc->loadXML($file_contents); $doc->loadXML($file_contents);
return $doc; return $doc;

View File

@ -1487,15 +1487,14 @@ class ConfigTest extends TestCase
FileTypeSelfRegisteringPlugin::$names = $names; FileTypeSelfRegisteringPlugin::$names = $names;
FileTypeSelfRegisteringPlugin::$flags = $flags; FileTypeSelfRegisteringPlugin::$flags = $flags;
$projectAnalyzer = $this->getProjectAnalyzerWithConfig( /** @var non-empty-string $xml */
TestConfig::loadFromXML( $xml = sprintf(
dirname(__DIR__, 2),
sprintf(
'<?xml version="1.0"?> '<?xml version="1.0"?>
<psalm><plugins><pluginClass class="%s"/></plugins></psalm>', <psalm><plugins><pluginClass class="%s"/></plugins></psalm>',
FileTypeSelfRegisteringPlugin::class FileTypeSelfRegisteringPlugin::class
) );
) $projectAnalyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(dirname(__DIR__, 2), $xml)
); );

View File

@ -982,10 +982,8 @@ class PluginTest extends TestCase
public function testPluginFilenameCanBeAbsolute(): void public function testPluginFilenameCanBeAbsolute(): void
{ {
$this->project_analyzer = $this->getProjectAnalyzerWithConfig( /** @var non-empty-string $xml */
TestConfig::loadFromXML( $xml = sprintf(
dirname(__DIR__, 2) . DIRECTORY_SEPARATOR,
sprintf(
'<?xml version="1.0"?> '<?xml version="1.0"?>
<psalm <psalm
errorLevel="1" errorLevel="1"
@ -998,8 +996,9 @@ class PluginTest extends TestCase
</plugins> </plugins>
</psalm>', </psalm>',
__DIR__ . '/../..' __DIR__ . '/../..'
) );
) $this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR, $xml)
); );
$this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer); $this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer);
@ -1010,10 +1009,8 @@ class PluginTest extends TestCase
$this->expectException(InvalidArgumentException::class); $this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('does-not-exist/plugins/StringChecker.php'); $this->expectExceptionMessage('does-not-exist/plugins/StringChecker.php');
$this->project_analyzer = $this->getProjectAnalyzerWithConfig( /** @var non-empty-string $xml */
TestConfig::loadFromXML( $xml = sprintf(
dirname(__DIR__, 2) . DIRECTORY_SEPARATOR,
sprintf(
'<?xml version="1.0"?> '<?xml version="1.0"?>
<psalm <psalm
errorLevel="1" errorLevel="1"
@ -1026,8 +1023,9 @@ class PluginTest extends TestCase
</plugins> </plugins>
</psalm>', </psalm>',
__DIR__ . '/..' __DIR__ . '/..'
) );
) $this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR, $xml)
); );
$this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer); $this->project_analyzer->getCodebase()->config->initializePlugins($this->project_analyzer);

View File

@ -387,6 +387,7 @@ class MethodCallTest extends TestCase
], ],
'domElementIteratorOrEmptyArray' => [ 'domElementIteratorOrEmptyArray' => [
'<?php '<?php
/** @param non-empty-string $XML */
function foo(string $XML) : void { function foo(string $XML) : void {
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadXML($XML); $dom->loadXML($XML);