Section key => captured HTML. */ private static $stash = array(); /** * Replace owned admin_notices callbacks with wrappers that buffer. * * Call on admin_notices at priority 0, so every callback below is still * pending. Each is re-registered at its own priority, so ordering survives. * * @param array> $owned Class => method => section key. */ public static function capture( array $owned ) { global $wp_filter; if ( ! isset( $wp_filter['admin_notices'] ) || ! is_object( $wp_filter['admin_notices'] ) || ! isset( $wp_filter['admin_notices']->callbacks ) ) { return; } $map = array(); foreach ( $owned as $class => $methods ) { // A build that carves the class never registers its callback, so // there is nothing to capture and nothing to name. if ( ! class_exists( $class, false ) ) { continue; } foreach ( $methods as $method => $section ) { $map[ $class . '::' . $method ] = $section; } } if ( ! $map ) { return; } foreach ( (array) $wp_filter['admin_notices']->callbacks as $priority => $entries ) { foreach ( (array) $entries as $entry ) { if ( ! isset( $entry['function'] ) || ! is_array( $entry['function'] ) || 2 !== count( $entry['function'] ) ) { continue; } list( $target, $method ) = array_values( $entry['function'] ); $class = is_object( $target ) ? get_class( $target ) : (string) $target; $name = $class . '::' . (string) $method; if ( ! isset( $map[ $name ] ) ) { continue; } $section = $map[ $name ]; $callback = $entry['function']; remove_action( 'admin_notices', $callback, (int) $priority ); add_action( 'admin_notices', static function () use ( $callback, $section ) { $level = ob_get_level(); ob_start(); try { call_user_func( $callback ); } finally { $html = ob_get_level() > $level ? (string) ob_get_clean() : ''; } if ( '' !== trim( $html ) ) { if ( ! isset( self::$stash[ $section ] ) ) { self::$stash[ $section ] = ''; } self::$stash[ $section ] .= $html; } }, (int) $priority, isset( $entry['accepted_args'] ) ? max( 0, (int) $entry['accepted_args'] ) : 1 ); } } } /** * Emit whatever was captured for a section, once. * * @param string $key Section key. */ public static function output( $key ) { $key = (string) $key; if ( empty( self::$stash[ $key ] ) ) { return; } // Already-rendered markup from this plugin's own panels, captured above. echo self::$stash[ $key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Buffered output of AI-O panels that escape their own values. self::$stash[ $key ] = ''; } /** * The panels both Settings and Schema Dash treat as site setup. * * @param string $section Section key to file them under. * @return array> */ public static function site_setup_panels( $section ) { return array( 'AI_O_Optimiser_Setup_Local_Clarity' => array( 'render_settings_setup' => $section ), 'AI_O_Optimiser_Site_Profile_AI_Guidance' => array( 'render' => $section ), 'AI_O_Optimiser_Site_Profile_UI' => array( 'render' => $section ), 'AI_O_Optimiser_Site_Profile' => array( 'render_admin_profile' => $section ), ); } }