diff --git a/.gitattributes b/.gitattributes index aa02dc6..7c4f104 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ /Tests export-ignore +/phpstan.neon export-ignore /phpunit.xml.dist export-ignore diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6cf4805..e68a7fe 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -74,3 +74,43 @@ jobs: - name: Run tests run: vendor/bin/phpunit + + phpstan: + name: PHPStan PHP ${{ matrix.php }} (Symfony ${{ matrix.symfony }}) + runs-on: ubuntu-latest + strategy: + matrix: + php: + - '8.5' + symfony: + - '8.1.*' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: flex + coverage: none + + - name: Get Composer Cache Directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ matrix.php }}-composer- + + - name: Update project dependencies + run: composer update --no-progress --ansi --prefer-stable + env: + SYMFONY_REQUIRE: ${{ matrix.symfony }} + + - name: Run PHPStan + run: vendor/bin/phpstan analyze --error-format=github diff --git a/Partial/Exception/ChildIsNotAnArrayNode.php b/Partial/Exception/ChildIsNotAnArrayNode.php index 7a16f6d..ca1cb22 100644 --- a/Partial/Exception/ChildIsNotAnArrayNode.php +++ b/Partial/Exception/ChildIsNotAnArrayNode.php @@ -6,6 +6,9 @@ class ChildIsNotAnArrayNode extends InvalidNodeNavigation { + /** + * @param non-empty-string $nodeName + */ public function __construct(BaseNode $parentNode, $nodeName) { parent::__construct( diff --git a/Partial/Exception/UndefinedChildNode.php b/Partial/Exception/UndefinedChildNode.php index a0fa22f..0b76f62 100644 --- a/Partial/Exception/UndefinedChildNode.php +++ b/Partial/Exception/UndefinedChildNode.php @@ -6,6 +6,9 @@ class UndefinedChildNode extends InvalidNodeNavigation { + /** + * @param non-empty-string $childNodeName + */ public function __construct(NodeInterface $parentNode, $childNodeName) { parent::__construct( diff --git a/Partial/PartialNode.php b/Partial/PartialNode.php index 733c56f..d838ec8 100644 --- a/Partial/PartialNode.php +++ b/Partial/PartialNode.php @@ -10,23 +10,14 @@ class PartialNode { - /** - * @var \ReflectionProperty - */ - private static $nodeChildrenProperty; - - /** - * @var \ReflectionProperty - */ - private static $nodePrototypeProperty; + private static ?\ReflectionProperty $nodeChildrenProperty = null; /** * Provide an ArrayNode instance (e.g. the root node created by a TreeBuilder) and a path that is relevant to you, * e.g. "dbal.connections": this will strip every node that is not contained in the given path (e.g. the "orm" node * would be removed entirely. * - * @param ArrayNode $node - * @param string $breadcrumbPath + * @param string|null $breadcrumbPath */ public static function excludeEverythingNotInBreadcrumbPath(ArrayNode $node, $breadcrumbPath) { @@ -40,7 +31,10 @@ public static function excludeEverythingNotInBreadcrumbPath(ArrayNode $node, $br } /** - * @param array $path + * @param list $path + * + * @throws ChildIsNotAnArrayNode if the child node is not an array node + * @throws UndefinedChildNode if the node does not have a child in the given path */ public static function excludeEverythingNotInPath(ArrayNode $node, array $path = []) { @@ -71,15 +65,14 @@ public static function excludeEverythingNotInPath(ArrayNode $node, array $path = } /** - * @param ArrayNode $node - * @param string $childNodeName + * @param non-empty-string $childNodeName * - * @return NodeInterface + * @throws UndefinedChildNode if the node does not have a child with the given name */ - private static function childNode(ArrayNode $node, $childNodeName) + private static function childNode(ArrayNode $node, string $childNodeName): NodeInterface { if ($node instanceof PrototypedArrayNode && '*' === $childNodeName) { - return self::nodePrototypeProperty()->getValue($node); + return $node->getPrototype(); } $children = self::nodeChildrenProperty()->getValue($node); @@ -94,25 +87,11 @@ private static function childNode(ArrayNode $node, $childNodeName) return $children[$childNodeName]; } - /** - * @return \ReflectionProperty - */ - private static function nodeChildrenProperty() + private static function nodeChildrenProperty(): \ReflectionProperty { return self::$nodeChildrenProperty ??= new \ReflectionProperty( ArrayNode::class, 'children' ); } - - /** - * @return \ReflectionProperty - */ - private static function nodePrototypeProperty() - { - return self::$nodePrototypeProperty ??= new \ReflectionProperty( - PrototypedArrayNode::class, - 'prototype' - ); - } } diff --git a/Partial/PartialProcessor.php b/Partial/PartialProcessor.php index 3165642..399232f 100644 --- a/Partial/PartialProcessor.php +++ b/Partial/PartialProcessor.php @@ -8,15 +8,23 @@ class PartialProcessor { + /** + * @param string|null $breadcrumbPath + * + * @return array + */ public function process(ArrayNode $node, $breadcrumbPath, array $configs) { PartialNode::excludeEverythingNotInBreadcrumbPath($node, $breadcrumbPath); - $processor = new Processor(); - - return $processor->process($node, $configs); + return (new Processor())->process($node, $configs); } + /** + * @param string|null $breadcrumbPath + * + * @return array + */ public function processConfiguration(ConfigurationInterface $configuration, $breadcrumbPath, array $configs) { return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $breadcrumbPath, $configs); diff --git a/PhpUnit/AbstractConfigurationConstraint.php b/PhpUnit/AbstractConfigurationConstraint.php index be9c182..21fbf0e 100644 --- a/PhpUnit/AbstractConfigurationConstraint.php +++ b/PhpUnit/AbstractConfigurationConstraint.php @@ -8,22 +8,40 @@ abstract class AbstractConfigurationConstraint extends Constraint { + /** + * @var ConfigurationInterface + */ protected $configuration; + + /** + * @var string|null + */ protected $breadcrumbPath; + /** + * @param string|null $breadcrumbPath + */ public function __construct(ConfigurationInterface $configuration, $breadcrumbPath = null) { $this->configuration = $configuration; $this->breadcrumbPath = $breadcrumbPath; } + /** + * @return array + */ protected function processConfiguration(array $configurationValues) { - $processor = new PartialProcessor(); - - return $processor->processConfiguration($this->configuration, $this->breadcrumbPath, $configurationValues); + return (new PartialProcessor())->processConfiguration($this->configuration, $this->breadcrumbPath, $configurationValues); } + /** + * @param mixed $configurationValues + * + * @return void + * + * @throws \InvalidArgumentException if the configuration values structure is not the required shape + */ protected function validateConfigurationValuesArray($configurationValues) { if (!is_array($configurationValues)) { diff --git a/PhpUnit/ConfigurationTestCaseTrait.php b/PhpUnit/ConfigurationTestCaseTrait.php index 98bbd19..fd260a8 100644 --- a/PhpUnit/ConfigurationTestCaseTrait.php +++ b/PhpUnit/ConfigurationTestCaseTrait.php @@ -3,10 +3,11 @@ namespace Matthias\SymfonyConfigTest\PhpUnit; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ConfigurationInterface; /** * Add this trait to your Test Case to add the ability of testing your configuration - * which should implement Symfony\Component\Config\Definition\ConfigurationInterface. + * which should implement {@see ConfigurationInterface}. */ trait ConfigurationTestCaseTrait { @@ -14,7 +15,7 @@ trait ConfigurationTestCaseTrait * Return the instance of ConfigurationInterface that should be used by the * Configuration-specific assertions in this test-case. * - * @return \Symfony\Component\Config\Definition\ConfigurationInterface + * @return ConfigurationInterface */ abstract protected function getConfiguration(); @@ -29,6 +30,8 @@ abstract protected function getConfiguration(); * @param array $configurationValues * @param string|null $expectedMessage * @param bool $useRegExp + * + * @return void */ protected function assertConfigurationIsInvalid(array $configurationValues, $expectedMessage = null, $useRegExp = false) { @@ -51,9 +54,11 @@ protected function assertConfigurationIsInvalid(array $configurationValues, $exp * to match the exception message using a regular expression. * * @param array $configurationValues - * @param string $breadcrumbPath The path that should be validated, e.g. "doctrine.orm" + * @param string|null $breadcrumbPath The path that should be validated, e.g. "doctrine.orm" * @param string|null $expectedMessage * @param bool $useRegExp + * + * @return void */ protected function assertPartialConfigurationIsInvalid( array $configurationValues, @@ -79,6 +84,8 @@ protected function assertPartialConfigurationIsInvalid( * * @param array $configurationValues * @param string|null $breadcrumbPath + * + * @return void */ protected function assertConfigurationIsValid(array $configurationValues, $breadcrumbPath = null) { @@ -99,6 +106,8 @@ protected function assertConfigurationIsValid(array $configurationValues, $bread * @param array $configurationValues * @param array $expectedProcessedConfiguration * @param string|null $breadcrumbPath + * + * @return void */ protected function assertProcessedConfigurationEquals( array $configurationValues, diff --git a/PhpUnit/ConfigurationValuesAreInvalidConstraint.php b/PhpUnit/ConfigurationValuesAreInvalidConstraint.php index 849995e..02e7cde 100644 --- a/PhpUnit/ConfigurationValuesAreInvalidConstraint.php +++ b/PhpUnit/ConfigurationValuesAreInvalidConstraint.php @@ -2,6 +2,7 @@ namespace Matthias\SymfonyConfigTest\PhpUnit; +use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\Constraint\ExceptionMessage; use PHPUnit\Framework\Constraint\ExceptionMessageIsOrContains; use PHPUnit\Framework\Constraint\ExceptionMessageMatchesRegularExpression; @@ -13,9 +14,21 @@ class ConfigurationValuesAreInvalidConstraint extends AbstractConfigurationConstraint { + /** + * @var string|null + */ private $expectedMessage; + + /** + * @var bool + */ private $useRegExp; + /** + * @param string|null $expectedMessage + * @param bool $useRegExp + * @param string|null $breadcrumbPath + */ public function __construct( ConfigurationInterface $configuration, $expectedMessage = null, @@ -28,6 +41,9 @@ public function __construct( $this->useRegExp = $useRegExp; } + /** + * {@inheritdoc} + */ public function evaluate($other, $description = '', $returnResult = false): ?bool { $this->validateConfigurationValuesArray($other); @@ -44,6 +60,7 @@ public function evaluate($other, $description = '', $returnResult = false): ?boo $this->fail($other, $description); + /** @phpstan-ignore deadCode.unreachable (legacy B/C layer) */ return null; } @@ -58,7 +75,7 @@ public function toString(): string return $toString; } - private function evaluateException(\Exception $exception, $description, $returnResult) + private function evaluateException(\Exception $exception, string $description = '', bool $returnResult = false): ?bool { if ($this->expectedMessage === null) { return true; @@ -68,7 +85,7 @@ private function evaluateException(\Exception $exception, $description, $returnR ->evaluate($exception->getMessage(), $description, $returnResult); } - private function createPhpUnitConstraint() + private function createPhpUnitConstraint(): Constraint { if ($this->useRegExp) { // Available since PHPUnit 10.0.15 @@ -78,10 +95,12 @@ private function createPhpUnitConstraint() // Available between PHPUnit 10.0.0 and 10.0.14 (inclusive) if (class_exists(MessageMatchesRegularExpression::class)) { + /** @phpstan-ignore return.type (legacy B/C layer) */ return new MessageMatchesRegularExpression('exception', $this->expectedMessage); } // Available in PHPUnit 9.6 + /** @phpstan-ignore class.notFound,return.type (legacy B/C layer) */ return new ExceptionMessageRegularExpression($this->expectedMessage); } @@ -92,10 +111,12 @@ private function createPhpUnitConstraint() // Available between PHPUnit 10.0.0 and 10.0.14 (inclusive) if (class_exists(MessageIsOrContains::class)) { + /** @phpstan-ignore return.type (legacy B/C layer) */ return new MessageIsOrContains('exception', $this->expectedMessage); } // Available in PHPUnit 9.6 + /** @phpstan-ignore class.notFound,return.type (legacy B/C layer) */ return new ExceptionMessage($this->expectedMessage); } } diff --git a/PhpUnit/ConfigurationValuesAreValidConstraint.php b/PhpUnit/ConfigurationValuesAreValidConstraint.php index 13dbfd4..242a4c8 100644 --- a/PhpUnit/ConfigurationValuesAreValidConstraint.php +++ b/PhpUnit/ConfigurationValuesAreValidConstraint.php @@ -2,16 +2,10 @@ namespace Matthias\SymfonyConfigTest\PhpUnit; -use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; class ConfigurationValuesAreValidConstraint extends AbstractConfigurationConstraint { - public function __construct(ConfigurationInterface $configuration, $breadcrumbPath = null) - { - parent::__construct($configuration, $breadcrumbPath); - } - public function evaluate($other, $description = '', $returnResult = false): ?bool { $this->validateConfigurationValuesArray($other); diff --git a/PhpUnit/ProcessedConfigurationEqualsConstraint.php b/PhpUnit/ProcessedConfigurationEqualsConstraint.php index 5fb5d22..f3b9f7e 100644 --- a/PhpUnit/ProcessedConfigurationEqualsConstraint.php +++ b/PhpUnit/ProcessedConfigurationEqualsConstraint.php @@ -7,8 +7,11 @@ class ProcessedConfigurationEqualsConstraint extends AbstractConfigurationConstraint { - private $configurationValues; + private array $configurationValues; + /** + * @param string|null $breadcrumbPath + */ public function __construct( ConfigurationInterface $configuration, array $configurationValues, @@ -24,9 +27,7 @@ public function evaluate($other, $description = '', $returnResult = false): ?boo { $processedConfiguration = $this->processConfiguration($this->configurationValues); - $constraint = new IsEqual($other); - - return $constraint->evaluate($processedConfiguration, '', $returnResult); + return (new IsEqual($other))->evaluate($processedConfiguration, '', $returnResult); } public function toString(): string diff --git a/Tests/Partial/PartialNodeTest.php b/Tests/Partial/PartialNodeTest.php index c0abc4d..8ef298c 100644 --- a/Tests/Partial/PartialNodeTest.php +++ b/Tests/Partial/PartialNodeTest.php @@ -30,8 +30,8 @@ public function it_strips_children_that_are_not_in_the_given_path_with_one_name( ->children() ->scalarNode('node_2_scalar_node'); + /** @var ArrayNode $node */ $node = $treeBuilder->buildTree(); - /* @var ArrayNode $node */ PartialNode::excludeEverythingNotInPath($node, ['node_2']); @@ -65,8 +65,8 @@ public function it_strips_children_that_are_not_in_the_given_path_with_several_n ->children() ->scalarNode('scalar_node'); + /** @var ArrayNode $node */ $node = $treeBuilder->buildTree(); - /* @var ArrayNode $node */ PartialNode::excludeEverythingNotInPath($node, ['node_1', 'node_1_b']); @@ -91,8 +91,8 @@ public function it_strips_children_when_leaf_node_is_not_an_array() ->end() ->scalarNode('node_3'); + /** @var ArrayNode $node */ $node = $treeBuilder->buildTree(); - /* @var ArrayNode $node */ PartialNode::excludeEverythingNotInPath($node, ['node_3']); @@ -141,7 +141,13 @@ public function it_fails_when_a_requested_child_node_does_not_exist() $node = $treeBuilder->buildTree(); $this->expectException(UndefinedChildNode::class); - $this->expectExceptionMessage('Undefined child node "non_existing_node" (the part of the path that was successful: "root.sub_node")'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('Undefined child node "non_existing_node" (the part of the path that was successful: "root.sub_node")'); + } else { + $this->expectExceptionMessage('Undefined child node "non_existing_node" (the part of the path that was successful: "root.sub_node")'); + } PartialNode::excludeEverythingNotInPath($node, ['sub_node', 'non_existing_node']); } @@ -160,15 +166,20 @@ public function it_fails_when_a_requested_child_node_is_no_array_node_itself_and $node = $treeBuilder->buildTree(); $this->expectException(ChildIsNotAnArrayNode::class); - $this->expectExceptionMessage('Child node "scalar_node" is not an array node (current path: "root.sub_node")'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('Child node "scalar_node" is not an array node (current path: "root.sub_node")'); + } else { + $this->expectExceptionMessage('Child node "scalar_node" is not an array node (current path: "root.sub_node")'); + } PartialNode::excludeEverythingNotInPath($node, ['sub_node', 'scalar_node', 'extra_node']); } - private function nodeOnlyHasChild(ArrayNode $node, $nodeName) + private function nodeOnlyHasChild(ArrayNode $node, string $nodeName) { - $property = new \ReflectionProperty($node, 'children'); - $children = $property->getValue($node); + $children = (new \ReflectionProperty($node, 'children'))->getValue($node); $this->assertCount(1, $children); $firstChild = reset($children); diff --git a/Tests/PhpUnit/ConfigurationTestCaseTraitTest.php b/Tests/PhpUnit/ConfigurationTestCaseTraitTest.php index c5604e5..9f36b77 100644 --- a/Tests/PhpUnit/ConfigurationTestCaseTraitTest.php +++ b/Tests/PhpUnit/ConfigurationTestCaseTraitTest.php @@ -32,7 +32,13 @@ public function it_can_assert_that_a_configuration_is_invalid() public function it_fails_when_a_configuration_is_valid_when_it_should_have_been_invalid() { $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('invalid'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('invalid'); + } else { + $this->expectExceptionMessage('invalid'); + } $this->assertConfigurationIsInvalid( [ @@ -87,7 +93,13 @@ public function it_fails_when_a_processed_configuration_does_not_match_the_expec $value = 'some value'; $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('equal'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('equal'); + } else { + $this->expectExceptionMessage('equal'); + } $this->assertProcessedConfigurationEquals( [ diff --git a/Tests/PhpUnit/ConfigurationValuesAreInvalidConstraintTest.php b/Tests/PhpUnit/ConfigurationValuesAreInvalidConstraintTest.php index 5edce39..deefbf0 100644 --- a/Tests/PhpUnit/ConfigurationValuesAreInvalidConstraintTest.php +++ b/Tests/PhpUnit/ConfigurationValuesAreInvalidConstraintTest.php @@ -17,7 +17,13 @@ public function if_configuration_values_is_no_array_it_fails() $constraint = new ConfigurationValuesAreInvalidConstraint(new AlwaysValidConfiguration()); $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('array'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('array'); + } else { + $this->expectExceptionMessage('array'); + } $constraint->evaluate('not an array'); } @@ -28,7 +34,13 @@ public function if_configuration_values_is_no_array_of_arrays_it_fails() $constraint = new ConfigurationValuesAreInvalidConstraint(new AlwaysValidConfiguration()); $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('array'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('array'); + } else { + $this->expectExceptionMessage('array'); + } $constraint->evaluate(['not an array']); } diff --git a/Tests/PhpUnit/ConfigurationValuesAreValidConstraintTest.php b/Tests/PhpUnit/ConfigurationValuesAreValidConstraintTest.php index 946b38c..1ff5356 100644 --- a/Tests/PhpUnit/ConfigurationValuesAreValidConstraintTest.php +++ b/Tests/PhpUnit/ConfigurationValuesAreValidConstraintTest.php @@ -16,7 +16,13 @@ public function if_configuration_values_is_no_array_it_fails() $constraint = new ConfigurationValuesAreValidConstraint(new AlwaysValidConfiguration()); $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('array'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('array'); + } else { + $this->expectExceptionMessage('array'); + } $constraint->evaluate('not an array'); } @@ -27,7 +33,13 @@ public function if_configuration_values_is_no_array_of_arrays_it_fails() $constraint = new ConfigurationValuesAreValidConstraint(new AlwaysValidConfiguration()); $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('array'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('array'); + } else { + $this->expectExceptionMessage('array'); + } $constraint->evaluate(['not an array']); } diff --git a/Tests/PhpUnit/PartialConfigurationIntegrationTest.php b/Tests/PhpUnit/PartialConfigurationIntegrationTest.php index cbdcd5c..9d9a44b 100644 --- a/Tests/PhpUnit/PartialConfigurationIntegrationTest.php +++ b/Tests/PhpUnit/PartialConfigurationIntegrationTest.php @@ -33,7 +33,13 @@ public function it_can_assert_that_a_configuration_is_invalid() public function it_fails_when_a_configuration_is_valid_when_it_should_have_been_invalid() { $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('invalid'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('invalid'); + } else { + $this->expectExceptionMessage('invalid'); + } $this->assertPartialConfigurationIsInvalid( [ @@ -66,7 +72,13 @@ public function it_can_assert_that_a_configuration_is_valid() public function it_fails_when_a_configuration_is_invalid_when_it_should_have_been_valid() { $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('valid'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('valid'); + } else { + $this->expectExceptionMessage('valid'); + } $this->assertConfigurationIsValid( [ @@ -106,7 +118,14 @@ public function it_fails_when_a_processed_configuration_does_not_match_the_expec $value = 'some value'; $this->expectException(ExpectationFailedException::class); - $this->expectExceptionMessage('equal'); + + /** @phpstan-ignore function.alreadyNarrowedType (compat layer for PHPUnit 13.2 deprecations) */ + if (method_exists($this, 'expectExceptionMessageIsOrContains')) { + $this->expectExceptionMessageIsOrContains('equal'); + } else { + $this->expectExceptionMessage('equal'); + } + $this->assertProcessedConfigurationEquals( [ [ diff --git a/composer.json b/composer.json index 581bf56..e1e3c29 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,11 @@ "phpunit/phpunit": "^10.5 || ^11.0 || ^12.0 || ^13.0", "symfony/config": "^5.4 || ^6.4 || ^7.0 || ^8.0" }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "2.2.14", + "phpstan/phpstan-phpunit": "2.0.18" + }, "autoload": { "psr-4" : { "Matthias\\SymfonyConfigTest\\" : "" }, "exclude-from-classmap": ["/Tests/"] @@ -24,6 +29,11 @@ "autoload-dev": { "psr-4" : { "Matthias\\SymfonyConfigTest\\Tests\\" : "Tests/" } }, + "config": { + "allow-plugins": { + "phpstan/extension-installer": true + } + }, "extra": { "branch-alias": { "dev-master": "6.x-dev" diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..55beb38 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 4 + paths: + - %currentWorkingDirectory%/Partial + - %currentWorkingDirectory%/PhpUnit + - %currentWorkingDirectory%/Tests