Skip to content
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
11 changes: 11 additions & 0 deletions includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ function ( $block ) {
array(
'blockTypes' => array_values( $block_types ),
'postType' => get_post_type(),
'StrictMode' => version_compare( $GLOBALS['wp_version'], '6.9', '>=' ),
)
);

Expand Down Expand Up @@ -1031,6 +1032,16 @@ function acf_ajax_fetch_block() {
$raw_context = $args['context'];
$post_id = $args['post_id'];

// Decode query if sent as a JSON string instead of an array.
// The block editor JS may serialize the query parameter as JSON,
// which causes a fatal TypeError on PHP 8.4 when accessing offsets.
if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix follows the existing pattern for $block and $raw_context above — nice.

Could you add a PHPUnit test for this? Something like a test in tests/phpunit/ that calls acf_ajax_fetch_block() (or the relevant helper) with query as a JSON string and verifies it gets decoded to an array without error. The $block and $raw_context decoding paths above would also benefit from tests eventually, but covering this new path would be a good start.


// Bail early if no block.
if ( ! $block ) {
wp_send_json_error();
Expand Down
111 changes: 111 additions & 0 deletions tests/php/includes/test-blocks-query-decoding.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php
/**
* Tests for block query parameter decoding in acf_ajax_fetch_block().
*
* @package wordpress/secure-custom-fields
*/

use WorDBless\BaseTestCase;

/**
* Class Test_Blocks_Query_Decoding
*
* Tests that the query parameter is correctly decoded from a JSON string
* to an array in acf_ajax_fetch_block(), preventing fatal TypeError
* on PHP 8.4 when accessing offsets on a string.
*
* @group blocks
*/
class Test_Blocks_Query_Decoding extends BaseTestCase {

/**
* Test that a JSON-encoded query string is decoded to an array.
*/
public function test_json_string_query_is_decoded_to_array() {
$query = wp_slash( '{"post_type":"post","posts_per_page":3}' );

// Simulate the decoding logic from acf_ajax_fetch_block().
if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}

$this->assertIsArray( $query );
$this->assertSame( 'post', $query['post_type'] );
$this->assertSame( 3, $query['posts_per_page'] );
}

/**
* Test that an invalid JSON string falls back to an empty array.
*/
public function test_invalid_json_string_falls_back_to_empty_array() {
$query = 'not valid json {{{';

if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}

$this->assertIsArray( $query );
$this->assertEmpty( $query );
}

/**
* Test that an array query is left unchanged.
*/
public function test_array_query_is_unchanged() {
$query = array(
'post_type' => 'page',
'posts_per_page' => 5,
);

if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}

$this->assertIsArray( $query );
$this->assertSame( 'page', $query['post_type'] );
$this->assertSame( 5, $query['posts_per_page'] );
}

/**
* Test that a JSON string encoding a non-array value falls back to empty array.
*/
public function test_json_non_array_value_falls_back_to_empty_array() {
$query = '"just a string"';

if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}

$this->assertIsArray( $query );
$this->assertEmpty( $query );
}

/**
* Test that an empty string falls back to an empty array.
*/
public function test_empty_string_falls_back_to_empty_array() {
$query = '';

if ( is_string( $query ) ) {
$query = json_decode( wp_unslash( $query ), true );
if ( ! is_array( $query ) ) {
$query = array();
}
}

$this->assertIsArray( $query );
$this->assertEmpty( $query );
}
}
Loading