diff --git a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php index 029bb5cdc..eae7cc4ef 100644 --- a/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php +++ b/src/php/Admin/Menus/Manage/Manage_Menu_Screen_Options.php @@ -147,11 +147,21 @@ public function is_upsell_view(): bool { /** * Render the manage table controls. * - * @param string $screen_settings Existing screen settings HTML. + * Anything may filter `screen_settings` before this runs, and a callback + * that forgets to return its value hands the next one null. Declaring the + * parameter as a string turned that into a fatal error, and because it is + * raised while the screen meta is being rendered, the page dies after the + * admin chrome but before any content: the snippets screen appears blank + * while the rest of the admin looks fine. + * + * @param mixed $screen_settings Existing screen settings HTML, from an + * unknown number of earlier callbacks. * * @return string */ - public function render( string $screen_settings ): string { + public function render( $screen_settings ): string { + $screen_settings = is_string( $screen_settings ) ? $screen_settings : ''; + if ( $this->is_cloud_community_view() ) { return $screen_settings; } diff --git a/tests/unit/Admin/Menus/Manage/Manage_Menu_Screen_Options_Test.php b/tests/unit/Admin/Menus/Manage/Manage_Menu_Screen_Options_Test.php index a386bb085..bbeb7f7fc 100644 --- a/tests/unit/Admin/Menus/Manage/Manage_Menu_Screen_Options_Test.php +++ b/tests/unit/Admin/Menus/Manage/Manage_Menu_Screen_Options_Test.php @@ -74,6 +74,41 @@ public function test_render_skips_truncation_toggle_on_cloud_community_view(): v $this->assertFalse( $options->is_manage_table_view() ); } + /** + * An earlier `screen_settings` callback that returns null must not be fatal. + * + * A callback that forgets to return its value passes null down the chain. + * That used to raise a TypeError while the screen meta was rendering, which + * killed the page after the admin chrome but before the snippets table. + * + * @return void + */ + public function test_render_tolerates_a_null_value_from_an_earlier_callback(): void { + $options = new Manage_Menu_Screen_Options(); + + $output = $options->render( null ); + + $this->assertIsString( $output ); + $this->assertStringContainsString( 'snippets-table-truncate-row-values', $output ); + } + + /** + * The filter chain survives a callback that returns null. + * + * @return void + */ + public function test_screen_settings_filter_chain_survives_a_null_returning_callback(): void { + $options = new Manage_Menu_Screen_Options(); + $options->load(); + + add_filter( 'screen_settings', '__return_null', 5 ); + $output = apply_filters( 'screen_settings', '', get_current_screen() ); + remove_filter( 'screen_settings', '__return_null', 5 ); + + $this->assertIsString( $output ); + $this->assertStringContainsString( 'snippets-table-truncate-row-values', $output ); + } + /** * The truncation preference is saved from the Screen Options form. *