diff --git a/composer.json b/composer.json index 5095195ac..f86b4092a 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "require-dev": { "automattic/vipwpcs": "^3.0", "dealerdirect/phpcodesniffer-composer-installer": "^1.2.1", + "php-stubs/wordpress-stubs": "7.0 as 6.9.4", "php-stubs/wp-cli-stubs": "^2.12", "phpcompatibility/phpcompatibility-wp": "^3.0.0-alpha", "phpstan/extension-installer": "^1.3", @@ -21,7 +22,7 @@ "slevomat/coding-standard": "^8.0", "szepeviktor/phpstan-wordpress": "^2.0.2", "wp-coding-standards/wpcs": "^3.2.0", - "wp-phpunit/wp-phpunit": "^6.9", + "wp-phpunit/wp-phpunit": "^7.0", "yoast/phpunit-polyfills": "^4.0" }, "autoload": { @@ -52,7 +53,7 @@ "scripts": { "format": "phpcbf --standard=phpcs.xml.dist", "lint": "phpcs --standard=phpcs.xml.dist", - "phpstan": "phpstan analyse --memory-limit=1G", + "phpstan": "phpstan analyse --memory-limit=2G", "test": "phpunit --strict-coverage" }, "scripts-descriptions": { diff --git a/composer.lock b/composer.lock index fef56097e..238c5cf27 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "89a0c10a53213f36ff8f0dad75824fd7", + "content-hash": "ead8c66a59688e637dff6f84de4ecdd8", "packages": [], "packages-dev": [ { @@ -465,16 +465,16 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.9.1", + "version": "v7.0.0", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7" + "reference": "d74b963ed4f47303859bf73c741c80f554c83dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", - "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/d74b963ed4f47303859bf73c741c80f554c83dc6", + "reference": "d74b963ed4f47303859bf73c741c80f554c83dc6", "shasum": "" }, "conflict": { @@ -484,7 +484,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^5.5", "php": "^7.4 || ^8.0", - "php-stubs/generator": "^0.8.3", + "php-stubs/generator": "^0.8.6", "phpdocumentor/reflection-docblock": "^6.0", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", @@ -511,9 +511,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v7.0.0" }, - "time": "2026-02-03T19:29:21+00:00" + "time": "2026-05-21T21:41:34+00:00" }, { "name": "php-stubs/wp-cli-stubs", @@ -3081,16 +3081,16 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "6.9.4", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "15fd216bf6516670d8d07b938675925bfa5c15b0" + "reference": "06828a65f8276e31368fbe4c5f3d445332abc4c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/15fd216bf6516670d8d07b938675925bfa5c15b0", - "reference": "15fd216bf6516670d8d07b938675925bfa5c15b0", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/06828a65f8276e31368fbe4c5f3d445332abc4c5", + "reference": "06828a65f8276e31368fbe4c5f3d445332abc4c5", "shasum": "" }, "type": "library", @@ -3125,7 +3125,7 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2026-02-04T01:48:23+00:00" + "time": "2026-05-21T02:56:35+00:00" }, { "name": "yoast/phpunit-polyfills", @@ -3191,7 +3191,14 @@ "time": "2025-02-09T18:58:54+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "php-stubs/wordpress-stubs", + "version": "7.0.0.0", + "alias": "6.9.4", + "alias_normalized": "6.9.4.0" + } + ], "minimum-stability": "dev", "stability-flags": {}, "prefer-stable": true, diff --git a/includes/Abilities/Image/Generate_Image.php b/includes/Abilities/Image/Generate_Image.php index 20007f388..a8c4f9b78 100644 --- a/includes/Abilities/Image/Generate_Image.php +++ b/includes/Abilities/Image/Generate_Image.php @@ -181,7 +181,11 @@ protected function meta(): array { * * @param string $prompt The prompt to generate an image from. * @param string|null $reference_image Optional base64-encoded image to use as a reference for edits. - * @return array{data: string, provider_metadata: array, model_metadata: array}|\WP_Error The generated image data, or a WP_Error on failure. + * @return array{ + * data: string, + * provider_metadata: array, + * model_metadata: array + * }|\WP_Error The generated image data, or a WP_Error on failure. */ protected function generate_image( string $prompt, ?string $reference_image = null ) { // phpcs:ignore Generic.NamingConventions.ConstructorName.OldStyle $prompt_builder = $this->get_prompt_builder( $prompt, $reference_image ); @@ -190,7 +194,13 @@ protected function generate_image( string $prompt, ?string $reference_image = nu return $prompt_builder; } - // Generate the image using the AI client. + /** + * Generate the image using the AI client. + * + * @var \WordPress\AiClient\Results\DTO\GenerativeAiResult $result + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $result = $prompt_builder->generate_image_result(); if ( is_wp_error( $result ) ) { @@ -258,7 +268,9 @@ private function get_prompt_builder( string $prompt, ?string $reference_image = } $prompt_builder = wp_ai_client_prompt( $prompt ) + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ ->using_request_options( $request_options ) + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ ->as_output_file_type( FileTypeEnum::inline() ); $prompt_builder = $this->set_provider_model_preference( $prompt_builder, Image_Generation_Feature::class, get_preferred_image_models() ); diff --git a/includes/Abstracts/Abstract_Ability.php b/includes/Abstracts/Abstract_Ability.php index b2f44addc..fd9f6e9df 100644 --- a/includes/Abstracts/Abstract_Ability.php +++ b/includes/Abstracts/Abstract_Ability.php @@ -314,6 +314,7 @@ protected function set_provider_model_preference( \WP_AI_Client_Prompt_Builder $ if ( $provider && $model ) { $prompt_builder->using_model( + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ AiClient::defaultRegistry()->getProviderModel( $provider, $model ) ); } else { diff --git a/includes/Abstracts/Abstract_Feature.php b/includes/Abstracts/Abstract_Feature.php index fe52055c3..327346876 100644 --- a/includes/Abstracts/Abstract_Feature.php +++ b/includes/Abstracts/Abstract_Feature.php @@ -26,7 +26,7 @@ abstract class Abstract_Feature implements Feature { * Feature identifier. * * @since 0.6.0 - * @var non-empty-string + * @var non-empty-string&lowercase-string */ protected string $id; diff --git a/includes/Admin/Dashboard/AI_Capabilities_Widget.php b/includes/Admin/Dashboard/AI_Capabilities_Widget.php index 2ae9e1b40..c724770ed 100644 --- a/includes/Admin/Dashboard/AI_Capabilities_Widget.php +++ b/includes/Admin/Dashboard/AI_Capabilities_Widget.php @@ -143,14 +143,29 @@ private function render_provider_capabilities(): void { try { $provider_class = $registry->getProviderClassName( $provider_id ); - /** @var \WordPress\AiClient\Providers\Contracts\ProviderInterface $provider_class */ - $metadata = $provider_class::metadata(); - $model_dir = $provider_class::modelMetadataDirectory(); + /** + * @var class-string<\WordPress\AiClient\Providers\Contracts\ProviderInterface> $provider_class + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ + $metadata = $provider_class::metadata(); + $model_dir = $provider_class::modelMetadataDirectory(); + + /** + * @var \WordPress\AiClient\Providers\Models\DTO\ModelMetadata[] $models + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $models = $model_dir->listModelMetadata(); $capabilities = array(); foreach ( $models as $model ) { foreach ( $model->getSupportedCapabilities() as $capability ) { + /** + * @var \WordPress\AiClient\Providers\Models\Enums\CapabilityEnum $capability + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $capabilities[ $capability->value ] = true; } } diff --git a/includes/Asset_Loader.php b/includes/Asset_Loader.php index 7a6eaecd7..167c48286 100644 --- a/includes/Asset_Loader.php +++ b/includes/Asset_Loader.php @@ -102,6 +102,9 @@ public static function enqueue_script( string $handle, string $file_name, array wp_enqueue_script( self::HANDLE_PREFIX . $handle, $script_url, + /** + * @phpstan-ignore argument.type (@todo This is a type-error in WordPress 7.0) + */ $asset_data['dependencies'], $asset_data['version'], $args diff --git a/includes/Contracts/Feature.php b/includes/Contracts/Feature.php index 5fe362067..519e7ca0a 100644 --- a/includes/Contracts/Feature.php +++ b/includes/Contracts/Feature.php @@ -24,7 +24,7 @@ interface Feature { * * @since 0.6.0 * - * @return non-empty-string Feature ID. + * @return non-empty-string&lowercase-string Feature ID. */ public static function get_id(): string; diff --git a/includes/Logging/REST/AI_Request_Log_Controller.php b/includes/Logging/REST/AI_Request_Log_Controller.php index d84eca86b..3b2d122ac 100644 --- a/includes/Logging/REST/AI_Request_Log_Controller.php +++ b/includes/Logging/REST/AI_Request_Log_Controller.php @@ -24,6 +24,19 @@ * @since 1.0.0 */ class AI_Request_Log_Controller extends WP_REST_Controller { + /** + * {@inheritDoc} + * + * @var non-falsy-string + */ + protected $namespace; + + /** + * {@inheritDoc} + * + * @var non-falsy-string + */ + protected $rest_base; /** * Log manager instance. diff --git a/includes/REST/Models_Controller.php b/includes/REST/Models_Controller.php index fe2ee8988..dca614059 100644 --- a/includes/REST/Models_Controller.php +++ b/includes/REST/Models_Controller.php @@ -157,18 +157,21 @@ private function build_requirements( string $capability ): ModelRequirements { switch ( $capability ) { case 'text_generation': return new ModelRequirements( + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ array( CapabilityEnum::textGeneration() ), array() ); case 'image_generation': return new ModelRequirements( + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ array( CapabilityEnum::imageGeneration() ), array() ); case 'vision': return new ModelRequirements( + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ array( CapabilityEnum::textGeneration() ), array( new RequiredOption( @@ -206,6 +209,11 @@ private function fetch_providers( ModelRequirements $requirements ): array { foreach ( array_keys( $active_connectors ) as $connector_id ) { try { + /** + * @var list<\WordPress\AiClient\Providers\Models\DTO\ModelMetadata> $models + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $models = $registry->findProviderModelsMetadataForSupport( $connector_id, $requirements ); if ( empty( $models ) ) { @@ -214,7 +222,11 @@ private function fetch_providers( ModelRequirements $requirements ): array { $provider_class = $registry->getProviderClassName( $connector_id ); - /** @var \WordPress\AiClient\Providers\Contracts\ProviderInterface $provider_class */ + /** + * @var class-string<\WordPress\AiClient\Providers\Contracts\ProviderInterface> $provider_class + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $provider_name = $provider_class::metadata()->getName(); $model_items = array(); diff --git a/includes/Services/AI_Service.php b/includes/Services/AI_Service.php index b239535ae..338e6153d 100644 --- a/includes/Services/AI_Service.php +++ b/includes/Services/AI_Service.php @@ -134,7 +134,9 @@ public function create_textgen_prompt( ?string $prompt = null, array $options = if ( ! empty( $options ) ) { $config_array = $this->map_options_to_config( $options ); if ( ! empty( $config_array ) ) { - $config = ModelConfig::fromArray( $config_array ); + $config = ModelConfig::fromArray( $config_array ); + + /** @phpstan-ignore argument.type (@todo This can be removed when php-ai-client uses FQCN) */ $builder = $builder->using_model_config( $config ); } } diff --git a/includes/helpers.php b/includes/helpers.php index 835d08367..3369bbd19 100644 --- a/includes/helpers.php +++ b/includes/helpers.php @@ -530,13 +530,27 @@ function has_image_generation_support( bool $reset_cache = false ): bool { } try { + /** + * @var class-string<\WordPress\AiClient\Providers\Contracts\ProviderInterface> $provider_class + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $provider_class = $registry->getProviderClassName( $connector_id ); - /** @var \WordPress\AiClient\Providers\Contracts\ProviderInterface $provider_class */ + /** + * @var list<\WordPress\AiClient\Providers\Models\DTO\ModelMetadata> $models + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ $models = $provider_class::modelMetadataDirectory()->listModelMetadata(); foreach ( $models as $model ) { foreach ( $model->getSupportedCapabilities() as $capability ) { + /** + * @var \WordPress\AiClient\Providers\Models\Enums\CapabilityEnum $capability + * + * @phpstan-ignore varTag.type (@todo This can be removed when php-ai-client uses FQCN) + */ if ( CapabilityEnum::IMAGE_GENERATION === $capability->value ) { $has_support = true; break 3; diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 55cd94a12..b374fa4ad 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -30,20 +30,10 @@ parameters: - vendor/phpstan/php-8-stubs/stubs/ext/standard/str_starts_with.php # Temporary while WP 7.0 beta AI Client migration is in progress. ignoreErrors: - - '#Function wp_ai_client_prompt not found\.#' - - '#WP_AI_Client_[A-Za-z0-9_]+#' - - '#WordPress\\AiClient\\#' - - '#Function wp_get_connectors not found\.#' - - '#Function wp_is_connector_registered not found\.#' - - '#Function wp_get_connector not found\.#' - '#class cli\\progress\\Bar#' - - '#Function wp_supports_ai not found\.#' - - '#Function wp_set_script_module_translations not found\.#' excludePaths: analyse: - tests/ - vendor/ - - includes/Logging/Logging_Integration.php - - includes/Logging/Logging_Http_Transporter.php analyseAndScan: - node_modules (?)