Skip to content

Commit e89e279

Browse files
committed
Interactivity API: Support length property on strings and arrays on the 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
1 parent c5a5d9b commit e89e279

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/wp-includes/interactivity-api/class-wp-interactivity-api.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,36 @@ private function evaluate( $directive_value ) {
579579
$path_segments = explode( '.', $path );
580580
$current = $store;
581581
foreach ( $path_segments as $path_segment ) {
582+
/*
583+
* Special case for numeric arrays and strings. Add length
584+
* property mimicking JavaScript behavior.
585+
*
586+
* @since 6.8.0
587+
*/
588+
if ( 'length' === $path_segment ) {
589+
if ( is_array( $current ) && array_is_list( $current ) ) {
590+
$current = count( $current );
591+
break;
592+
}
593+
594+
if ( is_string( $current ) ) {
595+
/*
596+
* Differences in encoding between PHP strings and
597+
* JavaScript mean that it's complicated to calculate
598+
* the string length JavaScript would see from PHP.
599+
* `strlen` is a reasonable approximation.
600+
*
601+
* Users that desire a more precise length likely have
602+
* more precise needs than "bytelength" and should
603+
* implement their own length calculation in derived
604+
* state taking into account encoding and their desired
605+
* output (codepoints, graphemes, bytes, etc.).
606+
*/
607+
$current = strlen( $current );
608+
break;
609+
}
610+
}
611+
582612
if ( ( is_array( $current ) || $current instanceof ArrayAccess ) && isset( $current[ $path_segment ] ) ) {
583613
$current = $current[ $path_segment ];
584614
} elseif ( is_object( $current ) && isset( $current->$path_segment ) ) {

tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,4 +1541,48 @@ public function test_get_element_outside_of_directive_processing() {
15411541
$element = $this->interactivity->get_element();
15421542
$this->assertNull( $element );
15431543
}
1544+
1545+
/**
1546+
* Verify behavior of .length directive access.
1547+
*
1548+
* @ticket 62582
1549+
*
1550+
* @covers ::process_directives
1551+
*
1552+
* @dataProvider data_length_directives
1553+
*
1554+
* @param mixed $value The property value.
1555+
* @param string $expected The expected property length as a string,
1556+
* or "" if no length is expected.
1557+
*/
1558+
public function test_process_directives_string_array_length( $value, string $expected ) {
1559+
$this->interactivity->state(
1560+
'myPlugin',
1561+
array( 'prop' => $value )
1562+
);
1563+
$html = '<div data-wp-text="myPlugin::state.prop.length"></div>';
1564+
$processed_html = $this->interactivity->process_directives( $html );
1565+
$processor = new WP_HTML_Tag_Processor( $processed_html );
1566+
$processor->next_tag( 'DIV' );
1567+
$processor->next_token();
1568+
$this->assertSame( $expected, $processor->get_modifiable_text() );
1569+
}
1570+
1571+
/**
1572+
* Data provider.
1573+
*
1574+
* @return array
1575+
*/
1576+
public static function data_length_directives(): array {
1577+
return array(
1578+
'numeric array' => array( array( 'a', 'b', 'c' ), '3' ),
1579+
'empty array' => array( array(), '0' ),
1580+
'string' => array( 'abc', '3' ),
1581+
'empty string' => array( '', '0' ),
1582+
1583+
// Failure cases resulting in empty string.
1584+
'non-numeric array' => array( array( 'a' => 'a' ), '' ),
1585+
'object' => array( new stdClass(), '' ),
1586+
);
1587+
}
15441588
}

0 commit comments

Comments
 (0)