Skip to content

Commit 52a9223

Browse files
Add SimpleXMLElementExtension (#143)
1 parent 8fda6d3 commit 52a9223

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-0
lines changed

extension.neon

+5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ services:
6262
tags:
6363
- exceptionRules.dynamicConstructorThrowTypeExtension
6464

65+
-
66+
class: Pepakriz\PHPStanExceptionRules\Extension\SimpleXMLElementExtension
67+
tags:
68+
- exceptionRules.dynamicConstructorThrowTypeExtension
69+
6570
-
6671
class: Pepakriz\PHPStanExceptionRules\Extension\SplFileObjectExtension
6772
tags:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Pepakriz\PHPStanExceptionRules\Extension;
4+
5+
use Exception;
6+
use Pepakriz\PHPStanExceptionRules\DynamicConstructorThrowTypeExtension;
7+
use Pepakriz\PHPStanExceptionRules\UnsupportedClassException;
8+
use PhpParser\Node\Arg;
9+
use PhpParser\Node\Expr\New_;
10+
use PHPStan\Analyser\Scope;
11+
use PHPStan\Reflection\MethodReflection;
12+
use PHPStan\Type\NeverType;
13+
use PHPStan\Type\ObjectType;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeCombinator;
16+
use PHPStan\Type\TypeUtils;
17+
use PHPStan\Type\VoidType;
18+
use SimpleXMLElement;
19+
use function is_a;
20+
21+
class SimpleXMLElementExtension implements DynamicConstructorThrowTypeExtension
22+
{
23+
24+
/**
25+
* @throws UnsupportedClassException
26+
*/
27+
public function getThrowTypeFromConstructor(MethodReflection $methodReflection, New_ $newNode, Scope $scope): Type
28+
{
29+
if (is_a($methodReflection->getDeclaringClass()->getName(), SimpleXMLElement::class, true)) {
30+
return $this->resolveThrowType($newNode->args, $scope);
31+
}
32+
33+
throw new UnsupportedClassException();
34+
}
35+
36+
/**
37+
* @param Arg[] $args
38+
*/
39+
private function resolveThrowType(array $args, Scope $scope): Type
40+
{
41+
$exceptionType = new ObjectType(Exception::class);
42+
if (!isset($args[0])) {
43+
return $exceptionType;
44+
}
45+
46+
$valueType = $scope->getType($args[0]->value);
47+
foreach (TypeUtils::getConstantStrings($valueType) as $constantString) {
48+
try {
49+
new SimpleXMLElement($constantString->getValue());
50+
} catch (Exception $e) {
51+
return $exceptionType;
52+
}
53+
54+
$valueType = TypeCombinator::remove($valueType, $constantString);
55+
}
56+
57+
if (!$valueType instanceof NeverType) {
58+
return $exceptionType;
59+
}
60+
61+
return new VoidType();
62+
}
63+
64+
}

tests/src/Rules/PhpInternalsTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Pepakriz\PHPStanExceptionRules\Extension\IntdivExtension;
1212
use Pepakriz\PHPStanExceptionRules\Extension\JsonEncodeDecodeExtension;
1313
use Pepakriz\PHPStanExceptionRules\Extension\ReflectionExtension;
14+
use Pepakriz\PHPStanExceptionRules\Extension\SimpleXMLElementExtension;
1415
use Pepakriz\PHPStanExceptionRules\Extension\SplFileObjectExtension;
1516
use Pepakriz\PHPStanExceptionRules\RuleTestCase;
1617
use PHPStan\Rules\Rule;
@@ -31,6 +32,7 @@ protected function getRule(): Rule
3132
$jsonEncodeDecodeExtension = new JsonEncodeDecodeExtension();
3233
$intdivExtension = new IntdivExtension();
3334
$domDocumentExtension = new DOMDocumentExtension();
35+
$simpleXMLElementEtension = new SimpleXMLElementExtension();
3436

3537
return new ThrowsPhpDocRule(
3638
new CheckedExceptionService(
@@ -48,6 +50,7 @@ protected function getRule(): Rule
4850
$dateIntervalExtension,
4951
$dateTimeExtension,
5052
$splFileObjectExtension,
53+
$simpleXMLElementEtension,
5154
],
5255
[
5356
$jsonEncodeDecodeExtension,

tests/src/Rules/data/throws-php-internal-functions.php

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use ReflectionObject;
1313
use ReflectionProperty;
1414
use ReflectionZendExtension;
15+
use SimpleXMLElement;
1516
use SplFileObject;
1617
use stdClass;
1718
use Throwable;
@@ -121,6 +122,13 @@ public function testSplFileObject(): void
121122
new SplFileObject(); // error: Missing @throws LogicException annotation; Missing @throws RuntimeException annotation
122123
}
123124

125+
public function testSimpleXMLElement(): void
126+
{
127+
new SimpleXMLElement(); // error: Missing @throws Exception annotation
128+
new SimpleXMLElement('foo'); // error: Missing @throws Exception annotation
129+
new SimpleXMLElement('<foo />');
130+
}
131+
124132
public function testIntdiv(): void
125133
{
126134
/** @var int $integer */

0 commit comments

Comments
 (0)