PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.2
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.2
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 28.2, at src/integrations/front-end/force-rewrite-title.php

182 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' ], 99_999 );
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 * @return void
119 */
120 public function force_rewrite_output_buffer() {
121 $this->ob_started = true;
122 $this->start_output_buffering();
123 }
124
125 /**
126 * Replaces the titles from the parts that contains a title.
127 *
128 * @param string $content The content to remove the titles from.
129 * @param array $parts_with_title The parts containing a title.
130 *
131 * @return string The modified content.
132 */
133 protected function replace_titles_from_content( $content, $parts_with_title ) {
134 if ( isset( $parts_with_title['before'] ) && \is_string( $parts_with_title['before'] ) ) {
135 $content = $this->replace_title( $parts_with_title['before'], $content );
136 }
137
138 if ( isset( $parts_with_title['after'] ) ) {
139 $content = $this->replace_title( $parts_with_title['after'], $content );
140 }
141
142 return $content;
143 }
144
145 /**
146 * Removes the title from the part that contains the title and put this modified part back
147 * into the content.
148 *
149 * @param string $part_with_title The part with the title that needs to be replaced.
150 * @param string $content The entire content.
151 *
152 * @return string The altered content.
153 */
154 protected function replace_title( $part_with_title, $content ) {
155 $part_without_title = \preg_replace( '/<title.*?\/title>/i', '', $part_with_title );
156
157 return \str_replace( $part_with_title, $part_without_title, $content );
158 }
159
160 /**
161 * Starts the output buffering.
162 *
163 * @codeCoverageIgnore
164 *
165 * @return void
166 */
167 protected function start_output_buffering() {
168 \ob_start();
169 }
170
171 /**
172 * Retrieves the buffered output.
173 *
174 * @codeCoverageIgnore
175 *
176 * @return string|false The buffered output.
177 */
178 protected function get_buffered_output() {
179 return \ob_get_clean();
180 }
181 }
182