@@ -290,4 +290,275 @@ private function populate_args_post_authors( array &$args, $expected_ids ) {
290290 $ post_ids_key = $ expected_ids [0 ];
291291 $ args ['author ' ] = self ::$ post_ids [ $ post_ids_key ]['post_author ' ];
292292 }
293+
294+ /**
295+ * Tests that export handles posts with NULL postmeta values without fatal errors.
296+ *
297+ * @since n.e.x.t
298+ *
299+ * @ticket 64347
300+ */
301+ public function test_export_with_null_postmeta_values () {
302+ global $ wpdb ;
303+
304+ $ post_id = self ::factory ()->post ->create (
305+ array (
306+ 'post_title ' => 'Test Post with NULL Meta ' ,
307+ 'post_content ' => 'Test content ' ,
308+ 'post_type ' => 'post ' ,
309+ )
310+ );
311+
312+ // Add multiple types of postmeta values.
313+ add_post_meta ( $ post_id , 'string_meta ' , 'normal string ' );
314+ add_post_meta ( $ post_id , 'numeric_string_meta ' , 123 );
315+ add_post_meta ( $ post_id , 'empty_string_meta ' , '' );
316+ add_post_meta (
317+ $ post_id ,
318+ 'array_meta ' ,
319+ array (
320+ 'key ' => 'value '
321+ )
322+ );
323+
324+ // Directly insert NULL and non-string values into postmeta.
325+ $ wpdb ->insert (
326+ $ wpdb ->postmeta ,
327+ array (
328+ 'post_id ' => $ post_id ,
329+ 'meta_key ' => 'null_meta ' ,
330+ 'meta_value ' => null ,
331+ ),
332+ array ( '%d ' , '%s ' , '%s ' )
333+ );
334+
335+ $ xml = $ this ->get_the_export (
336+ array (
337+ 'content ' => 'post ' ,
338+ )
339+ );
340+
341+ $ this ->assertNotFalse ( $ xml , 'Export should not fail with NULL postmeta values ' );
342+ $ this ->assertGreaterThan ( 0 , count ( $ xml ->channel ->item ), 'Export should contain items ' );
343+
344+ // Post should be present in export.
345+ $ found_post = false ;
346+ foreach ( $ xml ->channel ->item as $ item ) {
347+ $ wp_item = $ item ->children ( 'wp ' , true );
348+ if ( (int ) $ wp_item ->post_id === $ post_id ) {
349+ $ found_post = true ;
350+ break ;
351+ }
352+ }
353+
354+ $ this ->assertTrue ( $ found_post , 'Post with NULL metadata should be included in export ' );
355+ }
356+
357+ /**
358+ * Tests that export handles comments with NULL values without fatal errors.
359+ *
360+ * @since n.e.x.t
361+ *
362+ * @ticket 64347
363+ */
364+ public function test_export_with_null_comment_values () {
365+ global $ wpdb ;
366+
367+ $ post_id = self ::factory ()->post ->create (
368+ array (
369+ 'post_title ' => 'Test Post for Comments ' ,
370+ 'post_type ' => 'post ' ,
371+ )
372+ );
373+
374+ $ comment_id = self ::factory ()->comment ->create (
375+ array (
376+ 'comment_post_ID ' => $ post_id ,
377+ 'comment_content ' => 'Test comment ' ,
378+ )
379+ );
380+
381+ // Insert NULL comment meta.
382+ $ wpdb ->insert (
383+ $ wpdb ->commentmeta ,
384+ array (
385+ 'comment_id ' => $ comment_id ,
386+ 'meta_key ' => 'null_comment_meta ' ,
387+ 'meta_value ' => null ,
388+ ),
389+ array ( '%d ' , '%s ' , '%s ' )
390+ );
391+
392+ $ xml = $ this ->get_the_export (
393+ array (
394+ 'content ' => 'post ' ,
395+ )
396+ );
397+
398+ $ this ->assertNotFalse ( $ xml , 'Export should not fail with NULL comment meta values ' );
399+ $ this ->assertGreaterThan ( 0 , count ( $ xml ->channel ->item ), 'Export should contain items ' );
400+ }
401+
402+ /**
403+ * Tests that export handles term meta with NULL values without fatal errors.
404+ *
405+ * @since n.e.x.t
406+ *
407+ * @ticket 64347
408+ */
409+ public function test_export_with_null_term_meta_values () {
410+ global $ wpdb ;
411+
412+ // Create term.
413+ $ term = self ::factory ()->term ->create (
414+ array (
415+ 'taxonomy ' => 'category ' ,
416+ 'name ' => 'Test Category ' ,
417+ )
418+ );
419+
420+ $ post_id = self ::factory ()->post ->create (
421+ array (
422+ 'post_title ' => 'Test Post with Category ' ,
423+ 'post_type ' => 'post ' ,
424+ 'post_status ' => 'publish ' ,
425+ )
426+ );
427+
428+ wp_set_object_terms ( $ post_id , $ term , 'category ' );
429+
430+ // Insert NULL term meta.
431+ $ wpdb ->insert (
432+ $ wpdb ->termmeta ,
433+ array (
434+ 'term_id ' => $ term ,
435+ 'meta_key ' => 'null_term_meta ' ,
436+ 'meta_value ' => null ,
437+ ),
438+ array ( '%d ' , '%s ' , '%s ' )
439+ );
440+
441+ $ xml = $ this ->get_the_export (
442+ array (
443+ 'content ' => 'all ' ,
444+ )
445+ );
446+
447+ $ this ->assertNotFalse ( $ xml , 'Export should not fail with NULL term meta values ' );
448+ $ this ->assertGreaterThan ( 0 , count ( $ xml ->channel ->item ), 'Export should contain items ' );
449+ }
450+
451+ /**
452+ * Tests that export handles posts with NULL title and content without fatal errors.
453+ *
454+ * @since n.e.x.t
455+ *
456+ * @ticket 64347
457+ */
458+ public function test_export_with_null_post_fields () {
459+ global $ wpdb ;
460+
461+ // Create a post first.
462+ $ post_id = self ::factory ()->post ->create (
463+ array (
464+ 'post_title ' => 'Test Post ' ,
465+ 'post_content ' => 'Test content ' ,
466+ 'post_type ' => 'post ' ,
467+ )
468+ );
469+
470+ // Update to set NULL values directly (bypassing WordPress API).
471+ $ wpdb ->update (
472+ $ wpdb ->posts ,
473+ array (
474+ 'post_excerpt ' => null ,
475+ ),
476+ array ( 'ID ' => $ post_id ),
477+ array ( '%s ' ),
478+ array ( '%d ' )
479+ );
480+
481+ $ xml = $ this ->get_the_export (
482+ array (
483+ 'content ' => 'post ' ,
484+ )
485+ );
486+
487+ $ this ->assertNotFalse ( $ xml , 'Export should not fail with NULL post fields ' );
488+ $ this ->assertGreaterThan ( 0 , count ( $ xml ->channel ->item ), 'Export should contain items ' );
489+
490+ // Verify the post is in the export.
491+ $ found_post = false ;
492+ foreach ( $ xml ->channel ->item as $ item ) {
493+ $ wp_item = $ item ->children ( 'wp ' , true );
494+ if ( (int ) $ wp_item ->post_id === $ post_id ) {
495+ $ found_post = true ;
496+ break ;
497+ }
498+ }
499+
500+ $ this ->assertTrue ( $ found_post , 'Post with NULL excerpt should be included in export ' );
501+ }
502+
503+ /**
504+ * Tests that export handles user fields with potential NULL values.
505+ *
506+ * @since n.e.x.t
507+ *
508+ * @ticket 64347
509+ */
510+ public function test_export_with_users_having_empty_fields () {
511+ global $ wpdb ;
512+
513+ $ user_id = self ::factory ()->user ->create (
514+ array (
515+ 'user_login ' => 'testuser_export ' ,
516+ 'user_email ' =>
'[email protected] ' ,
517+ 'first_name ' => '' ,
518+ 'last_name ' => '' ,
519+ 'display_name ' => 'Test User ' ,
520+ )
521+ );
522+
523+ $ post_id = self ::factory ()->post ->create (
524+ array (
525+ 'post_title ' => 'Post by User with Empty Fields ' ,
526+ 'post_author ' => $ user_id ,
527+ 'post_type ' => 'post ' ,
528+ )
529+ );
530+
531+ $ wpdb ->update (
532+ $ wpdb ->users ,
533+ array (
534+ 'display_name ' => null ,
535+ ),
536+ array ( 'ID ' => $ user_id ),
537+ array ( '%s ' ),
538+ array ( '%d ' )
539+ );
540+
541+ clean_user_cache ( $ user_id );
542+
543+ $ xml = $ this ->get_the_export (
544+ array (
545+ 'content ' => 'post ' ,
546+ )
547+ );
548+
549+ $ this ->assertNotFalse ( $ xml , 'Export should not fail with users having empty name fields ' );
550+
551+ // Check that the author is in the export.
552+ $ authors = $ xml ->channel ->children ( 'wp ' , true );
553+ $ found_author = false ;
554+
555+ foreach ( $ authors as $ author ) {
556+ if ( isset ( $ author ->author_id ) && (int ) $ author ->author_id === $ user_id ) {
557+ $ found_author = true ;
558+ break ;
559+ }
560+ }
561+
562+ $ this ->assertTrue ( $ found_author , 'User with empty name fields should be included in export ' );
563+ }
293564}
0 commit comments