Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Debug\Exception; Chris@0: Chris@0: use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; Chris@0: Chris@0: /** Chris@0: * FlattenException wraps a PHP Exception to be able to serialize it. Chris@0: * Chris@0: * Basically, this class removes all objects from the trace. Chris@0: * Chris@0: * @author Fabien Potencier Chris@0: */ Chris@0: class FlattenException Chris@0: { Chris@0: private $message; Chris@0: private $code; Chris@0: private $previous; Chris@0: private $trace; Chris@0: private $class; Chris@0: private $statusCode; Chris@0: private $headers; Chris@0: private $file; Chris@0: private $line; Chris@0: Chris@0: public static function create(\Exception $exception, $statusCode = null, array $headers = array()) Chris@0: { Chris@0: $e = new static(); Chris@0: $e->setMessage($exception->getMessage()); Chris@0: $e->setCode($exception->getCode()); Chris@0: Chris@0: if ($exception instanceof HttpExceptionInterface) { Chris@0: $statusCode = $exception->getStatusCode(); Chris@0: $headers = array_merge($headers, $exception->getHeaders()); Chris@0: } Chris@0: Chris@0: if (null === $statusCode) { Chris@0: $statusCode = 500; Chris@0: } Chris@0: Chris@0: $e->setStatusCode($statusCode); Chris@0: $e->setHeaders($headers); Chris@0: $e->setTraceFromException($exception); Chris@0: $e->setClass(get_class($exception)); Chris@0: $e->setFile($exception->getFile()); Chris@0: $e->setLine($exception->getLine()); Chris@0: Chris@0: $previous = $exception->getPrevious(); Chris@0: Chris@0: if ($previous instanceof \Exception) { Chris@0: $e->setPrevious(static::create($previous)); Chris@0: } elseif ($previous instanceof \Throwable) { Chris@0: $e->setPrevious(static::create(new FatalThrowableError($previous))); Chris@0: } Chris@0: Chris@0: return $e; Chris@0: } Chris@0: Chris@0: public function toArray() Chris@0: { Chris@0: $exceptions = array(); Chris@0: foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) { Chris@0: $exceptions[] = array( Chris@0: 'message' => $exception->getMessage(), Chris@0: 'class' => $exception->getClass(), Chris@0: 'trace' => $exception->getTrace(), Chris@0: ); Chris@0: } Chris@0: Chris@0: return $exceptions; Chris@0: } Chris@0: Chris@0: public function getStatusCode() Chris@0: { Chris@0: return $this->statusCode; Chris@0: } Chris@0: Chris@0: public function setStatusCode($code) Chris@0: { Chris@0: $this->statusCode = $code; Chris@0: } Chris@0: Chris@0: public function getHeaders() Chris@0: { Chris@0: return $this->headers; Chris@0: } Chris@0: Chris@0: public function setHeaders(array $headers) Chris@0: { Chris@0: $this->headers = $headers; Chris@0: } Chris@0: Chris@0: public function getClass() Chris@0: { Chris@0: return $this->class; Chris@0: } Chris@0: Chris@0: public function setClass($class) Chris@0: { Chris@0: $this->class = $class; Chris@0: } Chris@0: Chris@0: public function getFile() Chris@0: { Chris@0: return $this->file; Chris@0: } Chris@0: Chris@0: public function setFile($file) Chris@0: { Chris@0: $this->file = $file; Chris@0: } Chris@0: Chris@0: public function getLine() Chris@0: { Chris@0: return $this->line; Chris@0: } Chris@0: Chris@0: public function setLine($line) Chris@0: { Chris@0: $this->line = $line; Chris@0: } Chris@0: Chris@0: public function getMessage() Chris@0: { Chris@0: return $this->message; Chris@0: } Chris@0: Chris@0: public function setMessage($message) Chris@0: { Chris@0: $this->message = $message; Chris@0: } Chris@0: Chris@0: public function getCode() Chris@0: { Chris@0: return $this->code; Chris@0: } Chris@0: Chris@0: public function setCode($code) Chris@0: { Chris@0: $this->code = $code; Chris@0: } Chris@0: Chris@0: public function getPrevious() Chris@0: { Chris@0: return $this->previous; Chris@0: } Chris@0: Chris@0: public function setPrevious(FlattenException $previous) Chris@0: { Chris@0: $this->previous = $previous; Chris@0: } Chris@0: Chris@0: public function getAllPrevious() Chris@0: { Chris@0: $exceptions = array(); Chris@0: $e = $this; Chris@0: while ($e = $e->getPrevious()) { Chris@0: $exceptions[] = $e; Chris@0: } Chris@0: Chris@0: return $exceptions; Chris@0: } Chris@0: Chris@0: public function getTrace() Chris@0: { Chris@0: return $this->trace; Chris@0: } Chris@0: Chris@0: public function setTraceFromException(\Exception $exception) Chris@0: { Chris@0: $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine()); Chris@0: } Chris@0: Chris@0: public function setTrace($trace, $file, $line) Chris@0: { Chris@0: $this->trace = array(); Chris@0: $this->trace[] = array( Chris@0: 'namespace' => '', Chris@0: 'short_class' => '', Chris@0: 'class' => '', Chris@0: 'type' => '', Chris@0: 'function' => '', Chris@0: 'file' => $file, Chris@0: 'line' => $line, Chris@0: 'args' => array(), Chris@0: ); Chris@0: foreach ($trace as $entry) { Chris@0: $class = ''; Chris@0: $namespace = ''; Chris@0: if (isset($entry['class'])) { Chris@0: $parts = explode('\\', $entry['class']); Chris@0: $class = array_pop($parts); Chris@0: $namespace = implode('\\', $parts); Chris@0: } Chris@0: Chris@0: $this->trace[] = array( Chris@0: 'namespace' => $namespace, Chris@0: 'short_class' => $class, Chris@0: 'class' => isset($entry['class']) ? $entry['class'] : '', Chris@0: 'type' => isset($entry['type']) ? $entry['type'] : '', Chris@0: 'function' => isset($entry['function']) ? $entry['function'] : null, Chris@0: 'file' => isset($entry['file']) ? $entry['file'] : null, Chris@0: 'line' => isset($entry['line']) ? $entry['line'] : null, Chris@0: 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(), Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: private function flattenArgs($args, $level = 0, &$count = 0) Chris@0: { Chris@0: $result = array(); Chris@0: foreach ($args as $key => $value) { Chris@0: if (++$count > 1e4) { Chris@0: return array('array', '*SKIPPED over 10000 entries*'); Chris@0: } Chris@0: if ($value instanceof \__PHP_Incomplete_Class) { Chris@0: // is_object() returns false on PHP<=7.1 Chris@0: $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value)); Chris@0: } elseif (is_object($value)) { Chris@0: $result[$key] = array('object', get_class($value)); Chris@0: } elseif (is_array($value)) { Chris@0: if ($level > 10) { Chris@0: $result[$key] = array('array', '*DEEP NESTED ARRAY*'); Chris@0: } else { Chris@0: $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count)); Chris@0: } Chris@0: } elseif (null === $value) { Chris@0: $result[$key] = array('null', null); Chris@0: } elseif (is_bool($value)) { Chris@0: $result[$key] = array('boolean', $value); Chris@0: } elseif (is_int($value)) { Chris@0: $result[$key] = array('integer', $value); Chris@0: } elseif (is_float($value)) { Chris@0: $result[$key] = array('float', $value); Chris@0: } elseif (is_resource($value)) { Chris@0: $result[$key] = array('resource', get_resource_type($value)); Chris@0: } else { Chris@0: $result[$key] = array('string', (string) $value); Chris@0: } Chris@0: } Chris@0: Chris@0: return $result; Chris@0: } Chris@0: Chris@0: private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) Chris@0: { Chris@0: $array = new \ArrayObject($value); Chris@0: Chris@0: return $array['__PHP_Incomplete_Class_Name']; Chris@0: } Chris@0: }