PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.3
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.3
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 / admin / class-my-yoast-proxy.php

class-my-yoast-proxy.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.3, at admin/class-my-yoast-proxy.php

210 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Admin
6 */
7
8 /**
9 * Loads the MyYoast proxy.
10 *
11 * This class registers a proxy page on `admin.php`. Which is reached with the `page=PAGE_IDENTIFIER` parameter.
12 * It will read external files and serves them like they are located locally.
13 */
14 class WPSEO_MyYoast_Proxy implements WPSEO_WordPress_Integration {
15
16 /**
17 * The page identifier used in WordPress to register the MyYoast proxy page.
18 *
19 * @var string
20 */
21 const PAGE_IDENTIFIER = 'wpseo_myyoast_proxy';
22
23 /**
24 * The cache control's max age. Used in the header of a successful proxy response.
25 *
26 * @var int
27 */
28 const CACHE_CONTROL_MAX_AGE = DAY_IN_SECONDS;
29
30 /**
31 * Registers the hooks when the user is on the right page.
32 *
33 * @codeCoverageIgnore
34 *
35 * @return void
36 */
37 public function register_hooks() {
38 if ( ! $this->is_proxy_page() ) {
39 return;
40 }
41
42 // Register the page for the proxy.
43 add_action( 'admin_menu', [ $this, 'add_proxy_page' ] );
44 add_action( 'admin_init', [ $this, 'handle_proxy_page' ] );
45 }
46
47 /**
48 * Registers the proxy page. It does not actually add a link to the dashboard.
49 *
50 * @codeCoverageIgnore
51 *
52 * @return void
53 */
54 public function add_proxy_page() {
55 add_dashboard_page( '', '', 'read', self::PAGE_IDENTIFIER, '' );
56 }
57
58 /**
59 * Renders the requested proxy page and exits to prevent the WordPress UI from loading.
60 *
61 * @codeCoverageIgnore
62 *
63 * @return void
64 */
65 public function handle_proxy_page() {
66 $this->render_proxy_page();
67
68 // Prevent the WordPress UI from loading.
69 exit;
70 }
71
72 /**
73 * Renders the requested proxy page.
74 *
75 * This is separated from the exits to be able to test it.
76 *
77 * @return void
78 */
79 public function render_proxy_page() {
80 $proxy_options = $this->determine_proxy_options();
81 if ( $proxy_options === [] ) {
82 // Do not accept any other file than implemented.
83 $this->set_header( 'HTTP/1.0 501 Requested file not implemented' );
84 return;
85 }
86
87 // Set the headers before serving the remote file.
88 $this->set_header( 'Content-Type: ' . $proxy_options['content_type'] );
89 $this->set_header( 'Cache-Control: max-age=' . self::CACHE_CONTROL_MAX_AGE );
90
91 try {
92 echo $this->get_remote_url_body( $proxy_options['url'] );
93 }
94 catch ( Exception $e ) {
95 /*
96 * Reset the file headers because the loading failed.
97 *
98 * Note: Due to supporting PHP 5.2 `header_remove` can not be used here.
99 * Overwrite the headers instead.
100 */
101 $this->set_header( 'Content-Type: text/plain' );
102 $this->set_header( 'Cache-Control: max-age=0' );
103
104 $this->set_header( 'HTTP/1.0 500 ' . $e->getMessage() );
105 }
106 }
107
108 /**
109 * Tries to load the given url via `wp_remote_get`.
110 *
111 * @codeCoverageIgnore
112 *
113 * @param string $url The url to load.
114 *
115 * @return string The body of the response.
116 *
117 * @throws Exception When `wp_remote_get` returned an error.
118 * @throws Exception When the response code is not 200.
119 */
120 protected function get_remote_url_body( $url ) {
121 $response = wp_remote_get( $url );
122
123 if ( $response instanceof WP_Error ) {
124 throw new Exception( 'Unable to retrieve file from MyYoast' );
125 }
126
127 if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
128 throw new Exception( 'Received unexpected response from MyYoast' );
129 }
130
131 return wp_remote_retrieve_body( $response );
132 }
133
134 /**
135 * Determines the proxy options based on the file and plugin version arguments.
136 *
137 * When the file is known it returns an array like this:
138 * <code>
139 * $array = array(
140 * 'content_type' => 'the content type'
141 * 'url' => 'the url, possibly with the plugin version'
142 * )
143 * </code>
144 *
145 * @return array Empty for an unknown file. See format above for known files.
146 */
147 protected function determine_proxy_options() {
148 if ( $this->get_proxy_file() === 'research-webworker' ) {
149 return [
150 'content_type' => 'text/javascript; charset=UTF-8',
151 'url' => 'https://my.yoast.com/api/downloads/file/analysis-worker?plugin_version=' . $this->get_plugin_version(),
152 ];
153 }
154
155 return [];
156 }
157
158 /**
159 * Checks if the current page is the MyYoast proxy page.
160 *
161 * @codeCoverageIgnore
162 *
163 * @return bool True when the page request parameter equals the proxy page.
164 */
165 protected function is_proxy_page() {
166 return filter_input( INPUT_GET, 'page' ) === self::PAGE_IDENTIFIER;
167 }
168
169 /**
170 * Returns the proxy file from the HTTP request parameters.
171 *
172 * @codeCoverageIgnore
173 *
174 * @return string The sanitized file request parameter.
175 */
176 protected function get_proxy_file() {
177 return filter_input( INPUT_GET, 'file', FILTER_SANITIZE_STRING );
178 }
179
180 /**
181 * Returns the plugin version from the HTTP request parameters.
182 *
183 * @codeCoverageIgnore
184 *
185 * @return string The sanitized plugin_version request parameter.
186 */
187 protected function get_plugin_version() {
188 $plugin_version = filter_input( INPUT_GET, 'plugin_version', FILTER_SANITIZE_STRING );
189 // Replace slashes to secure against requiring a file from another path.
190 $plugin_version = str_replace( [ '/', '\\' ], '_', $plugin_version );
191
192 return $plugin_version;
193 }
194
195 /**
196 * Sets the HTTP header.
197 *
198 * This is a tiny helper function to enable better testing.
199 *
200 * @codeCoverageIgnore
201 *
202 * @param string $header The header to set.
203 *
204 * @return void
205 */
206 protected function set_header( $header ) {
207 header( $header );
208 }
209 }
210