From 34cfcb7fc10c6d789fa4eeb1299f6f2e8f8553ab Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Tue, 10 Mar 2026 22:25:04 +0545 Subject: [PATCH 1/2] Harden select and checkbox custom option saving --- includes/fields/class-acf-field-checkbox.php | 28 +++++++--- includes/fields/class-acf-field-select.php | 21 ++++++-- .../fields/test-class-acf-field-checkbox.php | 49 ++++++++++++++++++ .../fields/test-class-acf-field-select.php | 51 +++++++++++++++++++ 4 files changed, 138 insertions(+), 11 deletions(-) diff --git a/includes/fields/class-acf-field-checkbox.php b/includes/fields/class-acf-field-checkbox.php index 5935a041..abd434c8 100644 --- a/includes/fields/class-acf-field-checkbox.php +++ b/includes/fields/class-acf-field-checkbox.php @@ -499,22 +499,34 @@ function update_value( $value, $post_id, $field ) { // select -> update_value() $value = acf_get_field_type( 'select' )->update_value( $value, $post_id, $field ); - // save_other_choice - if ( $field['save_custom'] ) { + // save_custom + if ( ! empty( $field['save_custom'] ) && is_array( $value ) ) { // get raw $field (may have been changed via repeater field) // if field is local, it won't have an ID - $selector = $field['ID'] ? $field['ID'] : $field['key']; - $field = acf_get_field( $selector ); - if ( ! $field ) { - return false; + $selector = ''; + if ( ! empty( $field['ID'] ) ) { + $selector = $field['ID']; + } elseif ( ! empty( $field['key'] ) ) { + $selector = $field['key']; } - // bail early if no ID (JSON only) - if ( ! $field['ID'] ) { + // bail early if we have no selector for lookup + if ( ! $selector ) { return $value; } + $field = acf_get_field( $selector ); + + // bail early if lookup failed or field is JSON only + if ( ! is_array( $field ) || empty( $field['ID'] ) ) { + return $value; + } + + if ( ! isset( $field['choices'] ) || ! is_array( $field['choices'] ) ) { + $field['choices'] = array(); + } + // loop foreach ( $value as $v ) { diff --git a/includes/fields/class-acf-field-select.php b/includes/fields/class-acf-field-select.php index bd9f810b..5fb07f2e 100644 --- a/includes/fields/class-acf-field-select.php +++ b/includes/fields/class-acf-field-select.php @@ -576,14 +576,29 @@ public function update_value( $value, $post_id, $field ) { // Save custom options back to the field definition if configured. if ( ! empty( $field['save_options'] ) && is_array( $value ) ) { // Get the raw field, using the ID if present or the key otherwise (i.e. when using JSON). - $selector = $field['ID'] ? $field['ID'] : $field['key']; - $field = acf_get_field( $selector ); + $selector = ''; + if ( ! empty( $field['ID'] ) ) { + $selector = $field['ID']; + } elseif ( ! empty( $field['key'] ) ) { + $selector = $field['key']; + } + + // Bail when field lookup can't be attempted. + if ( ! $selector ) { + return $value; + } + + $field = acf_get_field( $selector ); // Bail if we don't have a valid field or field ID (JSON only). - if ( empty( $field['ID'] ) ) { + if ( ! is_array( $field ) || empty( $field['ID'] ) ) { return $value; } + if ( ! isset( $field['choices'] ) || ! is_array( $field['choices'] ) ) { + $field['choices'] = array(); + } + foreach ( $value as $v ) { // Ignore if the option already exists. if ( isset( $field['choices'][ $v ] ) ) { diff --git a/tests/php/includes/fields/test-class-acf-field-checkbox.php b/tests/php/includes/fields/test-class-acf-field-checkbox.php index e46c28e1..cd683702 100644 --- a/tests/php/includes/fields/test-class-acf-field-checkbox.php +++ b/tests/php/includes/fields/test-class-acf-field-checkbox.php @@ -176,6 +176,55 @@ public function test_update_value_empty() { $this->assertEmpty( $result ); } + /** + * Test save_custom does not trigger warnings for JSON-only fields. + */ + public function test_update_value_save_custom_handles_json_field_without_id() { + $field = $this->get_field( + array( + 'save_custom' => 1, + ) + ); + + set_error_handler( + static function ( $errno, $errstr ) { + throw new \RuntimeException( $errstr, $errno ); + } + ); + + try { + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); + } finally { + restore_error_handler(); + } + } + + /** + * Test save_custom bails early when field key and ID are both missing. + */ + public function test_update_value_save_custom_handles_missing_field_identifier() { + $field = $this->get_field( + array( + 'save_custom' => 1, + ) + ); + unset( $field['key'] ); + + set_error_handler( + static function ( $errno, $errstr ) { + throw new \RuntimeException( $errstr, $errno ); + } + ); + + try { + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); + } finally { + restore_error_handler(); + } + } + /** * Test validate_value with valid selection. */ diff --git a/tests/php/includes/fields/test-class-acf-field-select.php b/tests/php/includes/fields/test-class-acf-field-select.php index 106b7969..c62aacf4 100644 --- a/tests/php/includes/fields/test-class-acf-field-select.php +++ b/tests/php/includes/fields/test-class-acf-field-select.php @@ -202,6 +202,57 @@ public function test_update_value_converts_to_strings() { $this->assertContains( 'blue', $result ); } + /** + * Test save_options does not trigger warnings for JSON-only fields. + */ + public function test_update_value_save_options_handles_json_field_without_id() { + $field = $this->get_field( + array( + 'save_options' => 1, + 'multiple' => 1, + ) + ); + + set_error_handler( + static function ( $errno, $errstr ) { + throw new \RuntimeException( $errstr, $errno ); + } + ); + + try { + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); + } finally { + restore_error_handler(); + } + } + + /** + * Test save_options bails early when field key and ID are both missing. + */ + public function test_update_value_save_options_handles_missing_field_identifier() { + $field = $this->get_field( + array( + 'save_options' => 1, + 'multiple' => 1, + ) + ); + unset( $field['key'] ); + + set_error_handler( + static function ( $errno, $errstr ) { + throw new \RuntimeException( $errstr, $errno ); + } + ); + + try { + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); + } finally { + restore_error_handler(); + } + } + /** * Test get_rest_schema returns valid schema. */ From 183fdb7479cd36efdd00330420b64e534f7738bc Mon Sep 17 00:00:00 2001 From: Developer Ravi Date: Wed, 11 Mar 2026 21:34:47 +0545 Subject: [PATCH 2/2] Fix PHPCS failures in select/checkbox regression tests --- .../fields/test-class-acf-field-checkbox.php | 28 +++---------------- .../fields/test-class-acf-field-select.php | 28 +++---------------- 2 files changed, 8 insertions(+), 48 deletions(-) diff --git a/tests/php/includes/fields/test-class-acf-field-checkbox.php b/tests/php/includes/fields/test-class-acf-field-checkbox.php index cd683702..63456bf7 100644 --- a/tests/php/includes/fields/test-class-acf-field-checkbox.php +++ b/tests/php/includes/fields/test-class-acf-field-checkbox.php @@ -186,18 +186,8 @@ public function test_update_value_save_custom_handles_json_field_without_id() { ) ); - set_error_handler( - static function ( $errno, $errstr ) { - throw new \RuntimeException( $errstr, $errno ); - } - ); - - try { - $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); - $this->assertSame( array( 'custom_value' ), $result ); - } finally { - restore_error_handler(); - } + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); } /** @@ -211,18 +201,8 @@ public function test_update_value_save_custom_handles_missing_field_identifier() ); unset( $field['key'] ); - set_error_handler( - static function ( $errno, $errstr ) { - throw new \RuntimeException( $errstr, $errno ); - } - ); - - try { - $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); - $this->assertSame( array( 'custom_value' ), $result ); - } finally { - restore_error_handler(); - } + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); } /** diff --git a/tests/php/includes/fields/test-class-acf-field-select.php b/tests/php/includes/fields/test-class-acf-field-select.php index c62aacf4..4e0c9b28 100644 --- a/tests/php/includes/fields/test-class-acf-field-select.php +++ b/tests/php/includes/fields/test-class-acf-field-select.php @@ -213,18 +213,8 @@ public function test_update_value_save_options_handles_json_field_without_id() { ) ); - set_error_handler( - static function ( $errno, $errstr ) { - throw new \RuntimeException( $errstr, $errno ); - } - ); - - try { - $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); - $this->assertSame( array( 'custom_value' ), $result ); - } finally { - restore_error_handler(); - } + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); } /** @@ -239,18 +229,8 @@ public function test_update_value_save_options_handles_missing_field_identifier( ); unset( $field['key'] ); - set_error_handler( - static function ( $errno, $errstr ) { - throw new \RuntimeException( $errstr, $errno ); - } - ); - - try { - $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); - $this->assertSame( array( 'custom_value' ), $result ); - } finally { - restore_error_handler(); - } + $result = $this->field_instance->update_value( array( 'custom_value' ), $this->post_id, $field ); + $this->assertSame( array( 'custom_value' ), $result ); } /**