options = $options; } /** * Pending messages. * * @return array> */ public function all(): array { $items = $this->options->get( self::OPTION, array() ); return is_array( $items ) ? array_values( $items ) : array(); } /** * Whether anything is waiting. * * @return bool */ public function is_empty(): bool { return array() === $this->all(); } /** * Add a message to the queue, dropping the oldest when full. * * @param string $text Body. * @return void */ public function push( string $text ): void { if ( '' === trim( $text ) ) { return; } $items = $this->all(); $items[] = array( 'text' => $text, 'attempts' => 0, ); if ( count( $items ) > self::LIMIT ) { $items = array_slice( $items, -self::LIMIT ); } $this->options->update( self::OPTION, $items ); } /** * Retry every pending message. * * @param callable $sender `function ( string $text ): bool`. * @return int Number of delivered messages. */ public function flush( callable $sender ): int { $items = $this->all(); $remaining = array(); $delivered = 0; foreach ( $items as $item ) { $text = isset( $item['text'] ) ? (string) $item['text'] : ''; $attempts = isset( $item['attempts'] ) ? (int) $item['attempts'] : 0; if ( $sender( $text ) ) { ++$delivered; continue; } ++$attempts; if ( $attempts >= self::MAX_ATTEMPTS ) { continue; } $remaining[] = array( 'text' => $text, 'attempts' => $attempts, ); } if ( array() === $remaining ) { $this->options->delete( self::OPTION ); } else { $this->options->update( self::OPTION, $remaining ); } return $delivered; } /** * Drop everything. * * @return void */ public function clear(): void { $this->options->delete( self::OPTION ); } }