PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
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 / helpers / url-helper.php

url-helper.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.5, at src/helpers/url-helper.php

294 lines 8.1 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\Helpers;
4
5 use Yoast\WP\SEO\Models\SEO_Links;
6
7 /**
8 * A helper object for URLs.
9 */
10 class Url_Helper {
11
12 /**
13 * Retrieve home URL with proper trailing slash.
14 *
15 * @param string $path Path relative to home URL.
16 * @param string|null $scheme Scheme to apply.
17 *
18 * @return string Home URL with optional path, appropriately slashed if not.
19 */
20 public function home( $path = '', $scheme = null ) {
21 $home_url = \home_url( $path, $scheme );
22
23 if ( ! empty( $path ) ) {
24 return $home_url;
25 }
26
27 $home_path = \wp_parse_url( $home_url, \PHP_URL_PATH );
28
29 if ( $home_path === '/' ) { // Home at site root, already slashed.
30 return $home_url;
31 }
32
33 if ( $home_path === null ) { // Home at site root, always slash.
34 return \trailingslashit( $home_url );
35 }
36
37 if ( \is_string( $home_path ) ) { // Home in subdirectory, slash if permalink structure has slash.
38 return \user_trailingslashit( $home_url );
39 }
40
41 return $home_url;
42 }
43
44 /**
45 * Determines whether the plugin is active for the entire network.
46 *
47 * @return bool Whether or not the plugin is network-active.
48 */
49 public function is_plugin_network_active() {
50 static $network_active = null;
51
52 if ( ! \is_multisite() ) {
53 return false;
54 }
55
56 // If a cached result is available, bail early.
57 if ( $network_active !== null ) {
58 return $network_active;
59 }
60
61 $network_active_plugins = \wp_get_active_network_plugins();
62
63 // Consider MU plugins and network-activated plugins as network-active.
64 $network_active = \strpos( \wp_normalize_path( \WPSEO_FILE ), \wp_normalize_path( \WPMU_PLUGIN_DIR ) ) === 0
65 || \in_array( \WP_PLUGIN_DIR . '/' . \WPSEO_BASENAME, $network_active_plugins, true );
66
67 return $network_active;
68 }
69
70 /**
71 * Retrieve network home URL if plugin is network-activated, or home url otherwise.
72 *
73 * @return string Home URL with optional path, appropriately slashed if not.
74 */
75 public function network_safe_home_url() {
76 /**
77 * Action: 'wpseo_home_url' - Allows overriding of the home URL.
78 */
79 \do_action( 'wpseo_home_url' );
80
81 // If the plugin is network-activated, use the network home URL.
82 if ( self::is_plugin_network_active() ) {
83 return \network_home_url();
84 }
85
86 return \home_url();
87 }
88
89 /**
90 * Check whether a url is relative.
91 *
92 * @param string $url URL string to check.
93 *
94 * @return bool True when url is relative.
95 */
96 public function is_relative( $url ) {
97 return ( \strpos( $url, 'http' ) !== 0 && \strpos( $url, '//' ) !== 0 );
98 }
99
100 /**
101 * Gets the path from the passed URL.
102 *
103 * @param string $url The URL to get the path from.
104 *
105 * @return string The path of the URL. Returns an empty string if URL parsing fails.
106 */
107 public function get_url_path( $url ) {
108 if ( \is_string( $url ) === false
109 && ( \is_object( $url ) === false || \method_exists( $url, '__toString' ) === false )
110 ) {
111 return '';
112 }
113
114 return (string) \wp_parse_url( $url, \PHP_URL_PATH );
115 }
116
117 /**
118 * Gets the host from the passed URL.
119 *
120 * @param string $url The URL to get the host from.
121 *
122 * @return string The host of the URL. Returns an empty string if URL parsing fails.
123 */
124 public function get_url_host( $url ) {
125 if ( \is_string( $url ) === false
126 && ( \is_object( $url ) === false || \method_exists( $url, '__toString' ) === false )
127 ) {
128 return '';
129 }
130
131 return (string) \wp_parse_url( $url, \PHP_URL_HOST );
132 }
133
134 /**
135 * Determines the file extension of the given url.
136 *
137 * @param string $url The URL.
138 *
139 * @return string The extension.
140 */
141 public function get_extension_from_url( $url ) {
142 $path = $this->get_url_path( $url );
143
144 if ( $path === '' ) {
145 return '';
146 }
147
148 $parts = \explode( '.', $path );
149 if ( empty( $parts ) || \count( $parts ) === 1 ) {
150 return '';
151 }
152
153 return \end( $parts );
154 }
155
156 /**
157 * Ensures that the given url is an absolute url.
158 *
159 * @param string $url The url that needs to be absolute.
160 *
161 * @return string The absolute url.
162 */
163 public function ensure_absolute_url( $url ) {
164 if ( ! \is_string( $url ) || $url === '' ) {
165 return $url;
166 }
167
168 if ( $this->is_relative( $url ) === true ) {
169 return $this->build_absolute_url( $url );
170 }
171
172 return $url;
173 }
174
175 /**
176 * Parse the home URL setting to find the base URL for relative URLs.
177 *
178 * @param string|null $path Optional path string.
179 *
180 * @return string
181 */
182 public function build_absolute_url( $path = null ) {
183 $path = \wp_parse_url( $path, \PHP_URL_PATH );
184 $url_parts = \wp_parse_url( \home_url() );
185
186 $base_url = \trailingslashit( $url_parts['scheme'] . '://' . $url_parts['host'] );
187
188 if ( \is_string( $path ) ) {
189 $base_url .= \ltrim( $path, '/' );
190 }
191
192 return $base_url;
193 }
194
195 /**
196 * Returns the link type.
197 *
198 * @param array $url The URL, as parsed by wp_parse_url.
199 * @param array|null $home_url Optional. The home URL, as parsed by wp_parse_url. Used to avoid reparsing the home_url.
200 * @param bool $is_image Whether or not the link is an image.
201 *
202 * @return string The link type.
203 */
204 public function get_link_type( $url, $home_url = null, $is_image = false ) {
205 // If there is no scheme and no host the link is always internal.
206 // Beware, checking just the scheme isn't enough as a link can be //yoast.com for instance.
207 if ( empty( $url['scheme'] ) && empty( $url['host'] ) ) {
208 return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
209 }
210
211 // If there is a scheme but it's not http(s) then the link is always external.
212 if ( \array_key_exists( 'scheme', $url ) && ! \in_array( $url['scheme'], [ 'http', 'https' ], true ) ) {
213 return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
214 }
215
216 $home_url ??= \wp_parse_url( \home_url() );
217
218 // When the base host is equal to the host.
219 if ( isset( $url['host'] ) && $url['host'] !== $home_url['host'] ) {
220 return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
221 }
222
223 // There is no base path and thus all URLs of the same domain are internal.
224 if ( empty( $home_url['path'] ) ) {
225 return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
226 }
227
228 // When there is a path and it matches the start of the url.
229 if ( isset( $url['path'] ) && \strpos( $url['path'], $home_url['path'] ) === 0 ) {
230 return ( $is_image ) ? SEO_Links::TYPE_INTERNAL_IMAGE : SEO_Links::TYPE_INTERNAL;
231 }
232
233 return ( $is_image ) ? SEO_Links::TYPE_EXTERNAL_IMAGE : SEO_Links::TYPE_EXTERNAL;
234 }
235
236 /**
237 * Recreate current URL.
238 *
239 * @param bool $with_request_uri Whether we want the REQUEST_URI appended.
240 *
241 * @return string
242 */
243 public function recreate_current_url( $with_request_uri = true ) {
244 $current_url = 'http';
245 if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ) {
246 $current_url .= 's';
247 }
248 $current_url .= '://';
249
250 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
251 $suffix = ( $with_request_uri && isset( $_SERVER['REQUEST_URI'] ) ) ? $_SERVER['REQUEST_URI'] : '';
252
253 if ( isset( $_SERVER['SERVER_NAME'] ) && ! empty( $_SERVER['SERVER_NAME'] ) ) {
254 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
255 $server_name = $_SERVER['SERVER_NAME'];
256 }
257 else {
258 // Early return with just the path.
259 return $suffix;
260 }
261
262 $server_port = '';
263 if ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] !== '80' && $_SERVER['SERVER_PORT'] !== '443' ) {
264 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
265 $server_port = $_SERVER['SERVER_PORT'];
266 }
267
268 if ( ! empty( $server_port ) ) {
269 $current_url .= $server_name . ':' . $server_port . $suffix;
270 }
271 else {
272 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- We know this is scary.
273 $current_url .= $server_name . $suffix;
274 }
275
276 return $current_url;
277 }
278
279 /**
280 * Parses a URL and returns its components, this wrapper function was created to support unit tests.
281 *
282 * @param string $parsed_url The URL to parse.
283 * @return array The parsed components of the URL.
284 */
285 public function parse_str_params( $parsed_url ) {
286 $array = [];
287
288 // @todo parse_str changes spaces in param names into `_`, we should find a better way to support them.
289 \wp_parse_str( $parsed_url, $array );
290
291 return $array;
292 }
293 }
294