db = $db; $this->table = $table; } public function get_or_create( string $slug, string $name, string $default_status = 'unknown' ): ?array { $record = $this->get_by_slug( $slug ); if ( $record ) { return $record; } if ( ! in_array( $default_status, $this->allowed_statuses, true ) ) { $default_status = 'unknown'; } $inserted = $this->db->insert( $this->table, [ 'bot_slug' => $slug, 'display_name' => $name, 'status' => $default_status, 'first_seen' => current_time( 'mysql' ), 'last_seen' => current_time( 'mysql' ), 'total_hits' => 0, 'allowed_hits' => 0, 'blocked_hits' => 0, ], [ '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d' ] ); if ( false === $inserted ) { return null; } return $this->get_by_slug( $slug ); } public function update_stats( string $slug, string $action ): void { $action = 'blocked' === $action ? 'blocked' : 'allowed'; $this->db->query( $this->db->prepare( "UPDATE {$this->table} SET last_seen = %s, total_hits = total_hits + 1, allowed_hits = allowed_hits + %d, blocked_hits = blocked_hits + %d WHERE bot_slug = %s", current_time( 'mysql' ), 'allowed' === $action ? 1 : 0, 'blocked' === $action ? 1 : 0, $slug ) ); } public function set_status( string $slug, string $status ): bool { if ( ! in_array( $status, $this->allowed_statuses, true ) ) { $status = 'unknown'; } return false !== $this->db->update( $this->table, [ 'status' => $status ], [ 'bot_slug' => $slug ], [ '%s' ], [ '%s' ] ); } public function get_status( string $slug ): string { $record = $this->get_by_slug( $slug ); return $record['status'] ?? 'unknown'; } public function get_all_crawlers( array $args = [] ): array { $defaults = [ 'limit' => 50, 'offset' => 0, 'search' => '', 'order' => 'last_seen', 'order_direction' => 'DESC', ]; $args = wp_parse_args( $args, $defaults ); $limit = absint( $args['limit'] ); $offset = absint( $args['offset'] ); $search = sanitize_text_field( $args['search'] ); $order = in_array( $args['order'], [ 'last_seen', 'first_seen', 'display_name', 'total_hits' ], true ) ? $args['order'] : 'last_seen'; $direction = 'ASC' === strtoupper( $args['order_direction'] ) ? 'ASC' : 'DESC'; $sql = "SELECT * FROM {$this->table}"; $where = []; $params = []; if ( $search ) { $like = '%' . $this->db->esc_like( $search ) . '%'; $where[] = '(display_name LIKE %s OR bot_slug LIKE %s)'; $params[] = $like; $params[] = $like; } if ( $where ) { $sql .= ' WHERE ' . implode( ' AND ', $where ); } $sql .= " ORDER BY {$order} {$direction} LIMIT %d OFFSET %d"; $params[] = $limit; $params[] = $offset; $query = $this->db->prepare( $sql, ...$params ); return $this->db->get_results( $query, ARRAY_A ) ?: []; } public function get_summary_stats(): array { $row = $this->db->get_row( "SELECT SUM(total_hits) AS total_hits, SUM(allowed_hits) AS allowed_hits, SUM(blocked_hits) AS blocked_hits FROM {$this->table}", ARRAY_A ); return [ 'total_hits' => isset( $row['total_hits'] ) ? (int) $row['total_hits'] : 0, 'allowed_hits' => isset( $row['allowed_hits'] ) ? (int) $row['allowed_hits'] : 0, 'blocked_hits' => isset( $row['blocked_hits'] ) ? (int) $row['blocked_hits'] : 0, ]; } public function get_status_counts(): array { $rows = $this->db->get_results( "SELECT status, COUNT(*) as total FROM {$this->table} GROUP BY status", ARRAY_A ); $counts = [ 'trusted' => 0, 'blocked' => 0, 'unknown' => 0 ]; foreach ( (array) $rows as $row ) { $status = $row['status']; if ( isset( $counts[ $status ] ) ) { $counts[ $status ] = (int) $row['total']; } } return $counts; } public function count_crawlers( array $args = [] ): int { $defaults = [ 'search' => '' ]; $args = wp_parse_args( $args, $defaults ); $search = sanitize_text_field( $args['search'] ); $sql = "SELECT COUNT(*) FROM {$this->table}"; $params = []; if ( $search ) { $like = '%' . $this->db->esc_like( $search ) . '%'; $sql .= ' WHERE (display_name LIKE %s OR bot_slug LIKE %s)'; $params = [ $like, $like ]; } if ( $params ) { return (int) $this->db->get_var( $this->db->prepare( $sql, ...$params ) ); } return (int) $this->db->get_var( $sql ); } private function get_by_slug( string $slug ): ?array { $query = $this->db->prepare( "SELECT * FROM {$this->table} WHERE bot_slug = %s LIMIT 1", $slug ); $row = $this->db->get_row( $query, ARRAY_A ); return $row ?: null; } }