Completed
Pull Request — develop (#720)
by Imants
03:58
created

BaseException::backtrace()   F

Complexity

Conditions 26
Paths 5892

Size

Total Lines 85
Code Lines 66

Duplication

Lines 35
Ratio 41.18 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
cc 26
c 3
b 2
f 0
dl 35
loc 85
rs 2.1418
eloc 66
nc 5892
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * BaseException is parent of all exceptions in Agile Toolkit which
4
 * are meant to be for informational purposes. There are also some
5
 * exceptions (StopInit) which are used for data-flow.
6
 */
7
class BaseException extends Exception
8
{
9
    // Exception defines it's methods as "final", which is complete nonsence
10
    // and incorrect behavor in my opinion. Therefore I need to re-declare
11
    // it's class and re-define the methods so I could extend my own methods
12
    // in my classes.
13
14
    /** @var array Backtrace array */
15
    public $my_backtrace;
16
17
    /** @var int Backtrace shift */
18
    public $shift = 0;
19
20
    /** @var string Classname of exception */
21
    public $name;
22
23
    /** @var string|int error code */
24
    public $code;
25
26
    /** @var array Array with more info */
27
    public $more_info = array();
28
29
    /** @var string Plain text recommendation on how the problem can be solved */
30
    public $recommendation;
31
32
    /** @var array Array of available actions */
33
    public $actions = array();
34
35
    /** @var Exception Link to another exception which caused this one */
36
    public $by_exception = null;
37
38
    /** @var AbstractObject Link to object into which we added this object */
39
    public $owner;
40
41
    /** @var App_CLI Always points to current Application */
42
    public $app;
43
44
    /**
45
     * @deprecated 4.3.0 Left for compatibility with ATK 4.2 and lower, use ->app instead
46
     */
47
    public $api;
48
49
50
51
    /**
52
     * Initialization.
53
     */
54
    public function init()
55
    {
56
    }
57
58
    /**
59
     * On class construct.
60
     *
61
     * @param string $msg  Error message
62
     * @param string|int $code Error code
63
     */
64
    public function __construct($msg, $code = 0)
65
    {
66
        parent::__construct($msg, $code);
67
        $this->collectBasicData($code);
68
    }
69
70
    /**
71
     * Collect basic data of exception.
72
     *
73
     * @param string|int $code Error code
74
     */
75
    public function collectBasicData($code)
76
    {
77
        $this->name = get_class($this);
78
        $this->my_backtrace = debug_backtrace();
79
        array_shift($this->my_backtrace);
80
        array_shift($this->my_backtrace);
81
    }
82
83
    /**
84
     * Call this to add additional information to the exception you are about
85
     * to throw.
86
     *
87
     * @param string $key
88
     * @param mixed $value
89
     *
90
     * @return $this
91
     */
92
    public function addMoreInfo($key, $value)
93
    {
94
        $this->more_info[$key] = $value;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Add reference to the object.
101
     * Do not call this directly, exception() method takes care of that.
102
     *
103
     * @param AbstractObject $obj
104
     */
105
    public function addThis($obj)
106
    {
107
        return $this->addMoreInfo('Raised by object', $obj);
108
    }
109
110
    /**
111
     * Set error code
112
     *
113
     * @param int $code
114
     */
115
    public function setCode(int $code)
116
    {
117
        $this->code = $code;
118
    }
119
120
    /**
121
     * Records another exception as a cause of your current exception.
122
     * Wrapping one exception inside another helps you to track problems
123
     * better.
124
     *
125
     * @param Exception $e
126
     *
127
     * @return $this
128
     */
129
    public function by(Exception $e)
130
    {
131
        $this->by_exception = $e;
132
133
        return $this;
134
    }
135
136
    /**
137
     * Actions will be displayed as links on the exception page allowing viewer
138
     * to perform additional debug functions.
139
     * addAction('show info',array('info'=>true)) will result in link to &info=1.
140
     *
141
     * @param string|array $key
142
     * @param string|array $descr
143
     *
144
     * @return $this
145
     */
146
    public function addAction($key, $descr)
147
    {
148
        if (is_array($key)) {
149
            $this->recommendation = (string) $descr;
150
            $this->actions = array_merge($this->actions, $key);
151
152
            return $this;
153
        }
154
        $this->actions[$key] = $descr;
155
156
        return $this;
157
    }
158
159
    /**
160
     * Return collected backtrace info.
161
     *
162
     * @return array
163
     */
164
    public function getMyTrace()
165
    {
166
        return $this->my_backtrace;
167
    }
168
169
    /**
170
     * Return filename from backtrace log.
171
     *
172
     * @return string
173
     */
174
    public function getMyFile()
175
    {
176
        return $this->my_backtrace[2]['file'];
177
    }
178
179
    /**
180
     * Return line number from backtract log.
181
     *
182
     * @return string
183
     */
184
    public function getMyLine()
185
    {
186
        return $this->my_backtrace[2]['line'];
187
    }
188
189
    /**
190
     * Returns HTML representation of the exception.
191
     *
192
     * @return string
193
     */
194
    public function getHTML()
195
    {
196
        $e = $this;
197
198
        $o = '<div class="atk-layout">';
199
200
        $o .= $this->getHTMLHeader();
201
202
        $o .= $this->getHTMLSolution();
203
204
        //$o.=$this->getHTMLBody();
205
206
        $o .= '<div class="atk-layout-row"><div class="atk-wrapper atk-section-small">';
207 View Code Duplication
        if (isset($e->more_info)) {
208
            $o .= '<h3>Additional information:</h3>';
209
            $o .= $this->print_r($e->more_info, '<ul>', '</ul>', '<li>', '</li>', ' ');
210
        }
211 View Code Duplication
        if (method_exists($e, 'getMyFile')) {
212
            $o .= '<div class="atk-effect-info">'.$e->getMyFile().':'.$e->getMyLine().'</div>';
213
        }
214
215 View Code Duplication
        if (method_exists($e, 'getMyTrace')) {
216
            $o .= $this->backtrace(3, $e->getMyTrace());
217
        } else {
218
            $o .= $this->backtrace(@$e->shift, $e->getTrace());
219
        }
220
221
        if (isset($e->by_exception)) {
222
            $o .= '<h3>This error was triggered by the following error:</h3>';
223
            if ($e->by_exception instanceof self) {
224
                $o .= $e->by_exception->getHTML();
225
            } elseif ($e->by_exception instanceof Exception) {
226
                $o .= $e->by_exception->getMessage();
227
            }
228
        }
229
        $o .= '</div></div>';
230
231
        return $o;
232
    }
233
234
    /**
235
     * @return string
236
     */
237
    public function getHeader()
238
    {
239
        return get_class($this).': '.htmlspecialchars($this->getMessage()).
240
            ($this->getCode() ? ' [code: '.$this->getCode().']' : '');
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getHTMLHeader()
247
    {
248
        return
249
            "<div class='atk-layout-row atk-effect-danger atk-swatch-red'>".
250
            "<div class='atk-wrapper atk-section-small atk-align-center'><h2>".
251
            $this->getHeader().
252
            "</h2>\n".
253
            '</div></div>';
254
    }
255
256
    /**
257
     * @return array
258
     */
259
    public function getSolution()
260
    {
261
        return $this->actions;
262
    }
263
264
    /**
265
     * @return string
266
     */
267
    public function getHTMLSolution()
268
    {
269
        $solution = $this->getSolution();
270
        if (empty($solution)) {
271
            return '';
272
        }
273
274
        return
275
            "<div class='atk-layout-row atk-effect-info'>".
276
            "<div class='atk-wrapper atk-section-small atk-swatch-white atk-align-center'>".
277
            "<h3>".$this->recommendation."</h3>".
278
            $this->getHTMLActions().
279
            '</div></div>';
280
    }
281
282
    /**
283
     * @return string
284
     */
285
    public function getHTMLActions()
286
    {
287
        $o = '';
288
        foreach ($this->actions as $label => $url) {
289
            $o .= "<a href='".$url."' class='atk-button atk-swatch-yellow'>".$label."</a>\n";
290
        }
291
292
        return $o;
293
    }
294
295
    /**
296
     * Utility.
297
     *
298
     * @param array|object|string $key
299
     * @param string $gs
300
     * @param string $ge
301
     * @param string $ls
302
     * @param string $le
303
     * @param string $ind
304
     *
305
     * @return null|string
306
     */
307 View Code Duplication
    public function print_r($key, $gs, $ge, $ls, $le, $ind = ' ')
308
    {
309
        $o = '';
310
        if (strlen($ind) > 3) {
311
            return;
312
        }
313
        if (is_array($key)) {
314
            $o = $gs;
315
            foreach ($key as $a => $b) {
316
                $o .= $ind.$ls.$a.': '.$this->print_r($b, $gs, $ge, $ls, $le, $ind.' ').$le;
317
            }
318
            $o .= $ge;
319
        } elseif (is_object($key)) {
320
            $o .= 'Object '.get_class($key);
321
        } else {
322
            $o .= $gs ? htmlspecialchars($key) : $key;
323
        }
324
325
        return $o;
326
    }
327
328
    /**
329
     * Classes define a DOC constant which points to a on-line resource
330
     * containing documentation for given class. This method will
331
     * return full URL for the specified object.
332
     *
333
     * @param AbstractObject $o
334
     *
335
     * @return bool|string
336
     */
337
    public function getDocURL($o)
338
    {
339
        if (!is_object($o)) {
340
            return false;
341
        }
342
343
        if (!$o instanceof AbstractObject) {
344
            return false;
345
        }
346
347
        /*$refl = new ReflectionClass($o);
348
        $parent = $refl->getParentClass();
349
350
351
        if($parent) {
352
            // check to make sure property is overriden in child
353
            $const = $parent->getConstants();
354
        var_Dump($const);
355
            if ($const['DOC'] == $o::DOC) return false;
356
        }
357
        */
358
359
        $url = $o::DOC;
360
        if (substr($url, 0, 4) != 'http') {
361
            return 'http://book.agiletoolkit.org/'.$url.'.html';
362
        }
363
364
        return $url;
365
    }
366
367
    /**
368
     * @param int $sh
369
     * @param array $backtrace
370
     *
371
     * @return string
372
     */
373
    public function backtrace($sh = null, $backtrace = null)
374
    {
375
        $output = '<div class="atk-box-small atk-table atk-table-zebra">';
376
        $output .= "<table>\n";
377
        $output .= "<tr><th align='right'>File</th><th>Object Name</th><th>Stack Trace</th><th>Help</th></tr>";
378
        if (!isset($backtrace)) {
379
            $backtrace = debug_backtrace();
380
        }
381
        $sh -= 2;
382
383
        $n = 0;
384
        foreach ($backtrace as $bt) {
385
            ++$n;
386
            $args = '';
387
            if (!isset($bt['args'])) {
388
                continue;
389
            }
390 View Code Duplication
            foreach ($bt['args'] as $a) {
391
                if (!empty($args)) {
392
                    $args .= ', ';
393
                }
394
                switch (gettype($a)) {
395
                    case 'integer':
396
                    case 'double':
397
                        $args .= $a;
398
                        break;
399
                    case 'string':
400
                        $a = htmlspecialchars(substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
401
                        $args .= "\"$a\"";
402
                        break;
403
                    case 'array':
404
                        $args .= 'Array('.count($a).')';
405
                        break;
406
                    case 'object':
407
                        $args .= 'Object('.get_class($a).')';
408
                        break;
409
                    case 'resource':
410
                        $args .= 'Resource('.strstr((string) $a, '#').')';
411
                        break;
412
                    case 'boolean':
413
                        $args .= $a ? 'True' : 'False';
414
                        break;
415
                    case 'NULL':
416
                        $args .= 'Null';
417
                        break;
418
                    default:
419
                        $args .= 'Unknown';
420
                }
421
            }
422
423 View Code Duplication
            if (($sh == null && strpos($bt['file'], '/atk4/lib/') === false)
424
                || (!is_int($sh) && $bt['function'] == $sh)
425
            ) {
426
                $sh = $n;
427
            }
428
429
            $doc = $this->getDocURL($bt['object']);
430
            if ($doc) {
431
                $doc .= '#'.get_class($bt['object']).'::'.$bt['function'];
432
            }
433
434
            $output .= '<tr><td valign=top align=right class=atk-effect-'.
435
                ($sh == $n ? 'danger' : 'info').'>'.htmlspecialchars(dirname($bt['file'])).'/'.
436
                '<b>'.htmlspecialchars(basename($bt['file'])).'</b>';
437
            $output .= ":{$bt['line']}</font>&nbsp;</td>";
438
            $name = (!isset($bt['object']->name)) ? get_class($bt['object']) : $bt['object']->name;
439
            if ($name) {
440
                $output .= '<td>'.$name.'</td>';
441
            } else {
442
                $output .= '<td></td>';
443
            }
444
            $output .= '<td valign=top class=atk-effect-'.($sh == $n ? 'danger' : 'success').'>'.
445
                get_class($bt['object'])."{$bt['type']}<b>{$bt['function']}</b>($args)</td>";
446
447
            if ($doc) {
448
                $output .= "<td><a href='".$doc."' target='_blank'><i class='icon-book'></i></a></td>";
449
            } else {
450
                $output .= '<td>&nbsp;</td>';
451
            }
452
            $output .= '</tr>';
453
        }
454
        $output .= "</table></div>\n";
455
456
        return $output;
457
    }
458
459
    /**
460
     * Returns Textual representation of the exception.
461
     *
462
     * @return string
463
     */
464
    public function getText()
465
    {
466
        $more_info = $this->print_r($this->more_info, '[', ']', '', ',', ' ');
467
468
        $text = get_class($this).': '.$this->getMessage().' ('.$more_info.')'.
469
            ' in '.$this->getMyFile().':'.$this->getMyLine();
470
471
        return $text;
472
    }
473
474
    /**
475
     * Redefine this function to add additional HTML output.
476
     *
477
     * @return string
478
     */
479
    public function getDetailedHTML()
480
    {
481
        return '';
482
    }
483
484
    /**
485
     * Undocumented.
486
     *
487
     * @todo Check this method, looks something useless. Optionally used only in Logger class.
488
     *
489
     * @return string
490
     */
491
    public function getAdditionalMessage()
492
    {
493
        return $this->recommendation;
494
    }
495
}
496