Make WordPress Core

Changeset 62266


Ignore:
Timestamp:
04/26/2026 11:59:30 PM (5 months ago)
Author:
peterwilsoncc
Message:

AI: Validate filtered default request timeout in WP_AI_Client_Prompt_Builder.

This checks that the return value of the wp_ai_client_default_request_timeout filter is a non-negative number before passing it to RequestOptions. If the filtered value is invalid, it is discarded in favor of the original default of 30.0 and a _doing_it_wrong() notice is issued. Without this check, a fatal error would ensue from the exception thrown in \WordPress\AiClient\Providers\Http\DTO\RequestOptions::validateTimeout().

The following static analysis issues are addressed:

  • Use float instead of int for the wp_ai_client_default_request_timeout filter parameter.
  • Add missing PHP imports for Message and MessagePart in the PHPDoc for wp_ai_client_prompt().
  • Add PHP return type hints for wp_ai_client_prompt() and WP_AI_Client_Cache::getMultiple().
  • Use native property type hints in WP_AI_Client_HTTP_Client.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/11596

Reviewed by peterwilsoncc.
Merges r62255 to the 7.0 branch.

Props westonruter, justlevine, flixos90, khushdoms, darshitrajyaguru97, adrmf25, jarodortegaaraya, tusharaddweb, gaurangsondagar.
Fixes #65094.

Location:
branches/7.0
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/7.0

  • branches/7.0/src/wp-includes/ai-client.php

    r62241 r62266  
    99
    1010use WordPress\AiClient\AiClient;
     11use WordPress\AiClient\Messages\DTO\Message;
     12use WordPress\AiClient\Messages\DTO\MessagePart;
    1113
    1214/**
    … …  
    5658 * @return WP_AI_Client_Prompt_Builder The prompt builder instance.
    5759 */
    58 function wp_ai_client_prompt( $prompt = null ) {
     60function wp_ai_client_prompt( $prompt = null ): WP_AI_Client_Prompt_Builder {
    5961        return new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry(), $prompt );
    6062}
  • branches/7.0/src/wp-includes/ai-client/adapters/class-wp-ai-client-cache.php

    r61700 r62266  
    105105         * @return array<string, mixed> A list of key => value pairs.
    106106         */
    107         public function getMultiple( $keys, $default_value = null ) {
     107        public function getMultiple( $keys, $default_value = null ): array {
    108108                /**
    109109                 * Keys array.
  • branches/7.0/src/wp-includes/ai-client/adapters/class-wp-ai-client-http-client.php

    r61700 r62266  
    3333         *
    3434         * @since 7.0.0
    35          * @var ResponseFactoryInterface
    36          */
    37         private $response_factory;
     35         */
     36        private ResponseFactoryInterface $response_factory;
    3837
    3938        /**
    … …  
    4140         *
    4241         * @since 7.0.0
    43          * @var StreamFactoryInterface
    44          */
    45         private $stream_factory;
     42         */
     43        private StreamFactoryInterface $stream_factory;
    4644
    4745        /**
  • branches/7.0/src/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php

    r62101 r62266  
    191191                }
    192192
     193                $default_timeout = 30.0;
     194
    193195                /**
    194196                 * Filters the default request timeout in seconds for AI Client HTTP requests.
    … …  
    196198                 * @since 7.0.0
    197199                 *
    198                  * @param int $default_timeout The default timeout in seconds.
     200                 * @param float $default_timeout The default timeout in seconds.
    199201                 */
    200                 $default_timeout = (int) apply_filters( 'wp_ai_client_default_request_timeout', 30 );
     202                $filtered_default_timeout = apply_filters( 'wp_ai_client_default_request_timeout', $default_timeout );
     203                if ( is_numeric( $filtered_default_timeout ) && (float) $filtered_default_timeout >= 0.0 ) {
     204                        $default_timeout = (float) $filtered_default_timeout;
     205                } else {
     206                        _doing_it_wrong(
     207                                __METHOD__,
     208                                sprintf(
     209                                        /* translators: %s: wp_ai_client_default_request_timeout */
     210                                        __( 'The %s filter must return a non-negative number.' ),
     211                                        '<code>wp_ai_client_default_request_timeout</code>'
     212                                ),
     213                                '7.0.0'
     214                        );
     215                }
    201216
    202217                $this->builder->usingRequestOptions(
  • branches/7.0/tests/phpunit/tests/ai-client/wpAiClientPromptBuilder.php

    r62265 r62266  
    193193
    194194        /**
    195          * Test that the constructor allows overriding the default request timeout.
    196          *
    197          * @ticket 64591
    198          */
    199         public function test_constructor_allows_overriding_request_timeout() {
     195         * Test that the constructor allows overriding the default request timeout with a valid value.
     196         *
     197         * @ticket 64591
     198         * @ticket 65094
     199         *
     200         * @dataProvider data_valid_request_timeout_overrides
     201         *
     202         * @param mixed $input    The timeout value returned by the filter.
     203         * @param float $expected The expected timeout stored on the request options.
     204         */
     205        public function test_constructor_allows_overriding_request_timeout_with_valid_timeout( $input, float $expected ) {
    200206                add_filter(
    201207                        'wp_ai_client_default_request_timeout',
    202                         static function () {
    203                                 return 45;
     208                        static function () use ( $input ) {
     209                                return $input;
    204210                        }
    205211                );
    … …  
    211217
    212218                $this->assertInstanceOf( RequestOptions::class, $request_options );
    213                 $this->assertSame( 45.0, $request_options->getTimeout() );
     219                $this->assertSame( $expected, $request_options->getTimeout() );
     220        }
     221
     222        /**
     223         * Data provider for {@see self::test_constructor_allows_overriding_request_timeout_with_valid_timeout()}.
     224         *
     225         * @return array<string, array{0: mixed, 1: float}>
     226         */
     227        public function data_valid_request_timeout_overrides(): array {
     228                return array(
     229                        'float'    => array( 45.5, 45.5 ),
     230                        'integer'  => array( 67, 67.0 ),
     231                        'string'   => array( '20', 20.0 ),
     232                        'infinity' => array( INF, INF ),
     233                        'zero'     => array( 0.0, 0.0 ),
     234                );
     235        }
     236
     237        /**
     238         * Test that the constructor disallows overriding the default request timeout with an invalid value.
     239         *
     240         * @ticket 65094
     241         *
     242         * @dataProvider data_invalid_request_timeouts
     243         *
     244         * @expectedIncorrectUsage WP_AI_Client_Prompt_Builder::__construct
     245         *
     246         * @param mixed $timeout The invalid timeout value returned by the filter.
     247         */
     248        public function test_constructor_disallows_overriding_with_invalid_request_timeout( $timeout ) {
     249                add_filter(
     250                        'wp_ai_client_default_request_timeout',
     251                        static function () use ( $timeout ) {
     252                                return $timeout;
     253                        }
     254                );
     255
     256                $builder = new WP_AI_Client_Prompt_Builder( AiClient::defaultRegistry() );
     257
     258                /** @var RequestOptions $request_options */
     259                $request_options = $this->get_wrapped_prompt_builder_property_value( $builder, 'requestOptions' );
     260
     261                $this->assertInstanceOf( RequestOptions::class, $request_options );
     262                $this->assertSame( 30.0, $request_options->getTimeout() );
     263        }
     264
     265        /**
     266         * Data provider for {@see self::test_constructor_disallows_overriding_with_invalid_request_timeout()}.
     267         *
     268         * @return array<string, array{0: mixed}>
     269         */
     270        public function data_invalid_request_timeouts(): array {
     271                return array(
     272                        'negative number' => array( -1 ),
     273                        'array'           => array( array() ),
     274                        'null'            => array( null ),
     275                );
    214276        }
    215277
Note: See TracChangeset for help on using the changeset viewer.