Make WordPress Core

Changeset 58327

Timestamp:
06/04/2024 10:59:01 AM (2 months ago)
Author:
gziolo
Message:

Interactivity API: Directives cannot derive state on the server

The Interactivity API has a concept of "derived state" but it only worked on the client (JavaScript). This is the implementation that mirrors it, so derived state has good server-side solution.

Props jonsurrell, darerodz, gziolo, luisherranz, cbravobernal.
Fixes #61037.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/interactivity-api/class-wp-interactivity-api.php

    r58321 r58327  
    7575
    7676    /**
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
    7799     * Gets and/or sets the initial state of an Interactivity API store for a
    78100     * given namespace.
     
    81103     * provided state with the existing one.
    82104     *
    83      * @since 6.5.0
    84      *
    85      * @param string $store_namespace The unique store namespace identifier.
     105     * When no namespace is specified, it returns the state defined for the
     106     * current value in the internal namespace stack during a `process_directives` call.
     107     *
     108     * @since 6.5.0
     109     * @since 6.6.0 The `$store_namespace` param is optional.
     110     *
     111     * @param string $store_namespace Optional. The unique store namespace identifier.
    86112     * @param array  $state           Optional. The array that will be merged with the existing state for the specified
    87113     *                                store namespace.
     
    89115     *               argument was provided.
    90116     */
    91     public function state( string $store_namespace, array $state = array() ): array {
     117    public function state( ?string $store_namespace = null, ?array $state = null ): array {
     118        if ( ! $store_namespace ) {
     119            if ( $state ) {
     120                _doing_it_wrong(
     121                    __METHOD__,
     122                    __( 'The namespace is required when state data is passed.' ),
     123                    '6.6.0'
     124                );
     125                return array();
     126            }
     127            if ( null !== $store_namespace ) {
     128                _doing_it_wrong(
     129                    __METHOD__,
     130                    __( 'The namespace should be a non-empty string.' ),
     131                    '6.6.0'
     132                );
     133                return array();
     134            }
     135            if ( null === $this->namespace_stack ) {
     136                _doing_it_wrong(
     137                    __METHOD__,
     138                    __( 'The namespace can only be omitted during directive processing.' ),
     139                    '6.6.0'
     140                );
     141                return array();
     142            }
     143
     144            $store_namespace = end( $this->namespace_stack );
     145        }
    92146        if ( ! isset( $this->state_data[ $store_namespace ] ) ) {
    93147            $this->state_data[ $store_namespace ] = array();
     
    213267
    214268    /**
     269
     270
     271
     272
     273
     274
     275
     276
     277
     278
     279
     280
     281
     282
     283
     284
     285
     286
     287
     288
     289
     290
     291
     292
     293
     294
     295
     296
     297
     298
     299
     300
     301
     302
     303
     304
     305
     306
     307
     308
    215309     * Registers the `@wordpress/interactivity` script modules.
    216310     *
     
    259353        }
    260354
    261         $context_stack   = array();
    262         $namespace_stack = array();
    263         $result          = $this->process_directives_args( $html, $context_stack, $namespace_stack );
     355        $this->namespace_stack = array();
     356        $this->context_stack   = array();
     357
     358        $result = $this->_process_directives( $html );
     359
     360        $this->namespace_stack = null;
     361        $this->context_stack   = null;
     362
    264363        return null === $result ? $html : $result;
    265364    }
     
    269368     * and updates the markup accordingly.
    270369     *
    271      * It needs the context and namespace stacks to be passed by reference, and
    272      * it returns null if the HTML contains unbalanced tags.
    273      *
    274      * @since 6.5.0
    275      * @since 6.6.0 The function displays a warning message when the HTML contains unbalanced tags or a directive appears in a MATH or SVG tag.
    276      *
    277      * @param string $html            The HTML content to process.
    278      * @param array  $context_stack   The reference to the array used to keep track of contexts during processing.
    279      * @param array  $namespace_stack The reference to the array used to manage namespaces during processing.
     370     * It uses the WP_Interactivity_API instance's context and namespace stacks,
     371     * which are shared between all calls.
     372     *
     373     * This method returns null if the HTML contains unbalanced tags.
     374     *
     375     * @since 6.6.0
     376     *
     377     * @param string $html The HTML content to process.
    280378     * @return string|null The processed HTML content. It returns null when the HTML contains unbalanced tags.
    281379     */
    282     private function process_directives_args( string $html, array &$context_stack, array &$namespace_stack ) {
     380    private function ) {
    283381        $p          = new WP_Interactivity_API_Directives_Processor( $html );
    284382        $tag_stack  = array();
     
    287385        $directive_processor_prefixes          = array_keys( self::$directive_processors );
    288386        $directive_processor_prefixes_reversed = array_reverse( $directive_processor_prefixes );
     387
     388
     389
     390
     391
     392
     393
    289394
    290395        while ( $p->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
     
    299404                if ( $p->get_attribute_names_with_prefix( 'data-wp-' ) ) {
    300405                    /* translators: 1: SVG or MATH HTML tag, 2: Namespace of the interactive block. */
    301                     $message = sprintf( __( 'Interactivity directives were detected on an incompatible %1$s tag when processing "%2$s". These directives will be ignored in the server side render.' ), $tag_name, end( $namespace_stack ) );
     406                    $message = sprintf( __( 'Interactivity directives were detected on an incompatible %1$s tag when processing "%2$s". These directives will be ignored in the server side render.' ), $tag_name, end( $namespace_stack ) );
    302407                    _doing_it_wrong( __METHOD__, $message, '6.6.0' );
    303408                }
     
    382487                        : array( $this, self::$directive_processors[ $directive_prefix ] );
    383488
    384                     call_user_func_array(
    385                         $func,
    386                         array( $p, $mode, &$context_stack, &$namespace_stack, &$tag_stack )
    387                     );
    388                 }
    389             }
    390         }
     489                    call_user_func_array( $func, array( $p, $mode, &$tag_stack ) );
     490                }
     491            }
     492        }
     493
     494        if ( $unbalanced ) {
     495            // Reset the namespace and context stacks to their previous values.
     496            array_splice( $this->namespace_stack, $namespace_stack_size );
     497            array_splice( $this->context_stack, $context_stack_size );
     498        }
     499
    391500        /*
    392501         * It returns null if the HTML is unbalanced because unbalanced HTML is
     
    398507            $tag_errored = 0 < count( $tag_stack ) ? end( $tag_stack )[0] : $tag_name;
    399508            /* translators: %1s: Namespace processed, %2s: The tag that caused the error; could be any HTML tag.  */
    400             $message = sprintf( __( 'Interactivity directives failed to process in "%1$s" due to a missing "%2$s" end tag.' ), end( $namespace_stack ), $tag_errored );
     509            $message = sprintf( __( 'Interactivity directives failed to process in "%1$s" due to a missing "%2$s" end tag.' ), end( $namespace_stack ), $tag_errored );
    401510            _doing_it_wrong( __METHOD__, $message, '6.6.0' );
    402511            return null;
     
    412521     * @since 6.5.0
    413522     * @since 6.6.0 The function now adds a warning when the namespace is null, falsy, or the directive value is empty.
    414      *
    415      * @param string|true $directive_value   The directive attribute value string or `true` when it's a boolean attribute.
    416      * @param string      $default_namespace The default namespace to use if none is explicitly defined in the directive
    417      *                                       value.
    418      * @param array|false $context           The current context for evaluating the directive or false if there is no
    419      *                                       context.
     523     * @since 6.6.0 Removed `default_namespace` and `context` arguments.
     524     *
     525     * @param string|true $directive_value The directive attribute value string or `true` when it's a boolean attribute.
    420526     * @return mixed|null The result of the evaluation. Null if the reference path doesn't exist or the namespace is falsy.
    421527     */
    422     private function evaluate( $directive_value, string $default_namespace, $context = false ) {
     528    private function evaluate( $directive_value ) {
     529        $default_namespace = end( $this->namespace_stack );
     530        $context           = end( $this->context_stack );
     531
    423532        list( $ns, $path ) = $this->extract_directive_value( $directive_value, $default_namespace );
    424533        if ( ! $ns || ! $path ) {
     
    451560        }
    452561
     562
     563
     564
     565
     566
     567
     568
     569
     570
     571
     572
     573
     574
     575
     576
     577
     578
     579
     580
     581
     582
     583
     584
     585
     586
     587
     588
    453589        // Returns the opposite if it contains a negation operator (!).
    454590        return $should_negate_value ? ! $current : $current;
     
    552688     * @since 6.5.0
    553689     *
    554      * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    555      * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    556      * @param array                                     $context_stack   The reference to the context stack.
    557      * @param array                                     $namespace_stack The reference to the store namespace stack.
    558      */
    559     private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     690     * @param WP_Interactivity_API_Directives_Processor $p    The directives processor instance.
     691     * @param string                                    $mode Whether the processing is entering or exiting the tag.
     692     */
     693    private function data_wp_interactive_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    560694        // When exiting tags, it removes the last namespace from the stack.
    561695        if ( 'exit' === $mode ) {
    562             array_pop( $namespace_stack );
     696            array_pop( $namespace_stack );
    563697            return;
    564698        }
     
    584718            }
    585719        }
    586         $namespace_stack[] = ( $new_namespace && 1 === preg_match( '/^([\w\-_\/]+)/', $new_namespace ) )
     720        $namespace_stack[] = ( $new_namespace && 1 === preg_match( '/^([\w\-_\/]+)/', $new_namespace ) )
    587721            ? $new_namespace
    588             : end( $namespace_stack );
     722            : end( $namespace_stack );
    589723    }
    590724
     
    599733     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    600734     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    601      * @param array                                     $context_stack   The reference to the context stack.
    602      * @param array                                     $namespace_stack The reference to the store namespace stack.
    603      */
    604     private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     735     */
     736    private function data_wp_context_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    605737        // When exiting tags, it removes the last context from the stack.
    606738        if ( 'exit' === $mode ) {
    607             array_pop( $context_stack );
     739            array_pop( $context_stack );
    608740            return;
    609741        }
    610742
    611743        $attribute_value = $p->get_attribute( 'data-wp-context' );
    612         $namespace_value = end( $namespace_stack );
     744        $namespace_value = end( $namespace_stack );
    613745
    614746        // Separates the namespace from the context JSON object.
     
    622754         */
    623755        if ( is_string( $namespace_value ) ) {
    624             $context_stack[] = array_replace_recursive(
    625                 end( $context_stack ) !== false ? end( $context_stack ) : array(),
     756            $context_stack[] = array_replace_recursive(
     757                end( $context_stack ) : array(),
    626758                array( $namespace_value => is_array( $decoded_json ) ? $decoded_json : array() )
    627759            );
     
    632764             * from the stack whenever it finds a `data-wp-context`'s closing tag.
    633765             */
    634             $context_stack[] = end( $context_stack );
     766            $context_stack );
    635767        }
    636768    }
     
    646778     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    647779     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    648      * @param array                                     $context_stack   The reference to the context stack.
    649      * @param array                                     $namespace_stack The reference to the store namespace stack.
    650      */
    651     private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     780     */
     781    private function data_wp_bind_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    652782        if ( 'enter' === $mode ) {
    653783            $all_bind_directives = $p->get_attribute_names_with_prefix( 'data-wp-bind--' );
     
    660790
    661791                $attribute_value = $p->get_attribute( $attribute_name );
    662                 $result          = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
     792                $result          = $this->evaluate( $attribute_value );
    663793
    664794                if (
     
    700830     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    701831     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    702      * @param array                                     $context_stack   The reference to the context stack.
    703      * @param array                                     $namespace_stack The reference to the store namespace stack.
    704      */
    705     private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     832     */
     833    private function data_wp_class_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    706834        if ( 'enter' === $mode ) {
    707835            $all_class_directives = $p->get_attribute_names_with_prefix( 'data-wp-class--' );
     
    714842
    715843                $attribute_value = $p->get_attribute( $attribute_name );
    716                 $result          = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
     844                $result          = $this->evaluate( $attribute_value );
    717845
    718846                if ( $result ) {
     
    735863     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    736864     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    737      * @param array                                     $context_stack   The reference to the context stack.
    738      * @param array                                     $namespace_stack The reference to the store namespace stack.
    739      */
    740     private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     865     */
     866    private function data_wp_style_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    741867        if ( 'enter' === $mode ) {
    742868            $all_style_attributes = $p->get_attribute_names_with_prefix( 'data-wp-style--' );
     
    749875
    750876                $directive_attribute_value = $p->get_attribute( $attribute_name );
    751                 $style_property_value      = $this->evaluate( $directive_attribute_value, end( $namespace_stack ), end( $context_stack ) );
     877                $style_property_value      = $this->evaluate( $directive_attribute_value );
    752878                $style_attribute_value     = $p->get_attribute( 'style' );
    753879                $style_attribute_value     = ( $style_attribute_value && ! is_bool( $style_attribute_value ) ) ? $style_attribute_value : '';
     
    828954     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    829955     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    830      * @param array                                     $context_stack   The reference to the context stack.
    831      * @param array                                     $namespace_stack The reference to the store namespace stack.
    832      */
    833     private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack ) {
     956     */
     957    private function data_wp_text_processor( WP_Interactivity_API_Directives_Processor $p, string $mode ) {
    834958        if ( 'enter' === $mode ) {
    835959            $attribute_value = $p->get_attribute( 'data-wp-text' );
    836             $result          = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
     960            $result          = $this->evaluate( $attribute_value );
    837961
    838962            /*
     
    9661090     * @param WP_Interactivity_API_Directives_Processor $p               The directives processor instance.
    9671091     * @param string                                    $mode            Whether the processing is entering or exiting the tag.
    968      * @param array                                     $context_stack   The reference to the context stack.
    969      * @param array                                     $namespace_stack The reference to the store namespace stack.
    9701092     * @param array                                     $tag_stack       The reference to the tag stack.
    9711093     */
    972     private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$context_stack, array &$namespace_stack, array &$tag_stack ) {
     1094    private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$tag_stack ) {
    9731095        if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
    9741096            $attribute_name   = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0];
     
    9761098            $item_name        = isset( $extracted_suffix[1] ) ? $this->kebab_to_camel_case( $extracted_suffix[1] ) : 'item';
    9771099            $attribute_value  = $p->get_attribute( $attribute_name );
    978             $result           = $this->evaluate( $attribute_value, end( $namespace_stack ), end( $context_stack ) );
     1100            $result           = $this->evaluate( $attribute_value );
    9791101
    9801102            // Gets the content between the template tags and leaves the cursor in the closer tag.
     
    10101132
    10111133            // Extracts the namespace from the directive attribute value.
    1012             $namespace_value         = end( $namespace_stack );
     1134            $namespace_value         = end( $namespace_stack );
    10131135            list( $namespace_value ) = is_string( $attribute_value ) && ! empty( $attribute_value )
    10141136                ? $this->extract_directive_value( $attribute_value, $namespace_value )
     
    10191141            foreach ( $result as $item ) {
    10201142                // Creates a new context that includes the current item of the array.
    1021                 $context_stack[] = array_replace_recursive(
    1022                     end( $context_stack ) !== false ? end( $context_stack ) : array(),
     1143                $context_stack[] = array_replace_recursive(
     1144                    end( $context_stack ) : array(),
    10231145                    array( $namespace_value => array( $item_name => $item ) )
    10241146                );
    10251147
    10261148                // Processes the inner content with the new context.
    1027                 $processed_item = $this->process_directives_args( $inner_content, $context_stack, $namespace_stack );
     1149                $processed_item = $this-> );
    10281150
    10291151                if ( null === $processed_item ) {
    10301152                    // If the HTML is unbalanced, stop processing it.
    1031                     array_pop( $context_stack );
     1153                    array_pop( $context_stack );
    10321154                    return;
    10331155                }
     
    10421164
    10431165                // Removes the current context from the stack.
    1044                 array_pop( $context_stack );
     1166                array_pop( $context_stack );
    10451167            }
    10461168
  • trunk/src/wp-includes/interactivity-api/interactivity-api.php

    r58234 r58327  
    4848 * provided state with the existing one.
    4949 *
     50
     51
     52
    5053 * @since 6.5.0
     54
    5155 *
    5256 * @param string $store_namespace The unique store namespace identifier.
     
    5660 *               provided.
    5761 */
    58 function wp_interactivity_state( string $store_namespace, array $state = array() ): array {
     62function wp_interactivity_state( , array $state = array() ): array {
    5963    return wp_interactivity()->state( $store_namespace, $state );
    6064}
     
    104108        '\'';
    105109}
     110
     111
     112
     113
     114
     115
     116
     117
     118
     119
     120
     121
     122
     123
     124
     125
     126
     127
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPI-wp-each.php

    r58321 r58327  
    582582     * @covers ::process_directives
    583583     *
    584      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     584     * @expectedIncorrectUsage WP_Interactivity_API::s
    585585     */
    586586    public function test_wp_each_unbalanced_tags() {
     
    602602     * @covers ::process_directives
    603603     *
    604      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     604     * @expectedIncorrectUsage WP_Interactivity_API::s
    605605     */
    606606    public function test_wp_each_unbalanced_tags_in_nested_template_tags() {
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPI-wp-text.php

    r58321 r58327  
    133133     * @covers ::process_directives
    134134     *
    135      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     135     * @expectedIncorrectUsage WP_Interactivity_API::s
    136136     */
    137137    public function test_wp_text_fails_with_unbalanced_and_same_tags_inside_content() {
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php

    r58321 r58327  
    3333
    3434    /**
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
    3563     * Tests that the state and config methods return an empty array at the
    3664     * beginning.
     
    424452JSON;
    425453        $this->assertEquals( $expected, $interactivity_data_string[1] );
     454
     455
     456
     457
     458
     459
     460
     461
     462
     463
     464
     465
     466
     467
     468
     469
     470
     471
     472
     473
     474
     475
     476
     477
     478
     479
     480
     481
     482
     483
     484
     485
     486
     487
     488
     489
     490
     491
     492
     493
     494
     495
     496
     497
     498
     499
     500
     501
     502
     503
     504
     505
     506
     507
     508
     509
     510
     511
     512
     513
     514
     515
     516
     517
     518
     519
     520
     521
     522
     523
     524
     525
     526
     527
     528
     529
     530
     531
     532
     533
     534
     535
     536
     537
     538
     539
     540
     541
     542
     543
     544
     545
     546
     547
     548
     549
     550
     551
     552
     553
     554
     555
     556
     557
     558
     559
     560
     561
     562
     563
     564
     565
     566
     567
     568
     569
     570
     571
     572
     573
     574
     575
     576
     577
     578
     579
     580
     581
     582
     583
     584
     585
     586
     587
     588
     589
     590
     591
     592
     593
     594
     595
     596
     597
     598
     599
     600
     601
     602
     603
     604
     605
     606
     607
     608
     609
     610
     611
     612
     613
     614
     615
     616
     617
     618
     619
     620
     621
     622
     623
     624
     625
     626
     627
     628
     629
     630
     631
     632
     633
     634
     635
     636
     637
     638
     639
     640
     641
     642
     643
     644
     645
     646
     647
     648
     649
     650
     651
     652
     653
     654
     655
     656
     657
     658
     659
     660
     661
     662
     663
     664
     665
     666
     667
     668
     669
    426670    }
    427671
     
    650894     * @dataProvider data_html_with_unbalanced_tags
    651895     *
    652      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     896     * @expectedIncorrectUsage WP_Interactivity_API::s
    653897     *
    654898     * @param string $html HTML containing unbalanced tags and also a directive.
     
    749993     *
    750994     * @covers ::process_directives
    751      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     995     * @expectedIncorrectUsage WP_Interactivity_API::s
    752996     */
    753997    public function test_process_directives_change_html_if_contains_math() {
     
    7841028     *
    7851029     * @covers ::process_directives
    786      * @expectedIncorrectUsage WP_Interactivity_API::process_directives_args
     1030     * @expectedIncorrectUsage WP_Interactivity_API::s
    7871031     * @expectedIncorrectUsage WP_Interactivity_API_Directives_Processor::skip_to_tag_closer
    7881032     */
     
    8141058     *
    8151059     * @param string $directive_value   The directive attribute value to evaluate.
    816      * @param string $default_namespace The default namespace used with directives.
    8171060     * @return mixed The result of the evaluate method.
    8181061     */
    819     private function evaluate( $directive_value, $default_namespace = 'myPlugin' ) {
    820         $generate_state = function ( $name ) {
    821             $obj       = new stdClass();
    822             $obj->prop = $name;
    823             return array(
    824                 'key'       => $name,
    825                 'nested'    => array( 'key' => $name . '-nested' ),
     1062    private function evaluate( $directive_value ) {
     1063        /*
     1064         * The global WP_Interactivity_API instance is momentarily replaced to
     1065         * make global functions like `wp_interactivity_state` and
     1066         * `wp_interactivity_get_config` work as expected.
     1067         */
     1068        global $wp_interactivity;
     1069        $wp_interactivity_prev = $wp_interactivity;
     1070        $wp_interactivity      = $this->interactivity;
     1071
     1072        $evaluate = new ReflectionMethod( $this->interactivity, 'evaluate' );
     1073        $evaluate->setAccessible( true );
     1074
     1075        $result = $evaluate->invokeArgs( $this->interactivity, array( $directive_value ) );
     1076
     1077        // Restore the original WP_Interactivity_API instance.
     1078        $wp_interactivity = $wp_interactivity_prev;
     1079
     1080        return $result;
     1081    }
     1082
     1083    /**
     1084     * Tests that the `evaluate` method operates correctly for valid expressions.
     1085     *
     1086     * @ticket 60356
     1087     *
     1088     * @covers ::evaluate
     1089     */
     1090    public function test_evaluate_value() {
     1091        $obj       = new stdClass();
     1092        $obj->prop = 'object property';
     1093        $this->interactivity->state(
     1094            'myPlugin',
     1095            array(
     1096                'key'       => 'myPlugin-state',
    8261097                'obj'       => $obj,
    8271098                'arrAccess' => new class() implements ArrayAccess {
    828                     #[\ReturnTypeWillChange]
    829                     public function offsetExists( $offset ) {
     1099                    public function offsetExists( $offset ): bool {
    8301100                        return true;
    8311101                    }
    8321102
    833                     public function offsetGet( $offset ): string {
     1103                    #[\ReturnTypeWillChange]
     1104                    public function offsetGet( $offset ) {
    8341105                        return $offset;
    8351106                    }
     
    8391110                    public function offsetUnset( $offset ): void {}
    8401111                },
    841             );
    842         };
    843         $this->interactivity->state( 'myPlugin', $generate_state( 'myPlugin-state' ) );
    844         $this->interactivity->state( 'otherPlugin', $generate_state( 'otherPlugin-state' ) );
    845         $context  = array(
    846             'myPlugin'    => $generate_state( 'myPlugin-context' ),
    847             'otherPlugin' => $generate_state( 'otherPlugin-context' ),
    848             'obj'         => new stdClass(),
    849         );
    850         $evaluate = new ReflectionMethod( $this->interactivity, 'evaluate' );
    851         $evaluate->setAccessible( true );
    852         return $evaluate->invokeArgs( $this->interactivity, array( $directive_value, $default_namespace, $context ) );
    853     }
    854 
    855     /**
    856      * Tests that the `evaluate` method operates correctly for valid expressions.
    857      *
    858      * @ticket 60356
    859      *
    860      * @covers ::evaluate
    861      */
    862     public function test_evaluate_value() {
     1112            )
     1113        );
     1114        $this->interactivity->state( 'otherPlugin', array( 'key' => 'otherPlugin-state' ) );
     1115        $this->set_internal_context_stack(
     1116            array(
     1117                'myPlugin'    => array( 'key' => 'myPlugin-context' ),
     1118                'otherPlugin' => array( 'key' => 'otherPlugin-context' ),
     1119            )
     1120        );
     1121        $this->set_internal_namespace_stack( 'myPlugin' );
     1122
    8631123        $result = $this->evaluate( 'state.key' );
    8641124        $this->assertEquals( 'myPlugin-state', $result );
     
    8741134
    8751135        $result = $this->evaluate( 'state.obj.prop' );
    876         $this->assertSame( 'myPlugin-state', $result );
     1136        $this->assertSame( '', $result );
    8771137
    8781138        $result = $this->evaluate( 'state.arrAccess.1' );
     
    8891149     */
    8901150    public function test_evaluate_value_negation() {
     1151
     1152
     1153
     1154
     1155
     1156
     1157
     1158
     1159
     1160
    8911161        $result = $this->evaluate( '!state.key' );
    8921162        $this->assertFalse( $result );
     
    9101180     */
    9111181    public function test_evaluate_non_existent_path() {
     1182
     1183
     1184
     1185
     1186
     1187
     1188
     1189
     1190
     1191
    9121192        $result = $this->evaluate( 'state.nonExistentKey' );
    9131193        $this->assertNull( $result );
     
    9371217     */
    9381218    public function test_evaluate_nested_value() {
     1219
     1220
     1221
     1222
     1223
     1224
     1225
     1226
     1227
     1228
     1229
     1230
     1231
     1232
     1233
     1234
     1235
     1236
     1237
     1238
     1239
     1240
     1241
     1242
    9391243        $result = $this->evaluate( 'state.nested.key' );
    9401244        $this->assertEquals( 'myPlugin-state-nested', $result );
     
    9591263     */
    9601264    public function test_evaluate_unvalid_namespaces() {
     1265
     1266
     1267
    9611268        $result = $this->evaluate( 'path', 'null' );
    9621269        $this->assertNull( $result );
     
    9661273
    9671274        $result = $this->evaluate( 'path', '{}' );
     1275
     1276
     1277
     1278
     1279
     1280
     1281
     1282
     1283
     1284
     1285
     1286
     1287
     1288
     1289
     1290
     1291
     1292
     1293
     1294
     1295
     1296
     1297
     1298
     1299
     1300
     1301
     1302
     1303
     1304
     1305
     1306
     1307
     1308
     1309
     1310
     1311
     1312
     1313
     1314
     1315
     1316
     1317
     1318
     1319
     1320
     1321
     1322
     1323
     1324
     1325
     1326
     1327
     1328
     1329
     1330
     1331
     1332
     1333
     1334
     1335
     1336
     1337
     1338
     1339
     1340
     1341
     1342
     1343
     1344
     1345
     1346
     1347
     1348
     1349
     1350
     1351
     1352
     1353
     1354
     1355
     1356
     1357
     1358
     1359
     1360
     1361
     1362
     1363
     1364
     1365
     1366
     1367
     1368
     1369
     1370
     1371
     1372
     1373
     1374
     1375
     1376
     1377
     1378
     1379
     1380
     1381
     1382
     1383
     1384
     1385
     1386
     1387
     1388
     1389
     1390
     1391
     1392
     1393
     1394
     1395
     1396
     1397
     1398
     1399
     1400
     1401
     1402
     1403
     1404
     1405
     1406
     1407
     1408
     1409
     1410
     1411
     1412
     1413
     1414
     1415
     1416
     1417
     1418
     1419
     1420
     1421
     1422
     1423
    9681424        $this->assertNull( $result );
    9691425    }
Note: See TracChangeset for help on using the changeset viewer.