* `count` (-1 = not counted), `done`, `state`, `threshold`, `unavailable`, `scanned_at`, * `bytes`, `recheck` (cleaned since; scan again to confirm), `sample_from` (where the count * first found rows, or null; the sample starts there). * * @since 1.0.0 */ final class Results { const OPTION = 'apwc_dbc_results'; /** * Seconds after which a count is too old to clean on. */ const FRESH = DAY_IN_SECONDS; /** * The stored results, checked. * * @since 1.0.0 * @return array{token: string, started: int, finished: int, full: bool, queue: string[], sizes: array>, failed: int|null, checks: array>} */ public static function get(): array { $stored = get_option( self::OPTION, array() ); $stored = is_array( $stored ) ? $stored : array(); $checks = array(); foreach ( (array) ( $stored['checks'] ?? array() ) as $id => $entry ) { if ( is_string( $id ) && is_array( $entry ) ) { $checks[ $id ] = self::entry( $entry ); } } return array( 'token' => is_string( $stored['token'] ?? null ) ? $stored['token'] : '', 'started' => (int) ( $stored['started'] ?? 0 ), 'finished' => (int) ( $stored['finished'] ?? 0 ), 'full' => ! empty( $stored['full'] ), 'queue' => array_values( array_filter( (array) ( $stored['queue'] ?? array() ), 'is_string' ) ), 'sizes' => is_array( $stored['sizes'] ?? null ) ? $stored['sizes'] : array(), 'failed' => isset( $stored['failed'] ) && is_numeric( $stored['failed'] ) ? (int) $stored['failed'] : null, 'checks' => $checks, ); } /** * One entry with every key. * * @param array $entry Stored entry. * @return array */ private static function entry( array $entry ): array { return array( 'count' => (int) ( $entry['count'] ?? 0 ), 'done' => ! empty( $entry['done'] ), 'state' => is_array( $entry['state'] ?? null ) ? $entry['state'] : array(), 'threshold' => isset( $entry['threshold'] ) && is_numeric( $entry['threshold'] ) ? (int) $entry['threshold'] : null, 'unavailable' => is_string( $entry['unavailable'] ?? null ) ? $entry['unavailable'] : '', 'scanned_at' => (int) ( $entry['scanned_at'] ?? 0 ), 'bytes' => (int) ( $entry['bytes'] ?? 0 ), 'recheck' => ! empty( $entry['recheck'] ), 'sample_from' => self::sample_from( $entry ), ); } /** * Where a check's last count first found rows (`t` target index, `from` key just below * them), from a final count state or a stored entry; null when there's none. The sample * starts there (OrphanRowsCheck). * * @since 1.0.0 * @param array $state Count state or stored entry. * @return array{t: int, from: int}|null */ public static function sample_from( array $state ): ?array { $hint = $state['sample_from'] ?? null; if ( ! is_array( $hint ) || ! isset( $hint['t'], $hint['from'] ) || ! is_numeric( $hint['t'] ) || ! is_numeric( $hint['from'] ) ) { return null; } return array( 't' => (int) $hint['t'], 'from' => (int) $hint['from'], ); } /** * Save. * * @since 1.0.0 * @param array $results Results. * @return void */ public static function save( array $results ): void { update_option( self::OPTION, $results, false ); // Adds it (not autoloaded) when missing. } /** * One check's entry, or null. * * @since 1.0.0 * @param string $id Check ID. * @return array|null */ public static function for_check( string $id ): ?array { return self::get()['checks'][ $id ] ?? null; } /** * Why a check's result can't be cleaned on ('' when it can): never scanned, still scanning, * older than a day, threshold or availability changed since, or cleaned since. * * @since 1.0.0 * @param Check $check Check. * @param array|null $entry Entry. * @return string */ public static function stale_reason( Check $check, ?array $entry ): string { if ( ! $entry || ! $entry['done'] || ! $entry['scanned_at'] ) { return __( 'Scan first.', 'admin-powerups-for-woocommerce' ); } if ( $entry['recheck'] ) { return __( 'Scan again to see what’s left.', 'admin-powerups-for-woocommerce' ); } if ( $entry['scanned_at'] < time() - self::FRESH ) { return __( 'The count is more than a day old. Scan again.', 'admin-powerups-for-woocommerce' ); } if ( $entry['threshold'] !== $check->threshold() ) { return __( 'The age setting changed since the scan. Scan again.', 'admin-powerups-for-woocommerce' ); } if ( $entry['unavailable'] !== $check->unavailable() ) { return __( 'Something changed since the scan. Scan again.', 'admin-powerups-for-woocommerce' ); } return ''; } /** * Update one entry and save. * * @since 1.0.0 * @param string $id Check ID. * @param array $changes Keys to set. * @return void */ public static function update_check( string $id, array $changes ): void { $results = self::get(); if ( ! isset( $results['checks'][ $id ] ) ) { return; } $results['checks'][ $id ] = array_merge( $results['checks'][ $id ], $changes ); self::save( $results ); } }