Plugin Directory

Changeset 3791852


Ignore:
Timestamp:
11/09/2026 05:34:27 AM (less than one hour ago)
Author:
mareinterno
Message:

2026.11.0

Location:
edgeshaping-lite/trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • edgeshaping-lite/trunk/admin/class-admin.php

    r3735949 r3791852  
    77        add_menu_page( 'EdgeShaping Lite', 'EdgeShaping Lite', 'manage_options', 'edgeshaping-lite', [ __CLASS__, 'render_dashboard' ], 'dashicons-visibility', 80 );
    88        add_submenu_page( 'edgeshaping-lite', esc_html__( 'Dashboard', 'edgeshaping-lite' ), esc_html__( 'Dashboard', 'edgeshaping-lite' ), 'manage_options', 'edgeshaping-lite', [ __CLASS__, 'render_dashboard' ] );
     9        add_submenu_page( 'edgeshaping-lite', esc_html__( 'AI Ready', 'edgeshaping-lite' ), esc_html__( 'AI Ready', 'edgeshaping-lite' ), 'manage_options', 'edgeshaping-ai-ready', [ __CLASS__, 'render_ai_ready' ] );
    910        add_submenu_page( 'edgeshaping-lite', esc_html__( 'Access Log', 'edgeshaping-lite' ), esc_html__( 'Access Log', 'edgeshaping-lite' ), 'manage_options', 'edgeshaping-log', [ __CLASS__, 'render_log' ] );
    1011        add_submenu_page( 'edgeshaping-lite', esc_html__( 'AHTG Matrix', 'edgeshaping-lite' ), esc_html__( 'AHTG Matrix', 'edgeshaping-lite' ), 'manage_options', 'edgeshaping-ahtg', [ __CLASS__, 'render_ahtg' ] );
    … …  
    127128    }
    128129
    129     private static function render_date_filter( array $range, string $page ): void {
     130    private static function render_date_filter( array $range, string $page, array $presets = [] ): void {
    130131        $base    = admin_url( 'admin.php?page=' . $page );
    131         $presets = [ '7days' => esc_html__( 'Last 7 days', 'edgeshaping-lite' ), '30days' => esc_html__( 'Last 30 days', 'edgeshaping-lite' ), 'this_month' => esc_html__( 'This month', 'edgeshaping-lite' ), 'last_month' => esc_html__( 'Last month', 'edgeshaping-lite' ), 'custom' => esc_html__( 'Custom range', 'edgeshaping-lite' ) ];
     132        if ( empty( $presets ) ) {
     133            $presets = [ '7days' => esc_html__( 'Last 7 days', 'edgeshaping-lite' ), '30days' => esc_html__( 'Last 30 days', 'edgeshaping-lite' ), 'this_month' => esc_html__( 'This month', 'edgeshaping-lite' ), 'last_month' => esc_html__( 'Last month', 'edgeshaping-lite' ), 'custom' => esc_html__( 'Custom range', 'edgeshaping-lite' ) ];
     134        }
    132135        echo '<div class="es-date-filter">';
    133136        echo '<div class="es-preset-buttons">';
    … …  
    206209        }
    207210        echo '</div></div></div>';
     211    }
     212
     213   
     214    private static function get_ai_ready_date_range(): array {
     215        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     216        $preset = isset( $_GET['preset'] ) ? sanitize_text_field( wp_unslash( $_GET['preset'] ) ) : 'today';
     217        $today  = current_time( 'Y-m-d' );
     218        switch ( $preset ) {
     219            case '7days':
     220                $from = gmdate( 'Y-m-d', strtotime( '-6 days', strtotime( $today ) ) );
     221                $to   = $today;
     222                break;
     223            case 'custom':
     224                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     225                $from = isset( $_GET['from'] ) ? sanitize_text_field( wp_unslash( $_GET['from'] ) ) : $today;
     226                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     227                $to   = isset( $_GET['to'] )   ? sanitize_text_field( wp_unslash( $_GET['to'] ) )   : $today;
     228                $earliest = gmdate( 'Y-m-d', strtotime( '-29 days', strtotime( $today ) ) );
     229                if ( $to > $today ) $to = $today;
     230                if ( $from < $earliest ) $from = $earliest;
     231                if ( $from > $to ) $from = $to;
     232                break;
     233            default:
     234                $preset = 'today';
     235                $from   = $today;
     236                $to     = $today;
     237                break;
     238        }
     239        return compact( 'preset', 'from', 'to' );
     240    }
     241
     242    public static function render_ai_ready(): void {
     243        $range = self::get_ai_ready_date_range();
     244        $names = EdgeShaping_Tracker::get_bot_names( $range['from'], $range['to'] );
     245
     246        echo '<div class="wrap edgeshaping-wrap">';
     247        echo '<h1>EdgeShaping Lite — ' . esc_html__( 'AI Ready', 'edgeshaping-lite' ) . '</h1>';
     248        echo '<p class="es-description">' . esc_html__( 'AI bots that reached this site in this period (including today, up to 30 days).', 'edgeshaping-lite' ) . '</p>';
     249
     250        self::render_date_filter( $range, 'edgeshaping-ai-ready', [
     251            'today' => esc_html__( 'Today', 'edgeshaping-lite' ),
     252            '7days' => esc_html__( 'Last 7 days', 'edgeshaping-lite' ),
     253            'custom' => esc_html__( 'Custom range', 'edgeshaping-lite' ),
     254        ] );
     255
     256        if ( empty( $names ) ) {
     257            echo '<p class="es-empty">' . esc_html__( 'No data available.', 'edgeshaping-lite' ) . '</p>';
     258        } else {
     259            echo '<table class="es-table"><thead><tr><th>' . esc_html__( 'Bot Name', 'edgeshaping-lite' ) . '</th></tr></thead><tbody>';
     260            foreach ( $names as $name ) {
     261                echo '<tr><td><strong>' . esc_html( $name ) . '</strong></td></tr>';
     262            }
     263            echo '</tbody></table>';
     264        }
     265        echo '</div>';
    208266    }
    209267
    … …  
    354412        echo '<div class="es-panel es-ahqg-extra" style="margin-top:20px;">';
    355413        echo '<h2>Inferred Path <span class="es-ahqg-quad-count">' . absint( count( $inferred ) ) . ' ' . esc_html__( 'pages', 'edgeshaping-lite' ) . '</span></h2>';
    356         echo '<p class="es-ahqg-extra-desc">'. esc_html__( 'URLs visited by AI but not in sitemap', 'edgeshaping-lite' ) .'('. esc_html__( 'spam/scan paths excluded', 'edgeshaping-lite' ) .')</p>';
     414        echo '<p class="es-ahqg-extra-desc">'. esc_html__( 'URLs visited by AI that are not published pages', 'edgeshaping-lite' ) .'('. esc_html__( 'spam/scan paths excluded', 'edgeshaping-lite' ) .')</p>';
    357415        if ( empty( $inferred ) ) {
    358416            echo '<p class="es-empty">'. esc_html__( 'None', 'edgeshaping-lite' ) .'</p>';
    … …  
    465523        echo '<div class="es-panel es-ahqg-extra" style="margin-top:20px;">';
    466524        echo '<h2>Inferred Path <span class="es-ahqg-quad-count">' . absint( count( $inferred ) ) . ' ' . esc_html__( 'pages', 'edgeshaping-lite' ) . '</span></h2>';
    467         echo '<p class="es-ahqg-extra-desc">' . esc_html__( 'URLs visited by AI but not in sitemap', 'edgeshaping-lite' ) . '(' . esc_html__( 'spam/scan paths excluded', 'edgeshaping-lite' ) . ')</p>';
     525        echo '<p class="es-ahqg-extra-desc">' . esc_html__( 'URLs visited by AI that are not published pages', 'edgeshaping-lite' ) . '(' . esc_html__( 'spam/scan paths excluded', 'edgeshaping-lite' ) . ')</p>';
    468526        if ( empty( $inferred ) ) {
    469527            echo '<p class="es-empty">' . esc_html__( 'None', 'edgeshaping-lite' ) . '</p>';
  • edgeshaping-lite/trunk/edgeshaping.php

    r3735949 r3791852  
    44 * Plugin URI:  https://edgeshap.ing
    55 * Description: Observe which AI bots visit which pages and when. WordPress implementation of the EdgeShaping framework by mare interno.
    6  * Version:     2026.10.0
     6 * Version:     2026.11.0
    77 * Author:      mare interno
    88 * Author URI:  https://mareinterno.com
    99 * License:     GPL-2.0+
    1010 * Text Domain: edgeshaping-lite
    11  * Requires at least: 6.0
    12  * Requires PHP:      7.4
     11 * Requires at least: 6.9
     12 * Requires PHP:      8.2
    1313 */
    1414
    1515if ( ! defined( 'ABSPATH' ) ) exit;
    1616
    17 define( 'EDGESHAPING_VERSION', '2026.10.0' );
     17define( 'EDGESHAPING_VERSION', '2026.11.0' );
    1818define( 'EDGESHAPING_DIR', plugin_dir_path( __FILE__ ) );
    1919define( 'EDGESHAPING_URL', plugin_dir_url( __FILE__ ) );
    … …  
    4848
    4949add_action( 'init', [ 'EdgeShaping_Aggregator', 'maybe_migrate_categories' ], 1 );
     50add_action( 'init', [ 'EdgeShaping_Aggregator', 'maybe_add_is_page_column' ], 1 );
    5051add_action( 'init', [ 'EdgeShaping_Tracker', 'observe' ] );
    5152add_action( 'rest_api_init', [ 'EdgeShaping_Tracker', 'register_beacon_route' ] );
    … …  
    8485        }
    8586    }
     87
     88   
     89    EdgeShaping_Aggregator::maybe_prepare_path_hash();
     90
     91   
     92    EdgeShaping_Aggregator::maybe_restore_unique_key();
     93
     94    EdgeShaping_Aggregator::maybe_migrate_is_page();
    8695} );
    8796
    … …  
    101110    header( 'Pragma: no-cache' );
    102111
    103     // phpcs:disable WordPress.WP.AlternativeFunctions -- php://output requires direct file functions
     112   
     113    // phpcs:disable WordPress.WP.AlternativeFunctions
    104114    $out = fopen( 'php://output', 'w' );
    105115
  • edgeshaping-lite/trunk/includes/class-aggregator.php

    r3735949 r3791852  
    1818                        path_hash    CHAR(32)        NOT NULL,
    1919                        verified     TINYINT(1)      NOT NULL DEFAULT 0,
     20                        is_page      TINYINT(1)      NULL DEFAULT NULL,
    2021                        cnt          INT UNSIGNED    NOT NULL DEFAULT 0,
    2122                        PRIMARY KEY (id),
    2223                        UNIQUE KEY idx_unique  (agg_date, bot_name, path_hash, verified),
    2324                        INDEX idx_agg_date     (agg_date),
     25                        INDEX idx_path_hash    (path_hash),
    2426                        INDEX idx_bot_category (bot_category)
    2527                ) {$charset};";
    … …  
    2830
    2931               
    30                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     32                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    3133                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
    3234                return $exists === $table;
    … …  
    4648
    4749               
    48                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     50                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    4951                $has_column = $wpdb->get_var( $wpdb->prepare(
    5052                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
    … …  
    5254                ) );
    5355                if ( ! $has_column ) {
    54                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    55                         $wpdb->query( "ALTER TABLE {$table} ADD COLUMN verified TINYINT(1) NOT NULL DEFAULT 0 AFTER path_hash" );
    56                 }
    57 
    58                
    59                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    60                 $wpdb->query( "ALTER TABLE {$table} DROP INDEX idx_unique" );
    61                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    62                 $wpdb->query( "ALTER TABLE {$table} ADD UNIQUE KEY idx_unique (agg_date, bot_name, path_hash, verified)" );
     56                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     57                        $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD COLUMN verified TINYINT(1) NOT NULL DEFAULT 0 AFTER path_hash', $table ) );
     58                }
     59
     60               
     61                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     62                $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i DROP INDEX idx_unique', $table ) );
     63                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     64                $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD UNIQUE KEY idx_unique (agg_date, bot_name, path_hash, verified)', $table ) );
    6365
    6466               
    6567                if ( $boundary ) {
    66                         $sql = $wpdb->prepare( "UPDATE {$table} SET verified = 1 WHERE agg_date <= %s", $boundary ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    67                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    68                         $wpdb->query( $sql );
     68                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     69                        $wpdb->query( $wpdb->prepare( 'UPDATE %i SET verified = 1 WHERE agg_date <= %s', $table, $boundary ) );
    6970                }
    7071
    … …  
    7778                $table = $wpdb->prefix . self::TABLE;
    7879
    79                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    80                 $bot_names = $wpdb->get_col( "SELECT DISTINCT bot_name FROM {$table} WHERE bot_category = ''" );
     80                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     81                $bot_names = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT bot_name FROM %i WHERE bot_category = ''", $table ) );
    8182                if ( empty( $bot_names ) ) return;
    8283
    … …  
    8485                        $category = EdgeShaping_Bot_Dictionary::get_category( $bot_name );
    8586                        if ( '' === $category ) continue;
    86                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
     87                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    8788                        $wpdb->query( $wpdb->prepare(
    88                                 "UPDATE {$table} SET bot_category = %s WHERE bot_name = %s AND bot_category = ''", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    89                                 $category, $bot_name
     89                                "UPDATE %i SET bot_category = %s WHERE bot_name = %s AND bot_category = ''",
     90                                $table, $category, $bot_name
    9091                        ) );
    9192                }
    … …  
    102103
    103104               
    104                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     105                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    105106                $log_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $log_table ) );
    106                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     107                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    107108                $agg_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $agg_table ) );
    108109                if ( $log_exists !== $log_table || $agg_exists !== $agg_table ) return;
    … …  
    110111                EdgeShaping_Tracker::maybe_add_category_column();
    111112
    112                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     113                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    113114                $agg_has = $wpdb->get_var( $wpdb->prepare(
    114115                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
    … …  
    116117                ) );
    117118                if ( ! $agg_has ) {
    118                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    119                         $wpdb->query( "ALTER TABLE {$agg_table} ADD COLUMN bot_category VARCHAR(50) NOT NULL DEFAULT '' AFTER bot_name" );
    120                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    121                         $wpdb->query( "ALTER TABLE {$agg_table} ADD INDEX idx_bot_category (bot_category)" );
     119                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     120                        $wpdb->query( $wpdb->prepare( "ALTER TABLE %i ADD COLUMN bot_category VARCHAR(50) NOT NULL DEFAULT '' AFTER bot_name", $agg_table ) );
     121                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     122                        $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD INDEX idx_bot_category (bot_category)', $agg_table ) );
    122123                }
    123124
    … …  
    127128               
    128129                foreach ( [ $log_table, $agg_table ] as $check_table ) {
    129                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     130                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    130131                        $has = $wpdb->get_var( $wpdb->prepare(
    131132                                'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
    … …  
    137138               
    138139                foreach ( [ $log_table, $agg_table ] as $check_table ) {
    139                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    140                         $remaining = $wpdb->get_col( "SELECT DISTINCT bot_name FROM {$check_table} WHERE bot_category = ''" );
     140                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     141                        $remaining = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT bot_name FROM %i WHERE bot_category = ''", $check_table ) );
    141142                        if ( ! is_array( $remaining ) ) return;
    142143                        foreach ( $remaining as $bot_name ) {
    … …  
    149150
    150151       
     152        public static function maybe_add_is_page_column(): void {
     153                global $wpdb;
     154
     155                if ( get_option( 'edgeshaping_is_page_column_added' ) ) return;
     156
     157                $table = $wpdb->prefix . self::TABLE;
     158
     159                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     160                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     161                if ( $exists !== $table ) return;
     162
     163                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     164                $has = $wpdb->get_var( $wpdb->prepare(
     165                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
     166                        DB_NAME, $table, 'is_page'
     167                ) );
     168                if ( ! $has ) {
     169                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     170                        $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD COLUMN is_page TINYINT(1) NULL DEFAULT NULL AFTER verified', $table ) );
     171                }
     172
     173               
     174                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     175                $has = $wpdb->get_var( $wpdb->prepare(
     176                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
     177                        DB_NAME, $table, 'is_page'
     178                ) );
     179                if ( $has ) {
     180                        update_option( 'edgeshaping_is_page_column_added', 1 );
     181                }
     182        }
     183
     184       
     185        public static function is_published_page( string $path ): bool {
     186                static $memo = [];
     187
     188                if ( array_key_exists( $path, $memo ) ) return $memo[ $path ];
     189
     190                if ( '/' === $path ) {
     191                        $memo[ $path ] = true;
     192                        return true;
     193                }
     194
     195                $parts = wp_parse_url( home_url() );
     196                $base  = $parts['scheme'] . '://' . $parts['host'];
     197                if ( ! empty( $parts['port'] ) ) {
     198                        $base .= ':' . $parts['port'];
     199                }
     200
     201                $id = url_to_postid( $base . $path );
     202                if ( 0 === (int) $id ) {
     203                        $memo[ $path ] = false;
     204                        return false;
     205                }
     206
     207                $memo[ $path ] = ( 'publish' === get_post_status( $id ) );
     208                return $memo[ $path ];
     209        }
     210
     211       
     212        public static function maybe_prepare_path_hash(): void {
     213                global $wpdb;
     214
     215                if ( get_option( 'edgeshaping_path_hash_prepared' ) ) return;
     216
     217                $table = $wpdb->prefix . self::TABLE;
     218
     219                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     220                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     221                if ( $exists !== $table ) return;
     222
     223                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     224                $wpdb->query( $wpdb->prepare( "UPDATE %i SET path_hash = MD5(path) WHERE path_hash = ''", $table ) );
     225
     226                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     227                $has_index = $wpdb->get_var( $wpdb->prepare(
     228                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND INDEX_NAME = %s',
     229                        DB_NAME, $table, 'idx_path_hash'
     230                ) );
     231                if ( ! $has_index ) {
     232                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     233                        $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD INDEX idx_path_hash (path_hash)', $table ) );
     234                }
     235
     236               
     237                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     238                $empty = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE path_hash = ''", $table ) );
     239                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     240                $has_index = $wpdb->get_var( $wpdb->prepare(
     241                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND INDEX_NAME = %s',
     242                        DB_NAME, $table, 'idx_path_hash'
     243                ) );
     244                if ( null !== $empty && 0 === (int) $empty && $has_index ) {
     245                        update_option( 'edgeshaping_path_hash_prepared', 1 );
     246                }
     247        }
     248
     249       
     250        public static function maybe_restore_unique_key(): void {
     251                global $wpdb;
     252
     253                if ( get_option( 'edgeshaping_unique_key_restored' ) ) return;
     254
     255                $table = $wpdb->prefix . self::TABLE;
     256
     257                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     258                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     259                if ( $exists !== $table ) return;
     260
     261                if ( self::has_unique_key( $table ) ) {
     262                        update_option( 'edgeshaping_unique_key_restored', 1 );
     263                        return;
     264                }
     265
     266                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     267                $empty = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE path_hash = ''", $table ) );
     268                if ( null === $empty || 0 !== (int) $empty ) return;
     269
     270                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     271                $dup = $wpdb->get_var( $wpdb->prepare(
     272                        'SELECT 1 FROM %i GROUP BY agg_date, bot_name, path_hash, verified HAVING COUNT(*) > 1 LIMIT 1',
     273                        $table
     274                ) );
     275                if ( null !== $dup ) return;
     276
     277                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     278                $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD UNIQUE KEY idx_unique (agg_date, bot_name, path_hash, verified)', $table ) );
     279
     280               
     281                if ( self::has_unique_key( $table ) ) {
     282                        update_option( 'edgeshaping_unique_key_restored', 1 );
     283                }
     284        }
     285
     286       
     287        private static function has_unique_key( string $table ): bool {
     288                global $wpdb;
     289                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     290                $count = $wpdb->get_var( $wpdb->prepare(
     291                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND INDEX_NAME = %s',
     292                        DB_NAME, $table, 'idx_unique'
     293                ) );
     294                return (int) $count > 0;
     295        }
     296
     297       
     298        public static function maybe_migrate_is_page(): void {
     299                global $wpdb;
     300
     301                if ( get_option( 'edgeshaping_is_page_migrated' ) ) return;
     302
     303                $table = $wpdb->prefix . self::TABLE;
     304
     305                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     306                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     307                if ( $exists !== $table ) return;
     308
     309                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     310                $has = $wpdb->get_var( $wpdb->prepare(
     311                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
     312                        DB_NAME, $table, 'is_page'
     313                ) );
     314                if ( ! $has ) return;
     315
     316                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     317                $paths = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT path FROM %i WHERE is_page IS NULL LIMIT 500', $table ) );
     318                if ( is_array( $paths ) ) {
     319                        foreach ( $paths as $path ) {
     320                                $path  = (string) $path;
     321                                $value = self::is_published_page( $path ) ? 1 : 0;
     322                                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     323                                $wpdb->query( $wpdb->prepare(
     324                                        "UPDATE %i SET is_page = %d WHERE path_hash = %s AND is_page IS NULL",
     325                                        $table, $value, md5( $path )
     326                                ) );
     327                        }
     328                }
     329
     330                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     331                $remaining = $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE is_page IS NULL', $table ) );
     332                if ( null !== $remaining && 0 === (int) $remaining ) {
     333                        update_option( 'edgeshaping_is_page_migrated', 1 );
     334                }
     335        }
     336
     337       
    151338        public static function run_daily( ?string $date = null ): void {
    152339                global $wpdb;
    … …  
    156343                $agg_table = $wpdb->prefix . self::TABLE;
    157344
    158                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    159                 $wpdb->query( $wpdb->prepare( "DELETE FROM {$agg_table} WHERE agg_date = %s", $date ) );
    160 
    161                
    162                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     345                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     346                $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE agg_date = %s', $agg_table, $date ) );
     347
     348               
     349                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    163350                $rows = $wpdb->get_results( $wpdb->prepare(
    164                         "SELECT bot_name, bot_category, request_url, ip, COUNT(*) AS cnt FROM {$log_table} " // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     351                        'SELECT bot_name, bot_category, request_url, ip, COUNT(*) AS cnt FROM %i '
    165352                        . 'WHERE visited_at BETWEEN %s AND %s GROUP BY bot_name, bot_category, request_url, ip',
    166                         $date . ' 00:00:00', $date . ' 23:59:59'
     353                        $log_table, $date . ' 00:00:00', $date . ' 23:59:59'
    167354                ) );
    168355
    … …  
    242429                                        'path_hash'    => md5( $item['path'] ),
    243430                                        'verified'     => (int) $item['verified'],
     431                                        'is_page'      => self::is_published_page( $item['path'] ) ? 1 : 0,
    244432                                        'cnt'          => $item['cnt'],
    245433                                ],
    246                                 [ '%s', '%s', '%s', '%s', '%s', '%d', '%d' ]
     434                                [ '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d' ]
    247435                        );
    248436                }
    … …  
    265453                       
    266454                        global $wpdb;
    267                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    268                         $oldest = $wpdb->get_var( "SELECT DATE(MIN(visited_at)) FROM {$wpdb->prefix}edgeshaping_log" );
     455                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     456                        $oldest = $wpdb->get_var( $wpdb->prepare( 'SELECT DATE(MIN(visited_at)) FROM %i', $wpdb->prefix . 'edgeshaping_log' ) );
    269457                        if ( ! $oldest ) {
    270458                                update_option( 'edgeshaping_last_aggregated_date', $target );
    … …  
    302490       
    303491        public static function get_counts_by_path( array $bot_names, string $date_from, string $date_to ): array {
     492                return self::read_counts_by_path( $bot_names, $date_from, $date_to, 1 );
     493        }
     494
     495       
     496        public static function get_inferred_counts( array $bot_names, string $date_from, string $date_to ): array {
     497                return self::read_counts_by_path( $bot_names, $date_from, $date_to, 0 );
     498        }
     499
     500       
     501        private static function read_counts_by_path( array $bot_names, string $date_from, string $date_to, int $is_page ): array {
    304502                global $wpdb;
    305503                if ( empty( $bot_names ) ) return [];
    … …  
    308506                if ( $date_from > $date_to ) return [];
    309507
     508                $is_page      = $is_page ? 1 : 0;
    310509                $result       = [];
    311510                $table        = $wpdb->prefix . self::TABLE;
    312511                $placeholders = implode( ',', array_fill( 0, count( $bot_names ), '%s' ) );
    313                 $args         = array_merge( $bot_names, [ $date_from, $date_to ] );
    314                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    315                 $sql  = "SELECT path, SUM(cnt) AS total FROM {$table} WHERE bot_name IN ({$placeholders}) AND agg_date BETWEEN %s AND %s AND verified = 1 GROUP BY path";
     512                $args         = array_merge( [ $table ], $bot_names, [ $date_from, $date_to, $is_page ] );
     513                $sql  = "SELECT path, SUM(cnt) AS total FROM %i WHERE bot_name IN ({$placeholders}) AND agg_date BETWEEN %s AND %s AND verified = 1 AND is_page = %d GROUP BY path";
    316514                // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    317515                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    … …  
    340538                }
    341539
    342                 $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1' ];
    343                 $args  = [ $date_from, $date_to ];
     540                $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1', 'is_page = 1' ];
     541                $args  = [ $table, $date_from, $date_to ];
    344542
    345543                $cats = array_filter( array_map( 'trim', explode( ',', $category ) ) );
    … …  
    350548                $where_sql = implode( ' AND ', $where );
    351549
    352                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    353                 $sql  = "SELECT path, bot_name, SUM(cnt) AS total FROM {$table} WHERE {$where_sql} GROUP BY path, bot_name";
     550                $sql  = "SELECT path, bot_name, SUM(cnt) AS total FROM %i WHERE {$where_sql} GROUP BY path, bot_name";
    354551                // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    355552                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    … …  
    380577                if ( $date_from > $date_to ) return [];
    381578
    382                 $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1' ];
    383                 $args  = [ $date_from, $date_to ];
     579                $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1', 'is_page = 1' ];
     580                $args  = [ $table, $date_from, $date_to ];
    384581                $cats  = array_filter( array_map( 'trim', explode( ',', $category ) ) );
    385582                if ( $cats ) {
    … …  
    389586                $where_sql = implode( ' AND ', $where );
    390587
    391                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    392                 $sql  = "SELECT path, bot_name, bot_category, cnt, agg_date FROM {$table} WHERE {$where_sql}";
     588                $sql  = "SELECT path, bot_name, bot_category, cnt, agg_date FROM %i WHERE {$where_sql}";
    393589                // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    394590                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    … …  
    417613                if ( $date_from > $date_to ) return [];
    418614
    419                 $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1' ];
    420                 $args  = [ $date_from, $date_to ];
     615                $where = [ 'agg_date BETWEEN %s AND %s', 'verified = 1', 'is_page = 1' ];
     616                $args  = [ $table, $date_from, $date_to ];
    421617                $cats  = array_filter( array_map( 'trim', explode( ',', $category ) ) );
    422618                if ( $cats ) {
    … …  
    426622                $where_sql = implode( ' AND ', $where );
    427623
    428                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    429                 $sql  = "SELECT path, cnt, agg_date FROM {$table} WHERE {$where_sql}";
     624                $sql  = "SELECT path, cnt, agg_date FROM %i WHERE {$where_sql}";
    430625                // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    431626                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    … …  
    452647                if ( $date_from > $date_to ) return [];
    453648
    454                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    455                 $sql  = "SELECT path, bot_name, bot_category, cnt, agg_date FROM {$table} WHERE agg_date BETWEEN %s AND %s AND verified = 1";
    456                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared
    457                 $rows = $wpdb->get_results( $wpdb->prepare( $sql, $date_from, $date_to ) );
     649                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     650                $rows = $wpdb->get_results( $wpdb->prepare(
     651                        'SELECT path, bot_name, bot_category, cnt, agg_date FROM %i WHERE agg_date BETWEEN %s AND %s AND verified = 1 AND is_page = 1',
     652                        $table, $date_from, $date_to
     653                ) );
    458654
    459655                $agg = [];
    … …  
    485681                global $wpdb;
    486682                $table = $wpdb->prefix . self::TABLE;
    487                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    488                 $cats = $wpdb->get_col( "SELECT DISTINCT bot_category FROM {$table} WHERE bot_category != ''" );
     683                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     684                $cats = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT bot_category FROM %i WHERE bot_category != ''", $table ) );
    489685                sort( $cats );
    490686                return $cats;
    … …  
    496692                $table = $wpdb->prefix . self::TABLE;
    497693
    498                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     694                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    499695                $total = (int) $wpdb->get_var( $wpdb->prepare(
    500                         "SELECT COUNT(*) FROM {$table} WHERE agg_date BETWEEN %s AND %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    501                         $date_from, $date_to
    502                 ) );
    503 
    504                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     696                        "SELECT COUNT(*) FROM %i WHERE agg_date BETWEEN %s AND %s",
     697                        $table, $date_from, $date_to
     698                ) );
     699
     700                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    505701                $rows = $wpdb->get_results( $wpdb->prepare(
    506                         "SELECT agg_date, bot_name, path, verified, cnt FROM {$table} WHERE agg_date BETWEEN %s AND %s ORDER BY agg_date DESC, bot_name ASC, path ASC, verified ASC LIMIT %d OFFSET %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    507                         $date_from, $date_to, $limit, $offset
     702                        "SELECT agg_date, bot_name, path, verified, cnt FROM %i WHERE agg_date BETWEEN %s AND %s ORDER BY agg_date DESC, bot_name ASC, path ASC, verified ASC LIMIT %d OFFSET %d",
     703                        $table, $date_from, $date_to, $limit, $offset
    508704                ) );
    509705
    … …  
    527723                global $wpdb;
    528724                $table = $wpdb->prefix . self::TABLE;
    529                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    530                 $wpdb->query( "DROP TABLE IF EXISTS {$table}" );
     725                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     726                $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $table ) );
    531727                delete_option( 'edgeshaping_last_aggregated_date' );
    532728        }
  • edgeshaping-lite/trunk/includes/class-ahqg.php

    r3735949 r3791852  
    77                return EdgeShaping_Matrix::build(
    88                        self::get_ai_counts( $date_from, $date_to ),
    9                         EdgeShaping_GSC::get_clicks( $date_from, $date_to )
     9                        EdgeShaping_GSC::get_clicks( $date_from, $date_to ),
     10                        self::get_inferred_counts( $date_from, $date_to )
    1011                );
    1112        }
    … …  
    1516                $bots = EdgeShaping_Bot_Dictionary::get_ahqg_bots();
    1617                return EdgeShaping_Aggregator::get_counts_by_path( $bots, $date_from, $date_to );
     18        }
     19
     20       
     21        private static function get_inferred_counts( string $date_from, string $date_to ): array {
     22                $bots = EdgeShaping_Bot_Dictionary::get_ahqg_bots();
     23                return EdgeShaping_Aggregator::get_inferred_counts( $bots, $date_from, $date_to );
    1724        }
    1825
  • edgeshaping-lite/trunk/includes/class-ahtg.php

    r3735949 r3791852  
    77                return EdgeShaping_Matrix::build(
    88                        self::get_ai_counts( $date_from, $date_to ),
    9                         EdgeShaping_Tracker::get_human_pv( $date_from, $date_to )
     9                        EdgeShaping_Tracker::get_human_pv( $date_from, $date_to ),
     10                        self::get_inferred_counts( $date_from, $date_to )
    1011                );
    1112        }
    … …  
    1718        }
    1819
     20       
     21        private static function get_inferred_counts( string $date_from, string $date_to ): array {
     22                $bots = EdgeShaping_Bot_Dictionary::get_ahqg_bots();
     23                return EdgeShaping_Aggregator::get_inferred_counts( $bots, $date_from, $date_to );
     24        }
     25
    1926        public static function label( string $quad ): string {
    2027                return EdgeShaping_Matrix::label( $quad );
  • edgeshaping-lite/trunk/includes/class-bot-dictionary.php

    r3735949 r3791852  
    4545                        'DuckAssistBot' => [ 'company' => 'DuckDuckGo', 'name' => 'DuckAssist Bot', 'category' => 'search_rag', 'tier' => self::TIER_OFFICIAL ],
    4646
     47                        'ElevenlabsBot' => [ 'company' => 'ElevenLabs', 'name' => 'ElevenLabs Bot', 'category' => 'user_trigger', 'tier' => self::TIER_OFFICIAL ],
     48                        'ElevenlabsCrawler' => [ 'company' => 'ElevenLabs', 'name' => 'ElevenLabs Crawler', 'category' => 'learning', 'tier' => self::TIER_OFFICIAL ],
     49
    4750                        'ExaSearchBot' => [ 'company' => 'Exa', 'name' => 'Exa Search Bot', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
     51                        'ExaUser' => [ 'company' => 'Exa', 'name' => 'Exa User', 'category' => 'user_trigger', 'tier' => self::TIER_OFFICIAL ],
    4852
    4953                        'AdsBot-Google' => [ 'company' => 'Google', 'name' => 'Google Ads Bot', 'category' => 'ads', 'tier' => self::TIER_OFFICIAL ],
    … …  
    5963
    6064                        'IbouBot' => [ 'company' => 'Ibou', 'name' => 'Ibou AI Search', 'category' => 'search_rag', 'tier' => self::TIER_OFFICIAL ],
     65                        'ibou-discovery' => [ 'company' => 'Ibou', 'name' => 'Ibou Discovery', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
     66                        'ibou-images' => [ 'company' => 'Ibou', 'name' => 'Ibou Images', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
     67                        'ibou-news' => [ 'company' => 'Ibou', 'name' => 'Ibou News', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
     68                        'ibou-search' => [ 'company' => 'Ibou', 'name' => 'Ibou Search', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
     69
     70                        'KeenableBot' => [ 'company' => 'Keenable.ai, Inc.', 'name' => 'Keenable Bot', 'category' => 'search_rag', 'tier' => self::TIER_OFFICIAL ],
    6171
    6272                        'LinerBot' => [ 'company' => 'Liner', 'name' => 'Liner AI', 'category' => 'crawl', 'tier' => self::TIER_OFFICIAL ],
  • edgeshaping-lite/trunk/includes/class-bot-ipranges.php

    r3735949 r3791852  
    2929                'IbouBot'                  => 'ibou_bot',
    3030                'IsonSearchBot'            => 'ison_searchbot',
     31                'KeenableBot'              => 'keenable_bot',
    3132                'Kimi-SearchBot'           => 'kimi_searchbot',
    3233                'Kimi-User'                => 'kimi_user',
    3334                'KimiBot'                  => 'kimi_bot',
    34                 'LinerBot'                 => 'liner_bot',
    3535                'LohiSoftBot'              => 'lohisoft_bot',
    3636                'MistralAI-Index'          => 'mistral_index',
    … …  
    4646                'bingbot'                  => 'bing_bingbot',
    4747                'facebookexternalhit'      => 'meta_asn',
     48                'ibou-discovery'           => 'ibou_media',
     49                'ibou-news'                => 'ibou_media',
     50                'ibou-search'              => 'ibou_search',
    4851                'meta-externalads'         => 'meta_asn',
    4952                'meta-externalagent'       => 'meta_asn',
    … …  
    31923195                ],
    31933196                'anthropic_all' => [
    3194                         [272243269, 272243269],
    3195                         [316796644, 316796644],
    3196                         [316924671, 316924671],
    3197                         [339753424, 339753439],
    3198                         [342240992, 342241007],
    3199                         [571154975, 571154975],
    3200                         [576040098, 576040098],
    3201                         [580317519, 580317519],
     3197                        [580418700, 580418700],
     3198                        [580457918, 580457918],
     3199                        [581049122, 581049122],
     3200                        [581064294, 581064294],
     3201                        [581065318, 581065318],
     3202                        [581070670, 581070670],
     3203                        [581081415, 581081415],
    32023204                        [581091153, 581091153],
    32033205                        [581101278, 581101278],
    32043206                        [581104711, 581104711],
    3205                         [582388831, 582388831],
    3206                         [582394255, 582394255],
    3207                         [582408731, 582408731],
    3208                         [582409301, 582409301],
    3209                         [582409765, 582409765],
    3210                         [582410663, 582410663],
    3211                         [582410903, 582410903],
    3212                         [582410973, 582410973],
    3213                         [582642851, 582642851],
    3214                         [601693614, 601693614],
    3215                         [603281903, 603281903],
    3216                         [603303809, 603303809],
    3217                         [679241008, 679241023],
    3218                         [2288758992, 2288758992],
     3207                        [582679556, 582679556],
     3208                        [2286915579, 2286915579],
     3209                        [2287146199, 2287146199],
     3210                        [2570964992, 2570969087],
     3211                        [2689558528, 2689560575],
    32193212                        [3628718080, 3628719103],
    32203213                ],
    … …  
    38033796                        [578031872, 578032031],
    38043797                        [578032128, 578032351],
    3805                         [578032384, 578032735],
    3806                         [578032896, 578033279],
     3798                        [578032384, 578032767],
     3799                        [578032896, 578033311],
    38073800                        [578033408, 578033535],
    38083801                        [578034688, 578035071],
    … …  
    38443837                        [1806879616, 1806879743],
    38453838                        [1806884864, 1806885375],
     3839                        [2289862656, 2289862751],
    38463840                ],
    38473841                'google_user_triggered_google' => [
    … …  
    38563850                        [1249760768, 1249761279],
    38573851                        [2398756864, 2398757279],
    3858                         [2902307840, 2902307903],
     3852                        [2902307840, 2902307935],
    38593853                        [2902308096, 2902308511],
    38603854                        [2902308608, 2902308863],
    38613855                        [3232892928, 3232893087],
    3862                         [3232893184, 3232893567],
     3856                        [3232893184, 3232893599],
    38633857                        [3232893696, 3232894175],
    38643858                        [3232894208, 3232894495],
    … …  
    38683862                        [3648111616, 3648111871],
    38693863                ],
     3864                'ibou_media' => [
     3865                        [2746018592, 2746018623],
     3866                ],
     3867                'ibou_search' => [
     3868                        [3169920512, 3169920767],
     3869                ],
    38703870                'ison_searchbot' => [
    38713871                        [1735987676, 1735987676],
    3872                         [1738306407, 1738306407],
     3872                        [1737934249, 1737934249],
     3873                ],
     3874                'keenable_bot' => [
     3875                        [571500091, 571500091],
     3876                        [573234952, 573234952],
     3877                        [574104761, 574104761],
     3878                        [574153928, 574153928],
     3879                        [574157134, 574157134],
     3880                        [574851913, 574851913],
     3881                        [578540162, 578540162],
     3882                        [578550513, 578550513],
     3883                        [579108881, 579108881],
     3884                        [579224957, 579224957],
     3885                        [599541115, 599541115],
     3886                        [599547018, 599547018],
     3887                        [599579798, 599579798],
     3888                        [599926666, 599926666],
     3889                        [600442542, 600442542],
     3890                        [600445005, 600445005],
     3891                        [600448528, 600448528],
     3892                        [601812747, 601812747],
     3893                        [602417899, 602417899],
     3894                        [603919901, 603919901],
     3895                        [603949630, 603949630],
     3896                        [1754986078, 1754986078],
     3897                        [1757769726, 1757769726],
     3898                        [1757860114, 1757860114],
     3899                        [2194896541, 2194896541],
     3900                        [2286012356, 2286012356],
     3901                        [2286355477, 2286355477],
     3902                        [2286389324, 2286389324],
     3903                        [2289167190, 2289167190],
     3904                        [2289347745, 2289347745],
    38733905                ],
    38743906                'kimi_bot' => [
    … …  
    38893921                        [793771306, 793771306],
    38903922                        [793771355, 793771355],
    3891                 ],
    3892                 'liner_bot' => [
    3893                         [571489880, 571489880],
    3894                         [572331825, 572331825],
    3895                         [572368558, 572368558],
    3896                         [572389706, 572389706],
    3897                         [574751062, 574751062],
    3898                         [578358084, 578358084],
    3899                         [579261752, 579261752],
    3900                         [581683130, 581683130],
    3901                         [601933340, 601933340],
    3902                         [602047433, 602047433],
    39033923                ],
    39043924                'lohisoft_bot' => [
    … …  
    39343954                        [520962048, 520978431],
    39353955                        [759179264, 759180287],
    3936                         [965541888, 965548543],
     3956                        [965541888, 965549567],
    39373957                        [965738496, 966000639],
    39383958                        [1121751040, 1121755135],
    … …  
    39814001                        [81971216, 81971231],
    39824002                        [81977888, 81977903],
    3983                         [159449088, 159481855],
     4003                        [159449088, 159514623],
    39844004                        [161489552, 161489567],
    39854005                        [161489936, 161489951],
    … …  
    40334053                        [349070784, 349070799],
    40344054                        [349361088, 349361103],
     4055                        [349454032, 349454047],
    40354056                        [349682640, 349682655],
    40364057                        [349690752, 349690767],
    … …  
    40394060                        [349839088, 349839103],
    40404061                        [349915072, 349915087],
     4062                        [349938112, 349938127],
    40414063                        [349943904, 349943919],
    40424064                        [350102720, 350102735],
    … …  
    40474069                        [351929984, 351929999],
    40484070                        [351963200, 351963215],
     4071                        [352242128, 352242143],
    40494072                        [392335024, 392335039],
    40504073                        [392346176, 392346191],
    … …  
    40594082                        [676650448, 676650479],
    40604083                        [678709712, 678709727],
     4084                        [678896880, 678896895],
    40614085                        [679256320, 679256335],
    40624086                        [819800240, 819800255],
    … …  
    40884112                        [884903488, 884903503],
    40894113                        [884915728, 884915743],
     4114                        [884931440, 884931455],
    40904115                        [887180240, 887180255],
    40914116                        [887561776, 887561791],
    … …  
    41354160                        [2279360976, 2279360991],
    41364161                        [2279361008, 2279361023],
     4162                        [2279498528, 2279498543],
    41374163                        [2280489936, 2280489951],
    41384164                        [2280490288, 2280490303],
    … …  
    41524178                        [2897711072, 2897711087],
    41534179                        [2897731200, 2897731215],
    4154                         [2897854896, 2897854911],
    41554180                        [2898292944, 2898292959],
    41564181                        [2898632896, 2898632911],
    … …  
    42374262                ],
    42384263                'perplexity_bot' => [
    4239                         [64191671, 64191671],
    42404264                        [64940271, 64940271],
    42414265                        [65027629, 65027629],
    4242                         [65506155, 65506155],
    4243                         [308347364, 308347367],
     4266                        [308347364, 308347364],
    42444267                        [308349280, 308349287],
    42454268                        [315776235, 315776235],
    … …  
    42474270                ],
    42484271                'perplexity_user' => [
    4249                         [308352256, 308352259],
    42504272                        [308357968, 308357975],
    42514273                        [583115572, 583115572],
    … …  
    43354357                        ["\x26\x00\x19\x00\x00\x00\x02\x60\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\x64\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43364358                        ["\x26\x00\x19\x00\x00\x00\x02\x70\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\x76\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    4337                         ["\x26\x00\x19\x00\x00\x00\x02\x80\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\x82\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4359                        ["\x26\x00\x19\x00\x00\x00\x02\x80\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\x83\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43384360                        ["\x26\x00\x19\x00\x00\x00\x02\x90\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\x97\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    4339                         ["\x26\x00\x19\x00\x00\x00\x02\xA0\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\xA3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4361                        ["\x26\x00\x19\x00\x00\x00\x02\xA0\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\xA4\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43404362                        ["\x26\x00\x19\x00\x00\x00\x02\xB0\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\xB3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43414363                        ["\x26\x00\x19\x00\x00\x00\x02\xC0\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x02\xC6\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    … …  
    43574379                        ["\x26\x00\x19\x00\x00\x00\x03\xC3\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x03\xC4\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43584380                        ["\x26\x00\x19\x00\x00\x00\x03\xD0\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x03\xD7\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4381                        ["\x26\x00\x19\x00\x00\x00\x04\x10\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x19\x00\x00\x00\x04\x12\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43594382                ],
    43604383                'google_user_triggered_google' => [
    … …  
    43654388                        ["\x20\x01\x48\x60\x48\x01\x40\x1A\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x1C\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43664389                        ["\x20\x01\x48\x60\x48\x01\x40\x20\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x2B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    4367                         ["\x20\x01\x48\x60\x48\x01\x40\x30\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x3E\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    4368                         ["\x20\x01\x48\x60\x48\x01\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x6E\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4390                        ["\x20\x01\x48\x60\x48\x01\x40\x30\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x6E\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43694391                        ["\x20\x01\x48\x60\x48\x01\x40\x70\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x8B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43704392                        ["\x20\x01\x48\x60\x48\x01\x40\x90\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\x96\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    … …  
    43744396                        ["\x20\x01\x48\x60\x48\x01\x40\xD0\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x40\xE3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43754397                        ["\x20\x01\x48\x60\x48\x01\x40\xF0\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x07\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    4376                         ["\x20\x01\x48\x60\x48\x01\x41\x10\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x13\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4398                        ["\x20\x01\x48\x60\x48\x01\x41\x10\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x14\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43774399                        ["\x20\x01\x48\x60\x48\x01\x41\x20\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x36\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43784400                        ["\x20\x01\x48\x60\x48\x01\x41\x40\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x47\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43794401                        ["\x20\x01\x48\x60\x48\x01\x41\x50\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x5E\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43804402                        ["\x20\x01\x48\x60\x48\x01\x41\x60\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x48\x60\x48\x01\x41\x6B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     4403                ],
     4404                'ibou_search' => [
     4405                        ["\x2A\x0E\x62\x40\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x0E\x62\x40\xE0\x0F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    43814406                ],
    43824407                'meta_asn' => [
  • edgeshaping-lite/trunk/includes/class-cache-bypass.php

    r3735949 r3791852  
    3636                if ( ! self::should_bypass() ) return;
    3737                if ( ! defined( 'DONOTCACHEPAGE' ) ) {
    38                         // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- 複数のキャッシュプラグイン(W3TC/WP Super Cache等)が共通で認識するサードパーティ規約上の定数名。接頭辞を付けると認識されなくなる。
     38                       
     39                        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
    3940                        define( 'DONOTCACHEPAGE', true );
    4041                }
  • edgeshaping-lite/trunk/includes/class-cdn-edges.php

    r3735949 r3791852  
    1616                        [306446336, 306511871],
    1717                        [409894912, 409927679],
     18                        [860492800, 860493567],
    1819                        [875429888, 875446271],
    1920                        [877821952, 877822463],
    … …  
    5758        const RANGES_V6 = [
    5859                'cloudfront' => [
     60                        ["\x20\x01\x3F\xC6\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x3F\xC6\x00\x05\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     61                        ["\x20\x01\x3F\xC6\x00\x3C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x20\x01\x3F\xC6\x00\x3D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     62                        ["\x24\x06\xDA\x10\x84\x7F\xA1\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x10\x84\x7F\xA1\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     63                        ["\x24\x06\xDA\x11\x04\x38\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x11\x04\x38\x23\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     64                        ["\x24\x06\xDA\x12\x00\x4B\xC2\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x12\x00\x4B\xC2\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     65                        ["\x24\x06\xDA\x12\x8B\x2E\x9C\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x12\x8B\x2E\x9C\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     66                        ["\x24\x06\xDA\x14\x17\xBD\x2F\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x14\x17\xBD\x2F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     67                        ["\x24\x06\xDA\x14\x80\xBB\xEA\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x14\x80\xBB\xEA\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     68                        ["\x24\x06\xDA\x16\x0C\x01\xC2\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x16\x0C\x01\xC2\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     69                        ["\x24\x06\xDA\x18\x09\xFA\x1B\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x18\x09\xFA\x1B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     70                        ["\x24\x06\xDA\x19\x0E\x19\x4A\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x19\x0E\x19\x4A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     71                        ["\x24\x06\xDA\x1A\x06\xDF\x6C\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1A\x06\xDF\x6C\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     72                        ["\x24\x06\xDA\x1B\x0E\x7C\xAD\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1B\x0E\x7C\xAD\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     73                        ["\x24\x06\xDA\x1C\x07\x87\x2B\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1C\x07\x87\x2B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     74                        ["\x24\x06\xDA\x1C\x8D\x8C\x46\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1C\x8D\x8C\x46\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     75                        ["\x24\x06\xDA\x1E\x07\x05\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1E\x07\x05\x16\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     76                        ["\x24\x06\xDA\x1F\x03\x96\x91\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x24\x06\xDA\x1F\x03\x96\x91\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     77                        ["\x26\x00\x1F\x11\x0E\x79\xA8\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x11\x0E\x79\xA8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     78                        ["\x26\x00\x1F\x13\x04\x17\x4D\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x13\x04\x17\x4D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     79                        ["\x26\x00\x1F\x16\x19\x23\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x16\x19\x23\x30\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     80                        ["\x26\x00\x1F\x17\x43\x56\xF1\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x17\x43\x56\xF1\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     81                        ["\x26\x00\x1F\x18\x75\x30\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x18\x75\x30\x72\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     82                        ["\x26\x00\x1F\x1A\x45\x68\xB5\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x1A\x45\x68\xB5\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     83                        ["\x26\x00\x1F\x1C\x00\xDA\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x1C\x00\xDA\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     84                        ["\x26\x00\x1F\x1E\x00\xA7\x23\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x1F\x1E\x00\xA7\x23\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    5985                        ["\x26\x00\x90\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x90\x00\x1F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    6086                        ["\x26\x00\x90\x00\x52\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x90\x00\x52\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    6187                        ["\x26\x00\x90\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x26\x00\x90\x00\x6F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     88                        ["\x2A\x05\xD0\x11\x05\x31\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x11\x05\x31\x18\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     89                        ["\x2A\x05\xD0\x12\x05\x81\xB4\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x12\x05\x81\xB4\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     90                        ["\x2A\x05\xD0\x14\x13\x62\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x14\x13\x62\xB0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     91                        ["\x2A\x05\xD0\x16\x09\xED\xDE\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x16\x09\xED\xDE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     92                        ["\x2A\x05\xD0\x18\x1A\x3A\x2D\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x18\x1A\x3A\x2D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     93                        ["\x2A\x05\xD0\x19\x08\x0B\xE3\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x19\x08\x0B\xE3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     94                        ["\x2A\x05\xD0\x1A\x08\xA9\xBE\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x1A\x08\xA9\xBE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     95                        ["\x2A\x05\xD0\x1C\x03\x43\xC3\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x1C\x03\x43\xC3\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
     96                        ["\x2A\x05\xD0\x25\x0E\x59\xFB\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x2A\x05\xD0\x25\x0E\x59\xFB\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"],
    6297                ],
    6398                'cloudflare' => [
  • edgeshaping-lite/trunk/includes/class-gsc.php

    r3622188 r3791852  
    3737
    3838       
    39         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     39        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    4040        $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
    4141        return $exists === $table;
    … …  
    5555        global $wpdb;
    5656        $table = $wpdb->prefix . self::TABLE;
    57         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    58         $wpdb->query( "DROP TABLE IF EXISTS {$table}" );
     57        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     58        $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $table ) );
    5959        delete_option( self::OPTION_LAST_INGESTED );
    6060    }
    … …  
    174174        global $wpdb;
    175175        $table = $wpdb->prefix . self::TABLE;
    176         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     176        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    177177        $rows = $wpdb->get_results( $wpdb->prepare(
    178             "SELECT path, SUM(clicks) AS total FROM {$table} WHERE gsc_date BETWEEN %s AND %s GROUP BY path", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    179             $date_from, $date_to
     178            "SELECT path, SUM(clicks) AS total FROM %i WHERE gsc_date BETWEEN %s AND %s GROUP BY path",
     179            $table, $date_from, $date_to
    180180        ) );
    181181
    … …  
    247247        global $wpdb;
    248248        $table = $wpdb->prefix . 'edgeshaping_daily_stats';
    249         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    250         $oldest = $wpdb->get_var( "SELECT MIN(agg_date) FROM {$table}" );
     249        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     250        $oldest = $wpdb->get_var( $wpdb->prepare( 'SELECT MIN(agg_date) FROM %i', $table ) );
    251251        $limit  = gmdate( 'Y-m-d', strtotime( '-16 months', current_time( 'timestamp' ) ) );
    252252
    … …  
    265265        $table = $wpdb->prefix . self::TABLE;
    266266
    267         // phpcs:ignore WordPress.DB.DirectDatabaseQuery,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    268         $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE gsc_date = %s", $date ) );
     267        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     268        $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE gsc_date = %s', $table, $date ) );
    269269
    270270        foreach ( $rows as $path => $clicks ) {
  • edgeshaping-lite/trunk/includes/class-matrix.php

    r3682914 r3791852  
    55
    66       
    7         public static function build( array $ai_counts, array $human_counts ): array {
     7        public static function build( array $ai_counts, array $human_counts, array $inferred_counts = [] ): array {
    88                $sitemap_urls = EdgeShaping_Sitemap::get_urls();
    99
    1010                $all_urls = array_unique( array_merge( array_keys( $ai_counts ), array_keys( $human_counts ) ) );
    1111
    12                 if ( empty( $all_urls ) ) {
     12                if ( empty( $all_urls ) && empty( $inferred_counts ) ) {
    1313                        return [ 'matrix' => [], 'threshold_ai' => 0, 'threshold_human' => 0, 'inferred' => [], 'dark' => [] ];
    1414                }
    … …  
    2020                $inferred = [];
    2121
     22               
     23                $inferred_paths = [];
     24                foreach ( $inferred_counts as $url => $ai ) {
     25                        $url = (string) $url;
     26                        if ( $ai <= 0 ) continue;
     27                        if ( self::is_spam_path( $url ) || EdgeShaping_Tracker::is_excluded_path( $url ) ) {
     28                                continue;
     29                        }
     30                        $inferred[]              = [ 'url' => $url, 'ai' => (int) $ai, 'human' => $human_counts[ $url ] ?? 0 ];
     31                        $inferred_paths[ $url ] = true;
     32                }
     33
     34               
    2235                foreach ( $all_urls as $url ) {
     36                        if ( isset( $inferred_paths[ $url ] ) ) continue;
     37
    2338                        $ai    = $ai_counts[ $url ] ?? 0;
    2439                        $human = $human_counts[ $url ] ?? 0;
    25 
    26                        
    27                        
    28                         if ( ! empty( $sitemap_urls ) && $ai > 0 && ! EdgeShaping_Sitemap::has_path( $url, $sitemap_urls ) ) {
    29                                 if ( self::is_spam_path( $url ) || EdgeShaping_Tracker::is_excluded_path( $url ) ) {
    30                                         continue;
    31                                 }
    32                                 $inferred[] = [ 'url' => $url, 'ai' => $ai, 'human' => $human ];
    33                                 continue;
    34                         }
    3540
    3641                        $quad     = self::classify( $ai, $human, $threshold_ai, $threshold_human );
    … …  
    4146                $dark = [];
    4247                if ( ! empty( $sitemap_urls ) ) {
    43                         $observed = array_map( function( $p ) { return rtrim( $p, '/' ); }, $all_urls );
     48                        $observed = array_map( function( $p ) { return rtrim( (string) $p, '/' ); }, array_merge( $all_urls, array_keys( $inferred_paths ) ) );
    4449                        foreach ( $sitemap_urls as $sitemap_path ) {
    4550                                $sitemap_path_trimmed = rtrim( $sitemap_path, '/' );
  • edgeshaping-lite/trunk/includes/class-tracker.php

    r3735949 r3791852  
    4747
    4848               
    49                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     49                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    5050                $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
    5151                return $exists === $table;
    … …  
    6262                ) );
    6363                if ( ! $exists ) {
    64                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    65                         $wpdb->query( "ALTER TABLE {$table} ADD COLUMN bot_category VARCHAR(50) NOT NULL DEFAULT '' AFTER bot_name" );
    66                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    67                         $wpdb->query( "ALTER TABLE {$table} ADD INDEX idx_bot_category (bot_category)" );
     64                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
     65                        $wpdb->query( $wpdb->prepare( "ALTER TABLE %i ADD COLUMN bot_category VARCHAR(50) NOT NULL DEFAULT '' AFTER bot_name", $table ) );
     66                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
     67                        $wpdb->query( $wpdb->prepare( 'ALTER TABLE %i ADD INDEX idx_bot_category (bot_category)', $table ) );
    6868                }
    6969        }
    … …  
    7777
    7878                $table = $wpdb->prefix . self::TABLE;
    79                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     79                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    8080                $exists = $wpdb->get_var( $wpdb->prepare(
    8181                        'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
    … …  
    8888                }
    8989
    90                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    91                 $wpdb->query( "ALTER TABLE {$table} ADD COLUMN ip VARCHAR(45) NOT NULL DEFAULT '' AFTER request_url" );
     90                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
     91                $wpdb->query( $wpdb->prepare( "ALTER TABLE %i ADD COLUMN ip VARCHAR(45) NOT NULL DEFAULT '' AFTER request_url", $table ) );
    9292
    9393               
    … …  
    108108                $jp_map = EdgeShaping_Bot_Dictionary::jp_to_key();
    109109                foreach ( $jp_map as $jp => $en ) {
    110                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     110                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    111111                        $wpdb->query( $wpdb->prepare(
    112                                 "UPDATE {$table} SET bot_category = %s WHERE bot_category = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    113                                 $en, $jp
     112                                "UPDATE %i SET bot_category = %s WHERE bot_category = %s",
     113                                $table, $en, $jp
    114114                        ) );
    115115                }
    116116
    117117               
    118                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    119                 $bot_names = $wpdb->get_col( "SELECT DISTINCT bot_name FROM {$table} WHERE bot_category = ''" );
     118                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     119                $bot_names = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT bot_name FROM %i WHERE bot_category = ''", $table ) );
    120120                if ( empty( $bot_names ) ) return;
    121121
    … …  
    123123                        $category = EdgeShaping_Bot_Dictionary::get_category( $bot_name );
    124124                        if ( '' === $category ) continue;
    125                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
     125                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    126126                        $wpdb->query( $wpdb->prepare(
    127                                 "UPDATE {$table} SET bot_category = %s WHERE bot_name = %s AND bot_category = ''", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    128                                 $category, $bot_name
     127                                "UPDATE %i SET bot_category = %s WHERE bot_name = %s AND bot_category = ''",
     128                                $table, $category, $bot_name
    129129                        ) );
    130130                }
    … …  
    169169                $today = current_time( 'Y-m-d' );
    170170
    171                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     171                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    172172                $wpdb->query( $wpdb->prepare(
    173                         "INSERT INTO {$table} (path, path_hash, pv_date, cnt) VALUES (%s, %s, %s, 1) " // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     173                        "INSERT INTO %i (path, path_hash, pv_date, cnt) VALUES (%s, %s, %s, 1) "
    174174                        . "ON DUPLICATE KEY UPDATE cnt = cnt + 1",
    175                         $path, md5( $path ), $today
     175                        $table, $path, md5( $path ), $today
    176176                ) );
    177177
    … …  
    231231                global $wpdb;
    232232                $table = $wpdb->prefix . self::HUMAN_PV_TABLE;
    233                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     233                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    234234                $rows = $wpdb->get_results( $wpdb->prepare(
    235                         "SELECT path, SUM(cnt) AS total FROM {$table} WHERE pv_date BETWEEN %s AND %s GROUP BY path", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    236                         $date_from, $date_to
     235                        "SELECT path, SUM(cnt) AS total FROM %i WHERE pv_date BETWEEN %s AND %s GROUP BY path",
     236                        $table, $date_from, $date_to
    237237                ) );
    238238
    … …  
    268268                global $wpdb;
    269269                $table = $wpdb->prefix . self::TABLE;
    270                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    271                 $wpdb->query( "DROP TABLE IF EXISTS {$table}" );
     270                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
     271                $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $table ) );
    272272                $human_table = $wpdb->prefix . self::HUMAN_PV_TABLE;
    273                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    274                 $wpdb->query( "DROP TABLE IF EXISTS {$human_table}" );
     273                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.SchemaChange
     274                $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $human_table ) );
    275275                delete_option( 'edgeshaping_db_version' );
    276276        }
    … …  
    289289                $table = $wpdb->prefix . self::TABLE;
    290290                $days  = defined( 'EDGESHAPING_RETENTION_DAYS' ) ? (int) EDGESHAPING_RETENTION_DAYS : 120;
    291                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    292                 $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE visited_at < DATE_SUB(NOW(), INTERVAL %d DAY)", $days ) );
     291                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     292                $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE visited_at < DATE_SUB(NOW(), INTERVAL %d DAY)', $table, $days ) );
    293293
    294294                $max = defined( 'EDGESHAPING_MAX_ROWS' ) ? (int) EDGESHAPING_MAX_ROWS : 20000;
    295                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    296                 $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
     295                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     296                $count = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i', $table ) );
    297297                if ( $count > $max ) {
    298                         // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    299                         $wpdb->query( $wpdb->prepare( "DELETE FROM {$table} ORDER BY visited_at ASC LIMIT %d", $count - $max ) );
     298                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     299                        $wpdb->query( $wpdb->prepare( 'DELETE FROM %i ORDER BY visited_at ASC LIMIT %d', $table, $count - $max ) );
    300300                }
    301301
    302302                $human_table = $wpdb->prefix . self::HUMAN_PV_TABLE;
    303                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    304                 $wpdb->query( $wpdb->prepare( "DELETE FROM {$human_table} WHERE pv_date < DATE_SUB(CURDATE(), INTERVAL %d DAY)", $days ) );
     303                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     304                $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE pv_date < DATE_SUB(CURDATE(), INTERVAL %d DAY)', $human_table, $days ) );
    305305        }
    306306
    … …  
    366366                update_option( 'edgeshaping_ip_disabled', 1 );
    367367                $table = $wpdb->prefix . self::TABLE;
    368                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    369                 $wpdb->query( "UPDATE {$table} SET ip = '' WHERE ip <> ''" );
    370                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    371                 $remaining = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE ip <> ''" );
     368                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     369                $wpdb->query( $wpdb->prepare( "UPDATE %i SET ip = '' WHERE ip <> ''", $table ) );
     370                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     371                $remaining = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE ip <> ''", $table ) );
    372372                return 0 === $remaining;
    373373        }
    … …  
    442442                global $wpdb;
    443443                $table      = $wpdb->prefix . self::TABLE;
    444                 $conditions = self::build_conditions( $date_from, $date_to, $category );
    445                 $where      = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
    446                 return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} {$where} ORDER BY visited_at DESC, id DESC LIMIT %d OFFSET %d", $per_page, $offset ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
     444                [ $where, $args ] = self::build_conditions( $date_from, $date_to, $category );
     445                $sql  = "SELECT * FROM %i {$where} ORDER BY visited_at DESC, id DESC LIMIT %d OFFSET %d";
     446                $args = array_merge( [ $table ], $args, [ $per_page, $offset ] );
     447                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
     448                return $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    447449        }
    448450
    … …  
    450452                global $wpdb;
    451453                $table      = $wpdb->prefix . self::TABLE;
    452                 $conditions = self::build_conditions( $date_from, $date_to, $category );
    453                 $where      = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
    454                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    455                 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table} {$where}" );
     454                [ $where, $args ] = self::build_conditions( $date_from, $date_to, $category );
     455                $sql  = "SELECT COUNT(*) FROM %i {$where}";
     456                $args = array_merge( [ $table ], $args );
     457                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
     458                return (int) $wpdb->get_var( $wpdb->prepare( $sql, ...$args ) );
     459        }
     460
     461       
     462        public static function get_bot_names( string $date_from, string $date_to ): array {
     463                global $wpdb;
     464                $table = $wpdb->prefix . self::TABLE;
     465                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     466                $names = $wpdb->get_col( $wpdb->prepare(
     467                        "SELECT DISTINCT bot_name FROM %i WHERE visited_at BETWEEN %s AND %s ORDER BY bot_name ASC",
     468                        $table, $date_from . ' 00:00:00',
     469                        $date_to . ' 23:59:59'
     470                ) );
     471                return is_array( $names ) ? array_map( 'strval', $names ) : [];
    456472        }
    457473
    … …  
    460476                global $wpdb;
    461477                $table      = $wpdb->prefix . self::TABLE;
    462                 $conditions = self::build_conditions( $date_from, $date_to, $category );
    463                 $where      = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
    464                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    465                 $rows = $wpdb->get_results( "SELECT bot_name, bot_category, request_url, visited_at FROM {$table} {$where}" );
     478                [ $where, $args ] = self::build_conditions( $date_from, $date_to, $category );
     479                $sql  = "SELECT bot_name, bot_category, request_url, visited_at FROM %i {$where}";
     480                $args = array_merge( [ $table ], $args );
     481                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
     482                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    466483
    467484               
    … …  
    495512                global $wpdb;
    496513                $table = $wpdb->prefix . self::TABLE;
    497                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    498                 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
     514                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
     515                return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i', $table ) );
    499516        }
    500517
    … …  
    503520                global $wpdb;
    504521                $table      = $wpdb->prefix . self::TABLE;
    505                 $conditions = self::build_conditions( $date_from, $date_to, $category );
    506                 $where      = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
    507                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    508                 $rows = $wpdb->get_results( "SELECT * FROM {$table} {$where} ORDER BY visited_at DESC" );
     522                [ $where, $args ] = self::build_conditions( $date_from, $date_to, $category );
     523                $sql  = "SELECT * FROM %i {$where} ORDER BY visited_at DESC";
     524                $args = array_merge( [ $table ], $args );
     525                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
     526                $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$args ) );
    509527
    510528                $result = [];
    … …  
    522540        }
    523541
     542       
    524543        private static function build_conditions( string $date_from, string $date_to, string $category = '' ): array {
    525                 global $wpdb;
    526544                $conditions = [];
     545                $args       = [];
    527546                if ( ! empty( $date_from ) ) {
    528                         $conditions[] = $wpdb->prepare( 'visited_at >= %s', $date_from . ' 00:00:00' );
     547                        $conditions[] = 'visited_at >= %s';
     548                        $args[]       = $date_from . ' 00:00:00';
    529549                }
    530550                if ( ! empty( $date_to ) ) {
    531                         $conditions[] = $wpdb->prepare( 'visited_at <= %s', $date_to . ' 23:59:59' );
     551                        $conditions[] = 'visited_at <= %s';
     552                        $args[]       = $date_to . ' 23:59:59';
    532553                }
    533554                if ( ! empty( $category ) ) {
    … …  
    535556                        $cats = array_filter( array_map( 'trim', explode( ',', $category ) ) );
    536557                        if ( count( $cats ) === 1 ) {
    537                                 $conditions[] = $wpdb->prepare( 'bot_category = %s', $cats[0] );
     558                                $conditions[] = 'bot_category = %s';
     559                                $args[]       = $cats[0];
    538560                        } elseif ( count( $cats ) > 1 ) {
    539                                 $placeholders  = implode( ',', array_fill( 0, count( $cats ), '%s' ) );
    540                                 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
    541                                 $conditions[] = $wpdb->prepare( "bot_category IN ({$placeholders})", ...$cats );
    542                         }
    543                 }
    544                 return $conditions;
     561                                $conditions[] = 'bot_category IN (' . implode( ',', array_fill( 0, count( $cats ), '%s' ) ) . ')';
     562                                $args         = array_merge( $args, $cats );
     563                        }
     564                }
     565                $where = $conditions ? 'WHERE ' . implode( ' AND ', $conditions ) : '';
     566                return [ $where, $args ];
    545567        }
    546568}
  • edgeshaping-lite/trunk/readme.txt

    r3735949 r3791852  
    22Contributors: mareinterno
    33Tags: ai crawler, ai bot, bot log, analytics, llm
    4 Requires at least: 6.0
     4Requires at least: 6.9
    55Tested up to: 7.1
    6 Requires PHP: 7.4
    7 Stable tag: 2026.10.0
     6Requires PHP: 8.2
     7Stable tag: 2026.11.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    8484AHTG Matrix uses the same four-quadrant model as AHQG, but the human axis is WordPress's own page views instead of Google Search Console clicks — no external connection required, and it reflects all traffic sources, not just search.
    8585
     86= Why is the dashboard empty right after installation? =
     87
     88The dashboard and the matrices show data aggregated once a day, up to yesterday, so they start showing data the day after installation. Today's bot visits can be checked in the access log.
     89
    8690== External Services ==
    8791
    … …  
    100104
    101105== Changelog ==
     106
     107= 2026.11.0 =
     108* Changed: the bot dictionary is updated for November. The bots used for the AI axis of the AHQG/AHTG matrices follow the updated dictionary, so matrix results for past periods may differ from what you saw before.
     109* Changed: the dashboard and the matrices now count only visits by verified bots to published pages (published posts and pages, and the front page). Inferred Path now lists the other paths visited by verified bots, instead of URLs missing from the sitemap. The access log is unfiltered as before.
     110* Added: an "AI Ready" screen that lists the AI bots that reached your site in a period (including today, up to 30 days).
     111* Changed: requires WordPress 6.9 or later and PHP 8.2 or later. PHP 8.2 reaches the end of official security support on December 31, 2026, and the first release in 2027 will require PHP 8.3 or later.
    102112
    103113= 2026.10.0 =
    … …  
    190200== Upgrade Notice ==
    191201
     202= 2026.11.0 =
     203November bot dictionary. The dashboard and matrices now count only verified bot visits to published pages, so numbers may be lower than before. Adds an "AI Ready" screen. Requires WordPress 6.9+ and PHP 8.2+; PHP 8.3+ from 2027. Existing data carries over.
     204
    192205= 2026.10.0 =
    193206Bot dictionary now lists only officially documented crawlers; undocumented bots are no longer recorded and past matrix results may change. Adds a read-only MCP ability and a setting to turn off IP address recording. Existing data carries over.
Note: See TracChangeset for help on using the changeset viewer.