Skip to content

Commit

Permalink
Add get_sites() dynamic function return type extension (#117)
Browse files Browse the repository at this point in the history
* Add tests for `get_posts()`

* Add `get_sites()` dynamic function return type extension

* Simplify switch

* Simplify `isFunctionSupported`

* No null coalescing
  • Loading branch information
swissspidy committed Sep 22, 2022
1 parent c9fb3dc commit e644df7
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 1 deletion.
4 changes: 4 additions & 0 deletions extension.neon
Expand Up @@ -35,6 +35,10 @@ services:
class: SzepeViktor\PHPStan\WordPress\GetPostsDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: SzepeViktor\PHPStan\WordPress\GetSitesDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: SzepeViktor\PHPStan\WordPress\GetTaxonomiesDynamicFunctionReturnTypeExtension
tags:
Expand Down
2 changes: 1 addition & 1 deletion src/GetPostsDynamicFunctionReturnTypeExtension.php
Expand Up @@ -22,7 +22,7 @@ class GetPostsDynamicFunctionReturnTypeExtension implements \PHPStan\Type\Dynami
{
public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return in_array($functionReflection->getName(), ['get_posts'], true);
return $functionReflection->getName() === 'get_posts';
}

/**
Expand Down
75 changes: 75 additions & 0 deletions src/GetSitesDynamicFunctionReturnTypeExtension.php
@@ -0,0 +1,75 @@
<?php

/**
* Set return type of get_sites().
*/

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress;

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

class GetSitesDynamicFunctionReturnTypeExtension implements \PHPStan\Type\DynamicFunctionReturnTypeExtension
{
public function isFunctionSupported(FunctionReflection $functionReflection): bool
{
return $functionReflection->getName() === 'get_sites';
}

/**
* @see https://developer.wordpress.org/reference/classes/wp_query/#return-fields-parameter
*/
// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
{
$args = $functionCall->getArgs();

// Called without arguments
if (count($args) === 0) {
return new ArrayType(new IntegerType(), new ObjectType('WP_Site'));
}

$fields = '';

$argumentType = $scope->getType($args[0]->value);

// Called with an array argument
if ($argumentType instanceof ConstantArrayType) {
foreach ($argumentType->getKeyTypes() as $index => $key) {
if (! $key instanceof ConstantStringType || $key->getValue() !== 'fields') {
continue;
}

$fieldsType = $argumentType->getValueTypes()[$index];
if ($fieldsType instanceof ConstantStringType) {
$fields = $fieldsType->getValue();
}
break;
}
}

// Called with a string argument
if ($argumentType instanceof ConstantStringType) {
parse_str($argumentType->getValue(), $variables);
$fields = $variables['fields'] ?? 'all';
}

switch ($fields) {
case 'count':
return new IntegerType();
case 'ids':
return new ArrayType(new IntegerType(), new IntegerType());
default:
return new ArrayType(new IntegerType(), new ObjectType('WP_Site'));
}
}
}
2 changes: 2 additions & 0 deletions tests/DynamicReturnTypeExtensionTest.php
Expand Up @@ -20,6 +20,8 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_comment.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_object_taxonomies.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_post.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_posts.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_sites.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/get_terms.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/has_filter.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/mysql2date.php');
Expand Down
12 changes: 12 additions & 0 deletions tests/data/get_posts.php
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress\Tests;

use function PHPStan\Testing\assertType;

assertType('array<int, WP_Post>', get_posts());
assertType('array<int, WP_Post>', get_posts(['fields' => 'all']));
assertType('array<int, int>', get_posts(['fields' => 'ids']));
assertType('array<int, int>', get_posts(['fields' => 'id=>parent']));
11 changes: 11 additions & 0 deletions tests/data/get_sites.php
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress\Tests;

use function PHPStan\Testing\assertType;

assertType('array<int, WP_Site>', get_sites());
assertType('int', get_sites(['fields' => 'count']));
assertType('array<int, int>', get_sites(['fields' => 'ids']));

0 comments on commit e644df7

Please sign in to comment.