File "permalink-manager-core-functions.php"
Full Path: /home/veodprin/public_html/wp-content/plugins/permalink-manager/includes/core/permalink-manager-core-functions.php
File size: 37.76 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Core functions
*/
class Permalink_Manager_Core_Functions {
public function __construct() {
add_action( 'init', array( $this, 'init_hooks' ), 99 );
}
/**
* Add hooks used by plugin to change the way the permalinks are detected
*
* @return false|void
*/
function init_hooks() {
global $permalink_manager_options;
// Trailing slashes
add_filter( 'permalink_manager_filter_final_term_permalink', array( $this, 'control_trailing_slashes' ), 9 );
add_filter( 'permalink_manager_filter_final_post_permalink', array( $this, 'control_trailing_slashes' ), 9 );
add_filter( 'permalink_manager_filter_post_sample_uri', array( $this, 'control_trailing_slashes' ), 9 );
add_filter( 'wpseo_canonical', array( $this, 'control_trailing_slashes' ), 9 );
add_filter( 'wpseo_opengraph_url', array( $this, 'control_trailing_slashes' ), 9 );
add_filter( 'paginate_links', array( $this, 'control_trailing_slashes' ), 9 );
/**
* Detect & canonical URL/redirect functions
*/
// Do not trigger in back-end
if ( is_admin() ) {
return false;
}
// Do not trigger if Customizer is loaded
if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
return false;
}
// Use the URIs set in this plugin
add_filter( 'request', array( $this, 'detect_post' ), 0, 1 );
// Redirect from old URIs to new URIs + adjust canonical redirect settings
add_action( 'template_redirect', array( $this, 'new_uri_redirect_and_404' ), 1 );
add_action( 'wp', array( $this, 'adjust_canonical_redirect' ), 1 );
// Case-insensitive permalinks
if ( ! empty( $permalink_manager_options['general']['case_insensitive_permalinks'] ) ) {
add_action( 'parse_request', array( $this, 'case_insensitive_permalinks' ), 0 );
}
// Force 404 on non-existing pagination pages
if ( ! empty( $permalink_manager_options['general']['pagination_redirect'] ) ) {
add_action( 'wp', array( $this, 'fix_pagination_pages' ), 0 );
}
}
/**
* Change the request array used by WordPress to load specific content item (post, term, archive, etc.)
*
* @param array $query
* @param bool $request_url
* @param bool $return_object
*
* @return array|WP_Post|WP_Term
*/
public static function detect_post( $query, $request_url = false, $return_object = false ) {
global $wp, $wp_rewrite, $permalink_manager_uris, $permalink_manager_options, $pm_query;
// Check if the array with custom URIs is set
if ( ! ( is_array( $permalink_manager_uris ) ) ) {
return $query;
}
// Used in debug mode & endpoints
$old_query = $query;
/**
* 1. Prepare URL and check if it is correct (make sure that both requested URL & home_url share the same protocol and get rid of www prefix)
*/
$request_url = ( ! empty( $request_url ) ) ? parse_url( $request_url, PHP_URL_PATH ) : $_SERVER['REQUEST_URI'];
$request_url = strtok( $request_url, "?" );
// Make sure that either $_SERVER['SERVER_NAME'] or $_SERVER['HTTP_HOST'] are set
if ( empty( $_SERVER['HTTP_HOST'] ) && empty( $_SERVER['SERVER_NAME'] ) ) {
return $query;
}
$http_host = ( ! empty( $_SERVER['HTTP_HOST'] ) ) ? $_SERVER['HTTP_HOST'] : preg_replace( '/www\./i', '', $_SERVER['SERVER_NAME'] );
$request_url = sprintf( "http://%s%s", str_replace( "www.", "", $http_host ), $request_url );
$raw_home_url = trim( get_option( 'home' ) );
$home_url = preg_replace( "/http(s)?:\/\/(www\.)?(.+?)\/?$/", "http://$3", $raw_home_url );
if ( filter_var( $request_url, FILTER_VALIDATE_URL ) ) {
// Check if "Deep Detect" is enabled
$deep_detect_enabled = apply_filters( 'permalink_manager_deep_uri_detect', true );
// Sanitize the URL
// $request_url = filter_var($request_url, FILTER_SANITIZE_URL);
// Keep only the URI
$request_url = str_replace( $home_url, "", $request_url );
// Hotfix for language plugins
if ( filter_var( $request_url, FILTER_VALIDATE_URL ) ) {
$request_url = parse_url( $request_url, PHP_URL_PATH );
}
$request_url = trim( $request_url, "/" );
// Get all the endpoints & pattern
$endpoints = Permalink_Manager_Helper_Functions::get_endpoints();
$pattern = "/^(.+?)(?|\/({$endpoints})(?|\/(.*)|$)|\/()([\d]+)\/?)?$/i";
// Use default REGEX to detect post
preg_match( $pattern, $request_url, $regex_parts );
$uri_parts['lang'] = false;
$uri_parts['uri'] = ( ! empty( $regex_parts[1] ) ) ? $regex_parts[1] : "";
$uri_parts['endpoint'] = ( ! empty( $regex_parts[2] ) ) ? $regex_parts[2] : "";
$uri_parts['endpoint_value'] = ( ! empty( $regex_parts[3] ) ) ? $regex_parts[3] : "";
// Allow to filter the results by third-parties + store the URI parts with $pm_query global
$uri_parts = apply_filters( 'permalink_manager_detect_uri', $uri_parts, $request_url, $endpoints );
// Support comment pages
preg_match( "/(.*)\/{$wp_rewrite->comments_pagination_base}-([\d]+)/", $uri_parts['uri'], $regex_parts );
if ( ! empty( $regex_parts[2] ) ) {
$uri_parts['uri'] = $regex_parts[1];
$uri_parts['endpoint'] = 'cpage';
$uri_parts['endpoint_value'] = $regex_parts[2];
}
// Support pagination endpoint
if ( $uri_parts['endpoint'] == $wp_rewrite->pagination_base ) {
$uri_parts['endpoint'] = 'page';
}
// Stop the function if $uri_parts is empty
if ( empty( $uri_parts ) ) {
return $query;
}
// Store the URI parts in a separate global variable
$pm_query = $uri_parts;
// Get the URI parts from REGEX parts
// $lang = $uri_parts['lang'];
$uri = $uri_parts['uri'];
$endpoint = $uri_parts['endpoint'];
$endpoint_value = $uri_parts['endpoint_value'];
// Trim slashes
$uri = trim( $uri, "/" );
// Ignore URLs with no URI grabbed
if ( empty( $uri ) ) {
return $query;
}
// Check what content type should be loaded in case of duplicate ("posts" or "terms")
$duplicates_priority = apply_filters( 'permalink_manager_duplicates_priority', false );
/**
* 2. Check if the requested URI matches any custom permalink assigned to a post or term
*/
$uri_query_iteration = 1;
$element_object = '';
$excluded_ids = array();
do {
// Store an array with custom permalinks in a separate variable
$all_uris = $permalink_manager_uris;
// Remove empty rows
$all_uris = array_filter( $all_uris );
// In case of multiple elements using the same URI, the function will follow the "permalink_manager_duplicates_priority" filter value to determine whether terms or posts should be ignored
if ( $duplicates_priority ) {
$duplicated_uris = array_keys( $all_uris, $uri );
if ( count( $duplicated_uris ) > 1 ) {
foreach ( $duplicated_uris as $duplicated_uri_id ) {
if ( ( $duplicates_priority == 'posts' && ! is_numeric( $duplicated_uri_id ) ) || ( $duplicates_priority !== 'posts' && is_numeric( $duplicated_uri_id ) ) ) {
$excluded_ids[] = $duplicated_uri_id;
}
}
}
}
// If the element was excluded in the previous iteration add it to the array
if ( ! empty( $excluded ) ) {
$excluded_ids[] = $excluded;
}
$excluded = '';
// Exclude all the element detected in the previous iterations
if ( ! empty( $excluded_ids ) ) {
$excluded_ids = array_unique( $excluded_ids );
foreach ( $excluded_ids as $excluded_element ) {
unset( $all_uris[ $excluded_element ] );
}
}
// Flip array for better performance
$all_uris = array_flip( $all_uris );
// Attempt 1.
// Find the element ID
$element_id = isset( $all_uris[ $uri ] ) ? $all_uris[ $uri ] : false;
// Attempt 2.
// Decode both request URI & URIs array & make them lowercase (and save in a separate variable)
if ( empty( $element_id ) ) {
$uri = strtolower( urldecode( $uri ) );
foreach ( $all_uris as $raw_uri => $uri_id ) {
$raw_uri = urldecode( $raw_uri );
$all_uris[ $raw_uri ] = $uri_id;
}
// Convert array keys lowercase
$all_uris = array_change_key_case( $all_uris );
$element_id = isset( $all_uris[ $uri ] ) ? $all_uris[ $uri ] : $element_id;
}
// Attempt 3.
// Check again in case someone used post/tax IDs instead of slugs
if ( $deep_detect_enabled && is_numeric( $endpoint_value ) && isset( $all_uris["{$uri}/{$endpoint_value}"] ) ) {
$element_id = $all_uris["{$uri}/{$endpoint_value}"];
$endpoint_value = $endpoint = "";
}
// Attempt 4.
// Check again for attachment custom URIs
if ( empty( $element_id ) && isset( $old_query['attachment'] ) ) {
$element_id = isset( $all_uris["{$uri}/{$endpoint}/{$endpoint_value}"] ) ? $all_uris["{$uri}/{$endpoint}/{$endpoint_value}"] : $element_id;
if ( $element_id ) {
$endpoint_value = $endpoint = "";
}
}
// Allow to filter the item_id by third-parties after initial detection
$element_id = apply_filters( 'permalink_manager_detected_element_id', $element_id, $uri_parts, $request_url );
// Clear the original query before it is filtered
$query = ( $element_id ) ? array() : $query;
/**
* 2A. Custom URI assigned to taxonomy
*/
if ( strpos( $element_id, 'tax-' ) !== false ) {
// Remove the "tax-" prefix
$term_element_id = intval( preg_replace( "/[^0-9]/", "", $element_id ) );
// Filter detected post ID
$term_element_id = apply_filters( 'permalink_manager_detected_term_id', $term_element_id, $uri_parts, true );
// Get the variables to filter wp_query and double-check if taxonomy exists
$term = $element_object = get_term( $term_element_id );
$term_taxonomy = ( ! empty( $term->taxonomy ) ) ? $term->taxonomy : false;
// Check if term is allowed
$disabled = ( $term_taxonomy && Permalink_Manager_Helper_Functions::is_term_excluded( $term ) ) ? true : false;
// Proceed only if the term is not removed and its taxonomy is not disabled
if ( ! $disabled && $term_taxonomy ) {
// Get some term data
if ( $term_taxonomy == 'category' ) {
$query_parameter = 'category_name';
} else if ( $term_taxonomy == 'post_tag' ) {
$query_parameter = 'tag';
} else {
$query["taxonomy"] = $term_taxonomy;
$query_parameter = $term_taxonomy;
}
$term_ancestors = get_ancestors( $element_id, $term_taxonomy );
$final_uri = $term->slug;
// Fix for hierarchical terms
if ( ! empty( $term_ancestors ) ) {
foreach ( $term_ancestors as $parent_id ) {
$parent = get_term( $parent_id, $term_taxonomy );
if ( ! empty( $parent->slug ) ) {
$final_uri = $parent->slug . '/' . $final_uri;
}
}
}
$query["term"] = $term->slug;
$query[ $query_parameter ] = $term->slug;
} else if ( $disabled ) {
$broken_uri = true;
$query = $old_query;
$excluded = $element_id;
} else {
$query = $old_query;
$excluded = $element_id;
}
}
/**
* 2B. Custom URI assigned to post/page/CPT item
*/
else if ( isset( $element_id ) && is_numeric( $element_id ) ) {
// Fix for revisions
$is_revision = wp_is_post_revision( $element_id );
if ( $is_revision ) {
$revision_id = $element_id;
$element_id = $is_revision;
}
// Filter detected post ID
$post_element_id = apply_filters( 'permalink_manager_detected_post_id', $element_id, $uri_parts );
$post_to_load = $element_object = get_post( $post_element_id );
$final_uri = ( ! empty( $post_to_load->post_name ) ) ? $post_to_load->post_name : false;
$post_type = ( ! empty( $post_to_load->post_type ) ) ? $post_to_load->post_type : false;
// Check if post is allowed
$disabled = ( $post_type && Permalink_Manager_Helper_Functions::is_post_excluded( $post_to_load ) ) ? true : false;
// Proceed only if the term is not removed and its taxonomy is not disabled
if ( ! $disabled && $post_type ) {
$post_type_object = get_post_type_object( $post_type );
// Fix for hierarchical CPT & pages
if ( ! ( empty( $post_to_load->ancestors ) ) && ! empty( $post_type_object->hierarchical ) ) {
foreach ( $post_to_load->ancestors as $parent ) {
$parent = get_post( $parent );
if ( $parent && $parent->post_name ) {
$final_uri = $parent->post_name . '/' . $final_uri;
}
}
}
// Alter the final query array
if ( $post_to_load->post_status == 'private' && ( ! is_user_logged_in() || current_user_can( 'read_private_posts', $element_id ) !== true ) ) {
$element_id = 0;
$query = $old_query;
} else if ( $post_to_load->post_status == 'draft' || empty( $final_uri ) ) {
// A. The draft permalinks should be allowed for logged-in users
if ( is_user_logged_in() ) {
if ( $post_type == 'page' ) {
$query['page_id'] = $element_id;
} else {
$query['p'] = $element_id;
}
$query['preview'] = true;
$query['post_type'] = $post_type;
} // B. The draft permalinks should be disabled for non-logged-in visitors
else if ( $post_to_load->post_status == 'draft' ) {
$query['pagename'] = '-';
$query['error'] = '404';
$element_id = 0;
} else {
$query = $old_query;
$excluded = $element_id;
}
} else if ( $post_type == 'page' ) {
$query['pagename'] = $final_uri;
// $query['post_type'] = $post_type;
} else if ( $post_type == 'post' ) {
$query['name'] = $final_uri;
} else if ( $post_type == 'attachment' ) {
$query['attachment'] = $final_uri;
} else {
// Get the query var
$query_var = ( ! empty( $post_type_object->query_var ) ) ? $post_type_object->query_var : $post_type;
$query['name'] = $final_uri;
$query['post_type'] = $post_type;
$query[ $query_var ] = $final_uri;
}
} else if ( $disabled ) {
$broken_uri = true;
$query = $old_query;
$excluded = $element_id;
} else {
$query = $old_query;
$excluded = $element_id;
}
}
// Auto-remove removed term custom URI & redirects (works if enabled in plugin settings)
if ( ! empty( $broken_uri ) && ( ! empty( $permalink_manager_options['general']['auto_fix_duplicates'] ) ) && $permalink_manager_options['general']['auto_fix_duplicates'] == 1 ) {
// Do not trigger if WP Rocket cache plugin is turned on
if ( ! defined( 'WP_ROCKET_VERSION' ) && is_array( $permalink_manager_uris ) ) {
$broken_element_id = ( ! empty( $revision_id ) ) ? $revision_id : $element_id;
$remove_broken_uri = ( ! empty( $broken_element_id ) ) ? Permalink_Manager_Actions::force_clear_single_element_uris_and_redirects( $broken_element_id ) : '';
// Reload page if success
if ( $remove_broken_uri && ! headers_sent() ) {
header( "Refresh:0" );
exit();
}
}
}
// Overwrite the detect function and decide whether to exclude the detected item
$excluded = apply_filters( 'permalink_manager_excluded_element_id', $excluded, $element_object, $old_query, $pm_query );
// Make sure the loop does not execute infinitely (limit it to 10 iterations)
$uri_query_iteration ++;
if ( $uri_query_iteration === 10 ) {
break;
}
} // If the detected element was excluded repeat the URI query and try to find a new one
while ( ! empty( $excluded ) );
/**
* 3A. Endpoints
*/
if ( ! empty( $element_id ) && empty( $disabled ) && ( ! empty( $endpoint ) || ! empty( $endpoint_value ) ) ) {
if ( is_array( $endpoint ) ) {
foreach ( $endpoint as $endpoint_name => $endpoint_value ) {
$query[ $endpoint_name ] = $endpoint_value;
}
} else if ( $endpoint == 'feed' ) {
$feed_rewrite = true;
// Check if /feed/ endpoint is allowed for selected post type or taxonomy
if ( ! empty( $post_type_object ) && empty( $post_type_object->rewrite['feeds'] ) ) {
$feed_rewrite = false;
}
if ( $feed_rewrite ) {
$query[ $endpoint ] = 'feed';
} else {
$element_id = '';
$query = array(
'error' => 404
);
}
} else if ( $endpoint == 'embed' ) {
$query[ $endpoint ] = true;
} else if ( $endpoint == 'page' ) {
$endpoint = 'paged';
if ( is_numeric( $endpoint_value ) ) {
$query[ $endpoint ] = $endpoint_value;
} else {
$query = $old_query;
}
} else if ( $endpoint == 'trackback' ) {
$endpoint = 'tb';
$query[ $endpoint ] = 1;
} else if ( empty( $endpoint ) && is_numeric( $endpoint_value ) ) {
$query['page'] = $endpoint_value;
} else {
$query[ $endpoint ] = $endpoint_value;
}
// Fix for attachments
if ( ! empty( $query['attachment'] ) ) {
$query = array( 'attachment' => $query['attachment'], 'do_not_redirect' => 1 );
}
}
/**
* 3B. Endpoints - check if any endpoint is set with $_GET parameter
*/
if ( ! empty( $element_id ) && $deep_detect_enabled && ! empty( $_GET ) ) {
$get_endpoints = array_intersect( $wp->public_query_vars, array_keys( $_GET ) );
if ( ! empty( $get_endpoints ) ) {
// Append query vars from $_GET parameters
foreach ( $get_endpoints as $endpoint ) {
// Numeric endpoints
$endpoint_value = ( in_array( $endpoint, array( 'page', 'paged', 'attachment_id' ) ) ) ? filter_var( $_GET[ $endpoint ], FILTER_SANITIZE_NUMBER_INT ) : $_GET[ $endpoint ];
// Ignore page endpoint if its value is empty or equal to 1
if ( in_array( $endpoint, array( 'page', 'paged' ) ) && ( empty( $endpoint_value ) || $endpoint_value == 1 ) ) {
continue;
}
// Replace whitespaces with '+' (for YITH WooCommerce Ajax Product Filter URLs only) and sanitize the value
$endpoint_value = ( isset( $_GET['yith_wcan'] ) ) ? preg_replace( '/\s+/', '+', $endpoint_value ) : $endpoint_value;
$query[ $endpoint ] = sanitize_text_field( $endpoint_value );
}
}
}
/**
* 4. Set global with detected item id
*/
if ( ! empty( $element_id ) && empty( $disabled ) && empty( $excluded ) ) {
if ( ! empty( $element_object->taxonomy ) ) {
$pm_query['id'] = $element_object->term_id;
$content_type = "Taxonomy: {$element_object->taxonomy}";
} else if ( ! empty( $element_object->post_type ) ) {
$pm_query['id'] = $element_object->ID;
$content_type = "Post type: {$element_object->post_type}";
}
// If language mismatch is detected do not set 'do_not_redirect' to allow canonical redirect
if ( empty( $pm_query['flag'] ) || $pm_query['flag'] !== 'language_mismatch' ) {
$query['do_not_redirect'] = 1;
}
}
}
/**
* 5. Debug data
*/
if ( empty ( $element_object ) || empty ( $content_type ) ) {
$content_type = $element_object = '';
}
$uri_parts = ( ! empty( $uri_parts ) ) ? $uri_parts : '';
$query = apply_filters( 'permalink_manager_filter_query', $query, $old_query, $uri_parts, $pm_query, $content_type, $element_object );
if ( $return_object && ! empty( $term ) ) {
return $term;
} else if ( $return_object && ! empty( $post_to_load ) ) {
return $post_to_load;
} else {
return $query;
}
}
/**
* Trailing slash & remove BOM and double slashes
*
* @param string $permalink
*
* @return string
*/
static function control_trailing_slashes( $permalink ) {
global $permalink_manager_options;
// Ignore empty permalinks
if ( empty( $permalink ) ) {
return $permalink;
}
// Keep the original permalink in a separate variable
$original_permalink = $permalink;
$trailing_slash_mode = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : "";
// Ignore homepage URLs
if ( ( filter_var( $permalink, FILTER_VALIDATE_URL ) && trim( parse_url( $permalink, PHP_URL_PATH ), '/' ) == '' ) ) {
return $permalink;
}
// Always remove trailing slashes from URLs/URIs that end with file extension (eg. .html)
if ( preg_match( '/(http(?:s)\:\/\/[^\/]+\/)?.*\.([a-zA-Z]{3,4})[\/]*(\?[^\/]+|$)/', $permalink ) ) {
$trailing_slash_mode = 2;
}
// Add trailing slashes
if ( in_array( $trailing_slash_mode, array( 1, 10 ) ) ) {
$permalink = preg_replace( '/(.+?)([\/]*)([\?\#][^\/]+|$)/', '$1/$3', $permalink ); // Instead of trailingslashit()
} // Remove trailing slashes
else if ( in_array( $trailing_slash_mode, array( 2, 20 ) ) ) {
$permalink = preg_replace( '/(.+?)([\/]*)([\?\#][^\/]+|$)/', '$1$3', $permalink ); // Instead of untrailingslashit()
} // Default settings
else {
$permalink = user_trailingslashit( $permalink );
}
// Remove double slashes
$permalink = preg_replace( '/(?<!:)(\/{2,})/', '/', $permalink );
// Remove trailing slashes from URLs that end with query string or anchors
$permalink = preg_replace( '/([\?\#]{1}[^\/]+)([\/]+)$/', '$1', $permalink );
return apply_filters( 'permalink_manager_control_trailing_slashes', $permalink, $original_permalink );
}
/**
* Display 404 if requested page does not exist in pagination or the pagination format is incorrect
*/
function fix_pagination_pages() {
global $wp_query, $wp, $pm_query;
// 1. Get the queried object
$post = get_queried_object();
$post = ( empty( $post ) && ! empty( $wp_query->post ) ) ? $wp_query->post : $post;
// 2. Check if post object is defined
if ( ( ! empty( $post->post_type ) && isset( $post->post_content ) ) || ( ! empty( $wp_query->max_num_pages ) ) ) {
// 2.1A. Check if pagination is detected
$current_page = ( ! empty( $wp_query->query_vars['page'] ) ) ? $wp_query->query_vars['page'] : 1;
$current_page = ( empty( $wp_query->query_vars['page'] ) && ! empty( $wp_query->query_vars['paged'] ) ) ? $wp_query->query_vars['paged'] : $current_page;
// 2.1B. Count post pages
$post_content = ( ! empty( $post->post_content ) ) ? $post->post_content : '';
$num_pages = ( is_home() || is_archive() || is_search() ) ? $wp_query->max_num_pages : substr_count( strtolower( $post_content ), '<!--nextpage-->' ) + 1;
// 2.1C. Remove 'do_not_redirect' parameter if the first page of content is requested to force canonical redirect
if ( ! empty( $pm_query['id'] ) && is_numeric( $pm_query['id'] ) && ! empty( $wp->query_vars['do_not_redirect'] ) && empty( $pm_query['endpoint'] ) && $pm_query['endpoint_value'] == 1 ) {
$is_404 = true;
$wp->query_vars['do_not_redirect'] = 0;
set_query_var( 'p', $pm_query['id'] );
} else {
$is_404 = ( $current_page > 1 && ( $current_page > $num_pages ) ) ? true : false;
}
} // 2.2. Force 404 if no posts are loaded
else if ( ! empty( $wp_query->query['paged'] ) && $wp_query->post_count == 0 ) {
$is_404 = true;
}
// 2.3. Force 404 if endpoint value is not set or not numeric
if ( ! empty( $pm_query['endpoint'] ) && $pm_query['endpoint'] == 'page' && ( empty( $pm_query['endpoint_value'] ) || ! is_numeric( $pm_query['endpoint_value'] ) ) ) {
$is_404 = true;
}
// 3. Block non-existent pages (Force 404 error)
if ( ! empty( $is_404 ) ) {
$wp_query->query = $wp_query->queried_object = $wp_query->queried_object_id = null;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
$pm_query = '';
}
}
/**
* Enhance the existing canonical redirect functionality, allowing users define custom redirects and support custom permalinks
*/
function new_uri_redirect_and_404() {
global $wp_query, $wp, $wp_rewrite, $wpdb, $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_external_redirects, $permalink_manager_options, $pm_query;
// Get the redirection mode & trailing slashes settings
$redirect_mode = ( ! empty( $permalink_manager_options['general']['redirect'] ) ) ? $permalink_manager_options['general']['redirect'] : false;
$trailing_slashes_mode = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : false;
$trailing_slashes_redirect = ( ! empty( $permalink_manager_options['general']['trailing_slashes_redirect'] ) ) ? $permalink_manager_options['general']['trailing_slashes_redirect'] : false;
$extra_redirects = ( ! empty( $permalink_manager_options['general']['extra_redirects'] ) ) ? $permalink_manager_options['general']['extra_redirects'] : false;
$canonical_redirect = ( ! empty( $permalink_manager_options['general']['canonical_redirect'] ) ) ? $permalink_manager_options['general']['canonical_redirect'] : false;
$old_slug_redirect = ( ! empty( $permalink_manager_options['general']['old_slug_redirect'] ) ) ? $permalink_manager_options['general']['old_slug_redirect'] : false;
$endpoint_redirect = ( ! empty( $permalink_manager_options['general']['endpoint_redirect'] ) ) ? $permalink_manager_options['general']['endpoint_redirect'] : false;
$pagination_redirect = ( ! empty( $permalink_manager_options['general']['pagination_redirect'] ) ) ? $permalink_manager_options['general']['pagination_redirect'] : false;
$copy_query_redirect = ( ! empty( $permalink_manager_options['general']['copy_query_redirect'] ) ) ? $permalink_manager_options['general']['copy_query_redirect'] : false;
$redirect_type = '-';
// Get home URL
$home_url = rtrim( get_option( 'home' ), "/" );
$home_dir = parse_url( $home_url, PHP_URL_PATH );
// Set up $correct_permalink variable
$correct_permalink = '';
// Get query string & URI
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
return;
}
$query_string = ( $copy_query_redirect && ! empty( $_SERVER['QUERY_STRING'] ) ) ? $_SERVER['QUERY_STRING'] : '';
$old_uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$old_uri = ( empty( $old_uri ) ) ? strtok( $_SERVER["REQUEST_URI"], '?' ) : $old_uri;
// Fix for WP installed in directories (remove the directory name from the URI)
if ( ! empty( $home_dir ) ) {
$home_dir_regex = preg_quote( trim( $home_dir ), "/" );
$old_uri = preg_replace( "/{$home_dir_regex}/", "", $old_uri, 1 );
}
// Do not use custom redirects on author pages, search & front page
if ( ! is_author() && ! is_front_page() && ! is_home() && ! is_feed() && ! is_search() && empty( $_GET['s'] ) ) {
// Sometimes $wp_query indicates the wrong object if requested directly
$queried_object = get_queried_object();
// Unset 404 if custom URI is detected
if ( ! empty( $pm_query['id'] ) && ( empty( $queried_object->post_status ) || $queried_object->post_status !== 'private' ) ) {
$wp_query->is_404 = false;
}
/**
* 1A. External redirect
*/
if ( ! empty( $pm_query['id'] ) && ! empty( $permalink_manager_external_redirects[ $pm_query['id'] ] ) ) {
$external_url = $permalink_manager_external_redirects[ $pm_query['id'] ];
if ( filter_var( $external_url, FILTER_VALIDATE_URL ) ) {
// Allow redirect
$wp_query->query_vars['do_not_redirect'] = 0;
wp_redirect( $external_url, 301, PERMALINK_MANAGER_PLUGIN_NAME );
exit();
}
}
/**
* 1B. Custom redirects
*/
if ( empty( $wp_query->query_vars['do_not_redirect'] ) && $extra_redirects && ! empty( $permalink_manager_redirects ) && is_array( $permalink_manager_redirects ) && ! empty( $wp->request ) && ! empty( $pm_query['uri'] ) ) {
$uri = $pm_query['uri'];
$endpoint_value = $pm_query['endpoint_value'];
// Make sure that URIs with non-ASCII characters are also detected + Check the URLs that end with number
$decoded_url = urldecode( $uri );
$endpoint_url = "{$uri}/{$endpoint_value}";
// Convert to lowercase to make case-insensitive
$force_lowercase = apply_filters( 'permalink_manager_force_lowercase_uris', true );
if ( $force_lowercase ) {
$uri = strtolower( $uri );
$decoded_url = strtolower( $decoded_url );
$endpoint_url = strtolower( $endpoint_url );
}
// Check if the URI is not assigned to any post/term's redirects
foreach ( $permalink_manager_redirects as $element => $redirects ) {
if ( ! is_array( $redirects ) ) {
continue;
}
if ( in_array( $uri, $redirects ) || in_array( $decoded_url, $redirects ) || ( is_numeric( $endpoint_value ) && in_array( $endpoint_url, $redirects ) ) ) {
// Post is detected
if ( is_numeric( $element ) ) {
$correct_permalink = get_permalink( $element );
} // Term is detected
else {
$term_id = intval( preg_replace( "/[^0-9]/", "", $element ) );
$correct_permalink = get_term_link( $term_id );
}
// The custom redirect is found so there is no need to query the rest of array
break;
}
}
$redirect_type = ( ! empty( $correct_permalink ) ) ? 'custom_redirect' : $redirect_type;
}
// Ignore WP-Content links
if ( strpos( $_SERVER['REQUEST_URI'], '/wp-content' ) !== false ) {
return;
}
/**
* 1C. Pagination redirect
*/
if ( $pagination_redirect && ( ( isset( $wp_query->query_vars['paged'] ) && $wp_query->query_vars['paged'] == 1 ) || ( isset( $wp_query->query_vars['page'] ) && $wp_query->query_vars['page'] == 1 && ! empty( $pm_query['endpoint_value'] ) ) ) ) {
$pm_query['endpoint'] = $pm_query['endpoint_value'] = '';
$wp_query->query_vars['do_not_redirect'] = 0;
}
/**
* 1D. Enhance native redirect
*/
if ( $canonical_redirect && empty( $wp_query->query_vars['do_not_redirect'] ) && ! empty( $queried_object ) && empty( $correct_permalink ) ) {
// Affect only posts with custom URI and old URIs
if ( ! empty( $queried_object->ID ) && isset( $permalink_manager_uris[ $queried_object->ID ] ) && empty( $wp_query->query['preview'] ) ) {
// Ignore posts with specific statuses
if ( ! ( empty( $queried_object->post_status ) ) && in_array( $queried_object->post_status, array( 'draft', 'pending', 'auto-draft', 'future' ) ) ) {
return;
}
// Check if the post is excluded
if ( Permalink_Manager_Helper_Functions::is_post_excluded( $queried_object ) ) {
return;
}
// Get the real URL
$correct_permalink = get_permalink( $queried_object->ID );
} // Affect only terms with custom URI and old URIs
else if ( ! empty( $queried_object->term_id ) && isset( $permalink_manager_uris["tax-{$queried_object->term_id}"] ) && defined( 'PERMALINK_MANAGER_PRO' ) ) {
// Check if the term is excluded
if ( Permalink_Manager_Helper_Functions::is_term_excluded( $queried_object ) ) {
return;
}
// Get the real URL
$correct_permalink = get_term_link( $queried_object->term_id, $queried_object->taxonomy );
}
$redirect_type = ( ! empty( $correct_permalink ) ) ? 'native_redirect' : $redirect_type;
}
/**
* 1E. Old slug redirect
*/
if ( $old_slug_redirect && ! empty( $pm_query['uri'] ) && empty( $wp_query->query_vars['do_not_redirect'] ) && is_404() && empty( $correct_permalink ) ) {
$slug = basename( $pm_query['uri'] );
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id from {$wpdb->postmeta} WHERE meta_key = '_wp_old_slug' AND meta_value = %s", $slug ) );
if ( ! empty( $post_id ) ) {
$correct_permalink = get_permalink( $post_id );
$redirect_type = 'old_slug_redirect';
}
}
/**
* 2. Prevent redirect loop
*/
if ( ! empty( $correct_permalink ) && is_string( $correct_permalink ) && ! empty( $wp->request ) && ! empty( $redirect_type ) && $redirect_type != 'slash_redirect' ) {
$current_uri = trim( $wp->request, "/" );
$redirect_uri = trim( parse_url( $correct_permalink, PHP_URL_PATH ), "/" );
$correct_permalink = ( $redirect_uri == $current_uri ) ? null : $correct_permalink;
}
/**
* 3. Add endpoints to redirect URL
*/
if ( ! empty( $correct_permalink ) && $endpoint_redirect && ( ! empty( $pm_query['endpoint_value'] ) || ! empty( $pm_query['endpoint'] ) ) ) {
$endpoint_value = $pm_query['endpoint_value'];
if ( empty( $pm_query['endpoint'] ) && is_numeric( $endpoint_value ) ) {
$correct_permalink = sprintf( "%s/%d", trim( $correct_permalink, "/" ), $endpoint_value );
} else if ( isset( $pm_query['endpoint'] ) && ! empty( $endpoint_value ) ) {
if ( $pm_query['endpoint'] == 'cpage' ) {
$correct_permalink = sprintf( "%s/%s-%s", trim( $correct_permalink, "/" ), $wp_rewrite->comments_pagination_base, $endpoint_value );
} else {
$correct_permalink = sprintf( "%s/%s/%s", trim( $correct_permalink, "/" ), $pm_query['endpoint'], $endpoint_value );
}
} else {
$correct_permalink = sprintf( "%s/%s", trim( $correct_permalink, "/" ), $pm_query['endpoint'] );
}
}
} else {
$queried_object = '-';
}
/**
* 4. Check trailing & duplicated slashes (ignore links with query parameters)
*/
if ( ( ( $trailing_slashes_mode && $trailing_slashes_redirect ) || preg_match( '/\/{2,}/', $old_uri ) ) && empty( $correct_permalink ) && empty( $query_string ) && ! empty( $old_uri ) && $old_uri !== "/" ) {
$trailing_slash = ( substr( $old_uri, - 1 ) == "/" ) ? true : false;
$obsolete_slash = ( preg_match( '/\/{2,}/', $old_uri ) || preg_match( "/.*\.([a-zA-Z]{3,4})\/$/", $old_uri ) );
if ( ( $trailing_slashes_mode == 1 && ! $trailing_slash ) || ( $trailing_slashes_mode == 2 && $trailing_slash ) || $obsolete_slash ) {
$new_uri = self::control_trailing_slashes( $old_uri );
if ( $new_uri !== $old_uri ) {
$correct_permalink = sprintf( "%s/%s", $home_url, ltrim( $new_uri, '/' ) );
$redirect_type = 'slash_redirect';
}
}
}
/**
* 5. WWW prefix | SSL mismatch redirect
*/
if ( ! empty( $permalink_manager_options['general']['sslwww_redirect'] ) ) {
$home_url_has_www = ( strpos( $home_url, 'www.' ) !== false ) ? true : false;
$requested_url_has_www = ( strpos( $_SERVER['HTTP_HOST'], 'www.' ) !== false ) ? true : false;
$home_url_has_ssl = ( strpos( $home_url, 'https' ) !== false ) ? true : false;
if ( ( $home_url_has_www !== $requested_url_has_www ) || ( ! is_ssl() && $home_url_has_ssl !== false ) ) {
$new_uri = ltrim( $old_uri, '/' );
$correct_permalink = sprintf( "%s/%s", $home_url, $new_uri );
$redirect_type = 'www_redirect';
}
}
/**
* 6. Debug redirect
*/
$correct_permalink = apply_filters( 'permalink_manager_filter_redirect', $correct_permalink, $redirect_type, $queried_object, $old_uri );
/**
* 7. Ignore default URIs (or do nothing if redirects are disabled)
*/
if ( ! empty( $correct_permalink ) && is_string( $correct_permalink ) && ! empty( $redirect_mode ) ) {
// Allow redirect
$wp_query->query_vars['do_not_redirect'] = 0;
// Append query string
$correct_permalink = ( ! empty( $query_string ) ) ? sprintf( "%s?%s", strtok( $correct_permalink, "?" ), $query_string ) : $correct_permalink;
// Adjust trailing slashes
$correct_permalink = self::control_trailing_slashes( $correct_permalink );
// Prevent redirect loop
$rel_old_uri = wp_make_link_relative( $old_uri );
$rel_new_uri = wp_make_link_relative( $correct_permalink );
if ( $redirect_type === 'www_redirect' || $rel_old_uri !== $rel_new_uri ) {
wp_safe_redirect( $correct_permalink, $redirect_mode, PERMALINK_MANAGER_PLUGIN_NAME );
exit();
}
}
}
/**
* Control how the canonical redirect function in WordPress and other popular plugins works
*/
function adjust_canonical_redirect() {
global $permalink_manager_options, $wp, $wp_rewrite;
// Adjust rewrite settings for trailing slashes
$trailing_slash_setting = ( ! empty( $permalink_manager_options['general']['trailing_slashes'] ) ) ? $permalink_manager_options['general']['trailing_slashes'] : "";
if ( in_array( $trailing_slash_setting, array( 1, 10 ) ) ) {
$wp_rewrite->use_trailing_slashes = true;
} else if ( in_array( $trailing_slash_setting, array( 2, 20 ) ) ) {
$wp_rewrite->use_trailing_slashes = false;
}
// Get endpoints
$endpoints = Permalink_Manager_Helper_Functions::get_endpoints();
$endpoints_array = ( $endpoints ) ? explode( "|", $endpoints ) : array();
// Check if any endpoint is called (fix for endpoints)
foreach ( $endpoints_array as $endpoint ) {
if ( ! empty( $wp->query_vars[ $endpoint ] ) && ! in_array( $endpoint, array( 'attachment', 'page', 'paged', 'feed' ) ) ) {
$wp->query_vars['do_not_redirect'] = 1;
break;
}
}
if ( empty( $permalink_manager_options['general']['canonical_redirect'] ) ) {
remove_action( 'template_redirect', 'redirect_canonical' );
}
if ( empty( $permalink_manager_options['general']['old_slug_redirect'] ) ) {
remove_action( 'template_redirect', 'wp_old_slug_redirect' );
}
if ( ! empty( $wp->query_vars['do_not_redirect'] ) ) {
if ( function_exists( 'rank_math' ) ) {
$rank_math_instance = rank_math();
if ( property_exists( $rank_math_instance, 'container' ) && is_array( $rank_math_instance->container ) && is_object( $rank_math_instance->container['manager'] ) && method_exists( $rank_math_instance->container['manager'], 'get_module' ) ) {
$rank_math_redirections_module = $rank_math_instance->container['manager']->get_module( 'redirections' );
if ( ! empty( $rank_math_redirections_module ) ) {
remove_action( 'template_redirect', array( $rank_math_redirections_module, 'do_redirection' ), 11 );
remove_action( 'wp', array( $rank_math_redirections_module, 'do_redirection' ), 11 );
}
}
}
// SEOPress
remove_action( 'template_redirect', 'seopress_category_redirect', 1 );
remove_action( 'template_redirect', 'wp_old_slug_redirect' );
remove_action( 'template_redirect', 'redirect_canonical' );
add_filter( 'wpml_is_redirected', '__return_false', 99, 2 );
add_filter( 'pll_check_canonical_url', '__return_false', 99, 2 );
}
}
/**
* Enable case-insensitive permalinks mode
*/
function case_insensitive_permalinks() {
global $permalink_manager_uris;
if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
$_SERVER['REQUEST_URI'] = strtolower( $_SERVER['REQUEST_URI'] );
$permalink_manager_uris = array_map( 'strtolower', $permalink_manager_uris );
}
}
}