PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.1
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / integrations / front-end / force-rewrite-title.php

force-rewrite-title.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.1, at src/integrations/front-end/force-rewrite-title.php

178 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Integrations\Front_End;
4
5 use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
6 use Yoast\WP\SEO\Helpers\Options_Helper;
7 use Yoast\WP\SEO\Integrations\Integration_Interface;
8 use Yoast\WP\SEO\Wrappers\WP_Query_Wrapper;
9
10 /**
11 * Class Force_Rewrite_Title.
12 */
13 class Force_Rewrite_Title implements Integration_Interface {
14
15 /**
16 * The options helper.
17 *
18 * @var Options_Helper
19 */
20 private $options;
21
22 /**
23 * Toggle indicating whether output buffering has been started.
24 *
25 * @var bool
26 */
27 private $ob_started = false;
28
29 /**
30 * The WP Query wrapper.
31 *
32 * @var WP_Query_Wrapper
33 */
34 private $wp_query;
35
36 /**
37 * Sets the helpers.
38 *
39 * @codeCoverageIgnore It just handles dependencies.
40 *
41 * @param Options_Helper $options Options helper.
42 * @param WP_Query_Wrapper $wp_query WP query wrapper.
43 */
44 public function __construct( Options_Helper $options, WP_Query_Wrapper $wp_query ) {
45 $this->options = $options;
46 $this->wp_query = $wp_query;
47 }
48
49 /**
50 * Returns the conditionals based in which this loadable should be active.
51 *
52 * @return array
53 */
54 public static function get_conditionals() {
55 return [ Front_End_Conditional::class ];
56 }
57
58 /**
59 * Initializes the integration.
60 *
61 * This is the place to register hooks and filters.
62 *
63 * @codeCoverageIgnore
64 *
65 * @return void
66 */
67 public function register_hooks() {
68 // When the option is disabled.
69 if ( ! $this->options->get( 'forcerewritetitle', false ) ) {
70 return;
71 }
72
73 // For WordPress versions below 4.4.
74 if ( \current_theme_supports( 'title-tag' ) ) {
75 return;
76 }
77
78 \add_action( 'template_redirect', [ $this, 'force_rewrite_output_buffer' ], 99999 );
79 \add_action( 'wp_footer', [ $this, 'flush_cache' ], -1 );
80 }
81
82 /**
83 * Used in the force rewrite functionality this retrieves the output, replaces the title with the proper SEO
84 * title and then flushes the output.
85 *
86 * @return bool
87 */
88 public function flush_cache() {
89 if ( $this->ob_started !== true ) {
90 return false;
91 }
92
93 $content = $this->get_buffered_output();
94
95 $old_wp_query = $this->wp_query->get_query();
96
97 \wp_reset_query();
98
99 // When the file has the debug mark.
100 if ( \preg_match( '/(?\'before\'.*)<!-- This site is optimized with the Yoast SEO.*<!-- \/ Yoast SEO( Premium)? plugin. -->(?\'after\'.*)/is', $content, $matches ) ) {
101 $content = $this->replace_titles_from_content( $content, $matches );
102
103 unset( $matches );
104 }
105
106 // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- The query gets reset here with the original query.
107 $GLOBALS['wp_query'] = $old_wp_query;
108
109 // phpcs:ignore WordPress.Security.EscapeOutput -- The output should already have been escaped, we are only filtering it.
110 echo $content;
111
112 return true;
113 }
114
115 /**
116 * Starts the output buffer so it can later be fixed by flush_cache().
117 */
118 public function force_rewrite_output_buffer() {
119 $this->ob_started = true;
120 $this->start_output_buffering();
121 }
122
123 /**
124 * Replaces the titles from the parts that contains a title.
125 *
126 * @param string $content The content to remove the titles from.
127 * @param array $parts_with_title The parts containing a title.
128 *
129 * @return string The modified content.
130 */
131 protected function replace_titles_from_content( $content, $parts_with_title ) {
132 if ( isset( $parts_with_title['before'] ) && \is_string( $parts_with_title['before'] ) ) {
133 $content = $this->replace_title( $parts_with_title['before'], $content );
134 }
135
136 if ( isset( $parts_with_title['after'] ) ) {
137 $content = $this->replace_title( $parts_with_title['after'], $content );
138 }
139
140 return $content;
141 }
142
143 /**
144 * Removes the title from the part that contains the title and put this modified part back
145 * into the content.
146 *
147 * @param string $part_with_title The part with the title that needs to be replaced.
148 * @param string $content The entire content.
149 *
150 * @return string The altered content.
151 */
152 protected function replace_title( $part_with_title, $content ) {
153 $part_without_title = \preg_replace( '/<title.*?\/title>/i', '', $part_with_title );
154
155 return \str_replace( $part_with_title, $part_without_title, $content );
156 }
157
158 /**
159 * Starts the output buffering.
160 *
161 * @codeCoverageIgnore
162 */
163 protected function start_output_buffering() {
164 \ob_start();
165 }
166
167 /**
168 * Retrieves the buffered output.
169 *
170 * @codeCoverageIgnore
171 *
172 * @return false|string The buffered output.
173 */
174 protected function get_buffered_output() {
175 return \ob_get_clean();
176 }
177 }
178