diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a6b50564944..ea9ac8fdc10 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -82,7 +82,7 @@ jobs:
ini-values: zend.assertions=1, assert.exception=1
tools: composer:v2
coverage: none
- extensions: decimal
+ extensions: none, curl, dom, filter, json, libxml, mbstring, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter
- uses: actions/checkout@v2
diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml
index 1f9c4e83ae4..3f8842ad797 100644
--- a/.github/workflows/windows-ci.yml
+++ b/.github/workflows/windows-ci.yml
@@ -52,6 +52,7 @@ jobs:
ini-values: zend.assertions=1, assert.exception=1
tools: composer:v2
coverage: none
+ extensions: none, curl, dom, filter, json, libxml, mbstring, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter
- uses: actions/checkout@v2
diff --git a/UPGRADING.md b/UPGRADING.md
index 8dafd844e3b..d25d8c8936d 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -165,6 +165,7 @@
- [BC] Property `Psalm\Config::$allow_phpstorm_generics` was removed
- [BC] Property `Psalm\Config::$exit_functions` was removed
- [BC] Property `Psalm\Config::$forbid_echo` was removed
+ - [BC] Property `Psalm\Config::$load_xdebug_stub` was removed
- [BC] Method `Psalm\Type::getEmpty()` was removed
- [BC] Legacy hook interfaces have been removed:
- `Psalm\Plugin\Hook\MethodReturnTypeProviderInterface`
diff --git a/config.xsd b/config.xsd
index 1a29b16b27c..a4f4b5042f5 100644
--- a/config.xsd
+++ b/config.xsd
@@ -21,6 +21,8 @@
+
+
@@ -47,18 +49,6 @@
-
-
-
- Default is runtime-specific: if not present, Psalm will only load the Xdebug stub if psalm has unloaded the extension.
-
-
-
-
- Deprecated. In Psalm 5 extensions will be loaded based on composer.json and overridden with enableExtensions/disableExtensions.
-
-
-
@@ -682,4 +672,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dictionaries/InternalTaintSinkMap.php b/dictionaries/InternalTaintSinkMap.php
index df5f7faf248..cb8c6077d35 100644
--- a/dictionaries/InternalTaintSinkMap.php
+++ b/dictionaries/InternalTaintSinkMap.php
@@ -43,9 +43,6 @@
'mysqli_stmt::prepare' => [['sql']],
'passthru' => [['shell']],
'pcntl_exec' => [['shell']],
-'PDO::prepare' => [['sql']],
-'PDO::query' => [['sql']],
-'PDO::exec' => [['sql']],
'pg_exec' => [[], ['sql']],
'pg_prepare' => [[], [], ['sql']],
'pg_put_line' => [[], ['sql']],
diff --git a/docs/running_psalm/configuration.md b/docs/running_psalm/configuration.md
index 26861e4ada4..d50b3633e32 100644
--- a/docs/running_psalm/configuration.md
+++ b/docs/running_psalm/configuration.md
@@ -246,16 +246,6 @@ When `true`, Psalm will attempt to find all unused code (including unused variab
```
When `true`, Psalm will report all `@psalm-suppress` annotations that aren't used, the equivalent of running with `--find-unused-psalm-suppress`. Defaults to `false`.
-#### loadXdebugStub
-```xml
-
-```
-If not present, Psalm will only load the Xdebug stub if Psalm has unloaded the extension.
-When `true`, Psalm will load the Xdebug extension stub (as the extension is unloaded when Psalm runs).
-Setting to `false` prevents the stub from loading.
-
#### ensureArrayStringOffsetsExist
```xml
```
@@ -432,6 +422,23 @@ Optional. Same format as ``. Directories Psalm should load but not
#### <fileExtensions>
Optional. A list of extensions to search over. See [Checking non-PHP files](checking_non_php_files.md) to understand how to extend this.
+#### <enableExtensions>
+Optional. A list of extensions to enable. By default, only extensions required by your composer.json will be enabled.
+```xml
+
+
+
+
+```
+
+#### <disableExtensions>
+Optional. A list of extensions to disable. By default, only extensions required by your composer.json will be enabled.
+```xml
+
+
+
+```
+
#### <plugins>
Optional. A list of `` entries. See the [Plugins](plugins/using_plugins.md) section for more information.
@@ -483,7 +490,7 @@ The following configuration declares custom types for super-globals (`$GLOBALS`
```xml
-
+
```
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 614c782fb9e..1f7e035e144 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -1,5 +1,5 @@
-
+
$comment_block->tags['variablesfrom'][0]
@@ -24,9 +24,6 @@
getAdditionalFileTypeAnalyzers
getAdditionalFileTypeScanners
-
- $this->load_xdebug_stub
-
@@ -203,12 +200,6 @@
$stmt->expr->getArgs()[0]
-
-
- $config->load_xdebug_stub
- $config->load_xdebug_stub
-
-
$callables[0]
@@ -286,8 +277,7 @@
$storage->template_extended_count
-
- $imported_type_data[3]
+
$l[4]
$r[4]
$var_line_parts[0]
diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php
index fe2208d0d80..56982c5fc52 100644
--- a/src/Psalm/Config.php
+++ b/src/Psalm/Config.php
@@ -46,6 +46,7 @@
use XdgBaseDir\Xdg;
use stdClass;
+use function array_key_exists;
use function array_map;
use function array_merge;
use function array_pad;
@@ -58,7 +59,6 @@
use function count;
use function dirname;
use function explode;
-use function extension_loaded;
use function file_exists;
use function file_get_contents;
use function filetype;
@@ -196,15 +196,6 @@ class Config
*/
public $throw_exception = false;
- /**
- * Whether or not to load Xdebug stub
- *
- * @deprecated going to be removed in Psalm 5
- *
- * @var bool|null
- */
- public $load_xdebug_stub;
-
/**
* The directory to store PHP Parser (and other) caches
*
@@ -574,6 +565,34 @@ class Config
/** @var ?int */
public $threads;
+ /**
+ * @psalm-readonly-allow-private-mutation
+ * @var array{
+ * decimal: bool,
+ * dom: bool,
+ * ds: bool,
+ * geos: bool,
+ * gmp: bool,
+ * mongodb: bool,
+ * mysqli: bool,
+ * pdo: bool,
+ * soap: bool,
+ * xdebug: bool,
+ * }
+ */
+ public $php_extensions = [
+ "decimal" => false,
+ "dom" => false,
+ "ds" => false,
+ "geos" => false,
+ "gmp" => false,
+ "mongodb" => false,
+ "mysqli" => false,
+ "pdo" => false,
+ "soap" => false,
+ "xdebug" => false,
+ ];
+
protected function __construct()
{
self::$instance = $this;
@@ -931,7 +950,6 @@ private static function fromXmlAndPaths(
'ignoreInternalFunctionFalseReturn' => 'ignore_internal_falsable_issues',
'ignoreInternalFunctionNullReturn' => 'ignore_internal_nullable_issues',
'includePhpVersionsInErrorBaseline' => 'include_php_versions_in_error_baseline',
- 'loadXdebugStub' => 'load_xdebug_stub',
'ensureArrayStringOffsetsExist' => 'ensure_array_string_offsets_exist',
'ensureArrayIntOffsetsExist' => 'ensure_array_int_offsets_exist',
'reportMixedIssues' => 'show_mixed_issues',
@@ -966,6 +984,36 @@ private static function fromXmlAndPaths(
$base_dir = $current_dir;
}
+ $composer_json_path = Composer::getJsonFilePath($config->base_dir);
+
+ $composer_json = null;
+ if (file_exists($composer_json_path)) {
+ if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) {
+ throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path);
+ }
+ }
+ foreach ($config->php_extensions as $ext => $_) {
+ $config->php_extensions[$ext] = isset($composer_json["require"]["ext-$ext"]);
+ }
+
+ if (isset($config_xml->enableExtensions) && isset($config_xml->enableExtensions->extension)) {
+ foreach ($config_xml->enableExtensions->extension as $extension) {
+ assert(isset($extension["name"]));
+ $extensionName = (string) $extension["name"];
+ assert(array_key_exists($extensionName, $config->php_extensions));
+ $config->php_extensions[$extensionName] = true;
+ }
+ }
+
+ if (isset($config_xml->disableExtensions) && isset($config_xml->disableExtensions->extension)) {
+ foreach ($config_xml->disableExtensions->extension as $extension) {
+ assert(isset($extension["name"]));
+ $extensionName = (string) $extension["name"];
+ assert(array_key_exists($extensionName, $config->php_extensions));
+ $config->php_extensions[$extensionName] = false;
+ }
+ }
+
if (isset($config_xml['phpVersion'])) {
$config->configured_php_version = (string) $config_xml['phpVersion'];
}
@@ -1969,7 +2017,6 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null):
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreGenericClasses.phpstub',
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreGenericIterators.phpstub',
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'CoreImmutableClasses.phpstub',
- $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'DOM.phpstub',
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Reflection.phpstub',
$dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'SPL.phpstub',
];
@@ -1984,39 +2031,11 @@ public function visitStubFiles(Codebase $codebase, ?Progress $progress = null):
$this->internal_stubs[] = $stringable_path;
}
- if (extension_loaded('PDO')) {
- $ext_pdo_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'pdo.phpstub';
- $this->internal_stubs[] = $ext_pdo_path;
- }
-
- if (extension_loaded('soap')) {
- $ext_soap_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'soap.phpstub';
- $this->internal_stubs[] = $ext_soap_path;
- }
-
- if (extension_loaded('ds')) {
- $ext_ds_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'ext-ds.phpstub';
- $this->internal_stubs[] = $ext_ds_path;
- }
-
- if (extension_loaded('mongodb')) {
- $ext_mongodb_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'mongodb.phpstub';
- $this->internal_stubs[] = $ext_mongodb_path;
- }
-
- if ($this->load_xdebug_stub) {
- $xdebug_stub_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'Xdebug.phpstub';
- $this->internal_stubs[] = $xdebug_stub_path;
- }
-
- if (extension_loaded('mysqli')) {
- $ext_mysqli_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'mysqli.phpstub';
- $this->internal_stubs[] = $ext_mysqli_path;
- }
-
- if (extension_loaded('decimal')) {
- $ext_decimal_path = $dir_lvl_2 . DIRECTORY_SEPARATOR . 'stubs' . DIRECTORY_SEPARATOR . 'decimal.phpstub';
- $this->internal_stubs[] = $ext_decimal_path;
+ foreach ($this->php_extensions as $ext => $enabled) {
+ if ($enabled) {
+ $this->internal_stubs[] = $dir_lvl_2 . DIRECTORY_SEPARATOR . "stubs"
+ . DIRECTORY_SEPARATOR . "extensions" . DIRECTORY_SEPARATOR . "$ext.phpstub";
+ }
}
foreach ($this->internal_stubs as $stub_path) {
diff --git a/src/Psalm/Internal/Cli/Psalm.php b/src/Psalm/Internal/Cli/Psalm.php
index 70893dddc4a..2063355c711 100644
--- a/src/Psalm/Internal/Cli/Psalm.php
+++ b/src/Psalm/Internal/Cli/Psalm.php
@@ -253,7 +253,7 @@ public static function run(array $argv): void
self::emitMacPcreWarning($options, $threads);
- self::restart($options, $config, $threads);
+ self::restart($options, $threads);
if (isset($options['debug-emitted-issues'])) {
$config->debug_emitted_issues = true;
@@ -882,7 +882,7 @@ private static function emitMacPcreWarning(array $options, int $threads): void
}
}
- private static function restart(array $options, Config $config, int $threads): void
+ private static function restart(array $options, int $threads): void
{
$ini_handler = new PsalmRestarter('PSALM');
@@ -907,10 +907,6 @@ private static function restart(array $options, Config $config, int $threads): v
// If Xdebug is enabled, restart without it
$ini_handler->check();
-
- if ($config->load_xdebug_stub === null && PsalmRestarter::getSkippedVersion() !== '') {
- $config->load_xdebug_stub = true;
- }
}
private static function detectThreads(array $options, Config $config, bool $in_ci): int
diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php
index 1d2521ea003..6ef85193afc 100644
--- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php
+++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php
@@ -2,7 +2,7 @@
namespace Psalm\Internal\Provider\ReturnTypeProvider;
-use PDO;
+use Psalm\Config;
use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
use Psalm\Type;
@@ -15,8 +15,6 @@
use Psalm\Type\Atomic\TScalar;
use Psalm\Type\Union;
-use function class_exists;
-
/**
* @internal
*/
@@ -29,11 +27,12 @@ public static function getClassLikeNames(): array
public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
{
+ $config = Config::getInstance();
$source = $event->getSource();
$call_args = $event->getCallArgs();
$method_name_lowercase = $event->getMethodNameLowercase();
if ($method_name_lowercase === 'fetch'
- && class_exists('PDO')
+ && $config->php_extensions["pdo"]
&& isset($call_args[0])
&& ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value))
&& $first_arg_type->isSingleIntLiteral()
@@ -41,7 +40,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
$fetch_mode = $first_arg_type->getSingleIntLiteral()->value;
switch ($fetch_mode) {
- case PDO::FETCH_ASSOC: // array|false
+ case 2: // PDO::FETCH_ASSOC - array|false
return new Union([
new TArray([
Type::getString(),
@@ -53,7 +52,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
new TFalse(),
]);
- case PDO::FETCH_BOTH: // array|false
+ case 4: // PDO::FETCH_BOTH - array|false
return new Union([
new TArray([
Type::getArrayKey(),
@@ -65,16 +64,16 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
new TFalse(),
]);
- case PDO::FETCH_BOUND: // bool
+ case 6: // PDO::FETCH_BOUND - bool
return Type::getBool();
- case PDO::FETCH_CLASS: // object|false
+ case 8: // PDO::FETCH_CLASS - object|false
return new Union([
new TObject(),
new TFalse(),
]);
- case PDO::FETCH_LAZY: // object|false
+ case 1: // PDO::FETCH_LAZY - object|false
// This actually returns a PDORow object, but that class is
// undocumented, and its attributes are all dynamic anyway
return new Union([
@@ -82,7 +81,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
new TFalse(),
]);
- case PDO::FETCH_NAMED: // array>|false
+ case 11: // PDO::FETCH_NAMED - array>|false
return new Union([
new TArray([
Type::getString(),
@@ -94,7 +93,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
new TFalse(),
]);
- case PDO::FETCH_NUM: // list|false
+ case 3: // PDO::FETCH_NUM - list|false
return new Union([
new TList(
new Union([
@@ -105,7 +104,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event)
new TFalse(),
]);
- case PDO::FETCH_OBJ: // stdClass|false
+ case 5: // PDO::FETCH_OBJ - stdClass|false
return new Union([
new TNamedObject('stdClass'),
new TFalse(),
diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php
index cd88b142770..c8eba436197 100644
--- a/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php
+++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php
@@ -2,7 +2,6 @@
namespace Psalm\Internal\Provider\ReturnTypeProvider;
-use PDO;
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Plugin\EventHandler\Event\MethodParamsProviderEvent;
@@ -62,7 +61,7 @@ public static function getMethodParams(MethodParamsProviderEvent $event): ?array
$value = $first_call_arg_type->getSingleIntLiteral()->value;
switch ($value) {
- case PDO::FETCH_COLUMN:
+ case 7: // PDO::FETCH_COLUMN
$params[] = new FunctionLikeParameter(
'colno',
false,
@@ -73,7 +72,7 @@ public static function getMethodParams(MethodParamsProviderEvent $event): ?array
);
break;
- case PDO::FETCH_CLASS:
+ case 8: // PDO::FETCH_CLASS
$params[] = new FunctionLikeParameter(
'classname',
false,
@@ -93,7 +92,7 @@ public static function getMethodParams(MethodParamsProviderEvent $event): ?array
);
break;
- case PDO::FETCH_INTO:
+ case 9: // PDO::FETCH_INTO
$params[] = new FunctionLikeParameter(
'object',
false,
diff --git a/stubs/decimal.phpstub b/stubs/extensions/decimal.phpstub
similarity index 100%
rename from stubs/decimal.phpstub
rename to stubs/extensions/decimal.phpstub
diff --git a/stubs/DOM.phpstub b/stubs/extensions/dom.phpstub
similarity index 100%
rename from stubs/DOM.phpstub
rename to stubs/extensions/dom.phpstub
diff --git a/stubs/ext-ds.phpstub b/stubs/extensions/ds.phpstub
similarity index 100%
rename from stubs/ext-ds.phpstub
rename to stubs/extensions/ds.phpstub
diff --git a/stubs/ext-geos.phpstub b/stubs/extensions/geos.phpstub
similarity index 100%
rename from stubs/ext-geos.phpstub
rename to stubs/extensions/geos.phpstub
diff --git a/stubs/extensions/gmp.phpstub b/stubs/extensions/gmp.phpstub
new file mode 100644
index 00000000000..6a621bc0d66
--- /dev/null
+++ b/stubs/extensions/gmp.phpstub
@@ -0,0 +1,11 @@
+
+ */
+class PDOStatement implements Traversable
+{
+ /**
+ * @psalm-taint-sink callable $class
+ *
+ * @template T of object
+ * @param class-string $class
+ * @param array $ctorArgs
+ * @return false|T
+ */
+ public function fetchObject($class = \stdclass::class, array $ctorArgs = array()) {}
+}
+
+ class PDOException extends RuntimeException {
+ protected string $code;
+ public ?array $errorInfo = null;
+}
diff --git a/stubs/soap.phpstub b/stubs/extensions/soap.phpstub
similarity index 92%
rename from stubs/soap.phpstub
rename to stubs/extensions/soap.phpstub
index 0eb107823ef..8a3fafa4dcd 100644
--- a/stubs/soap.phpstub
+++ b/stubs/extensions/soap.phpstub
@@ -282,3 +282,30 @@ class SoapClient {
public function __setSoapHeaders ($soapheaders = null) {}
}
+
+class SoapFault extends Exception {
+ /**
+ * @param array|string|null $code
+ */
+ public function __construct(
+ $code,
+ string $string,
+ ?string $actor = null,
+ mixed $details = null,
+ ?string $name = null,
+ mixed $headerFault = null
+ ) {}
+}
+
+class SoapHeader {
+ public function __construct(
+ string $namespace,
+ string $name,
+ // Actually doesn't have a default, not specifying results in no SoapHeader::$data property. Specifying null
+ // results in a SoapHeader::$data property with null as the value. This probably makes no difference.
+ mixed $data = null,
+ bool $mustUnderstand = false,
+ // Same as $data, no default. The documentation specifies this as a `string` but it accepts null.
+ ?string $actor = null
+ ) {}
+}
diff --git a/stubs/Xdebug.phpstub b/stubs/extensions/xdebug.phpstub
similarity index 100%
rename from stubs/Xdebug.phpstub
rename to stubs/extensions/xdebug.phpstub
diff --git a/stubs/pdo.phpstub b/stubs/pdo.phpstub
deleted file mode 100644
index 0abf5053353..00000000000
--- a/stubs/pdo.phpstub
+++ /dev/null
@@ -1,19 +0,0 @@
-
- */
-class PDOStatement implements Traversable
-{
- /**
- * @psalm-taint-sink callable $class
- *
- * @template T of object
- * @param class-string $class
- * @param array $ctorArgs
- * @return false|T
- */
- public function fetchObject($class = \stdclass::class, array $ctorArgs = array()) {}
-}
diff --git a/tests/BinaryOperationTest.php b/tests/BinaryOperationTest.php
index 2f2e37f197d..9b268caf1e6 100644
--- a/tests/BinaryOperationTest.php
+++ b/tests/BinaryOperationTest.php
@@ -8,8 +8,6 @@
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
-use function class_exists;
-
use const DIRECTORY_SEPARATOR;
class BinaryOperationTest extends TestCase
@@ -19,10 +17,6 @@ class BinaryOperationTest extends TestCase
public function testGMPOperations(): void
{
- if (class_exists('GMP') === false) {
- $this->markTestSkipped('Cannot run test, base class "GMP" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'markTestSkipped('Cannot run test, base class "Decimal\\Decimal" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'markTestSkipped('Cannot run test, base class "mysqli" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'file_path, trim('
+
+
+
+
+
+
+
+ '));
+
+ $config_file = new ConfigFile((string)getcwd(), $this->file_path);
+ $config = $config_file->getConfig();
+
+ $this->assertTrue($config->php_extensions["mysqli"]);
+ $this->assertTrue($config->php_extensions["pdo"]);
+ }
+
+ public function testDisableExtensions(): void
+ {
+ file_put_contents($this->file_path, trim('
+
+
+
+
+
+
+
+
+
+
+
+ '));
+
+ $config_file = new ConfigFile((string)getcwd(), $this->file_path);
+ $config = $config_file->getConfig();
+
+ $this->assertFalse($config->php_extensions["mysqli"]);
+ $this->assertFalse($config->php_extensions["pdo"]);
+ }
+
+ public function testInvalidExtension(): void
+ {
+ $this->expectException(ConfigException::class);
+
+ file_put_contents($this->file_path, trim('
+
+
+
+
+
+
+ '));
+
+ (new ConfigFile((string)getcwd(), $this->file_path))->getConfig();
+ }
+
/**
* @param string $expected_template
* @param string $contents
diff --git a/tests/MethodCallTest.php b/tests/MethodCallTest.php
index 9d3eeb449ec..0102eaf050a 100644
--- a/tests/MethodCallTest.php
+++ b/tests/MethodCallTest.php
@@ -6,8 +6,6 @@
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
-use function class_exists;
-
use const DIRECTORY_SEPARATOR;
class MethodCallTest extends TestCase
@@ -17,10 +15,6 @@ class MethodCallTest extends TestCase
public function testExtendDocblockParamType(): void
{
- if (class_exists('SoapClient') === false) {
- $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'expectExceptionMessage('ImplementedParamTypeMismatch');
$this->expectException(CodeException::class);
- if (class_exists('SoapClient') === false) {
- $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
$this->addFile(
'somefile.php',
@@ -286,10 +269,6 @@ public function testExtendDocblockParamTypeWithWrongParam(): void
$this->expectException(CodeException::class);
$this->expectExceptionMessage('MethodSignatureMismatch');
- if (class_exists('SoapClient') === false) {
- $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!');
- }
-
$this->addFile(
'somefile.php',
'php_extensions as $ext => $_enabled) {
+ $this->php_extensions[$ext] = true;
+ }
+
$this->throw_exception = true;
$this->use_docblock_types = true;
$this->level = 1;