11<?php namespace Understand \UnderstandLaravel5 ;
22
3+ use Exception ;
4+ use Throwable ;
5+ use InvalidArgumentException ;
6+ use SplFileObject ;
7+
38class ExceptionEncoder
49{
510
11+ /**
12+ * @var string
13+ */
14+ protected $ projectRoot ;
15+
16+ /**
17+ * @param string $projectRoot
18+ */
19+ public function setProjectRoot ($ projectRoot )
20+ {
21+ $ this ->projectRoot = $ projectRoot ;
22+ }
23+
624 /**
725 * Serialize exception object
826 *
9- * @param \Exception $exception
10- * @return type
27+ * @param mixed $exception
28+ * @return array
1129 */
12- public function exceptionToArray (\ Exception $ exception )
30+ public function exceptionToArray ($ exception )
1331 {
32+ if ( ! ($ exception instanceof Exception || $ exception instanceof Throwable))
33+ {
34+ throw new InvalidArgumentException ('$exception must be instance of Exception or Throwable ' );
35+ }
36+
1437 $ trace = $ exception ->getTrace ();
1538 $ className = get_class ($ exception );
1639 $ message = $ exception ->getMessage () ? $ exception ->getMessage () : $ className ;
@@ -19,24 +42,103 @@ public function exceptionToArray(\Exception $exception)
1942 'message ' => $ message ,
2043 'class ' => $ className ,
2144 'code ' => $ exception ->getCode (),
22- 'file ' => $ exception ->getFile (),
45+ 'file ' => $ this -> removeProjectRoot ( $ exception ->getFile () ),
2346 'line ' => $ exception ->getLine (),
24- 'stack ' => $ this ->stackTraceToArray ($ trace )
47+ 'stack ' => $ this ->stackTraceToArray ($ trace, $ exception -> getFile (), $ exception -> getLine () )
2548 ];
2649 }
2750
51+ /**
52+ * @param array $errorLog
53+ * @return array
54+ */
55+ public function setCurrentStackTrace (array $ errorLog )
56+ {
57+ $ level = isset ($ errorLog ['level ' ]) ? $ errorLog ['level ' ] : null ;
58+ $ stackTrace = $ this ->getCurrentStackTrace ($ level );
59+ $ firstLineSet = false ;
60+
61+ foreach ($ stackTrace as $ trace )
62+ {
63+ if ($ firstLineSet )
64+ {
65+ break ;
66+ }
67+
68+ $ firstLineSet = true ;
69+
70+ $ errorLog ['class ' ] = null ;
71+ $ errorLog ['file ' ] = isset ($ trace ['file ' ]) ? $ trace ['file ' ] : null ;
72+ $ errorLog ['line ' ] = isset ($ trace ['line ' ]) ? $ trace ['line ' ] : null ;
73+ }
74+
75+ $ errorLog ['stack ' ] = $ stackTrace ;
76+
77+ return $ errorLog ;
78+ }
79+
80+ /**
81+ * @return array
82+ */
83+ protected function getCurrentStackTrace ()
84+ {
85+ $ stackTrace = debug_backtrace (DEBUG_BACKTRACE_PROVIDE_OBJECT , 100 );
86+ $ vendorExcluded = false ;
87+
88+ foreach ($ stackTrace as $ index => $ trace )
89+ {
90+ // exclude Understand service provider and helper classes
91+ if (isset ($ trace ['class ' ]) && strpos ($ trace ['class ' ], 'Understand\UnderstandLaravel5 \\' ) === 0 )
92+ {
93+ unset($ stackTrace [$ index ]);
94+ }
95+
96+ if ( ! isset ($ trace ['file ' ]))
97+ {
98+ $ vendorExcluded = true ;
99+ }
100+
101+ if ($ vendorExcluded )
102+ {
103+ continue ;
104+ }
105+
106+ // exclude `vendor` folder until project path reached
107+ if (strpos ($ trace ['file ' ], $ this ->projectRoot . 'vendor ' . DIRECTORY_SEPARATOR ) === 0 )
108+ {
109+ unset($ stackTrace [$ index ]);
110+ }
111+ else
112+ {
113+ $ vendorExcluded = true ;
114+ }
115+ }
116+
117+ return $ this ->stackTraceToArray ($ stackTrace );
118+ }
119+
28120 /**
29121 * Serialize stack trace to array
30122 *
31123 * @param array $stackTrace
32124 * @return array
33125 */
34- public function stackTraceToArray (array $ stackTrace )
126+ public function stackTraceToArray (array $ stackTrace, $ topFile = null , $ topLine = null )
35127 {
36128 $ stack = [];
37129
38130 foreach ($ stackTrace as $ trace )
39131 {
132+ // Exception object `getTrace` does not return file and line number for the first line
133+ // http://php.net/manual/en/exception.gettrace.php#107563
134+ if (isset ($ topFile , $ topLine ) && $ topFile && $ topLine )
135+ {
136+ $ trace ['file ' ] = $ topFile ;
137+ $ trace ['line ' ] = $ topLine ;
138+
139+ unset($ topFile , $ topLine );
140+ }
141+
40142 $ type = $ this ->stackTraceCallToString ($ trace );
41143 $ args = $ this ->stackTraceArgsToArray ($ trace );
42144
@@ -46,13 +148,57 @@ public function stackTraceToArray(array $stackTrace)
46148 'args ' => $ args ,
47149 'type ' => $ type ,
48150 'file ' => $ this ->getStackTraceFile ($ trace ),
49- 'line ' => $ this ->getStackTraceLine ($ trace )
151+ 'line ' => $ this ->getStackTraceLine ($ trace ),
152+ 'code ' => $ this ->getCode ($ this ->getStackTraceFile ($ trace ), $ this ->getStackTraceLine ($ trace )),
50153 ];
51154 }
52155
53156 return $ stack ;
54157 }
55158
159+ /**
160+ * @param $relativePath
161+ * @param $line
162+ * @param int $linesAround
163+ * @return array|void
164+ */
165+ public function getCode ($ relativePath , $ line , $ linesAround = 6 )
166+ {
167+ if ( ! $ relativePath || ! $ line )
168+ {
169+ return ;
170+ }
171+
172+ $ filePath = $ this ->projectRoot . $ relativePath ;
173+
174+ try
175+ {
176+ $ file = new SplFileObject ($ filePath );
177+ $ file ->setMaxLineLen (250 );
178+ $ file ->seek (PHP_INT_MAX );
179+ $ codeLines = [];
180+
181+ $ from = max (0 , $ line - $ linesAround - 2 );
182+ $ to = min ($ line + $ linesAround -1 , $ file ->key () + 1 );
183+
184+ $ file ->seek ($ from );
185+
186+ while ($ file ->key () < $ to && ! $ file ->eof ())
187+ {
188+ $ file ->next ();
189+ // `key()` returns 0 as the first line
190+ $ codeLines [] = [
191+ 'line ' => $ file ->key () + 1 ,
192+ 'code ' => rtrim ($ file ->current ())
193+ ];
194+ }
195+
196+ return $ codeLines ;
197+ }
198+ catch (\Exception $ e )
199+ {}
200+ }
201+
56202 /**
57203 * Return stack trace line number
58204 *
@@ -77,7 +223,7 @@ protected function getStackTraceFile(array $trace)
77223 {
78224 if (isset ($ trace ['file ' ]))
79225 {
80- return $ trace ['file ' ];
226+ return $ this -> removeProjectRoot ( $ trace ['file ' ]) ;
81227 }
82228 }
83229
@@ -159,4 +305,15 @@ protected function stackTraceArgsToArray(array $trace)
159305 return $ params ;
160306 }
161307
308+ /**
309+ * @param $path
310+ * @return string
311+ */
312+ protected function removeProjectRoot ($ path )
313+ {
314+ if (substr ($ path , 0 , strlen ($ this ->projectRoot )) == $ this ->projectRoot )
315+ {
316+ return substr ($ path , strlen ($ this ->projectRoot ));
317+ }
318+ }
162319}
0 commit comments