repository = $repository ?? new Repository(); } /** * Register WordPress hooks. * * @return void * @since 1.5.0 */ public function register_hooks(): void { add_action( self::HOOK, [ $this, 'run' ] ); } /** * Expire what lapsed, fail what stalled, delete what is old. * * @return void * @since 1.5.0 */ public function run(): void { try { // Read before writing: a bulk UPDATE knows a count and not which rows, // and the audit trail needs to name them. $lapsed = $this->repository->list_lapsed(); $stale = $this->repository->list_stale_claims( self::STALE_CLAIM_SECONDS ); $this->repository->expire_lapsed(); $this->repository->fail_stale_claims( self::STALE_CLAIM_SECONDS ); foreach ( $lapsed as $action ) { AuditTrail::expired( $action ); } foreach ( $stale as $action ) { AuditTrail::abandoned( $action ); } $this->repository->purge_decided( self::retention_days() ); } catch ( \Throwable $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch // Never let a cron failure surface to the site. } } /** * How many days a decided row is kept. * * @return int Days; 0 or less disables deletion. * @since 1.5.0 */ private static function retention_days(): int { /** * Filters how long decided safe-mode actions are kept before deletion. * * Return 0 or less to keep them indefinitely. Worth knowing before you * do: these rows hold the input each call was about to run with, so * keeping them forever keeps request payloads in every backup. * * @since 1.5.0 * * @param int $days Days to keep a decided action. Default 30. */ return (int) apply_filters( 'albert/safe_mode/retention_days', self::RETENTION_DAYS ); } /** * Schedule the daily sweep if not already scheduled. * * @return void * @since 1.5.0 */ public static function schedule(): void { if ( ! wp_next_scheduled( self::HOOK ) ) { wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', self::HOOK ); } } /** * Unschedule the daily sweep. * * @return void * @since 1.5.0 */ public static function unschedule(): void { $timestamp = wp_next_scheduled( self::HOOK ); while ( $timestamp !== false ) { wp_unschedule_event( $timestamp, self::HOOK ); $timestamp = wp_next_scheduled( self::HOOK ); } } }