Skip to content

Commit f6107ad

Browse files
committed
MethodsClassReflectionExtension for SoapClient magic methods
1 parent 179107e commit f6107ad

File tree

7 files changed

+170
-1
lines changed

7 files changed

+170
-1
lines changed

build/baseline-7.4.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,6 @@ parameters:
9494
path: ../src/Reflection/ReflectionProvider/SetterReflectionProviderProvider.php
9595

9696
-
97-
message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:270 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#"
97+
message: "#^Class class@anonymous/src/Testing/TestCase\\.php\\:271 has an uninitialized property \\$reflectionProvider\\. Give it default value or assign it in the constructor\\.$#"
9898
count: 1
9999
path: ../src/Testing/TestCase.php

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,11 @@ services:
650650
-
651651
implement: PHPStan\Reflection\Php\PhpMethodReflectionFactory
652652

653+
-
654+
class: PHPStan\Reflection\Php\Soap\SoapClientMethodsClassReflectionExtension
655+
tags:
656+
- phpstan.broker.methodsClassReflectionExtension
657+
653658
-
654659
class: PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension
655660
tags:
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Php\Soap;
4+
5+
use PHPStan\Reflection\ClassMemberReflection;
6+
use PHPStan\Reflection\ClassReflection;
7+
use PHPStan\Reflection\FunctionVariant;
8+
use PHPStan\Reflection\MethodReflection;
9+
use PHPStan\TrinaryLogic;
10+
use PHPStan\Type\Generic\TemplateTypeMap;
11+
use PHPStan\Type\MixedType;
12+
use PHPStan\Type\ObjectType;
13+
use PHPStan\Type\Type;
14+
15+
class SoapClientMethodReflection implements MethodReflection
16+
{
17+
18+
private ClassReflection $declaringClass;
19+
20+
private string $name;
21+
22+
public function __construct(ClassReflection $declaringClass, string $name)
23+
{
24+
$this->declaringClass = $declaringClass;
25+
$this->name = $name;
26+
}
27+
28+
public function getDeclaringClass(): ClassReflection
29+
{
30+
return $this->declaringClass;
31+
}
32+
33+
public function isStatic(): bool
34+
{
35+
return false;
36+
}
37+
38+
public function isPrivate(): bool
39+
{
40+
return false;
41+
}
42+
43+
public function isPublic(): bool
44+
{
45+
return true;
46+
}
47+
48+
public function getDocComment(): ?string
49+
{
50+
return null;
51+
}
52+
53+
public function getName(): string
54+
{
55+
return $this->name;
56+
}
57+
58+
public function getPrototype(): ClassMemberReflection
59+
{
60+
return $this;
61+
}
62+
63+
public function getVariants(): array
64+
{
65+
return [
66+
new FunctionVariant(
67+
TemplateTypeMap::createEmpty(),
68+
TemplateTypeMap::createEmpty(),
69+
[],
70+
true,
71+
new MixedType(true)
72+
),
73+
];
74+
}
75+
76+
public function isDeprecated(): TrinaryLogic
77+
{
78+
return TrinaryLogic::createNo();
79+
}
80+
81+
public function getDeprecatedDescription(): ?string
82+
{
83+
return null;
84+
}
85+
86+
public function isFinal(): TrinaryLogic
87+
{
88+
return TrinaryLogic::createNo();
89+
}
90+
91+
public function isInternal(): TrinaryLogic
92+
{
93+
return TrinaryLogic::createNo();
94+
}
95+
96+
public function getThrowType(): ?Type
97+
{
98+
return new ObjectType('SoapFault');
99+
}
100+
101+
public function hasSideEffects(): TrinaryLogic
102+
{
103+
return TrinaryLogic::createYes();
104+
}
105+
106+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Php\Soap;
4+
5+
use PHPStan\Reflection\ClassReflection;
6+
use PHPStan\Reflection\MethodReflection;
7+
use PHPStan\Reflection\MethodsClassReflectionExtension;
8+
9+
class SoapClientMethodsClassReflectionExtension implements MethodsClassReflectionExtension
10+
{
11+
12+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
13+
{
14+
return $classReflection->getName() === 'SoapClient' || $classReflection->isSubclassOf('SoapClient');
15+
}
16+
17+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
18+
{
19+
return new SoapClientMethodReflection($classReflection, $methodName);
20+
}
21+
22+
}

src/Testing/TestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
use PHPStan\Reflection\Php\PhpFunctionReflection;
6161
use PHPStan\Reflection\Php\PhpMethodReflection;
6262
use PHPStan\Reflection\Php\PhpMethodReflectionFactory;
63+
use PHPStan\Reflection\Php\Soap\SoapClientMethodsClassReflectionExtension;
6364
use PHPStan\Reflection\Php\UniversalObjectCratesClassReflectionExtension;
6465
use PHPStan\Reflection\ReflectionProvider;
6566
use PHPStan\Reflection\ReflectionProvider\ClassBlacklistReflectionProvider;
@@ -356,6 +357,7 @@ public function create(
356357
$classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension($phpExtension);
357358
$classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension(new MixinMethodsClassReflectionExtension([]));
358359
$classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension($annotationsMethodsClassReflectionExtension);
360+
$classReflectionExtensionRegistryProvider->addMethodsClassReflectionExtension(new SoapClientMethodsClassReflectionExtension());
359361

360362
$setterReflectionProviderProvider->setReflectionProvider($actualReflectionProvider);
361363
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ public function dataFileAsserts(): iterable
387387
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4838.php');
388388
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4879.php');
389389
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4820.php');
390+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4822.php');
390391
}
391392

392393
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Bug4822;
4+
5+
use PHPStan\TrinaryLogic;
6+
use function PHPStan\Testing\assertVariableCertainty;
7+
8+
class Foo
9+
{
10+
11+
/**
12+
* @throws \Exception
13+
*/
14+
function save(): void {
15+
16+
}
17+
18+
function doFoo()
19+
{
20+
$soapClient = new \SoapClient('https://example.com/?wsdl');
21+
22+
try {
23+
$response = $soapClient->test();
24+
25+
if (is_array($response)) {
26+
$this->save();
27+
}
28+
} catch (\Exception $e) {
29+
assertVariableCertainty(TrinaryLogic::createMaybe(), $response);
30+
}
31+
}
32+
33+
}

0 commit comments

Comments
 (0)