lithium\analysis\Debugger::trace()

public static method

Outputs a stack trace based on the supplied options.

Parameters

  • array $options

    Format for outputting stack trace. Available options are:

    • 'args': A boolean indicating if arguments should be included.
    • 'depth': The maximum depth of the trace.
    • 'format': Either null, 'points' or 'array'.
    • 'includeScope': A boolean indicating if items within scope should be included.
    • 'scope': Scope for items to include.
    • 'start': The depth to start with.
    • 'trace': A trace to use instead of generating one.

Returns

string|array|null

Stack trace formatted according to 'format' option.

Source

	public static function trace(array $options = []) {
		$defaults = [
			'depth' => 999,
			'format' => null,
			'args' => false,
			'start' => 0,
			'scope' => [],
			'trace' => [],
			'includeScope' => true,
			'closures' => true
		];
		$options += $defaults;

		$backtrace = $options['trace'] ?: debug_backtrace();
		$scope = $options['scope'];
		$count = count($backtrace);
		$back = [];
		$traceDefault = [
			'line' => '??', 'file' => '[internal]', 'class' => null, 'function' => '[main]'
		];

		for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
			$trace = array_merge(['file' => '[internal]', 'line' => '??'], $backtrace[$i]);
			$function = '[main]';

			if (isset($backtrace[$i + 1])) {
				$next = $backtrace[$i + 1] + $traceDefault;
				$function = $next['function'];

				if (!empty($next['class'])) {
					$function = $next['class'] . '::' . $function . '(';
					if ($options['args'] && isset($next['args'])) {
						$args = array_map(['static', 'export'], $next['args']);
						$function .= join(', ', $args);
					}
					$function .= ')';
				}
			}

			if ($options['closures'] && strpos($function, '{closure}') !== false) {
				$function = static::_closureDef($backtrace[$i], $function);
			}
			if (in_array($function, ['call_user_func_array', 'trigger_error'])) {
				continue;
			}
			$trace['functionRef'] = $function;

			if ($options['format'] === 'points' && $trace['file'] !== '[internal]') {
				$back[] = ['file' => $trace['file'], 'line' => $trace['line']];
			} elseif (is_string($options['format']) && $options['format'] !== 'array') {
				$back[] = Text::insert($options['format'], array_map(
					function($data) { return is_object($data) ? get_class($data) : $data; },
					$trace
				));
			} elseif (empty($options['format'])) {
				$back[] = $function . ' - ' . $trace['file'] . ', line ' . $trace['line'];
			} else {
				$back[] = $trace;
			}

			if (!empty($scope) && array_intersect_assoc($scope, $trace) == $scope) {
				if (!$options['includeScope']) {
					$back = array_slice($back, 0, count($back) - 1);
				}
				break;
			}
		}

		if ($options['format'] === 'array' || $options['format'] === 'points') {
			return $back;
		}
		return join("\n", $back);
	}