$data Log data. */ public function insert( array $data ): int { global $wpdb; $tables = Installer::get_table_names(); $context = isset( $data['context'] ) ? $data['context'] : null; if ( is_array( $context ) || is_object( $context ) ) { $context = wp_json_encode( $context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); } $row = wp_parse_args( $data, array( 'level' => 'info', 'event' => '', 'source' => '', 'message' => '', 'context' => null, 'user_id' => function_exists( 'get_current_user_id' ) ? get_current_user_id() : 0, 'created_at' => current_time( 'mysql' ), ) ); $row['level'] = sanitize_key( (string) $row['level'] ); $row['event'] = sanitize_key( (string) $row['event'] ); $row['source'] = sanitize_text_field( (string) $row['source'] ); $row['message'] = sanitize_textarea_field( (string) $row['message'] ); $row['context'] = is_scalar( $context ) ? (string) $context : null; $row['user_id'] = empty( $row['user_id'] ) ? null : absint( $row['user_id'] ); $inserted = $wpdb->insert( $tables['plugin_logs'], $row, array( '%s', '%s', '%s', '%s', '%s', '%d', '%s' ) ); return false === $inserted ? 0 : (int) $wpdb->insert_id; } /** * Get plugin logs with filters. * * @param array $args Query args. * @return array */ public function get_logs( array $args = array() ): array { global $wpdb; $tables = Installer::get_table_names(); $where = array( '1=1' ); $params = array(); $limit = isset( $args['limit'] ) ? max( 1, min( 5000, absint( $args['limit'] ) ) ) : 50; $offset = isset( $args['offset'] ) ? max( 0, absint( $args['offset'] ) ) : 0; $this->apply_filters( $where, $params, $args ); $sql = "SELECT * FROM {$tables['plugin_logs']} WHERE " . implode( ' AND ', $where ) . ' ORDER BY created_at DESC, id DESC LIMIT %d OFFSET %d'; $params[] = $limit; $params[] = $offset; return $wpdb->get_results( $wpdb->prepare( $sql, $params ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Internal table names and fixed WHERE clauses; all values are bound by prepare(). } /** * Count plugin logs with filters. * * @param array $args Query args. */ public function count_logs( array $args = array() ): int { global $wpdb; $tables = Installer::get_table_names(); $where = array( '1=1' ); $params = array(); $this->apply_filters( $where, $params, $args ); $sql = "SELECT COUNT(*) FROM {$tables['plugin_logs']} WHERE " . implode( ' AND ', $where ); if ( empty( $params ) ) { return (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- No user values: the query contains only internal table names and fixed clauses. } return (int) $wpdb->get_var( $wpdb->prepare( $sql, $params ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Internal table names and fixed WHERE clauses; all values are bound by prepare(). } /** * Apply common query filters. * * @param string[] $where SQL where parts. * @param mixed[] $params SQL params. * @param array $args Query args. */ private function apply_filters( array &$where, array &$params, array $args ): void { global $wpdb; $search = isset( $args['search'] ) ? trim( (string) $args['search'] ) : ''; $level = isset( $args['level'] ) ? sanitize_key( (string) $args['level'] ) : ''; $event = isset( $args['event'] ) ? sanitize_key( (string) $args['event'] ) : ''; $date_from = isset( $args['date_from'] ) ? trim( (string) $args['date_from'] ) : ''; $date_to = isset( $args['date_to'] ) ? trim( (string) $args['date_to'] ) : ''; if ( '' !== $search ) { $where[] = '(message LIKE %s OR source LIKE %s OR context LIKE %s)'; $like = '%' . $wpdb->esc_like( $search ) . '%'; $params[] = $like; $params[] = $like; $params[] = $like; } if ( '' !== $level ) { $where[] = 'level = %s'; $params[] = $level; } if ( '' !== $event ) { $where[] = 'event = %s'; $params[] = $event; } if ( '' !== $date_from ) { $where[] = 'created_at >= %s'; $params[] = $date_from . ' 00:00:00'; } if ( '' !== $date_to ) { $where[] = 'created_at <= %s'; $params[] = $date_to . ' 23:59:59'; } } }