> */ public static function runs(): array { $stored = get_option( self::OPTION, array() ); return is_array( $stored ) ? array_values( $stored ) : array(); } /** * @return array|null */ public static function run( string $import_id ): ?array { foreach ( self::runs() as $run ) { if ( (string) ( $run['import_id'] ?? '' ) === $import_id ) { return $run; } } return null; } /** * Create or update a run record. Newest first. * * @param array $patch * @return array */ public static function save( string $import_id, array $patch ): array { $runs = self::runs(); $index = null; foreach ( $runs as $i => $run ) { if ( (string) ( $run['import_id'] ?? '' ) === $import_id ) { $index = $i; break; } } $record = null === $index ? array( 'import_id' => $import_id ) : $runs[ $index ]; $record = array_merge( $record, $patch ); if ( null === $index ) { array_unshift( $runs, $record ); } else { $runs[ $index ] = $record; } usort( $runs, static fn( array $a, array $b ) => (int) ( $b['started'] ?? 0 ) <=> (int) ( $a['started'] ?? 0 ) ); $runs = array_slice( $runs, 0, self::KEEP ); update_option( self::OPTION, $runs, false ); return $record; } /** * Record one failed record. Bounded — the count is what matters, the first * few messages are what a support conversation needs. */ public static function error( string $import_id, string $what, string $message ): void { $run = self::run( $import_id ) ?? array( 'import_id' => $import_id ); $errors = (array) ( $run['errors'] ?? array() ); if ( count( $errors ) < self::ERRORS ) { $errors[] = array( 'what' => $what, 'message' => mb_substr( $message, 0, 300 ) ); } $counts = (array) ( $run['counts'] ?? array() ); $counts['errors'] = (int) ( $counts['errors'] ?? 0 ) + 1; self::save( $import_id, array( 'errors' => $errors, 'counts' => $counts ) ); } /** * Drop the journals of runs that have fallen off the list, so the table * cannot outlive the history that explains it. */ public static function prune(): void { global $wpdb; $ids = array(); foreach ( self::runs() as $run ) { $ids[] = (string) ( $run['import_id'] ?? '' ); } $ids = array_values( array_filter( $ids ) ); if ( empty( $ids ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( 'DELETE FROM %i', Database::import_undo() ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name from this plugin's own Database class/adapter, never from input; values go through $wpdb->prepare(). return; } $in = implode( ',', array_fill( 0, count( $ids ), '%s' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . Database::import_undo() . " WHERE import_id NOT IN ({$in})", ...$ids ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- table name from this plugin's own Database class/adapter, never from input; values go through $wpdb->prepare(). } }