PHP redirects are disabled intentionally, return false. if ( ! empty( $options['disable_php_redirect'] ) && $options['disable_php_redirect'] === 'on' ) { return false; } // PHP redirects are the enabled method of redirecting. return true; } /** * Saves the default redirects options to the DB. */ public function save_default_redirect_options() { $redirect_option = WPSEO_Premium_Redirect_Option::get_instance(); \update_option( 'wpseo_redirect', $redirect_option->get_defaults(), true ); } /** * Gets the request URI, with fallback for super global. * * @return string */ protected function get_request_uri() { $options = [ 'options' => [ 'default' => '' ] ]; $request_uri = \filter_input( \INPUT_SERVER, 'REQUEST_URI', \FILTER_SANITIZE_URL, $options ); // Because there isn't an usable value, try the fallback. if ( empty( $request_uri ) && isset( $_SERVER['REQUEST_URI'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- this value is compared. I don't want to change the behavior. $request_uri = \filter_var( $_SERVER['REQUEST_URI'], \FILTER_SANITIZE_URL, $options ); } $request_uri = $this->strip_subdirectory( $request_uri ); return \rawurldecode( $request_uri ); } /** * Normalizes the redirects by raw url decoding the origin. * * @param array $redirects The redirects to normalize. * * @return array The normalized redirects. */ protected function normalize_redirects( $redirects ) { $normalized_redirects = []; foreach ( $redirects as $origin => $redirect ) { $normalized_redirects[ \rawurldecode( $origin ) ] = $redirect; } return $normalized_redirects; } /** * Sets the request URL and sanitize the slashes for it. * * @return void */ protected function set_request_url() { $this->request_url = $this->get_request_uri(); } /** * Finds the URL in the redirects. * * @param string $url The needed URL. * * @return bool|string The found url or false if not found. */ protected function find_url( $url ) { $redirect_url = $this->search( $url ); if ( ! empty( $redirect_url ) ) { return $redirect_url; } return $this->find_url_fallback( $url ); } /** * Searches for the given URL in the redirects array. * * @param string $url The URL to search for. * * @return string|bool The found url or false if not found. */ protected function search( $url ) { if ( ! empty( $this->redirects[ $url ] ) ) { return $this->redirects[ $url ]; } return false; } /** * Searches for alternatives with slashes if requested URL isn't found. * * This will add a slash if there isn't a slash or it will remove a trailing slash when there isn't one. * * @todo Discuss: Maybe we should add slashes to all the values we handle instead of using a fallback. * * @param string $url The URL that have to be matched. * * @return bool|string The found url or false if not found. */ protected function find_url_fallback( $url ) { $no_trailing_slash = \rtrim( $url, '/' ); $checks = [ 'no_trailing_slash' => $no_trailing_slash, 'trailing_slash' => $no_trailing_slash . '/', ]; foreach ( $checks as $check ) { $redirect_url = $this->search( $check ); if ( ! empty( $redirect_url ) ) { return $redirect_url; } } return false; } /** * Parses the target URL. * * @param string $target_url The URL to parse. When there isn't found a scheme, just parse it based on the home URL. * * @return string The parsed url. */ protected function parse_target_url( $target_url ) { if ( $this->has_url_scheme( $target_url ) ) { return $target_url; } $target_url = $this->trailingslashit( $target_url ); $target_url = $this->format_for_multisite( $target_url ); return $this->home_url( $target_url ); } /** * Checks if given url has a scheme. * * @param string $url The url to check. * * @return bool True when url has scheme. */ protected function has_url_scheme( $url ) { $scheme = \wp_parse_url( $url, \PHP_URL_SCHEME ); return ! empty( $scheme ); } /** * Determines whether the target URL ends with a slash and adds one if necessary. * * @param string $target_url The url to format. * * @return string The url with trailing slash. */ protected function trailingslashit( $target_url ) { // Adds slash to target URL when permalink structure ends with a slash. if ( $this->requires_trailing_slash( $target_url ) ) { return \trailingslashit( $target_url ); } return $target_url; } /** * Formats the target url for the multisite if needed. * * @param string $target_url The url to format. * * @return string The formatted url. */ protected function format_for_multisite( $target_url ) { if ( ! \is_multisite() ) { return $target_url; } $blog_details = \get_blog_details(); if ( $blog_details && ! empty( $blog_details->path ) ) { $blog_path = \ltrim( $blog_details->path, '/' ); if ( ! empty( $blog_path ) && \strpos( $target_url, $blog_path ) === 0 ) { $target_url = \substr( $target_url, \strlen( $blog_path ) ); } } return $target_url; } /** * Gets the redirect URL by given URL. * * @param string $redirect_url The URL that has to be redirected. * * @return string The redirect url. */ protected function home_url( $redirect_url ) { $redirect_url = $this->strip_subdirectory( $redirect_url ); return \home_url( $redirect_url ); } /** * Strips the subdirectory from the given url. * * @param string $url The url to strip the subdirectory from. * * @return string The url with the stripped subdirectory. */ protected function strip_subdirectory( $url ) { return WPSEO_Redirect_Util::strip_base_url_path_from_url( $this->get_home_url(), $url ); } /** * Returns the URL PATH from the home url. * * @return string|null The url path or null if there isn't one. */ protected function get_home_url() { return \home_url(); } /** * Returns the redirects from the option table in the database. * * @return array The stored redirects. */ protected function get_redirects_from_options() { global $wpdb; static $redirects; if ( $redirects !== null ) { return $redirects; } // The code below is needed because we used to not autoload our redirect options. This fixes that. $all_options = \wp_cache_get( 'alloptions', 'options' ); foreach ( [ $this->normal_option_name, $this->regex_option_name ] as $option ) { $redirects[ $option ] = isset( $all_options[ $option ] ) ? \maybe_unserialize( $all_options[ $option ] ) : false; if ( $redirects[ $option ] === false ) { $redirects[ $option ] = \get_option( $option, false ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Normal methods only work if the option value has changed. $wpdb->update( $wpdb->options, [ 'autoload' => 'yes' ], [ 'option_name' => $option ] ); } } return $redirects; } /** * Sets the hook for setting the template include. This is the file that we want to show. * * @param string $template_to_set The template to look for. * * @return bool True when template should be included. */ protected function set_template_include_hook( $template_to_set ) { $this->template_file_path = $this->get_query_template( $template_to_set ); if ( ! empty( $this->template_file_path ) ) { \add_filter( 'template_include', [ $this, 'set_template_include' ] ); return true; } return false; } /** * Wraps the WordPress status_header function. * * @param int $code HTTP status code. * @param string $description Optional. A custom description for the HTTP status. * * @return void */ protected function status_header( $code, $description = '' ) { \status_header( $code, $description ); } /** * Returns instance of WP_Query. * * @return WP_Query Instance of WP_Query. */ protected function get_wp_query() { global $wp_query; if ( \is_object( $wp_query ) ) { return $wp_query; } return new WP_Query(); } /** * Handles the redirects without a target by setting the needed hooks. * * @param string $redirect_type The type of the redirect. * * @return void */ protected function handle_redirect_without_target( $redirect_type ) { if ( $redirect_type === 410 ) { \add_action( 'wp', [ $this, 'do_410' ] ); } if ( $redirect_type === 451 ) { \add_action( 'wp', [ $this, 'do_451' ] ); } } /** * Wrapper method for doing the actual redirect. * * @param string $location The path to redirect to. * @param int $status Status code to use. * * @return void */ protected function redirect( $location, $status = 302 ) { if ( ! \function_exists( 'wp_redirect' ) ) { require_once \ABSPATH . 'wp-includes/pluggable.php'; } \wp_redirect( $location, $status, 'Yoast SEO Premium' ); exit; } /** * Returns whether or not a target URL requires a trailing slash. * * @param string $target_url The target URL to check. * * @return bool True when trailing slash is required. */ protected function requires_trailing_slash( $target_url ) { return WPSEO_Redirect_Util::requires_trailing_slash( $target_url ); } /** * Returns the query template. * * @param string $filename Filename without extension. * * @return string Full path to template file. */ protected function get_query_template( $filename ) { return \get_query_template( $filename ); } }