$data Webhook log data. */ public function insert( array $data ): int { global $wpdb; $tables = Installer::get_table_names(); $row = wp_parse_args( $data, array( 'route' => '', 'method' => '', 'remote_addr' => '', 'content_type' => '', 'headers' => null, 'query_params' => null, 'payload' => null, 'raw_body' => null, 'result' => null, 'status' => 'received', 'created_at' => current_time( 'mysql' ), ) ); foreach ( array( 'headers', 'query_params', 'payload', 'result' ) as $json_field ) { if ( is_array( $row[ $json_field ] ) || is_object( $row[ $json_field ] ) ) { $row[ $json_field ] = wp_json_encode( $row[ $json_field ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); } } $row['route'] = sanitize_text_field( (string) $row['route'] ); $row['method'] = sanitize_key( (string) $row['method'] ); $row['remote_addr'] = sanitize_text_field( (string) $row['remote_addr'] ); $row['content_type'] = sanitize_text_field( (string) $row['content_type'] ); $row['headers'] = is_scalar( $row['headers'] ) ? (string) $row['headers'] : null; $row['query_params'] = is_scalar( $row['query_params'] ) ? (string) $row['query_params'] : null; $row['payload'] = is_scalar( $row['payload'] ) ? (string) $row['payload'] : null; $row['raw_body'] = is_scalar( $row['raw_body'] ) ? (string) $row['raw_body'] : null; $row['result'] = is_scalar( $row['result'] ) ? (string) $row['result'] : null; $row['status'] = sanitize_key( (string) $row['status'] ); $inserted = $wpdb->insert( $tables['webhook_logs'], $row, array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ); return false === $inserted ? 0 : (int) $wpdb->insert_id; } /** * Get webhook 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['webhook_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 webhook 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['webhook_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'] ) : ''; $status = isset( $args['status'] ) ? sanitize_key( (string) $args['status'] ) : ''; $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[] = '(payload LIKE %s OR raw_body LIKE %s OR result LIKE %s OR headers LIKE %s OR remote_addr LIKE %s)'; $like = '%' . $wpdb->esc_like( $search ) . '%'; $params[] = $like; $params[] = $like; $params[] = $like; $params[] = $like; $params[] = $like; } if ( '' !== $status ) { $where[] = 'status = %s'; $params[] = $status; } 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'; } } }