> Batch ID => entry, oldest first. */ public static function all(): array { $raw = get_option( self::OPTION, array() ); if ( ! is_array( $raw ) ) { return array(); } $out = array(); foreach ( $raw as $batch => $entry ) { if ( Marker::valid_batch( $batch ) && is_array( $entry ) ) { $out[ $batch ] = self::normalize( $entry ); } if ( count( $out ) >= self::MAX ) { break; } } return $out; } /** * One entry, or null. * * @since 1.0.0 * @param string $batch Batch ID. * @return array|null */ public static function get( string $batch ): ?array { return self::all()[ $batch ] ?? null; } /** * Add or replace an entry. * * @since 1.0.0 * @param string $batch Batch ID. * @param array $entry Entry. * @return void */ public static function put( string $batch, array $entry ): void { if ( ! Marker::valid_batch( $batch ) ) { return; } $all = self::all(); $all[ $batch ] = self::normalize( $entry ); self::save( $all ); } /** * Change some fields of an entry (nothing when it's not in the list). * * @since 1.0.0 * @param string $batch Batch ID. * @param array $changes Fields. * @return void */ public static function update( string $batch, array $changes ): void { $all = self::all(); if ( ! isset( $all[ $batch ] ) ) { return; } $all[ $batch ] = self::normalize( array_merge( $all[ $batch ], $changes ) ); self::save( $all ); } /** * Remove an entry. * * @since 1.0.0 * @param string $batch Batch ID. * @return void */ public static function remove( string $batch ): void { $all = self::all(); if ( isset( $all[ $batch ] ) ) { unset( $all[ $batch ] ); self::save( $all ); } } /** * Every batch on this site: the listed ones and any found only by markers, with live counts. * * @since 1.0.0 * @param array>|null $counts Marker::counts(), when already read. * @return array|null, counts: array}> Newest first. */ public static function known( ?array $counts = null ): array { $counts = null === $counts ? Marker::counts() : $counts; $list = self::all(); $out = array(); foreach ( $list as $batch => $entry ) { $out[ $batch ] = array( 'entry' => $entry, 'counts' => $counts[ $batch ] ?? array_fill_keys( Marker::TYPES, 0 ), ); } foreach ( $counts as $batch => $types ) { if ( ! isset( $out[ $batch ] ) ) { $out[ $batch ] = array( 'entry' => null, 'counts' => $types, ); } } uasort( $out, static function ( $a, $b ) { $ta = $a['entry'] ? (int) $a['entry']['created_gmt'] : 0; $tb = $b['entry'] ? (int) $b['entry']['created_gmt'] : 0; return $tb <=> $ta; } ); return $out; } /** * An entry with every field present and typed. * * @since 1.0.0 * @param array $entry Entry. * @return array */ public static function normalize( array $entry ): array { $ints = static function ( $value ) { $out = array(); foreach ( is_array( $value ) ? $value : array() as $key => $number ) { if ( is_string( $key ) && is_numeric( $number ) ) { $out[ sanitize_key( $key ) ] = max( 0, (int) $number ); } } return $out; }; $kept = array(); foreach ( is_array( $entry['kept'] ?? null ) ? $entry['kept'] : array() as $type => $reasons ) { if ( is_string( $type ) && is_array( $reasons ) ) { $kept[ sanitize_key( $type ) ] = $ints( $reasons ); } } $status = (string) ( $entry['status'] ?? 'complete' ); return array( 'label' => mb_substr( sanitize_text_field( is_string( $entry['label'] ?? null ) ? $entry['label'] : '' ), 0, Data\Limits::LABEL_MAX ), 'created_gmt' => max( 0, (int) ( $entry['created_gmt'] ?? 0 ) ), 'user_id' => max( 0, (int) ( $entry['user_id'] ?? 0 ) ), 'env' => in_array( $entry['env'] ?? '', Environment::TYPES, true ) ? (string) $entry['env'] : 'production', 'status' => in_array( $status, self::STATUSES, true ) ? $status : 'complete', 'requested' => $ints( $entry['requested'] ?? array() ), 'made' => $ints( $entry['made'] ?? array() ), 'kept' => $kept, 'finished_gmt' => max( 0, (int) ( $entry['finished_gmt'] ?? 0 ) ), ); } /** * Translated status label. * * @since 1.0.0 * @param string $status Status. * @param bool $stale Whether a running entry has had no step for a while. * @return string */ public static function status_label( string $status, bool $stale = false ): string { if ( 'running' === $status && $stale ) { return __( 'Paused', 'admin-powerups-for-woocommerce' ); } $labels = array( 'running' => __( 'Generating', 'admin-powerups-for-woocommerce' ), 'complete' => __( 'Complete', 'admin-powerups-for-woocommerce' ), 'stopped' => __( 'Stopped', 'admin-powerups-for-woocommerce' ), 'deleting' => __( 'Deleting', 'admin-powerups-for-woocommerce' ), 'partial' => __( 'Partly deleted', 'admin-powerups-for-woocommerce' ), ); return $labels[ $status ] ?? $labels['complete']; } /** * Save the list (never autoloaded). * * @param array> $all List. * @return void */ private static function save( array $all ): void { if ( ! $all ) { delete_option( self::OPTION ); return; } update_option( self::OPTION, array_slice( $all, -self::MAX, null, true ), false ); } }