diff --git a/src/Client/TrapHandle.php b/src/Client/TrapHandle.php index 1fb9bcae..d3132f79 100644 --- a/src/Client/TrapHandle.php +++ b/src/Client/TrapHandle.php @@ -4,27 +4,27 @@ namespace Buggregator\Trap\Client; +use Buggregator\Trap\Client\TrapHandle\Counter; use Symfony\Component\VarDumper\Caster\TraceStub; use Symfony\Component\VarDumper\VarDumper; /** * @internal - * @psalm-internal Buggregator\Trap */ final class TrapHandle { - private array $values; private bool $haveToSend = true; + private int $times = 0; + private string $timesCounterKey = ''; public static function fromArray(array $array): self { - $new = new self(); - $new->values = $array; - return $new; + return new self($array); } /** * Dump only if the condition is true. + * The check is performed immediately upon declaration. */ public function if(bool|callable $condition): self { @@ -36,9 +36,36 @@ public function if(bool|callable $condition): self return $this; } + /** + * Dump only $times times. + * The counter isn't incremented if the dump is not sent (any other condition is not met). + * It might be useful for debugging in loops, recursive or just multiple function calls. + * + * @param positive-int $times + * @param bool $fullStack If true, the counter is incremented for each stack trace, not for the line. + */ + public function times(int $times, bool $fullStack = false): self + { + $this->times = $times; + $this->timesCounterKey = (\serialize( + $fullStack + ? $this->stackTrace() + : $this->stackTrace()[0] + )); + return $this; + } + + /** + * Dump values only once. + */ + public function once(): self + { + return $this->times(1); + } + public function __destruct() { - $this->haveToSend and $this->sendUsingDump(); + $this->haveToSend() and $this->sendUsingDump(); } private function sendUsingDump(): void @@ -88,19 +115,18 @@ private function sendUsingDump(): void * args?: array * }> */ - private function stackTrace(string $baseDir): array + private function stackTrace(string $baseDir = ''): array { - $dir = \getcwd() . \DIRECTORY_SEPARATOR; + $dir = $baseDir . \DIRECTORY_SEPARATOR; $cwdLen = \strlen($dir); - // Replace paths with relative paths $stack = []; foreach (\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) { - if (($frame['class'] ?? null) === __CLASS__) { + if (($frame['file'] ?? null) === __FILE__) { continue; } // Convert absolute paths to relative ones - isset($frame['file']) && \str_starts_with($frame['file'], $dir) + $cwdLen > 1 && isset($frame['file']) && \str_starts_with($frame['file'], $dir) and $frame['file'] = '.' . \DIRECTORY_SEPARATOR . \substr($frame['file'], $cwdLen); $stack[] = $frame; @@ -108,4 +134,22 @@ private function stackTrace(string $baseDir): array return $stack; } + + private function __construct( + private array $values, + ) { + } + + private function haveToSend(): bool + { + if (!$this->haveToSend) { + return false; + } + + if ($this->times > 0) { + return Counter::checkAndIncrement($this->timesCounterKey, $this->times); + } + + return true; + } } diff --git a/src/Client/TrapHandle/Counter.php b/src/Client/TrapHandle/Counter.php new file mode 100644 index 00000000..42920389 --- /dev/null +++ b/src/Client/TrapHandle/Counter.php @@ -0,0 +1,26 @@ +> */ + private static array $counters = []; + + /** + * Returns true if the counter of related stack trace is less than $times. In this case, the counter is incremented. + */ + public static function checkAndIncrement(string $key, int $times): bool + { + self::$counters[$key] ??= 0; + + if (self::$counters[$key] < $times) { + self::$counters[$key]++; + return true; + } + + return false; + } +}