` tags in post content on save and updates `wp_modulustools_meta`. * * @param int $post_id Post ID. * @param \WP_Post $post Post object. * @return void */ public function analyze_post_links_on_save( int $post_id, \WP_Post $post ): void { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( wp_is_post_revision( $post_id ) || 'publish' !== $post->post_status ) { return; } $home_host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); $outbound_count = 0; $external_count = 0; $linked_post_ids = array(); if ( preg_match_all( '/]*href=["\']([^"\']+)["\'][^>]*>/i', $post->post_content, $matches ) ) { foreach ( $matches[1] as $href ) { $href = trim( $href ); if ( '' === $href || 0 === strpos( $href, '#' ) || 0 === strpos( $href, 'mailto:' ) || 0 === strpos( $href, 'tel:' ) ) { continue; } $link_host = strtolower( (string) wp_parse_url( $href, PHP_URL_HOST ) ); if ( '' === $link_host || $link_host === $home_host ) { ++$outbound_count; $target_id = url_to_postid( $href ); if ( $target_id > 0 && $target_id !== $post_id ) { $linked_post_ids[] = $target_id; } } else { ++$external_count; } } } $inbound_count = self::calculate_inbound_links_for_post( $post_id ); Core::save_meta( $post_id, 'post', array( 'inbound_links_count' => $inbound_count, 'outbound_links_count' => $outbound_count, 'external_links_count' => $external_count, ) ); $linked_post_ids = array_unique( $linked_post_ids ); foreach ( array_slice( $linked_post_ids, 0, 20 ) as $target_post_id ) { $target_inbound = self::calculate_inbound_links_for_post( (int) $target_post_id ); Core::save_meta( (int) $target_post_id, 'post', array( 'inbound_links_count' => $target_inbound ) ); } } /** * Counts how many published posts link to the given `$post_id`. * * @param int $post_id Target Post ID. * @return int */ public static function calculate_inbound_links_for_post( int $post_id ): int { global $wpdb; $permalink = get_permalink( $post_id ); if ( ! $permalink ) { return 0; } $rel_path = (string) wp_parse_url( $permalink, PHP_URL_PATH ); if ( '' === $rel_path || '/' === $rel_path ) { return 0; } $like_pattern = '%' . $wpdb->esc_like( $rel_path ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $count = $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(ID) FROM %i WHERE post_status = %s AND ID != %d AND post_content LIKE %s', $wpdb->posts, 'publish', $post_id, $like_pattern ) ); return absint( $count ); } /** * Generates contextual internal link recommendations based on shared keywords and categories. * * @param int $current_post_id Current Post ID. * @param string $focus_keyword Current focus keyword. * @return array> */ public static function get_contextual_suggestions( int $current_post_id, string $focus_keyword = '' ): array { global $wpdb; $suggestions = array(); if ( '' !== trim( $focus_keyword ) ) { $words = preg_split( '/\s+/', trim( $focus_keyword ) ); $first_word = is_array( $words ) && ! empty( $words[0] ) ? $words[0] : $focus_keyword; $like_kw = '%' . $wpdb->esc_like( $first_word ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT object_id, focus_keyword FROM %i WHERE object_type = %s AND object_id != %d AND focus_keyword LIKE %s ORDER BY is_pillar DESC LIMIT 5', $wpdb->prefix . 'modulustools_meta', 'post', $current_post_id, $like_kw ) ); if ( is_array( $rows ) ) { foreach ( $rows as $r ) { $p = get_post( (int) $r->object_id ); if ( $p && 'publish' === $p->post_status ) { $suggestions[] = array( 'title' => get_the_title( $p ), 'url' => (string) get_permalink( $p ), 'anchor' => '' !== $r->focus_keyword ? $r->focus_keyword : strtolower( get_the_title( $p ) ), ); } } } } if ( count( $suggestions ) < 5 ) { $cats = wp_get_post_categories( $current_post_id ); if ( ! empty( $cats ) ) { $rel_posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 6, 'category__in' => $cats, ) ); foreach ( $rel_posts as $rp ) { if ( (int) $rp->ID === $current_post_id ) { continue; } $rp_meta = Core::get_meta( (int) $rp->ID, 'post' ); $suggestions[] = array( 'title' => get_the_title( $rp ), 'url' => (string) get_permalink( $rp ), 'anchor' => $rp_meta && ! empty( $rp_meta->focus_keyword ) ? $rp_meta->focus_keyword : strtolower( get_the_title( $rp ) ), ); } } } return array_slice( $suggestions, 0, 5 ); } /** * Renders the Orphan Pages & Internal Link Graph view in the Admin Dashboard. * * @return void */ public static function render_orphan_pages_view(): void { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $orphans = $wpdb->get_results( $wpdb->prepare( 'SELECT p.ID, p.post_title, p.post_type, p.post_date, COALESCE(m.inbound_links_count, 0) AS inbound_links, COALESCE(m.outbound_links_count, 0) AS outbound_links, COALESCE(m.focus_keyword, %s) AS focus_keyword FROM %i p LEFT JOIN %i m ON (p.ID = m.object_id AND m.object_type = %s) WHERE p.post_status = %s AND p.post_type IN (%s, %s) AND (m.inbound_links_count IS NULL OR m.inbound_links_count = 0) ORDER BY p.post_date DESC LIMIT 100', '', $wpdb->posts, $wpdb->prefix . 'modulustools_meta', 'post', 'publish', 'post', 'page' ) ); ?>

focus_keyword ? $item->focus_keyword : strtolower( $item->post_title ); $url = get_permalink( (int) $item->ID ); ?>
post_title ); ?> post_type ); ?> focus_keyword ? esc_html( $item->focus_keyword ) : '—'; ?> 0 Inbound (Orphan) outbound_links ); ?> <a href=""></a>