$length */ private int $length; /** @var int<0, max> */ private int $currentIndex = 0; /** * Rolling buffer, where the modulo of the index is used. Fe: when writing 'a', 'b', 'c', 'd', 'e', 'f' to a buffer of length 3: * ['a'] * ['a', 'b'] * ['a', 'b', 'c'] * ['d', 'b', 'c'] * ['d', 'e', 'c'] * ['d', 'e', 'f'] * * @var array, string> */ private array $buffer = []; /** @phpstan-assert int<1, max> $length */ public function __construct(int $length) { if ($length < 1) { throw new InvalidArgumentException(sprintf('A negative or zero buffer length doesn\'t make sense, %d provided', $length)); } $this->length = $length; } public function next(string $char): self { $this->currentIndex++; $this->buffer[$this->currentIndex % $this->length] = $char; return $this; } /** @throws InvalidArgumentException */ public function getPreviousCharacter(int $nAgo = 1): ?string { if ($nAgo >= $this->length) { throw new InvalidArgumentException('Buffer length of "' . $this->length . '" configured, but character "-' . $nAgo . '" requested'); } return $this->buffer[($this->currentIndex - $nAgo) % $this->length] ?? null; } /** * @phpstan-assert non-empty-string $string * * @throws InvalidArgumentException */ public function seenString(string $string): bool { $strlen = strlen($string); if ($strlen === 0) { throw new InvalidArgumentException('Cannot assert if non empty string has been encountered'); } if ($strlen > $this->length) { throw new InvalidArgumentException(sprintf('Buffer length of %d configured, but value with length %d requested', $this->length, strlen($string))); } foreach (str_split($string) as $index => $char) { $previousChar = $this->getPreviousCharacter($strlen - $index - 1); if ($previousChar !== $char) { return false; } } return true; } /** @throws InvalidArgumentException */ public function seenReverseString(string $string): bool { if (strlen($string) > $this->length) { throw new InvalidArgumentException(sprintf('Buffer length of %d configured, but enum with length %d requested', $this->length, strlen($string))); } foreach (str_split($string) as $index => $char) { $previousChar = $this->getPreviousCharacter($index); if ($previousChar !== $char) { return false; } } return true; } }