Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix/86: Display notice with vertices that cause a redirect cycle/loop #341

Merged
merged 3 commits into from Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 34 additions & 13 deletions inc/classes/class-srm-loop-detection.php
Expand Up @@ -62,7 +62,10 @@ private static function get_directed_graph() {
}

// Add an edge from the source URL to the destination URL.
$graph[ $source_url ][] = $destination_url;
$graph[ $source_url ][] = array(
'id' => $redirect['ID'],
'destination' => $destination_url,
);
}
}

Expand All @@ -78,27 +81,25 @@ private static function get_directed_graph() {
* @param string $vertex Node in the graph that holds URL of source or destination.
* @param array $visited Array of nodes visited during traversal.
* @param array $current_path Array of paths traversed.
* @param array $cycle_source Array of starting point of detected cycles.
*
* @return boolean
* @return void
*/
private static function has_cycle_recursive( $graph, $vertex, &$visited, &$current_path ) {
private static function has_cycle_recursive( $graph, $vertex, &$visited, &$current_path, &$cycle_source ) {
$visited[ $vertex ] = true;
$current_path[ $vertex ] = true;

if ( isset( $graph[ $vertex ] ) ) {
foreach ( $graph[ $vertex ] as $neighbor ) {
if ( ! isset( $visited[ $neighbor ] ) ) {
if ( self::has_cycle_recursive( $graph, $neighbor, $visited, $current_path ) ) {
return true;
}
} elseif ( isset( $current_path[ $neighbor ] ) ) {
return true;
if ( ! isset( $visited[ $neighbor['destination'] ] ) ) {
self::has_cycle_recursive( $graph, $neighbor['destination'], $visited, $current_path, $cycle_source );
} elseif ( isset( $current_path[ $neighbor['destination'] ] ) ) {
$cycle_source[] = $neighbor;
}
}
}

unset( $current_path[ $vertex ] );
return false;
}

/**
Expand All @@ -110,15 +111,35 @@ public static function detect_redirect_loops() {
$graph = self::get_directed_graph();
$visited = array();
$current_path = array();
$cycle_source = array();

foreach ( $graph as $vertex => $destinations ) {
if ( ! isset( $visited[ $vertex ] ) ) {
if ( self::has_cycle_recursive( $graph, $vertex, $visited, $current_path ) ) {
return true;
if ( self::has_cycle_recursive( $graph, $vertex, $visited, $current_path, $cycle_source ) ) {
return $cycle_source;
}
}
}

return false;
return $cycle_source;
}

/**
* Returns an array of "Redirect From" values that are the starting vertices of redirect loops.
*
* @param array $cycle_source Array of redirect loops.
*
* @return array
*/
public static function get_cycle_source( $cycle_source = array() ) {
return array_map(
function( $source ) {
return array(
'path' => wp_parse_url( esc_url( $source['destination'] ), PHP_URL_PATH ),
'id' => $source['id'],
);
},
$cycle_source
);
}
}
31 changes: 24 additions & 7 deletions inc/classes/class-srm-post-type.php
Expand Up @@ -231,14 +231,31 @@ public function action_redirect_chain_alert() {
if ( $this->is_plugin_page() ) {

/**
* check_for_possible_redirect_loops() runs in best case Theta(n^2) so if you have 100 redirects, this method
* will be running slow. Let's disable it by default.
* Hook to toggle check for redirect loop.
*
* @param boolean $enable_loop_detection Detects redirect loop if true.
*/
if ( apply_filters( 'srm_check_for_possible_redirect_loops', false ) ) {
if ( SRM_Loop_Detection::detect_redirect_loops() ) {
if ( apply_filters( 'srm_check_for_possible_redirect_loops', true ) ) {
$cycle_source = SRM_Loop_Detection::detect_redirect_loops();
$paths = SRM_Loop_Detection::get_cycle_source( $cycle_source );

if ( ! empty( $cycle_source ) ) {
?>
<div class="notice notice-warning">
<p><?php esc_html_e( 'Safe Redirect Manager Warning: Possible redirect loops and/or chains have been created.', 'safe-redirect-manager' ); ?></p>
<p><?php esc_html_e( 'Safe Redirect Manager Warning: The following redirects with the "Redirect To" value have created redirect chain/loops.', 'safe-redirect-manager' ); ?></p>
<ul style="list-style: inside;">
<?php foreach ( $paths as $path ) : ?>
<li>
<?php
printf(
'<a href="%s">%s</a>',
esc_url( get_edit_post_link( esc_html( $path['id'] ) ) ),
esc_html( $path['path'] )
);
?>
</li>
<?php endforeach; ?>
</ul style>
</div>
<?php
}
Expand Down Expand Up @@ -710,8 +727,8 @@ public function load_resources() {
'redirectjs',
'redirectValidation',
array(
'urlError' => __( 'There are some issues validating the URL. Please try again.', 'safe-redirect-manager' ),
'fail' => __( 'There is an existing redirect with the same Redirect From URL. You may <a href="%s">Edit</a> the redirect or try other `from` URL.', 'safe-redirect-manager' ),
'urlError' => __( 'There are some issues validating the URL. Please try again.', 'safe-redirect-manager' ),
'fail' => __( 'There is an existing redirect with the same Redirect From URL. You may <a href="%s">Edit</a> the redirect or try other `from` URL.', 'safe-redirect-manager' ),
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'srm_autocomplete_nonce' ),
)
Expand Down