insert( $table, array( 'module' => sanitize_key( $module ), 'zone' => sanitize_key( $zone ), 'hook' => substr( sanitize_text_field( $hook ), 0, 255 ), 'message' => $message, 'context' => $context ? wp_json_encode( $context, JSON_UNESCAPED_UNICODE ) : null, 'created_at' => current_time( 'mysql', true ), ), array( '%s', '%s', '%s', '%s', '%s', '%s' ) ); } /** * Return recent errors, optionally filtered by module. * * @param string $module Module name (empty = all modules). * @param int $limit Max rows to return. * @return array */ public static function get_recent( string $module = '', int $limit = 100 ): array { global $wpdb; $table = TMDO_DB::table( 'wpdo_errors' ); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from TMDO_DB::table(). if ( $module ) { return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$table}` WHERE module = %s ORDER BY id DESC LIMIT %d", $module, $limit ), ARRAY_A ) ?: array(); } return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$table}` ORDER BY id DESC LIMIT %d", $limit ), ARRAY_A ) ?: array(); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared } /** * Delete errors older than a given number of days. * * @param int $days Keep errors from the last N days. * @return int Number of rows deleted. */ public static function purge( int $days = 30 ): int { global $wpdb; $table = TMDO_DB::table( 'wpdo_errors' ); $now = current_time( 'mysql', true ); if ( TMDO_IS_SQLITE ) { $sql = $wpdb->prepare( "DELETE FROM `{$table}` WHERE created_at < datetime(%s, %s)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $now, "-{$days} days" ); } else { $sql = $wpdb->prepare( "DELETE FROM `{$table}` WHERE created_at < DATE_SUB(%s, INTERVAL %d DAY)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $now, $days ); } return (int) $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } }