Skip to content

Commit

Permalink
Introduce dynamic return type for the get_approved_comments function (
Browse files Browse the repository at this point in the history
#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`.
  • Loading branch information
lipemat authored Feb 23, 2024
1 parent 6fbfbdf commit 6bbffec
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
4 changes: 4 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ services:
class: SzepeViktor\PHPStan\WordPress\EchoKeyDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: SzepeViktor\PHPStan\WordPress\GetApprovedCommentsDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: SzepeViktor\PHPStan\WordPress\GetPermalinkDynamicFunctionReturnTypeExtension
tags:
Expand Down
95 changes: 95 additions & 0 deletions src/GetApprovedCommentsDynamicFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/**
* Set return type of get_approved_comments() based on its passed arguments.
*/

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress;

use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;

class GetApprovedCommentsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
{
/**
* @var string[]
*/
protected static $supported = [
'get_approved_comments',
];


public function isFunctionSupported( FunctionReflection $functionReflection ): bool {
return \in_array( $functionReflection->getName(), static::$supported, true );
}


/**
* - Return 'WP_Comment[]' by default.
* - Return `int[]` if `$fields = 'ids'`.
* - Return `int` if `$count = true`.
*
* @link https://developer.wordpress.org/reference/functions/get_approved_comments/#parameters
*/
public function getTypeFromFunctionCall( FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope ): ?Type {
$args = $functionCall->getArgs();

if ( \count( $args ) < 2 ) {
return self::defaultType();
}

$argumentType = $scope->getType( $args[1]->value );
if ( $argumentType->isConstantArray()->no() ) {
return self::getIndeterminedType();
}

foreach ( $argumentType->getConstantArrays() as $array ) {
if ( $array->hasOffsetValueType( new ConstantStringType( 'count' ) )->yes() ) {
$fieldsValueTypes = $array->getOffsetValueType( new ConstantStringType( 'count' ) );
if ( $fieldsValueTypes->isTrue()->yes() ) {
return new IntegerType();
}
}
if ( $array->hasOffsetValueType( new ConstantStringType( 'fields' ) )->yes() ) {
$fieldsValueTypes = $array->getOffsetValueType( new ConstantStringType( 'fields' ) )->getConstantStrings();
if ( \count( $fieldsValueTypes ) === 0 ) {
return self::getIndeterminedType();
}
if ( 'ids' === $fieldsValueTypes[0]->getValue() ) {
return new ArrayType( new IntegerType(), new IntegerType() );
}
}
}

return self::defaultType();
}


protected static function defaultType(): Type {
return new ArrayType( new IntegerType(), new ObjectType( 'WP_Comment' ) );
}


/**
* Type defined on the PHPDocs.
*
* @return Type
*/
protected static function getIndeterminedType(): Type {
return TypeCombinator::union(
new ArrayType( new IntegerType(), new ObjectType( 'WP_Comment' ) ),
new ArrayType( new IntegerType(), new IntegerType() ),
new IntegerType()
);
}
}
1 change: 1 addition & 0 deletions tests/DynamicReturnTypeExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function dataFileAsserts(): iterable
yield from self::gatherAssertTypes(__DIR__ . '/data/echo_key.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/echo_parameter.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/esc_sql.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/get_approved_comments.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/get_comment.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/get_object_taxonomies.php');
yield from self::gatherAssertTypes(__DIR__ . '/data/get_permalink.php');
Expand Down
25 changes: 25 additions & 0 deletions tests/data/get_approved_comments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare( strict_types=1 );

use function PHPStan\Testing\assertType;

assertType( 'array<int, WP_Comment>', get_approved_comments( 1 ) );

assertType( 'int', get_approved_comments( 1, [
'count' => true,
] ) );
assertType( 'int', get_approved_comments( 1, [
'count' => true,
'fields' => 'ids',
] ) );
assertType( 'array<int, WP_Comment>', get_approved_comments( 1, [
'count' => false,
] ) );
assertType( 'array<int, int>', get_approved_comments( 1, [
'fields' => 'ids',
] ) );
assertType( 'array<int, int>', get_approved_comments( 1, [
'count' => false,
'fields' => 'ids',
] ) );

0 comments on commit 6bbffec

Please sign in to comment.