Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into import-extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
IanDelMar committed Sep 7, 2024
2 parents 16407a8 + fc274f7 commit 2e05e60
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 27 deletions.
25 changes: 23 additions & 2 deletions functionMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* @link https://github.com/phpstan/phpstan-src/blob/1.10.x/resources/functionMap.php
*/
return [
'_get_list_table' => ["(\$class_name is 'WP_Posts_List_Table'|'WP_Media_List_Table'|'WP_Terms_List_Table'|'WP_Users_List_Table'|'WP_Comments_List_Table'|'WP_Post_Comments_List_Table'|'WP_Links_List_Table'|'WP_Plugin_Install_List_Table'|'WP_Themes_List_Table'|'WP_Theme_Install_List_Table'|'WP_Plugins_List_Table'|'WP_Application_Passwords_List_Table'|'WP_MS_Sites_List_Table'|'WP_MS_Users_List_Table'|'WP_MS_Themes_List_Table'|'WP_Privacy_Data_Export_Requests_List_Table'|'WP_Privacy_Data_Removal_Requests_List_Table' ? \WP_List_Table : false)", 'args' => 'array{screen?: string}'],
'_get_list_table' => ["(\$class_name is 'WP_Posts_List_Table'|'WP_Media_List_Table'|'WP_Terms_List_Table'|'WP_Users_List_Table'|'WP_Comments_List_Table'|'WP_Post_Comments_List_Table'|'WP_Links_List_Table'|'WP_Plugin_Install_List_Table'|'WP_Themes_List_Table'|'WP_Theme_Install_List_Table'|'WP_Plugins_List_Table'|'WP_Application_Passwords_List_Table'|'WP_MS_Sites_List_Table'|'WP_MS_Users_List_Table'|'WP_MS_Themes_List_Table'|'WP_Privacy_Data_Export_Requests_List_Table'|'WP_Privacy_Data_Removal_Requests_List_Table' ? T : false)", '@phpstan-template' => 'T', 'class_name' => 'class-string<T>', 'args' => 'array{screen?: string}'],
'addslashes_gpc' => ['T', '@phpstan-template' => 'T', 'gpc' => 'T'],
'add_submenu_page' => [null, 'callback' => "''|callable"],
'have_posts' => [null, '@phpstan-impure' => ''],
Expand All @@ -46,6 +46,7 @@
'wp_die' => ['($args is array{exit: false} ? void : never))'],
'wp_dropdown_languages' => ["(\$args is array{id: null|''} ? void : (\$args is array{name: null|''} ? void : string))"],
'wp_clear_scheduled_hook' => ['(0|positive-int|($wp_error is false ? false : \WP_Error))', 'args' => $cronArgsType],
'wp_generate_tag_cloud' => ["(\$args is array{format: 'array'} ? array<int, string> : string)"],
'wp_get_schedule' => [null, 'args' => $cronArgsType],
'wp_get_scheduled_event' => [null, 'args' => $cronArgsType],
'wp_get_archives' => ['($args is array{echo: false|0} ? string : void)'],
Expand All @@ -72,6 +73,7 @@
'wp_schedule_event' => ['($wp_error is false ? bool : true|\WP_Error)', 'args' => $cronArgsType],
'wp_schedule_single_event' => ['($wp_error is false ? bool : true|\WP_Error)', 'args' => $cronArgsType],
'wp_slash' => ['T', '@phpstan-template' => 'T', 'value' => 'T'],
'wp_tag_cloud' => ["(\$args is array{format: 'array'} ? array<int, string>|void : (\$args is array{echo: false|0} ? string|void : void))"],
'wp_unschedule_event' => ['($wp_error is false ? bool : true|\WP_Error)', 'args' => $cronArgsType],
'wp_unslash' => ['T', '@phpstan-template' => 'T', 'value' => 'T'],
'wp_widget_rss_form' => ['void', 'args' => $wpWidgetRssFormArgsType, 'inputs' => $wpWidgetRssFormInputsType],
Expand All @@ -85,7 +87,24 @@
'WP_REST_Request::get_params' => ['T'],
'WP_REST_Request::set_param' => ['void', '@phpstan-template' => 'TOffset of key-of<T>', 'key' => 'TOffset', 'value' => 'T[TOffset]'],
'WP_REST_Request::has_param' => [null, 'key' => 'key-of<T>'],
'WP_Theme' => [null, '@phpstan-type' => "ThemeKey 'Name'|'Version'|'Status'|'Title'|'Author'|'Author Name'|'Author URI'|'Description'|'Template'|'Stylesheet'|'Template Files'|'Stylesheet Files'|'Template Dir'|'Stylesheet Dir'|'Screenshot'|'Tags'|'Theme Root'|'Theme Root URI'|'Parent Theme'"],
'WP_Theme' => [
null,
'@phpstan-type' => "ThemeKey 'Name'|'Version'|'Status'|'Title'|'Author'|'Author Name'|'Author URI'|'Description'|'Template'|'Stylesheet'|'Template Files'|'Stylesheet Files'|'Template Dir'|'Stylesheet Dir'|'Screenshot'|'Tags'|'Theme Root'|'Theme Root URI'|'Parent Theme'",
'@phpstan-property-read string $name' => '',
'@phpstan-property-read string $title' => '',
'@phpstan-property-read string $version' => '',
'@phpstan-property-read string $parent_theme' => '',
'@phpstan-property-read string $template_dir' => '',
'@phpstan-property-read string $stylesheet_dir' => '',
'@phpstan-property-read string $template' => '',
'@phpstan-property-read string $stylesheet' => '',
'@phpstan-property-read string $screenshot' => '',
'@phpstan-property-read string $description' => '',
'@phpstan-property-read string $author' => '',
'@phpstan-property-read list<string> $tags' => '',
'@phpstan-property-read string $theme_root' => '',
'@phpstan-property-read string $theme_root_uri' => '',
],
'WP_Theme::get' => ["(\$header is 'Name'|'ThemeURI'|'Description'|'Author'|'AuthorURI'|'Version'|'Template'|'Status'|'Tags'|'TextDomain'|'DomainPath'|'RequiresWP'|'RequiresPHP'|'UpdateURI' ? (\$header is 'Tags' ? string[] : string) : false)"],
'WP_Theme::offsetExists' => ['($offset is ThemeKey ? true : false)'],
'WP_Theme::offsetGet' => ['($offset is ThemeKey ? mixed : null)'],
Expand Down Expand Up @@ -170,4 +189,6 @@
'wp_parse_list' => ['($input_list is array ? array<scalar> : list<string>)'],
'wp_parse_str' => [null, '@phpstan-param-out' => 'array<int|string, array|string> $result'],
'size_format' => ["(\$bytes is not numeric ? false : (\$bytes is negative-int|'0' ? false : string))"],
'WP_Translations::translate' => ['($singular is null ? null : string)'],
'WP_Translations::translate_plural' => ['($singular is null ? null : ($plural is null ? T : string))', '@phpstan-template T' => 'of string|null', 'singular' => 'T', 'count' => 'int'],
];
6 changes: 6 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
</rule>

<!-- Rules not applied to tests -->
<rule ref="NeutronStandard.Functions.LongFunction.LongFunction">
<exclude-pattern>tests/TypeInferenceTest.php</exclude-pattern>
</rule>
<rule ref="PSR12NeutronRuleset.NamingConventions.MeaningfulVariableName">
<exclude-pattern>tests/</exclude-pattern>
</rule>
Expand All @@ -29,6 +32,9 @@
<rule ref="SlevomatCodingStandard.PHP.RequireExplicitAssertion">
<exclude-pattern>tests/</exclude-pattern>
</rule>
<rule ref="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps">
<exclude-pattern>tests/</exclude-pattern>
</rule>
<rule ref="Squiz.PHP.CommentedOutCode">
<exclude-pattern>tests/</exclude-pattern>
</rule>
Expand Down
15 changes: 9 additions & 6 deletions src/Visitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@

class Visitor extends NodeVisitor
{
/**
* The number of spaces used for indentation in WordPress Doc Comments.
*/
private const WP_INDENT_SIZE = 4;

private \phpDocumentor\Reflection\DocBlockFactoryInterface $docBlockFactory;

/** @var array<string,array<int|string,string>> */
Expand Down Expand Up @@ -200,10 +205,8 @@ private function postProcessNode(Node $node): void
*/
private function generateAdditionalTagsFromDoc(Doc $docComment): array
{
$docCommentText = $docComment->getText();

try {
$docblock = $this->docBlockFactory->create($docCommentText);
$docblock = $this->docBlockFactory->create($docComment->getText());
} catch (\RuntimeException | \InvalidArgumentException $e) {
return [];
}
Expand Down Expand Up @@ -288,7 +291,7 @@ static function (WordPressTag $tag): string {

$newDocComment = sprintf(
"%s\n%s\n */",
substr($docCommentText, 0, -4),
substr($docCommentText, 0, -self::WP_INDENT_SIZE),
implode("\n", $additionStrings)
);

Expand Down Expand Up @@ -477,7 +480,7 @@ private function addStringTags(string $name, Doc $docComment): ?Doc
$docCommentText = $docComment->getText();
$newDocComment = sprintf(
"%s\n * %s\n */",
substr($docCommentText, 0, -4),
substr($docCommentText, 0, -self::WP_INDENT_SIZE),
implode("\n * ", $additions)
);

Expand Down Expand Up @@ -712,7 +715,7 @@ private static function getElementsFromDescription(Description $tagDescription,
private static function getTypesAtLevel(string $text, bool $optional, int $level): array
{
// Populate `$types` with the value of each top level `@type`.
$spaces = str_repeat(' ', ($level * 4));
$spaces = str_repeat(' ', ($level * self::WP_INDENT_SIZE));
$types = preg_split("/\R+{$spaces}@type /", $text);

if ($types === false) {
Expand Down
3 changes: 3 additions & 0 deletions tests/TypeInferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_die.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_dropdown_languages.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_error_parameter.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_generate_tag_cloud.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_get_archives.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_get_post_categories.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_get_post_tags.php');
Expand All @@ -54,7 +55,9 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_list_pages.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_parse_list.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_rest_request.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_tag_cloud.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_theme.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wp_translations.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/wpdb.php');
}

Expand Down
34 changes: 17 additions & 17 deletions tests/data/_get_list_table.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@

assertType('false', _get_list_table('Not_WP_List_Table'));

assertType('WP_List_Table', _get_list_table('WP_Posts_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Media_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Terms_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Users_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Comments_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Post_Comments_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Links_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Plugin_Install_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Themes_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Theme_Install_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Plugins_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Application_Passwords_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_MS_Sites_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_MS_Users_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_MS_Themes_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Privacy_Data_Export_Requests_List_Table'));
assertType('WP_List_Table', _get_list_table('WP_Privacy_Data_Removal_Requests_List_Table'));
assertType('WP_Posts_List_Table', _get_list_table('WP_Posts_List_Table'));
assertType('WP_Media_List_Table', _get_list_table('WP_Media_List_Table'));
assertType('WP_Terms_List_Table', _get_list_table('WP_Terms_List_Table'));
assertType('WP_Users_List_Table', _get_list_table('WP_Users_List_Table'));
assertType('WP_Comments_List_Table', _get_list_table('WP_Comments_List_Table'));
assertType('WP_Post_Comments_List_Table', _get_list_table('WP_Post_Comments_List_Table'));
assertType('WP_Links_List_Table', _get_list_table('WP_Links_List_Table'));
assertType('WP_Plugin_Install_List_Table', _get_list_table('WP_Plugin_Install_List_Table'));
assertType('WP_Themes_List_Table', _get_list_table('WP_Themes_List_Table'));
assertType('WP_Theme_Install_List_Table', _get_list_table('WP_Theme_Install_List_Table'));
assertType('WP_Plugins_List_Table', _get_list_table('WP_Plugins_List_Table'));
assertType('WP_Application_Passwords_List_Table', _get_list_table('WP_Application_Passwords_List_Table'));
assertType('WP_MS_Sites_List_Table', _get_list_table('WP_MS_Sites_List_Table'));
assertType('WP_MS_Users_List_Table', _get_list_table('WP_MS_Users_List_Table'));
assertType('WP_MS_Themes_List_Table', _get_list_table('WP_MS_Themes_List_Table'));
assertType('WP_Privacy_Data_Export_Requests_List_Table', _get_list_table('WP_Privacy_Data_Export_Requests_List_Table'));
assertType('WP_Privacy_Data_Removal_Requests_List_Table', _get_list_table('WP_Privacy_Data_Removal_Requests_List_Table'));
28 changes: 28 additions & 0 deletions tests/data/wp_generate_tag_cloud.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace PhpStubs\WordPress\Core\Tests;

use function wp_generate_tag_cloud;
use function PHPStan\Testing\assertType;

/** @var array<\WP_Term> $args */
$tags = $_GET['tags'];

// Default $args['format] value.
assertType('string', wp_generate_tag_cloud($tags));
assertType('string', wp_generate_tag_cloud($tags, []));

// Requesting array
assertType('array<int, string>', wp_generate_tag_cloud($tags, ['format' => 'array', 'key' => 'value']));

// Requesting string
assertType('string', wp_generate_tag_cloud($tags, ['format' => 'list', 'key' => 'value']));
assertType('string', wp_generate_tag_cloud($tags, ['format' => 'flat', 'key' => 'value']));

// Unexpected $args['format] value
assertType('string', wp_generate_tag_cloud($tags, ['format' => 'unexpected', 'key' => 'value']));

// Unknown $args['format] value
assertType('array<int, string>|string', wp_generate_tag_cloud($tags, ['format' => (string)$_GET['format'], 'key' => 'value']));
48 changes: 48 additions & 0 deletions tests/data/wp_tag_cloud.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* Note:
* Starting from PHPStan 1.10.49, void types, including void in unions, are
* transformed into null.
*
* @link https://github.com/phpstan/phpstan-src/pull/2778
*/

declare(strict_types=1);

namespace PhpStubs\WordPress\Core\Tests;

use function PHPStan\Testing\assertType;
use function wp_tag_cloud;

// Default value
assertType('null', wp_tag_cloud());

// Echo true
assertType('null', wp_tag_cloud(['echo' => true]));
assertType('null', wp_tag_cloud(['format' => 'flat', 'echo' => true]));
assertType('null', wp_tag_cloud(['format' => 'list', 'echo' => true]));
assertType('null', wp_tag_cloud(['format' => 'unexpected', 'echo' => true]));

// Echo true, but format (maybe) array
assertType('array<int, string>|null', wp_tag_cloud(['format' => 'array', 'echo' => true]));
assertType('array<int, string>|null', wp_tag_cloud(['format' => (string)$_GET['format'], 'echo' => true]));

// Echo false
assertType('string|null', wp_tag_cloud(['echo' => false]));
assertType('string|null', wp_tag_cloud(['format' => 'flat', 'echo' => false]));
assertType('string|null', wp_tag_cloud(['format' => 'list', 'echo' => false]));
assertType('array<int, string>|null', wp_tag_cloud(['format' => 'array', 'echo' => false]));
assertType('string|null', wp_tag_cloud(['format' => 'unexpected', 'echo' => false]));
assertType('array<int, string>|string|null', wp_tag_cloud(['format' => (string)$_GET['format'], 'echo' => false]));

// Echo unknown
/** @var bool|0|1 $echo */
$boolZeroOne = $_GET['echo'];

assertType('string|null', wp_tag_cloud(['echo' => $boolZeroOne]));
assertType('string|null', wp_tag_cloud(['format' => 'flat', 'echo' => $boolZeroOne]));
assertType('string|null', wp_tag_cloud(['format' => 'list', 'echo' => $boolZeroOne]));
assertType('array<int, string>|null', wp_tag_cloud(['format' => 'array', 'echo' => $boolZeroOne]));
assertType('string|null', wp_tag_cloud(['format' => 'unexpected', 'echo' => $boolZeroOne]));
assertType('array<int, string>|string|null', wp_tag_cloud(['format' => (string)$_GET['format'], 'echo' => $boolZeroOne]));
19 changes: 18 additions & 1 deletion tests/data/wp_theme.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@
use function PHPStan\Testing\assertType;

/** @var \WP_Theme */
$theme = $theme;
$theme = $_GET['theme'];

// WP_Theme::__get()
assertType('string', $theme->name);
assertType('string', $theme->title);
assertType('string', $theme->version);
assertType('string', $theme->parent_theme);
assertType('string', $theme->template_dir);
assertType('string', $theme->stylesheet_dir);
assertType('string', $theme->template);
assertType('string', $theme->stylesheet);
assertType('string', $theme->screenshot);
assertType('string', $theme->description);
assertType('string', $theme->author);
assertType('list<string>', $theme->tags);
assertType('string', $theme->theme_root);
assertType('string', $theme->theme_root_uri);
assertType('*ERROR*', $theme->unknown_property);

// WP_Theme::get()
assertType('string', $theme->get('Name'));
Expand Down
24 changes: 24 additions & 0 deletions tests/data/wp_translations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace PhpStubs\WordPress\Core\Tests;

use function PHPStan\Testing\assertType;

/** @var \WP_Translations */
$translations = $_GET['translations'];

$string = (string)$_GET['string'];
$singular = $_GET['singular'] ? $string : null;

// WP_Translation::translate()
assertType('null', $translations->translate(null));
assertType('string', $translations->translate($string));

// WP_Translation::translate_plural()
assertType('null', $translations->translate_plural(null, null));
assertType('string', $translations->translate_plural($string, null));
assertType('null', $translations->translate_plural(null, $string));
assertType('string', $translations->translate_plural($string, $string));
assertType('string|null', $translations->translate_plural($singular, $string));
Loading

0 comments on commit 2e05e60

Please sign in to comment.