> */ public function all(): array { $entries = get_option( self::OPTION, array() ); return is_array( $entries ) ? array_values( $entries ) : array(); } /** * Record a change. * * @param string $batch Batch ID (one bulk action). * @param string $action Action name. * @param string $source_type Source type. * @param int $source_id Source ID. * @param string $source_key Source key. * @param string $before Previous raw value. * @param string $after New raw value. * @param string[] $urls URLs affected. */ public function record( string $batch, string $action, string $source_type, int $source_id, string $source_key, string $before, string $after, array $urls ): void { $entries = $this->all(); $entries[] = array( 'batch' => $batch, 'action' => $action, 'time' => Clock::now(), 'user' => get_current_user_id(), 'source_type' => $source_type, 'source_id' => $source_id, 'source_key' => $source_key, 'urls' => array_values( array_slice( $urls, 0, 20 ) ), 'before' => $before, 'after_hash' => self::hash( $after ), ); $entries = array_slice( $entries, -self::MAX_ENTRIES ); $size = strlen( (string) maybe_serialize( $entries ) ); // Never drop entries of the batch being recorded: the last action must stay fully undoable. while ( $size > self::MAX_BYTES && count( $entries ) > 1 && $batch !== $entries[0]['batch'] ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found -- The array shrinks each iteration. $removed = array_shift( $entries ); $size -= strlen( (string) maybe_serialize( $removed ) ); } update_option( self::OPTION, $entries, false ); } /** * Batch ID of the most recent change ('' if none). */ public function last_batch(): string { $entries = $this->all(); if ( empty( $entries ) ) { return ''; } $last = end( $entries ); return (string) $last['batch']; } /** * Entries of one batch, newest first. * * @param string $batch Batch ID. * @return array> */ public function batch( string $batch ): array { $out = array(); foreach ( array_reverse( $this->all() ) as $entry ) { if ( $batch === $entry['batch'] ) { $out[] = $entry; } } return $out; } /** * Remove a batch from the log (after it was undone). * * @param string $batch Batch ID. */ public function forget( string $batch ): void { $kept = array(); foreach ( $this->all() as $entry ) { if ( $batch !== $entry['batch'] ) { $kept[] = $entry; } } update_option( self::OPTION, $kept, false ); } /** * Summaries for the UI (without the stored "before" values). * * @param int $limit Max entries. * @return array> */ public function recent( int $limit = 50 ): array { $out = array(); foreach ( array_slice( array_reverse( $this->all() ), 0, $limit ) as $entry ) { unset( $entry['before'], $entry['after_hash'] ); $out[] = $entry; } return $out; } }