From 8ecd5dbef8cc951719a1a8227f411e75439b90de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 12 Feb 2025 10:44:51 +0100 Subject: [PATCH 1/9] Upgrade PHPStan to v2 (#274) --- composer.json | 2 +- phpstan.neon.dist | 11 ++++++++--- tests/Faker.php | 4 +++- tests/data/Faker.php | 6 +++--- tests/data/_wp_json_sanity_check.php | 2 +- tests/data/stripslashes.php | 4 ++-- tests/data/wpdb.php | 4 ++-- tests/phpstan.neon | 2 -- 8 files changed, 20 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index fb8ebe88..8cd42f5d 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "nikic/php-parser": "^4.13", "php-stubs/generator": "^0.8.3", "phpdocumentor/reflection-docblock": "^5.4.1", - "phpstan/phpstan": "^1.11", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 92981a87..c9c6402c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,7 +7,12 @@ parameters: - tests/ excludePaths: - tests/data/ + phpVersion: + min: 80000 + max: 80300 level: 9 - featureToggles: - alwaysTrueAlwaysReported: true - listType: true + ignoreErrors: + - + path: tests/Faker.php + identifier: class.notFound + count: 8 diff --git a/tests/Faker.php b/tests/Faker.php index cecbb79c..d47e632f 100644 --- a/tests/Faker.php +++ b/tests/Faker.php @@ -88,8 +88,10 @@ public static function list($type = null): array * @template T * @param T ...$types * @return T + * + * @phpcs:disable NeutronStandard.Functions.TypeHint */ - public static function union(...$types): mixed + public static function union(...$types) { return $types[0]; } diff --git a/tests/data/Faker.php b/tests/data/Faker.php index 7f5182e5..042b993f 100644 --- a/tests/data/Faker.php +++ b/tests/data/Faker.php @@ -26,7 +26,7 @@ assertType('non-empty-string', Faker::nonEmptyString()); // Arrays with default values -assertType('array', Faker::array()); +assertType('array', Faker::array()); assertType('array', Faker::intArray()); assertType('array', Faker::strArray()); assertType('list', Faker::list()); @@ -44,8 +44,8 @@ assertType('array|bool|int|string', Faker::union(Faker::bool(), Faker::int(), Faker::string(), Faker::intArray(Faker::int()))); assertType('array', Faker::union(Faker::intArray(Faker::int()), Faker::strArray(Faker::string()))); assertType('array', Faker::union(Faker::array(Faker::int()), Faker::strArray(Faker::string()))); -assertType('array', Faker::union(Faker::array(), Faker::strArray())); -assertType('array', Faker::union(Faker::array(), Faker::intArray())); +assertType('array', Faker::union(Faker::array(), Faker::strArray())); +assertType('array', Faker::union(Faker::array(), Faker::intArray())); assertType('string|null', Faker::union(Faker::string(), null)); // Other diff --git a/tests/data/_wp_json_sanity_check.php b/tests/data/_wp_json_sanity_check.php index ceab47f9..28fdc1bd 100644 --- a/tests/data/_wp_json_sanity_check.php +++ b/tests/data/_wp_json_sanity_check.php @@ -11,6 +11,6 @@ assertType('bool', _wp_json_sanity_check(Faker::bool(), 1)); assertType('int', _wp_json_sanity_check(Faker::int(), 1)); assertType('string', _wp_json_sanity_check(Faker::string(), 1)); -assertType('array', _wp_json_sanity_check(Faker::array(), 1)); +assertType('array', _wp_json_sanity_check(Faker::array(), 1)); assertType('stdClass', _wp_json_sanity_check(Faker::stdClass(), 1)); assertType('mixed', _wp_json_sanity_check(Faker::mixed(), 1)); diff --git a/tests/data/stripslashes.php b/tests/data/stripslashes.php index 3d3e8a7f..1d937372 100644 --- a/tests/data/stripslashes.php +++ b/tests/data/stripslashes.php @@ -13,7 +13,7 @@ assertType('int', stripslashes_deep(Faker::int())); assertType('float', stripslashes_deep(Faker::float())); assertType('string', stripslashes_deep(Faker::string())); -assertType('array', stripslashes_deep(Faker::array())); +assertType('array', stripslashes_deep(Faker::array())); assertType('resource', stripslashes_deep(Faker::resource())); assertType('object', stripslashes_deep(Faker::object())); @@ -22,6 +22,6 @@ assertType('int', stripslashes_from_strings_only(Faker::int())); assertType('float', stripslashes_from_strings_only(Faker::float())); assertType('string', stripslashes_from_strings_only(Faker::string())); -assertType('array', stripslashes_from_strings_only(Faker::array())); +assertType('array', stripslashes_from_strings_only(Faker::array())); assertType('resource', stripslashes_from_strings_only(Faker::resource())); assertType('object', stripslashes_from_strings_only(Faker::object())); diff --git a/tests/data/wpdb.php b/tests/data/wpdb.php index 551a0298..6244536f 100644 --- a/tests/data/wpdb.php +++ b/tests/data/wpdb.php @@ -10,11 +10,11 @@ // wpdb::get_row() assertType('stdClass|null', wpdb::get_row()); assertType('stdClass|null', wpdb::get_row(null, 'OBJECT')); -assertType('array|null', wpdb::get_row(null, 'ARRAY_A')); +assertType('array|null', wpdb::get_row(null, 'ARRAY_A')); assertType('list|null', wpdb::get_row(null, 'ARRAY_N')); // wpdb::get_results() -assertType('list|null', wpdb::get_results(null, 'ARRAY_A')); +assertType('list>|null', wpdb::get_results(null, 'ARRAY_A')); assertType('list>|null', wpdb::get_results(null, 'ARRAY_N')); assertType('list|null', wpdb::get_results()); assertType('list|null', wpdb::get_results(null, 'OBJECT')); diff --git a/tests/phpstan.neon b/tests/phpstan.neon index 098f5a1d..1673aee1 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -1,5 +1,3 @@ parameters: bootstrapFiles: - ../wordpress-stubs.php - featureToggles: - listType: true From 99dabe49f5b153da0eca2f113475c410a66d342a Mon Sep 17 00:00:00 2001 From: IanDelMar <42134098+IanDelMar@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:05:34 +0100 Subject: [PATCH 2/9] Narrow return type for get_current_blog_id (#275) --- functionMap.php | 1 + wordpress-stubs.php | 1 + 2 files changed, 2 insertions(+) diff --git a/functionMap.php b/functionMap.php index 8e857253..8fd0de88 100644 --- a/functionMap.php +++ b/functionMap.php @@ -219,4 +219,5 @@ 'wp_nonce_url' => [null, 'action' => '-1|string'], 'wp_nonce_field' => [null, 'action' => '-1|string'], 'did_action' => ['int<0, max>'], + 'get_current_blog_id' => ['int<0, max>'], ]; diff --git a/wordpress-stubs.php b/wordpress-stubs.php index 361bbbfd..fa6b6000 100644 --- a/wordpress-stubs.php +++ b/wordpress-stubs.php @@ -121165,6 +121165,7 @@ function absint($maybeint) * @global int $blog_id * * @return int Site ID. + * @phpstan-return int<0, max> */ function get_current_blog_id() { From 5389907fafc42cf1012af9334372c30a4d05dbd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 16 Apr 2025 14:01:06 +0000 Subject: [PATCH 3/9] Generate stubs for WordPress 6.8.0 --- source/composer.json | 2 +- wordpress-stubs.php | 1603 ++++++++++++++++++++++++++++++------------ 2 files changed, 1139 insertions(+), 466 deletions(-) diff --git a/source/composer.json b/source/composer.json index d1b80e15..58482730 100644 --- a/source/composer.json +++ b/source/composer.json @@ -7,7 +7,7 @@ "ext-mbstring": "*", "ext-openssl": "*", "ext-sodium": "*", - "johnpbloch/wordpress": "6.7.2" + "johnpbloch/wordpress": "6.8.0" }, "minimum-stability": "stable", "config": { diff --git a/wordpress-stubs.php b/wordpress-stubs.php index fa6b6000..cb6cc4ed 100644 --- a/wordpress-stubs.php +++ b/wordpress-stubs.php @@ -732,7 +732,7 @@ public function clear_destination($remote_destination) * @since 6.2.0 Use move_dir() instead of copy_dir() when possible. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. - * @global array $wp_theme_directories + * @global string[] $wp_theme_directories * * @param array|string $args { * Optional. Array or string of arguments for installing a package. Default empty array. @@ -3179,6 +3179,14 @@ class Walker_Nav_Menu extends \Walker * @see Walker::$db_fields */ public $db_fields = array('parent' => 'menu_item_parent', 'id' => 'db_id'); + /** + * Constructor. + * + * @since 6.8.0 + */ + public function __construct() + { + } /** * Starts the list before the elements are added. * @@ -7330,7 +7338,7 @@ public function cmpr_strlen($a, $b) * @param bool $head * @return array */ - public function get_page($url, $username = '', $password = '', $head = \false) + public function get_page($url, $username = '', #[\SensitiveParameter] $password = '', $head = \false) { } /** @@ -7801,6 +7809,7 @@ public function column_title($post) * Handles the author column output. * * @since 4.3.0 + * @since 6.8.0 Added fallback text when author's name is unknown. * * @param WP_Post $post The current WP_Post object. */ @@ -9218,6 +9227,7 @@ public function column_comments($post) * Handles the post author column output. * * @since 4.3.0 + * @since 6.8.0 Added fallback text when author's name is unknown. * * @param WP_Post $post The current WP_Post object. */ @@ -9418,7 +9428,8 @@ public function column_cb($item) * @since 4.9.6 * * @param WP_User_Request $item Item being shown. - * @return string Status column markup. + * @return string|void Status column markup. Returns a string if no status is found, + * otherwise it displays the markup. */ public function column_status($item) { @@ -10234,7 +10245,8 @@ public function run_tests() * @param string $constant The name of the constant to check. * @param bool|string|array $value The value that the constant should be, if set, * or an array of acceptable values. - * @return array The test results. + * @return array|null The test results if there are any constants set incorrectly, + * or null if the test passed. */ public function test_constants($constant, $value) { @@ -10244,7 +10256,8 @@ public function test_constants($constant, $value) * * @since 5.2.0 * - * @return array The test results. + * @return array|null The test results if wp_version_check() is disabled, + * or null if the test passed. */ public function test_wp_version_check_attached() { @@ -10254,7 +10267,8 @@ public function test_wp_version_check_attached() * * @since 5.2.0 * - * @return array The test results. + * @return array|null The test results if the {@see 'automatic_updater_disabled'} filter is set, + * or null if the test passed. */ public function test_filters_automatic_updater_disabled() { @@ -10264,7 +10278,7 @@ public function test_filters_automatic_updater_disabled() * * @since 5.3.0 * - * @return array|false The test results. False if auto-updates are enabled. + * @return array|false The test results if auto-updates are disabled, false otherwise. */ public function test_wp_automatic_updates_disabled() { @@ -10274,7 +10288,7 @@ public function test_wp_automatic_updates_disabled() * * @since 5.2.0 * - * @return array|false The test results. False if the auto-updates failed. + * @return array|false The test results if auto-updates previously failed, false otherwise. */ public function test_if_failed_update() { @@ -10306,7 +10320,9 @@ public function test_check_wp_filesystem_method() * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * - * @return array|false The test results. False if they're not writeable. + * @return array|false The test results if at least some of WordPress core files are writeable, + * or if a list of the checksums could not be retrieved from WordPress.org. + * False if the core files are not writeable. */ public function test_all_files_writable() { @@ -10316,7 +10332,8 @@ public function test_all_files_writable() * * @since 5.2.0 * - * @return array|false The test results. False if it isn't a development version. + * @return array|false|null The test results if development updates are blocked. + * False if it isn't a development version. Null if the test passed. */ public function test_accepts_dev_updates() { @@ -10326,7 +10343,8 @@ public function test_accepts_dev_updates() * * @since 5.2.0 * - * @return array The test results. + * @return array|null The test results if minor updates are blocked, + * or null if the test passed. */ public function test_accepts_minor_updates() { @@ -15458,7 +15476,7 @@ class PHPMailer * You can set your own, but it must be in the format "", * as defined in RFC5322 section 3.6.4 or it will be ignored. * - * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 + * @see https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4 * * @var string */ @@ -15577,7 +15595,7 @@ class PHPMailer * 'DELAY' will notify you if there is an unusual delay in delivery, but the actual * delivery's outcome (success or failure) is not yet decided. * - * @see https://tools.ietf.org/html/rfc3461 See section 4.1 for more information about NOTIFY + * @see https://www.rfc-editor.org/rfc/rfc3461.html#section-4.1 for more information about NOTIFY */ public $dsn = ''; /** @@ -15906,7 +15924,7 @@ class PHPMailer * * @var string */ - const VERSION = '6.9.2'; + const VERSION = '6.9.3'; /** * Error severity: message only, continue processing. * @@ -17181,7 +17199,7 @@ public function DKIM_Sign($signHeader) * Uses the 'relaxed' algorithm from RFC6376 section 3.4.2. * Canonicalized headers should *always* use CRLF, regardless of mailer setting. * - * @see https://tools.ietf.org/html/rfc6376#section-3.4.2 + * @see https://www.rfc-editor.org/rfc/rfc6376#section-3.4.2 * * @param string $signHeader Header * @@ -17195,7 +17213,7 @@ public function DKIM_HeaderC($signHeader) * Uses the 'simple' algorithm from RFC6376 section 3.4.3. * Canonicalized bodies should *always* use CRLF, regardless of mailer setting. * - * @see https://tools.ietf.org/html/rfc6376#section-3.4.3 + * @see https://www.rfc-editor.org/rfc/rfc6376#section-3.4.3 * * @param string $body Message Body * @@ -17331,7 +17349,7 @@ class SMTP * * @var string */ - const VERSION = '6.9.2'; + const VERSION = '6.9.3'; /** * SMTP line break constant. * @@ -17354,7 +17372,7 @@ class SMTP * The maximum line length allowed by RFC 5321 section 4.5.3.1.6, * *excluding* a trailing CRLF break. * - * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.6 + * @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.6 * * @var int */ @@ -17363,7 +17381,7 @@ class SMTP * The maximum line length allowed for replies in RFC 5321 section 4.5.3.1.5, * *including* a trailing CRLF line break. * - * @see https://tools.ietf.org/html/rfc5321#section-4.5.3.1.5 + * @see https://www.rfc-editor.org/rfc/rfc5321#section-4.5.3.1.5 * * @var int */ @@ -29774,6 +29792,7 @@ public static function is_in_use() * * @since 5.6.0 * @since 5.7.0 Returns WP_Error if application name already exists. + * @since 6.8.0 The hashed password value now uses wp_fast_hash() instead of phpass. * * @param int $user_id User ID. * @param array $args { @@ -29897,6 +29916,7 @@ public static function application_name_exists_for_user($user_id, $name) * Updates an application password. * * @since 5.6.0 + * @since 6.8.0 The actual password should now be hashed using wp_fast_hash(). * * @param int $user_id User ID. * @param string $uuid The password's UUID. @@ -30003,7 +30023,30 @@ protected static function set_user_application_passwords($user_id, $passwords) * @param string $raw_password The raw application password. * @return string The chunked password. */ - public static function chunk_password($raw_password) + public static function chunk_password(#[\SensitiveParameter] $raw_password) + { + } + /** + * Hashes a plaintext application password. + * + * @since 6.8.0 + * + * @param string $password Plaintext password. + * @return string Hashed password. + */ + public static function hash_password(#[\SensitiveParameter] string $password) : string + { + } + /** + * Checks a plaintext application password against a hashed password. + * + * @since 6.8.0 + * + * @param string $password Plaintext password. + * @param string $hash Hash of the password to check against. + * @return bool Whether the password matches the hashed password. + */ + public static function check_password(#[\SensitiveParameter] string $password, string $hash) : bool { } } @@ -30488,6 +30531,23 @@ public static function register_collection($path, $manifest) public static function get_metadata($file_or_folder) { } + /** + * Gets the list of absolute paths to all block metadata files that are part of the given collection. + * + * For instance, if a block metadata collection is registered with path `WP_PLUGIN_DIR . '/my-plugin/blocks/'`, + * and the manifest file includes metadata for two blocks `'block-a'` and `'block-b'`, the result of this method + * will be an array containing: + * * `WP_PLUGIN_DIR . '/my-plugin/blocks/block-a/block.json'` + * * `WP_PLUGIN_DIR . '/my-plugin/blocks/block-b/block.json'` + * + * @since 6.8.0 + * + * @param string $path The absolute base path for a previously registered collection. + * @return string[] List of block metadata file paths, or an empty array if the given `$path` is invalid. + */ + public static function get_collection_block_metadata_files($path) + { + } /** * Checks if metadata exists for a given block name in a specific collection. * @@ -32033,7 +32093,7 @@ class WP_Block * @var array * @access protected */ - protected $available_context; + protected $available_context = array(); /** * Block type registry. * @@ -32108,6 +32168,35 @@ class WP_Block public function __construct($block, $available_context = array(), $registry = \null) { } + /** + * Updates the context for the current block and its inner blocks. + * + * The method updates the context of inner blocks, if any, by passing down + * any context values the block provides (`provides_context`). + * + * If the block has inner blocks, the method recursively processes them by creating new instances of `WP_Block` + * for each inner block and updating their context based on the block's `provides_context` property. + * + * @since 6.8.0 + */ + public function refresh_context_dependents() + { + } + /** + * Updates the parsed block content for the current block and its inner blocks. + * + * This method sets the `inner_html` and `inner_content` properties of the block based on the parsed + * block content provided during initialization. It ensures that the block instance reflects the + * most up-to-date content for both the inner HTML and any string fragments around inner blocks. + * + * If the block has inner blocks, this method initializes a new `WP_Block_List` for them, ensuring the + * correct content and context are updated for each nested block. + * + * @since 6.8.0 + */ + public function refresh_parsed_block_dependents() + { + } /** * Returns a value from an inaccessible property. * @@ -32638,7 +32727,7 @@ final class WP_Comment * @since 4.4.0 * @var string */ - public $comment_post_ID = 0; + public $comment_post_ID = '0'; /** * Comment author name. * @@ -32696,7 +32785,7 @@ final class WP_Comment * @since 4.4.0 * @var string */ - public $comment_karma = 0; + public $comment_karma = '0'; /** * Comment approval status. * @@ -32727,7 +32816,7 @@ final class WP_Comment * @since 4.4.0 * @var string */ - public $comment_parent = 0; + public $comment_parent = '0'; /** * Comment author ID. * @@ -32736,7 +32825,7 @@ final class WP_Comment * @since 4.4.0 * @var string */ - public $user_id = 0; + public $user_id = '0'; /** * Retrieves a WP_Comment instance. * @@ -32860,8 +32949,8 @@ public function populated_children($set) * * @since 4.4.0 * - * @param string $name Property name. - * @return bool + * @param string $name Property to check if set. + * @return bool Whether the property is set. */ public function __isset($name) { @@ -33086,7 +33175,7 @@ public function __construct($manager, $id, $args = array()) { } /** - * Enqueue control related scripts/styles. + * Enqueues control related scripts/styles. * * @since 3.4.0 */ @@ -33094,7 +33183,7 @@ public function enqueue() { } /** - * Check whether control is active to current Customizer preview. + * Checks whether control is active to current Customizer preview. * * @since 4.0.0 * @@ -33117,7 +33206,7 @@ public function active_callback() { } /** - * Fetch a setting's value. + * Fetches a setting's value. * Grabs the main setting by default. * * @since 3.4.0 @@ -33129,7 +33218,7 @@ public final function value($setting_key = 'default') { } /** - * Refresh the parameters passed to the JavaScript via JSON. + * Refreshes the parameters passed to the JavaScript via JSON. * * @since 3.4.0 */ @@ -33137,7 +33226,7 @@ public function to_json() { } /** - * Get the data to export to the client via JSON. + * Gets the data to export to the client via JSON. * * @since 4.1.0 * @@ -33162,7 +33251,7 @@ public final function check_capabilities() { } /** - * Get the control's content for insertion into the Customizer pane. + * Gets the control's content for insertion into the Customizer pane. * * @since 4.1.0 * @@ -33172,7 +33261,7 @@ public final function get_content() { } /** - * Check capabilities and render the control. + * Checks capabilities and render the control. * * @since 3.4.0 * @uses WP_Customize_Control::render() @@ -33190,31 +33279,32 @@ protected function render() { } /** - * Get the data link attribute for a setting. + * Gets the data link attribute for a setting. * * @since 3.4.0 * @since 4.9.0 Return a `data-customize-setting-key-link` attribute if a setting is not registered for the supplied setting key. * * @param string $setting_key - * @return string Data link parameter, a `data-customize-setting-link` attribute if the `$setting_key` refers to a pre-registered setting, - * and a `data-customize-setting-key-link` attribute if the setting is not yet registered. + * @return string Data link parameter, a `data-customize-setting-link` attribute if the `$setting_key` refers + * to a pre-registered setting, and a `data-customize-setting-key-link` attribute if the setting + * is not yet registered. */ public function get_link($setting_key = 'default') { } /** - * Render the data link attribute for the control's input element. + * Renders the data link attribute for the control's input element. * * @since 3.4.0 * @uses WP_Customize_Control::get_link() * - * @param string $setting_key + * @param string $setting_key Default 'default'. */ public function link($setting_key = 'default') { } /** - * Render the custom attributes for the control's input element. + * Renders the custom attributes for the control's input element. * * @since 4.0.0 */ @@ -33222,7 +33312,7 @@ public function input_attrs() { } /** - * Render the control's content. + * Renders the control's content. * * Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`. * @@ -33238,7 +33328,7 @@ protected function render_content() { } /** - * Render the control's JS template. + * Renders the control's JS template. * * This function is only run for control types that have been registered with * WP_Customize_Manager::register_control_type(). @@ -37839,7 +37929,7 @@ final class _WP_Editors * @type string|int $tabindex Tabindex value to use. Default empty. * @type string $tabfocus_elements The previous and next element ID to move the focus to * when pressing the Tab key in TinyMCE. Default ':prev,:next'. - * @type string $editor_css Intended for extra styles for both Visual and Text editors. + * @type string $editor_css Intended for extra styles for both Visual and Code editors. * Should include `