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;
}
}