markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); return; } $this->addFile( 'somefile.php', ' $arguments * @param array $options default null * @param array $input_headers default null * @param array $output_headers default null * @return mixed */ public function __soapCall( $function_name, $arguments, $options = [], $input_headers = [], &$output_headers = [] ) { return $_GET["foo"]; } } class B extends SoapClient { public function __soapCall( $function_name, $arguments, $options = [], $input_headers = [], &$output_headers = [] ) { return $_GET["foo"]; } } class C extends SoapClient { public function __soapCall( string $function_name, $arguments, $options = [], $input_headers = [], &$output_headers = [] ) { return $_GET["foo"]; } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MethodSignatureMismatch * * @return void */ public function testExtendDocblockParamTypeWithWrongParam() { if (class_exists('SoapClient') === false) { $this->markTestSkipped('Cannot run test, base class "SoapClient" does not exist!'); return; } $this->addFile( 'somefile.php', ' $options default null * @param array $input_headers default null * @param array $output_headers default null * @return mixed */ public function __soapCall( $function_name, string $arguments, $options = [], $input_headers = [], &$output_headers = [] ) { } }' ); $this->analyzeFile('somefile.php', new Context()); } /** * @return iterable,error_levels?:string[]}> */ public function providerValidCodeParse() { return [ 'privateArgs' => [ ' [ 'foo(null);', ], 'nullableSubclassParamWithDefault' => [ 'foo();', ], 'allowSubclassesForNonInheritedMethodParams' => [ 'bar(); }', ], 'allowNoReturnInSubclassWithNullableReturnType' => [ ' [ ' [ ' [ '$b' => 'B', ], ], 'allowSomeCovariance' => [ ' [ ' [ 'id, ] = (array) \unserialize((string) $serialized); } public function serialize() : string { return serialize([$this->id]); } }', ], 'clashWithCallMapClass' => [ ' [ ' [ ' [ ' [ ' [ ' [ 'boo(new A()); (new Z())->boo(new A());', ], 'allowMixedExtensionOfIteratorAggregate' => [ ' [ 'f(); (new C)->f("b"); (new C)->f("b", 3); (new C)->f("b", 3, 0.5); (new C)->f("b", 3, 0.5, 0.8);', ], 'allowLessSpecificDocblockTypeOnParent' => [ 'getTargets();', [ '$a' => 'string', ] ], ]; } /** * @return iterable */ public function providerInvalidCodeParse() { return [ 'moreArguments' => [ ' 'Method B::fooFoo has more required parameters than parent method A::fooFoo', ], 'fewerArguments' => [ ' 'Method B::fooFoo has fewer parameters than parent method A::fooFoo', ], 'differentArguments' => [ ' 'Argument 1 of B::fooFoo has wrong type \'bool\', expecting \'int\' as defined ' . 'by A::fooFoo', ], 'nonNullableSubclassParam' => [ ' 'Argument 1 of B::foo has wrong type \'string\', expecting \'string|null\' as', ], 'mismatchingCovariantReturn' => [ ' 'MethodSignatureMismatch', ], 'mismatchingCovariantReturnWithSelf' => [ ' 'MethodSignatureMismatch', ], 'misplacedRequiredParam' => [ ' 'MisplacedRequiredParam', ], 'clasginByRef' => [ ' 'MethodSignatureMismatch', ], 'disallowSubclassesForNonInheritedMethodParams' => [ 'bar(); } }', 'error_message' => 'MoreSpecificImplementedParamType', ], 'disallowVoidToNullConversionSignature' => [ ' 'MethodSignatureMismatch', ], 'abstractExtendsNonAbstractWithMethod' => [ ' 'MethodSignatureMismatch', ], 'traitReturnTypeMismatch' => [ ' 'TraitMethodSignatureMismatch', ], 'abstractTraitMethodWithDifferentReturnType' => [ ' 'TraitMethodSignatureMismatch', ], 'traitMoreParams' => [ ' 'TraitMethodSignatureMismatch', ], 'abstractTraitMethodWithDifferentParamType' => [ ' 'TraitMethodSignatureMismatch', ], 'mustOmitReturnType' => [ ' 'MethodSignatureMustOmitReturnType', ], 'requireParam' => [ ' 'MethodSignatureMismatch - src' . DIRECTORY_SEPARATOR . 'somefile.php:6:27 - Method C::foo has more required', ], 'inheritParamTypes' => [ 'foo(new stdClass);', 'error_message' => 'InvalidArgument', ], 'interfaceHasFewerConstructorArgs' => [ ' 'MethodSignatureMismatch', ], 'enforceParameterInheritanceWithInheritDoc' => [ 'boo(new A());', 'error_message' => 'ArgumentTypeCoercion', ], 'enforceParameterInheritanceWithCapitalizedInheritDoc' => [ 'boo(new A());', 'error_message' => 'ArgumentTypeCoercion', ], 'warnAboutMismatchingClassParamDoc' => [ ' 'MismatchingDocblockParamType', ], 'warnAboutMismatchingInterfaceParamDoc' => [ ' 'MismatchingDocblockParamType', ], 'interfaceInsertDocblockTypes' => [ ' */ public function getFoos() : array; } class A implements I { public function getFoos() : array { return [new Bar()]; } }', 'error_message' => 'InvalidReturnStatement', ], 'classInsertDocblockTypesFromParent' => [ ' */ public function getFoos() : array { return [new Foo()]; } } class A extends B { public function getFoos() : array { return [new Bar()]; } }', 'error_message' => 'InvalidReturnStatement', ], 'preventInterfaceOverload' => [ ' $f */ public function f($f): void {} }', 'error_message' => 'MethodSignatureMismatch', ['MoreSpecificImplementedParamType'] ], ]; } }