<?php function stack(\Throwable|array $context = null, string $prune = 'vendor', int $text = 64, int $arr = 4): void { $currentTrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $current = array_shift($currentTrace); $caller = implode(':', [$current['file'] ?? '', $current['line'] ?? '']); $exception = $context instanceof \Throwable ? get_class($context) : ''; $message = $context instanceof \Throwable ? $context->getMessage() : ''; $trace = $context instanceof \Throwable ? $context->getTrace() : ($context ?: $currentTrace); if ($context instanceof \Throwable) { array_unshift($trace, ['line' => $context->getFile() . ':' . $context->getLine()]); } $trace = array_map(function (array $line) use ($text, $arr): array { $args = array_map($self = function (mixed $arg) use (&$self, $text, $arr): string { static $depth = 0; $depth++; $getItemTypes = function (array $items) use ($self, $depth, $text, $arr): string { $count = (string)count($items); if ($depth > 1) { return $count; } $sample = array_splice($items, 0, $arr); $types = array_map('gettype', $sample); $types = (array_is_list($sample) && count(array_unique($types)) == 1) ? current($types) : array_map($self, $sample); $type = fn (string $type, string|int $index): string => "$index: $type"; $types = is_array($types) ? array_map($type, $types, array_keys($types)) : [$types . "[$count]"]; return implode(', ', $items ? array_merge($types, ['...+' . count($items)]) : $types); }; $response = match (gettype($arg)) { 'string' => '"' . (($l = strlen($arg)) > ($t = $text + 3) ? substr($arg, 0, $text) . '...+' . $l - $t : $arg) . '"', 'object' => $arg instanceof \Closure ? 'closure' : get_class($arg), 'resource' => 'resource', 'array' => (array_is_list($arg) ? 'array' : 'hash') . '(' . $getItemTypes($arg) . ')', 'NULL' => 'null', 'boolean' => $arg ? 'true' : 'false', 'integer', 'double' => (string)$arg, default => $arg }; $depth--; return $response; }, $line['args'] ?? []); $function = implode('::', array_filter([$line['class'] ?? null, $line['function'] ?? null])); $function .= '(' . implode(', ', $args) . ')'; $line = implode(':', array_filter([$line['file'] ?? null, $line['line'] ?? null])); return array_filter(compact('function', 'line')); }, $trace); if ($prune) { $truncate = function (array $line, int $key) use ($trace, $prune) : array|string { $isEnd = !$key || $key == array_key_last($trace); $isRegex = ($delim = substr($prune, 0, 1)) == substr($prune, -1, 1) && in_array($delim, ['\\', '~']); $prune = $isRegex ? $prune : '~' . preg_quote($prune, '~') . '~i'; return !$isEnd && preg_match($prune, $line['line'] ?? '') ? '...' : $line; }; $trace = array_map($truncate, $trace, array_keys($trace)); $truncate = fn (array|string $line, int $key): bool => $line != '...' || ($trace[$key - 1] ?? '') != '...'; $trace = array_filter($trace, $truncate, ARRAY_FILTER_USE_BOTH); } die(print_r(array_filter(compact('caller', 'exception', 'message', 'trace')), true)); }