prefix . 'il_pilares') whose names and column lists // are constants under plugin control. Generic WordPress checks // for direct DB queries, caching and prepared-SQL interpolation // don't apply meaningfully here. See docs/LICENSING.md for the // full architecture rationale. class FLINK_Stats { /** * Counts plugin-inserted links (data-il="1") pointing to a pilar URL. */ public static function count_real_links_for_pilar( $pilar ) { $detail = self::get_link_detail_for_pilar( $pilar ); return array_sum( array_column( $detail, 'count' ) ); } /** * Returns detail of all posts linking to this pilar via plugin links. * Only counts — manual editor links are excluded. */ public static function get_link_detail_for_pilar( $pilar ) { global $wpdb; if ( empty( $pilar->url ) ) return array(); $url = esc_url_raw( $pilar->url ); $url_trail = trailingslashit( $url ); $like_il = '%' . $wpdb->esc_like( 'data-il="1"' ) . '%'; $like1 = '%' . $wpdb->esc_like( 'href="' . $url . '"' ) . '%'; $like2 = '%' . $wpdb->esc_like( 'href="' . $url_trail . '"' ) . '%'; $posts = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_title, post_type, post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type NOT IN ('revision','attachment','nav_menu_item') AND post_content LIKE %s AND ( post_content LIKE %s OR post_content LIKE %s ) ORDER BY post_title ASC", $like_il, $like1, $like2 ) ); $detail = array(); foreach ( $posts as $post ) { // Build a pattern that matches tags containing BOTH data-il="1" // AND href pointing to this URL, in any attribute order. // Using lookaheads ensures each tag is matched only once. $url_alts = implode( '|', array_unique( array_map( function( $u ) { return preg_quote( $u, '/' ); }, array( $url, $url_trail ) ) ) ); $pattern = '/]*\bdata-il=["\']1["\'])(?=[^>]*\bhref=["\'](?:' . $url_alts . ')["\'])[^>]*>(.*?)<\/a>/is'; $count = preg_match_all( $pattern, $post->post_content, $m ); if ( ! $count ) continue; $anchors = array(); foreach ( $m[1] as $html ) { $clean = trim( wp_strip_all_tags( $html ) ); if ( $clean ) $anchors[] = $clean; } if ( $count === 0 ) continue; $detail[] = array( 'post_id' => $post->ID, 'title' => $post->post_title, 'url' => get_permalink( $post->ID ), 'type' => $post->post_type, 'count' => $count, 'anchors' => array_values( array_unique( $anchors ) ), ); } usort( $detail, function( $a, $b ) { return $b['count'] - $a['count']; } ); return $detail; } /** * Counts REAL links in post content for a specific rule. * Matches by BOTH destination URL AND the rule's keywords (anchor text). */ public static function count_links_by_rule( $rule_id ) { global $wpdb; $reglas = new FLINK_Reglas(); $regla = $reglas->get( (int) $rule_id ); if ( ! $regla ) return 0; // Get destination URL $dest_url = ''; if ( ! empty( $regla->destino_valor ) ) { $dest_url = $regla->destino_valor; } elseif ( ! empty( $regla->pilar_id ) ) { $pilar = ( new FLINK_Pilares() )->get( (int) $regla->pilar_id ); if ( $pilar ) $dest_url = $pilar->url; } if ( empty( $dest_url ) ) return 0; // Get all keywords for this rule $keywords = array( $regla->nombre ); if ( ! empty( $regla->descripcion ) ) { $syns = array_filter( array_map( 'trim', explode( "\n", $regla->descripcion ) ) ); $keywords = array_merge( $keywords, $syns ); } $keywords = array_unique( array_filter( $keywords ) ); if ( empty( $keywords ) ) return 0; // Find posts with plugin links pointing to this URL $like_il = '%' . $wpdb->esc_like( 'data-il="1"' ) . '%'; $url_esc = esc_url_raw( $dest_url ); $like_url = '%' . $wpdb->esc_like( $url_esc ) . '%'; $posts = $wpdb->get_results( $wpdb->prepare( "SELECT post_content FROM {$wpdb->posts} WHERE post_status IN ('publish','draft','pending') AND post_content LIKE %s AND post_content LIKE %s", $like_il, $like_url ) ); $total = 0; $url_alts = array_unique( array( $url_esc, trailingslashit( $url_esc ) ) ); foreach ( $posts as $post ) { foreach ( $keywords as $kw ) { $kw_esc = preg_quote( trim( $kw ), '/' ); foreach ( $url_alts as $u ) { $u_esc = preg_quote( $u, '/' ); // Match KEYWORD in any attribute order $pat = '/]*\bdata-il=["\']1["\'])(?=[^>]*\bhref=["\']' . $u_esc . '["\'])[^>]*>\s*' . $kw_esc . '\s*<\/a>/iu'; $total += preg_match_all( $pat, $post->post_content ); } } } return $total; } }