Skip to content

Commit 6bbffec

Browse files
authored
Introduce dynamic return type for the get_approved_comments function (#211)
`get_approved_comments` changes the return type based on value within the pass `$args` parameter. - Return `WP_Comment[]` by default. - Return `int[]` if `$fields = 'ids'`. - Return `int` if `$count = true`.
1 parent 6fbfbdf commit 6bbffec

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

extension.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ services:
1515
class: SzepeViktor\PHPStan\WordPress\EchoKeyDynamicFunctionReturnTypeExtension
1616
tags:
1717
- phpstan.broker.dynamicFunctionReturnTypeExtension
18+
-
19+
class: SzepeViktor\PHPStan\WordPress\GetApprovedCommentsDynamicFunctionReturnTypeExtension
20+
tags:
21+
- phpstan.broker.dynamicFunctionReturnTypeExtension
1822
-
1923
class: SzepeViktor\PHPStan\WordPress\GetPermalinkDynamicFunctionReturnTypeExtension
2024
tags:
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/**
4+
* Set return type of get_approved_comments() based on its passed arguments.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace SzepeViktor\PHPStan\WordPress;
10+
11+
use PhpParser\Node\Expr\FuncCall;
12+
use PHPStan\Analyser\Scope;
13+
use PHPStan\Reflection\FunctionReflection;
14+
use PHPStan\Type\ArrayType;
15+
use PHPStan\Type\Constant\ConstantStringType;
16+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
17+
use PHPStan\Type\IntegerType;
18+
use PHPStan\Type\ObjectType;
19+
use PHPStan\Type\Type;
20+
use PHPStan\Type\TypeCombinator;
21+
22+
class GetApprovedCommentsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
23+
{
24+
/**
25+
* @var string[]
26+
*/
27+
protected static $supported = [
28+
'get_approved_comments',
29+
];
30+
31+
32+
public function isFunctionSupported( FunctionReflection $functionReflection ): bool {
33+
return \in_array( $functionReflection->getName(), static::$supported, true );
34+
}
35+
36+
37+
/**
38+
* - Return 'WP_Comment[]' by default.
39+
* - Return `int[]` if `$fields = 'ids'`.
40+
* - Return `int` if `$count = true`.
41+
*
42+
* @link https://developer.wordpress.org/reference/functions/get_approved_comments/#parameters
43+
*/
44+
public function getTypeFromFunctionCall( FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope ): ?Type {
45+
$args = $functionCall->getArgs();
46+
47+
if ( \count( $args ) < 2 ) {
48+
return self::defaultType();
49+
}
50+
51+
$argumentType = $scope->getType( $args[1]->value );
52+
if ( $argumentType->isConstantArray()->no() ) {
53+
return self::getIndeterminedType();
54+
}
55+
56+
foreach ( $argumentType->getConstantArrays() as $array ) {
57+
if ( $array->hasOffsetValueType( new ConstantStringType( 'count' ) )->yes() ) {
58+
$fieldsValueTypes = $array->getOffsetValueType( new ConstantStringType( 'count' ) );
59+
if ( $fieldsValueTypes->isTrue()->yes() ) {
60+
return new IntegerType();
61+
}
62+
}
63+
if ( $array->hasOffsetValueType( new ConstantStringType( 'fields' ) )->yes() ) {
64+
$fieldsValueTypes = $array->getOffsetValueType( new ConstantStringType( 'fields' ) )->getConstantStrings();
65+
if ( \count( $fieldsValueTypes ) === 0 ) {
66+
return self::getIndeterminedType();
67+
}
68+
if ( 'ids' === $fieldsValueTypes[0]->getValue() ) {
69+
return new ArrayType( new IntegerType(), new IntegerType() );
70+
}
71+
}
72+
}
73+
74+
return self::defaultType();
75+
}
76+
77+
78+
protected static function defaultType(): Type {
79+
return new ArrayType( new IntegerType(), new ObjectType( 'WP_Comment' ) );
80+
}
81+
82+
83+
/**
84+
* Type defined on the PHPDocs.
85+
*
86+
* @return Type
87+
*/
88+
protected static function getIndeterminedType(): Type {
89+
return TypeCombinator::union(
90+
new ArrayType( new IntegerType(), new ObjectType( 'WP_Comment' ) ),
91+
new ArrayType( new IntegerType(), new IntegerType() ),
92+
new IntegerType()
93+
);
94+
}
95+
}

tests/DynamicReturnTypeExtensionTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public function dataFileAsserts(): iterable
1818
yield from self::gatherAssertTypes(__DIR__ . '/data/echo_key.php');
1919
yield from self::gatherAssertTypes(__DIR__ . '/data/echo_parameter.php');
2020
yield from self::gatherAssertTypes(__DIR__ . '/data/esc_sql.php');
21+
yield from self::gatherAssertTypes(__DIR__ . '/data/get_approved_comments.php');
2122
yield from self::gatherAssertTypes(__DIR__ . '/data/get_comment.php');
2223
yield from self::gatherAssertTypes(__DIR__ . '/data/get_object_taxonomies.php');
2324
yield from self::gatherAssertTypes(__DIR__ . '/data/get_permalink.php');
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare( strict_types=1 );
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
assertType( 'array<int, WP_Comment>', get_approved_comments( 1 ) );
8+
9+
assertType( 'int', get_approved_comments( 1, [
10+
'count' => true,
11+
] ) );
12+
assertType( 'int', get_approved_comments( 1, [
13+
'count' => true,
14+
'fields' => 'ids',
15+
] ) );
16+
assertType( 'array<int, WP_Comment>', get_approved_comments( 1, [
17+
'count' => false,
18+
] ) );
19+
assertType( 'array<int, int>', get_approved_comments( 1, [
20+
'fields' => 'ids',
21+
] ) );
22+
assertType( 'array<int, int>', get_approved_comments( 1, [
23+
'count' => false,
24+
'fields' => 'ids',
25+
] ) );

0 commit comments

Comments
 (0)