Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactivity API: Support .length on numeric arrays and strings on the server #7751

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (codepints, graphemes, bytes, etc.).
sirreal marked this conversation as resolved.
Show resolved Hide resolved
*/
$current = strlen( $current );
sirreal marked this conversation as resolved.
Show resolved Hide resolved
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(), '' ),
);
}
}
Loading