Skip to content

Commit 7e5ed70

Browse files
authored
Merge pull request #91 from johnbillion/term_exists
2 parents 927630b + 97c351e commit 7e5ed70

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

extension.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ services:
5959
class: SzepeViktor\PHPStan\WordPress\WPErrorParameterDynamicFunctionReturnTypeExtension
6060
tags:
6161
- phpstan.broker.dynamicFunctionReturnTypeExtension
62+
-
63+
class: SzepeViktor\PHPStan\WordPress\TermExistsDynamicFunctionReturnTypeExtension
64+
tags:
65+
- phpstan.broker.dynamicFunctionReturnTypeExtension
6266
rules:
6367
- SzepeViktor\PHPStan\WordPress\HookDocsRule
6468
parameters:

src/HookDocBlock.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
class HookDocBlock
1717
{
18-
1918
/** @var \PHPStan\Type\FileTypeMapper */
2019
protected $fileTypeMapper;
2120

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
//phpcs:disable SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter
4+
5+
/**
6+
* Set return type of term_exists().
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace SzepeViktor\PHPStan\WordPress;
12+
13+
use PhpParser\Node\Expr\FuncCall;
14+
use PHPStan\Analyser\Scope;
15+
use PHPStan\Reflection\FunctionReflection;
16+
use PHPStan\Type\Type;
17+
use PHPStan\Type\Constant\ConstantArrayType;
18+
use PHPStan\Type\Constant\ConstantIntegerType;
19+
use PHPStan\Type\StringType;
20+
use PHPStan\Type\Constant\ConstantStringType;
21+
use PHPStan\Type\MixedType;
22+
use PHPStan\Type\NullType;
23+
use PHPStan\Type\TypeCombinator;
24+
25+
class TermExistsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
26+
{
27+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
28+
{
29+
return in_array(
30+
$functionReflection->getName(),
31+
[
32+
'is_term',
33+
'tag_exists',
34+
'term_exists',
35+
],
36+
true
37+
);
38+
}
39+
40+
/**
41+
* @see https://developer.wordpress.org/reference/functions/term_exists/
42+
*/
43+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
44+
{
45+
$args = $functionCall->getArgs();
46+
47+
if (count($args) === 0) {
48+
return new MixedType();
49+
}
50+
51+
$termType = $scope->getType($args[0]->value);
52+
$taxonomyType = isset($args[1]) ? $scope->getType($args[1]->value) : new ConstantStringType('');
53+
54+
if ($termType instanceof NullType) {
55+
return new NullType();
56+
}
57+
58+
if (($termType instanceof ConstantIntegerType) && $termType->getValue() === 0) {
59+
return new ConstantIntegerType(0);
60+
}
61+
62+
$withTaxonomy = new ConstantArrayType(
63+
[
64+
new ConstantStringType('term_id'),
65+
new ConstantStringType('term_taxonomy_id'),
66+
],
67+
[
68+
new StringType(),
69+
new StringType(),
70+
]
71+
);
72+
$withoutTaxonomy = new StringType();
73+
74+
if (($taxonomyType instanceof ConstantStringType) && $taxonomyType->getValue() !== '') {
75+
return TypeCombinator::union(
76+
$withTaxonomy,
77+
new NullType()
78+
);
79+
}
80+
81+
if (($taxonomyType instanceof ConstantStringType) && $taxonomyType->getValue() === '') {
82+
return TypeCombinator::union(
83+
$withoutTaxonomy,
84+
new NullType()
85+
);
86+
}
87+
88+
return TypeCombinator::union(
89+
$withTaxonomy,
90+
$withoutTaxonomy,
91+
new NullType()
92+
);
93+
}
94+
}

tests/DynamicReturnTypeExtensionTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public function dataFileAsserts(): iterable
2020
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_post.php');
2121
yield from $this->gatherAssertTypes(__DIR__ . '/data/mysql2date.php');
2222
yield from $this->gatherAssertTypes(__DIR__ . '/data/shortcode_atts.php');
23+
yield from $this->gatherAssertTypes(__DIR__ . '/data/term_exists.php');
2324
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_error_parameter.php');
2425
}
2526

tests/data/term_exists.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SzepeViktor\PHPStan\WordPress\Tests;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
$term = $_GET['term'] ?? 123;
10+
$taxo = $_GET['taxo'] ?? 'category';
11+
12+
// Empty taxonomy
13+
assertType('string|null', term_exists(123));
14+
assertType('string|null', term_exists(123, ''));
15+
assertType('string|null', term_exists($term));
16+
assertType('string|null', term_exists($term, ''));
17+
18+
// Fixed taxonomy string
19+
assertType('array{term_id: string, term_taxonomy_id: string}|null', term_exists(123, 'category'));
20+
assertType('array{term_id: string, term_taxonomy_id: string}|null', term_exists($term, 'category'));
21+
22+
// Unknown taxonomy type
23+
assertType('array{term_id: string, term_taxonomy_id: string}|string|null', term_exists(123, $taxo));
24+
25+
// Term 0
26+
assertType('0', term_exists(0));
27+
assertType('0', term_exists(0, $taxo));
28+
assertType('0', term_exists(0, 'category'));
29+
assertType('0', term_exists(0, ''));
30+
31+
// Term null
32+
assertType('null', term_exists(null));
33+
assertType('null', term_exists(null, $taxo));
34+
assertType('null', term_exists(null, 'category'));
35+
assertType('null', term_exists(null, ''));
36+
37+
// Oh dear
38+
assertType('mixed', term_exists());

0 commit comments

Comments
 (0)