Skip to content

Commit

Permalink
Interactivity API: Support length property on strings and arrays on t…
Browse files Browse the repository at this point in the history
…he server

The Interactivity API tries to align client and server rendering so that the behavior is the same. Adds missing handling for `.length` to directives processing on the server on strings and numeric arrays which is inherently supported through JavaScript language on the client.

Props jonsurrell, gziolo, luisherranz.
Fixes #62582.



git-svn-id: https://develop.svn.wordpress.org/trunk@59477 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
gziolo committed Dec 2, 2024
1 parent c5a5d9b commit e89e279
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/wp-includes/interactivity-api/class-wp-interactivity-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,36 @@ private function evaluate( $directive_value ) {
$path_segments = explode( '.', $path );
$current = $store;
foreach ( $path_segments as $path_segment ) {
/*
* Special case for numeric arrays and strings. Add length
* property mimicking JavaScript behavior.
*
* @since 6.8.0
*/
if ( 'length' === $path_segment ) {
if ( is_array( $current ) && array_is_list( $current ) ) {
$current = count( $current );
break;
}

if ( is_string( $current ) ) {
/*
* Differences in encoding between PHP strings and
* JavaScript mean that it's complicated to calculate
* the string length JavaScript would see from PHP.
* `strlen` is a reasonable approximation.
*
* Users that desire a more precise length likely have
* more precise needs than "bytelength" and should
* implement their own length calculation in derived
* state taking into account encoding and their desired
* output (codepoints, graphemes, bytes, etc.).
*/
$current = strlen( $current );
break;
}
}

if ( ( is_array( $current ) || $current instanceof ArrayAccess ) && isset( $current[ $path_segment ] ) ) {
$current = $current[ $path_segment ];
} elseif ( is_object( $current ) && isset( $current->$path_segment ) ) {
Expand Down
44 changes: 44 additions & 0 deletions tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -1541,4 +1541,48 @@ public function test_get_element_outside_of_directive_processing() {
$element = $this->interactivity->get_element();
$this->assertNull( $element );
}

/**
* Verify behavior of .length directive access.
*
* @ticket 62582
*
* @covers ::process_directives
*
* @dataProvider data_length_directives
*
* @param mixed $value The property value.
* @param string $expected The expected property length as a string,
* or "" if no length is expected.
*/
public function test_process_directives_string_array_length( $value, string $expected ) {
$this->interactivity->state(
'myPlugin',
array( 'prop' => $value )
);
$html = '<div data-wp-text="myPlugin::state.prop.length"></div>';
$processed_html = $this->interactivity->process_directives( $html );
$processor = new WP_HTML_Tag_Processor( $processed_html );
$processor->next_tag( 'DIV' );
$processor->next_token();
$this->assertSame( $expected, $processor->get_modifiable_text() );
}

/**
* Data provider.
*
* @return array
*/
public static function data_length_directives(): array {
return array(
'numeric array' => array( array( 'a', 'b', 'c' ), '3' ),
'empty array' => array( array(), '0' ),
'string' => array( 'abc', '3' ),
'empty string' => array( '', '0' ),

// Failure cases resulting in empty string.
'non-numeric array' => array( array( 'a' => 'a' ), '' ),
'object' => array( new stdClass(), '' ),
);
}
}

0 comments on commit e89e279

Please sign in to comment.