N1PQE9My8mDLLHIX6hyPI7fqbMszjiK+LIv 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 $helpers The helpers surface. * @param WPSEO_Replace_Vars $replace_vars The replace vars helper. */ public function __construct( Meta_Tags_Context_Memoizer $context_memoizer, ContainerInterface $service_container, Options_Helper $options, Request_Helper $request, Helpers_Surface $helpers, WPSEO_Replace_Vars $replace_vars ) { $this->container = $service_container; $this->context_memoizer = $context_memoizer; $this->options = $options; $this->request = $request; $this->helpers = $helpers; $this->replace_vars = $replace_vars; } /** * Registers the appropriate hooks to show the SEO metadata on the frontend. * * Removes some actions to remove metadata that WordPress shows on the frontend, * to avoid duplicate and/or mismatched metadata. */ public function register_hooks() { \add_action( 'wp_head', [ $this, 'call_wpseo_head' ], 1 ); // Filter the title for compatibility with other plugins and themes. \add_filter( 'wp_title', [ $this, 'filter_title' ], 15 ); // Filter the title for compatibility with block-based themes. \add_filter( 'pre_get_document_title', [ $this, 'filter_title' ], 15 ); // Removes our robots presenter from the list when wp_robots is handling this. \add_filter( 'wpseo_frontend_presenter_classes', [ $this, 'filter_robots_presenter' ] ); \add_action( 'wpseo_head', [ $this, 'present_head' ], -9999 ); \remove_action( 'wp_head', 'rel_canonical' ); \remove_action( 'wp_head', 'index_rel_link' ); \remove_action( 'wp_head', 'start_post_rel_link' ); \remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' ); \remove_action( 'wp_head', 'noindex', 1 ); \remove_action( 'wp_head', '_wp_render_title_tag', 1 ); \remove_action( 'wp_head', '_block_template_render_title_tag', 1 ); \remove_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); } /** * Filters the title, mainly used for compatibility reasons. * * @return string */ public function filter_title() { $context = $this->context_memoizer->for_current_page(); $title_presenter = new Title_Presenter(); /** This filter is documented in src/integrations/front-end-integration.php */ $title_presenter->presentation = \apply_filters( 'wpseo_frontend_presentation', $context->presentation, $context ); $title_presenter->replace_vars = $this->replace_vars; $title_presenter->helpers = $this->helpers; \remove_filter( 'pre_get_document_title', [ $this, 'filter_title' ], 15 ); $title = \esc_html( $title_presenter->get() ); \add_filter( 'pre_get_document_title', [ $this, 'filter_title' ], 15 ); return $title; } /** * Filters our robots presenter, but only when wp_robots is attached to the wp_head action. * * @param array $presenters The presenters for current page. * * @return array The filtered presenters. */ public function filter_robots_presenter( $presenters ) { if ( ! \function_exists( 'wp_robots' ) ) { return $presenters; } if ( ! \has_action( 'wp_head', 'wp_robots' ) ) { return $presenters; } if ( $this->request->is_rest_request() ) { return $presenters; } return \array_diff( $presenters, [ 'Yoast\\WP\\SEO\\Presenters\\Robots_Presenter' ] ); } /** * Presents the head in the front-end. Resets wp_query if it's not the main query. * * @codeCoverageIgnore It just calls a WordPress function. */ public function call_wpseo_head() { global $wp_query; $old_wp_query = $wp_query; // phpcs:ignore WordPress.WP.DiscouragedFunctions.wp_reset_query_wp_reset_query -- Reason: The recommended function, wp_reset_postdata, doesn't reset wp_query. \wp_reset_query(); \do_action( 'wpseo_head' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Reason: we have to restore the query. $GLOBALS['wp_query'] = $old_wp_query; } /** * Echoes all applicable presenters for a page. */ public function present_head() { $context = $this->context_memoizer->for_current_page(); $presenters = $this->get_presenters( $context->page_type, $context ); /** * Filter 'wpseo_frontend_presentation' - Allow filtering the presentation used to output our meta values. * * @api Indexable_Presention The indexable presentation. */ $presentation = \apply_filters( 'wpseo_frontend_presentation', $context->presentation, $context ); echo \PHP_EOL; foreach ( $presenters as $presenter ) { $presenter->presentation = $presentation; $presenter->helpers = $this->helpers; $presenter->replace_vars = $this->replace_vars; $output = $presenter->present(); if ( ! empty( $output ) ) { // phpcs:ignore WordPress.Security.EscapeOutput -- Presenters are responsible for correctly escaping their output. echo "\t" . $output . \PHP_EOL; } } echo \PHP_EOL . \PHP_EOL; } /** * Returns all presenters for this page. * * @param string $page_type The page type. * @param Meta_Tags_Context|null $context The meta tags context for the current page. * * @return Abstract_Indexable_Presenter[] The presenters. */ public function get_presenters( $page_type, $context = null ) { if ( \is_null( $context ) ) { $context = $this->context_memoizer->for_current_page(); } $needed_presenters = $this->get_needed_presenters( $page_type ); $callback = static function( $presenter ) { if ( ! \class_exists( $presenter ) ) { return null; } return new $presenter(); }; $presenters = \array_filter( \array_map( $callback, $needed_presenters ) ); /** * Filter 'wpseo_frontend_presenters' - Allow filtering the presenter instances in or out of the request. * * @param array $presenters The presenters. * @param Meta_Tags_Context $context The meta tags context for the current page. * * @api Abstract_Indexable_Presenter[] List of presenter instances. */ $presenter_instances = \apply_filters( 'wpseo_frontend_presenters', $presenters, $context ); if ( ! \is_array( $presenter_instances ) ) { $presenter_instances = $presenters; } $is_presenter_callback = static function ( $presenter_instance ) { return $presenter_instance instanceof Abstract_Indexable_Presenter; }; $presenter_instances = \array_filter( $presenter_instances, $is_presenter_callback ); return \array_merge( [ new Marker_Open_Presenter() ], $presenter_instances, [ new Marker_Close_Presenter() ] ); } /** * Generate the array of presenters we need for the current request. * * @param string $page_type The page type we're retrieving presenters for. * * @return string[] The presenters. */ private function get_needed_presenters( $page_type ) { $presenters = $this->get_presenters_for_page_type( $page_type ); $presenters = $this->maybe_remove_title_presenter( $presenters ); $callback = static function ( $presenter ) { return "Yoast\WP\SEO\Presenters\\{$presenter}_Presenter"; }; $presenters = \array_map( $callback, $presenters ); /** * Filter 'wpseo_frontend_presenter_classes' - Allow filtering presenters in or out of the request. * * @param array $presenters List of presenters. * @param string $page_type The current page type. */ $presenters = \apply_filters( 'wpseo_frontend_presenter_classes', $presenters, $page_type ); return $presenters; } /** * Filters the presenters based on the page type. * * @param string $page_type The page type. * * @return string[] The presenters. */ private function get_presenters_for_page_type( $page_type ) { if ( $page_type === 'Error_Page' ) { $presenters = $this->base_presenters; if ( $this->options->get( 'opengraph' ) === true ) { $presenters = \array_merge( $presenters, $this->open_graph_error_presenters ); } return \array_merge( $presenters, $this->closing_presenters ); } $presenters = $this->get_all_presenters(); if ( \in_array( $page_type, [ 'Static_Home_Page', 'Home_Page' ], true ) ) { $presenters = \array_merge( $presenters, $this->webmaster_verification_presenters ); } // Filter out the presenters only needed for singular pages on non-singular pages. if ( ! \in_array( $page_type, [ 'Post_Type', 'Static_Home_Page' ], true ) ) { $presenters = \array_diff( $presenters, $this->singular_presenters ); } return $presenters; } /** * Returns a list of all available presenters based on settings. * * @return string[] The presenters. */ private function get_all_presenters() { $presenters = \array_merge( $this->base_presenters, $this->indexing_directive_presenters ); if ( $this->options->get( 'opengraph' ) === true ) { $presenters = \array_merge( $presenters, $this->open_graph_presenters ); } if ( $this->options->get( 'twitter' ) === true && \apply_filters( 'wpseo_output_twitter_card', true ) !== false ) { $presenters = \array_merge( $presenters, $this->twitter_card_presenters ); } if ( $this->options->get( 'enable_enhanced_slack_sharing' ) === true && \apply_filters( 'wpseo_output_enhanced_slack_data', true ) !== false ) { $presenters = \array_merge( $presenters, $this->slack_presenters ); } return \array_merge( $presenters, $this->closing_presenters ); } /** * Whether the title presenter should be removed. * * @return bool True when the title presenter should be removed, false otherwise. */ public function should_title_presenter_be_removed() { return ! \get_theme_support( 'title-tag' ) && ! $this->options->get( 'forcerewritetitle', false ); } /** * Checks if the Title presenter needs to be removed. * * @param string[] $presenters The presenters. * * @return string[] The presenters. */ private function maybe_remove_title_presenter( $presenters ) { // Do not remove the title if we're on a REST request. if ( $this->request->is_rest_request() ) { return $presenters; } // Remove the title presenter if the theme is hardcoded to output a title tag so we don't have two title tags. if ( $this->should_title_presenter_be_removed() ) { $presenters = \array_diff( $presenters, [ 'Title' ] ); } return $presenters; } }