From c20584c7c7359b2c71217d8a0e0a505e4a5796cd Mon Sep 17 00:00:00 2001 From: Aaron Jorbin <622599+aaronjorbin@users.noreply.github.com> Date: Fri, 23 Jan 2026 17:15:51 -0600 Subject: [PATCH] backport [61405] --- src/wp-admin/includes/export.php | 4 +- tests/phpunit/tests/admin/exportWp.php | 151 +++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/export.php b/src/wp-admin/includes/export.php index 9d5903a98c5c3..916980113bdf9 100644 --- a/src/wp-admin/includes/export.php +++ b/src/wp-admin/includes/export.php @@ -239,10 +239,12 @@ function export_wp( $args = array() ) { * * @since 2.1.0 * - * @param string $str String to wrap in XML CDATA tag. + * @param string|null $str String to wrap in XML CDATA tag. May be null. * @return string */ function wxr_cdata( $str ) { + $str = (string) $str; + if ( ! wp_is_valid_utf8( $str ) ) { $str = utf8_encode( $str ); } diff --git a/tests/phpunit/tests/admin/exportWp.php b/tests/phpunit/tests/admin/exportWp.php index ebb60018c0e7a..1f1219f8267ac 100644 --- a/tests/phpunit/tests/admin/exportWp.php +++ b/tests/phpunit/tests/admin/exportWp.php @@ -290,4 +290,155 @@ private function populate_args_post_authors( array &$args, $expected_ids ) { $post_ids_key = $expected_ids[0]; $args['author'] = self::$post_ids[ $post_ids_key ]['post_author']; } + + /** + * Tests that export handles posts with NULL postmeta values without fatal errors. + * + * @ticket 64347 + */ + public function test_export_with_null_postmeta_values() { + global $wpdb; + + $post_id = self::factory()->post->create( + array( + 'post_title' => 'Test Post with NULL Meta', + 'post_content' => 'Test content', + 'post_type' => 'post', + ) + ); + + // Add multiple types of postmeta values. + add_post_meta( $post_id, 'string_meta', 'normal string' ); + add_post_meta( $post_id, 'numeric_string_meta', 123 ); + add_post_meta( $post_id, 'empty_string_meta', '' ); + add_post_meta( + $post_id, + 'array_meta', + array( + 'key' => 'value', + ) + ); + + // Directly insert NULL and non-string values into postmeta. + $wpdb->insert( + $wpdb->postmeta, + array( + 'post_id' => $post_id, + 'meta_key' => 'null_meta', + 'meta_value' => null, + ), + array( '%d', '%s', '%s' ) + ); + + $xml = $this->get_the_export( + array( + 'content' => 'post', + ) + ); + + $this->assertNotFalse( $xml, 'Export should not fail with NULL postmeta values' ); + $this->assertGreaterThan( 0, count( $xml->channel->item ), 'Export should contain items' ); + + // Post should be present in export. + $found_post = false; + foreach ( $xml->channel->item as $item ) { + $wp_item = $item->children( 'wp', true ); + if ( (int) $wp_item->post_id === $post_id ) { + $found_post = true; + break; + } + } + + $this->assertTrue( $found_post, 'Post with NULL metadata should be included in export' ); + } + + /** + * Tests that export handles comments with NULL values without fatal errors. + * + * @ticket 64347 + */ + public function test_export_with_null_comment_values() { + global $wpdb; + + $post_id = self::factory()->post->create( + array( + 'post_title' => 'Test Post for Comments', + 'post_type' => 'post', + ) + ); + + $comment_id = self::factory()->comment->create( + array( + 'comment_post_ID' => $post_id, + 'comment_content' => 'Test comment', + ) + ); + + // Insert NULL comment meta. + $wpdb->insert( + $wpdb->commentmeta, + array( + 'comment_id' => $comment_id, + 'meta_key' => 'null_comment_meta', + 'meta_value' => null, + ), + array( '%d', '%s', '%s' ) + ); + + $xml = $this->get_the_export( + array( + 'content' => 'post', + ) + ); + + $this->assertNotFalse( $xml, 'Export should not fail with NULL comment meta values' ); + $this->assertGreaterThan( 0, count( $xml->channel->item ), 'Export should contain items' ); + } + + /** + * Tests that export handles term meta with NULL values without fatal errors. + * + * @ticket 64347 + */ + public function test_export_with_null_term_meta_values() { + global $wpdb; + + // Create term. + $term = self::factory()->term->create( + array( + 'taxonomy' => 'category', + 'name' => 'Test Category', + ) + ); + + $post_id = self::factory()->post->create( + array( + 'post_title' => 'Test Post with Category', + 'post_type' => 'post', + 'post_status' => 'publish', + ) + ); + + wp_set_object_terms( $post_id, $term, 'category' ); + + // Insert NULL term meta. + $wpdb->insert( + $wpdb->termmeta, + array( + 'term_id' => $term, + 'meta_key' => 'null_term_meta', + 'meta_value' => null, + ), + array( '%d', '%s', '%s' ) + ); + + $xml = $this->get_the_export( + array( + 'content' => 'all', + ) + ); + + $this->assertNotFalse( $xml, 'Export should not fail with NULL term meta values' ); + $this->assertGreaterThan( 0, count( $xml->channel->item ), 'Export should contain items' ); + } }