Skip to content

Commit e644df7

Browse files
authored
Add get_sites() dynamic function return type extension (#117)
* Add tests for `get_posts()` * Add `get_sites()` dynamic function return type extension * Simplify switch * Simplify `isFunctionSupported` * No null coalescing
1 parent c9fb3dc commit e644df7

6 files changed

+105
-1
lines changed

extension.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ services:
3535
class: SzepeViktor\PHPStan\WordPress\GetPostsDynamicFunctionReturnTypeExtension
3636
tags:
3737
- phpstan.broker.dynamicFunctionReturnTypeExtension
38+
-
39+
class: SzepeViktor\PHPStan\WordPress\GetSitesDynamicFunctionReturnTypeExtension
40+
tags:
41+
- phpstan.broker.dynamicFunctionReturnTypeExtension
3842
-
3943
class: SzepeViktor\PHPStan\WordPress\GetTaxonomiesDynamicFunctionReturnTypeExtension
4044
tags:

src/GetPostsDynamicFunctionReturnTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class GetPostsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\Dynami
2222
{
2323
public function isFunctionSupported(FunctionReflection $functionReflection): bool
2424
{
25-
return in_array($functionReflection->getName(), ['get_posts'], true);
25+
return $functionReflection->getName() === 'get_posts';
2626
}
2727

2828
/**
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/**
4+
* Set return type of get_sites().
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\Type;
15+
use PHPStan\Type\ArrayType;
16+
use PHPStan\Type\IntegerType;
17+
use PHPStan\Type\ObjectType;
18+
use PHPStan\Type\Constant\ConstantArrayType;
19+
use PHPStan\Type\Constant\ConstantStringType;
20+
21+
class GetSitesDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
22+
{
23+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
24+
{
25+
return $functionReflection->getName() === 'get_sites';
26+
}
27+
28+
/**
29+
* @see https://developer.wordpress.org/reference/classes/wp_query/#return-fields-parameter
30+
*/
31+
// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter
32+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
33+
{
34+
$args = $functionCall->getArgs();
35+
36+
// Called without arguments
37+
if (count($args) === 0) {
38+
return new ArrayType(new IntegerType(), new ObjectType('WP_Site'));
39+
}
40+
41+
$fields = '';
42+
43+
$argumentType = $scope->getType($args[0]->value);
44+
45+
// Called with an array argument
46+
if ($argumentType instanceof ConstantArrayType) {
47+
foreach ($argumentType->getKeyTypes() as $index => $key) {
48+
if (! $key instanceof ConstantStringType || $key->getValue() !== 'fields') {
49+
continue;
50+
}
51+
52+
$fieldsType = $argumentType->getValueTypes()[$index];
53+
if ($fieldsType instanceof ConstantStringType) {
54+
$fields = $fieldsType->getValue();
55+
}
56+
break;
57+
}
58+
}
59+
60+
// Called with a string argument
61+
if ($argumentType instanceof ConstantStringType) {
62+
parse_str($argumentType->getValue(), $variables);
63+
$fields = $variables['fields'] ?? 'all';
64+
}
65+
66+
switch ($fields) {
67+
case 'count':
68+
return new IntegerType();
69+
case 'ids':
70+
return new ArrayType(new IntegerType(), new IntegerType());
71+
default:
72+
return new ArrayType(new IntegerType(), new ObjectType('WP_Site'));
73+
}
74+
}
75+
}

tests/DynamicReturnTypeExtensionTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public function dataFileAsserts(): iterable
2020
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_comment.php');
2121
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_object_taxonomies.php');
2222
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_post.php');
23+
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_posts.php');
24+
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_sites.php');
2325
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_terms.php');
2426
yield from $this->gatherAssertTypes(__DIR__ . '/data/has_filter.php');
2527
yield from $this->gatherAssertTypes(__DIR__ . '/data/mysql2date.php');

tests/data/get_posts.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SzepeViktor\PHPStan\WordPress\Tests;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
assertType('array<int, WP_Post>', get_posts());
10+
assertType('array<int, WP_Post>', get_posts(['fields' => 'all']));
11+
assertType('array<int, int>', get_posts(['fields' => 'ids']));
12+
assertType('array<int, int>', get_posts(['fields' => 'id=>parent']));

tests/data/get_sites.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SzepeViktor\PHPStan\WordPress\Tests;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
assertType('array<int, WP_Site>', get_sites());
10+
assertType('int', get_sites(['fields' => 'count']));
11+
assertType('array<int, int>', get_sites(['fields' => 'ids']));

0 commit comments

Comments
 (0)