• Resolved captain1975

    (@captain1975)


    Hello Team,

    Currently I am facing the following problem due to a possible conflict between Rank Math SEO plugin and a custom Keyword Linker plugin.

    The custom plugin is working fine, but now when I try to save any post, I get the following warning message:

    Could not update the meta value of _genesis_hide_title in database

    Upon deactivating plugins individually, I found that when Rank Math plugin is deactivated, the problem goes away.

    I also tried installing other popular SEO plugins, and could not see this issue happening.

    So I am curious to know what is causing this conflict only with Rank Math plugin?

    Below given is my full php code of custom plugin for your kind reference:

    <?php 
    /*
    Plugin Name: Random Keyword Auto Linker
    Description: Links up to five manually entered keywords across all posts in a selected category, in batches of 20. Links only unlinked keyword instances, randomly picks unique related posts to link. Avoids linking the same keyword more than once per post or repeating target links. Skips posts already linked. Tracks last used category, offset, and last entered keywords.
    Version: 1.6.0
    Author: Captain
    */

    if (!defined('ABSPATH')) exit;

    class Random_Keyword_Auto_Linker {
    private $max_links_per_post = 5;
    private $batch_size = 20;
    private $option_state = 'rk_auto_linker_last_state';
    private $option_keywords = 'rk_auto_linker_last_keywords';

    public function __construct() {
    add_action('admin_menu', [$this, 'add_admin_menu']);
    add_action('admin_post_run_random_keyword_linking', [$this, 'process_keyword_linking']);
    }

    public function add_admin_menu() {
    add_menu_page('Keyword Linker', 'Keyword Linker', 'manage_options', 'random-keyword-linker', [$this, 'admin_page']);
    }

    public function admin_page() {
    if (!current_user_can('manage_options')) wp_die('Unauthorized user');

    $categories = get_categories(['hide_empty' => false]);
    $last_state = get_option($this->option_state, ['category_id' => '', 'offset' => '']);
    $last_keywords = get_option($this->option_keywords, [
    'keyword1' => '', 'keyword2' => '', 'keyword3' => '', 'keyword4' => '', 'keyword5' => ''
    ]);

    echo '<div class="wrap"><h1>Random Keyword Auto Linker</h1>';
    echo '<form method="post" action="' . admin_url('admin-post.php') . '">';
    wp_nonce_field('run_random_keyword_linking', 'random_keyword_nonce');
    echo '<input type="hidden" name="action" value="run_random_keyword_linking">';
    // 5 keyword fields
    for ($i = 1; $i <= 5; $i++) {
    echo '<p><strong>Enter Keyword ' . $i . ':</strong> <input type="text" name="keyword' . $i . '" value="' . esc_attr($last_keywords['keyword' . $i]) . '"></p>';
    }
    echo '<p><strong>Select Category:</strong> <select name="category_id">';
    foreach ($categories as $cat) {
    $selected = $cat->term_id == $last_state['category_id'] ? ' selected' : '';
    echo '<option value="' . $cat->term_id . '"' . $selected . '>' . esc_html($cat->name) . '</option>';
    }
    echo '</select></p>';
    echo '<p><strong>Offset (use 0 for first batch, then 20, 40, etc.):</strong>
    <input type="number" name="offset" value="' . esc_attr($last_state['offset']) . '" step="20"></p>';
    submit_button('Run Linking for This Batch');
    echo '</form>';

    echo '<h3>Last Scan Info</h3>';
    if ($last_state['category_id']) {
    $cat_name = get_cat_name($last_state['category_id']);
    echo '<p><strong>Category:</strong> ' . esc_html($cat_name) . '</p>';
    echo '<p><strong>Last Offset Used:</strong> ' . intval($last_state['offset']) . '</p>';
    } else {
    echo '<p>No previous scan data found.</p>';
    }

    echo '</div>';
    }

    public function process_keyword_linking() {
    if (!current_user_can('manage_options')) wp_die('Unauthorized user');
    if (!isset($_POST['random_keyword_nonce']) || !wp_verify_nonce($_POST['random_keyword_nonce'], 'run_random_keyword_linking')) wp_die('Nonce check failed');

    // Collect up to 5 keywords
    $keywords = [];
    $keywords_option = [];
    for ($i = 1; $i <= 5; $i++) {
    $kw = sanitize_text_field($_POST['keyword' . $i] ?? '');
    $keywords_option['keyword' . $i] = $kw;
    if (!empty($kw)) $keywords[] = $kw;
    }
    $category_id = (int)($_POST['category_id'] ?? 0);
    $offset = (int)($_POST['offset'] ?? 0);

    if (empty($keywords) || !$category_id) wp_die('At least one keyword and category are required');

    update_option($this->option_state, ['category_id' => $category_id, 'offset' => $offset]);
    update_option($this->option_keywords, $keywords_option);

    $args = [
    'post_type' => 'post',
    'posts_per_page' => $this->batch_size,
    'offset' => $offset,
    'cat' => $category_id,
    'post_status' => 'publish',
    ];

    $batch_posts = get_posts($args);
    if (empty($batch_posts)) {
    wp_redirect(admin_url('admin.php?page=random-keyword-linker&done=1'));
    exit;
    }

    // Gather candidate links for each keyword
    $all_links = [];
    foreach ($keywords as $kw) {
    $linked_candidates = get_posts([
    'post_type' => 'post',
    'posts_per_page' => -1,
    'post_status' => 'publish',
    'cat' => $category_id,
    's' => $kw,
    ]);
    foreach ($linked_candidates as $p) {
    if (stripos($p->post_content, $kw) !== false) {
    $all_links[$kw][$p->ID] = get_permalink($p->ID);
    }
    }
    }

    foreach ($batch_posts as $post) {
    $content = $post->post_content;
    $original_content = $content;

    // Find all already linked URLs in the post
    preg_match_all('/<a[^>]+href=["\']([^"\']+)["\']/i', $content, $matches);
    $already_linked_urls = $matches[1] ?? [];

    // Use a DOMDocument only for finding tags, but do replacements on the raw HTML to preserve everything
    $used_targets = [];
    $used_keywords = [];
    $links_added = 0;

    foreach ($keywords as $kw) {
    if (in_array($kw, $used_keywords)) continue;

    $self_removed_links = $all_links[$kw] ?? [];
    unset($self_removed_links[$post->ID]);

    $available_links = array_diff($self_removed_links, $already_linked_urls, $used_targets);
    if (empty($available_links)) continue;

    // Only link if the keyword is not already linked
    $pattern = '/(<a\b[^>]*>[^<]*?)(' . preg_quote($kw, '/') . ')(.*?<\/a>)/i';
    $plain_pattern = '/\b(' . preg_quote($kw, '/') . ')\b/i';

    // Find all <a>...</a> and mark their positions
    $skip_positions = [];
    if (preg_match_all('/<a\b[^>]*>.*?<\/a>/is', $content, $a_matches, PREG_OFFSET_CAPTURE)) {
    foreach ($a_matches[0] as $a_match) {
    $skip_positions[] = [$a_match[1], $a_match[1] + strlen($a_match[0])];
    }
    }

    // Find first unlinked keyword occurrence not inside <a>
    $offset_pos = 0;
    $found = false;
    while (preg_match($plain_pattern, $content, $m, PREG_OFFSET_CAPTURE, $offset_pos)) {
    $kw_pos = $m[0][1];
    $inside_link = false;
    foreach ($skip_positions as $range) {
    if ($kw_pos >= $range[0] && $kw_pos < $range[1]) {
    $inside_link = true;
    break;
    }
    }
    if (!$inside_link) {
    // Replace this occurrence
    $linked_url = $available_links[array_rand($available_links)];
    $used_targets[] = $linked_url;
    $replace = '<a href="' . esc_url($linked_url) . '">' . $m[0][0] . '</a>';
    $content = substr_replace($content, $replace, $kw_pos, strlen($m[0][0]));
    $links_added++;
    $used_keywords[] = $kw;
    $found = true;
    break;
    }
    $offset_pos = $kw_pos + strlen($m[0][0]);
    }
    if ($links_added >= $this->max_links_per_post) break;
    }

    if ($content !== $original_content) {
    wp_update_post([
    'ID' => $post->ID,
    'post_content' => $content
    ]);
    }
    }

    wp_redirect(admin_url('admin.php?page=random-keyword-linker&done=1'));
    exit;
    }
    }

    new Random_Keyword_Auto_Linker();
    • This topic was modified 10 months ago by captain1975.
Viewing 3 replies - 1 through 3 (of 3 total)
  • Plugin Support Rank Math Support

    (@rankmathsupport)

    Hello @captain1975
     
    Thank you so much for getting in touch.
     
    The warning message you’re seeing likely stems from how the custom plugin is updating posts. Rank Math hooks into the post update process to manage SEO metadata, and if wp_update_post() is used without proper post meta handling, it can lead to such conflicts.
     
    You could try modifying your plugin to use remove_action('save_post', ...) temporarily before calling wp_update_post(), and re-adding it afterward. This can prevent unintended interference from plugins like Rank Math during programmatic updates.
     
    Unfortunately, beyond some of the ideas we shared we cannot fully debug a custom plugin due to time constraints so we recommend checking with the plugin developer for a full debugging.
     
    Don’t hesitate to get in touch if you have any questions about our plugin.

    Thread Starter captain1975

    (@captain1975)

    Thank you Support, for your detailed clarification. I appreciate it very much!

    Actually it is an AI generated plugin code, and because it looks unreliable, I have removed it, so now the problem is gone.

    Many Thanks again!

    Plugin Support Rank Math Support

    (@rankmathsupport)

    Hello @captain1975,

    Sure, we completely understand that.

    If you have any other questions about the plugin don’t hesitate to get in touch with us.

    Thank you.

Viewing 3 replies - 1 through 3 (of 3 total)

The topic ‘Conflict with a Custom Plugin’ is closed to new replies.