Completed
Push — fetcher_factories ( 10a56f...fd93ea )
by David
13:44
created

ExceptionUtils   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 227
Duplicated Lines 12.33 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 43
c 5
b 0
f 0
lcom 1
cbo 0
dl 28
loc 227
rs 8.3157

7 Methods

Rating   Name   Duplication   Size   Complexity  
C getHTMLBackTrace() 14 35 11
B getHtmlForException() 0 29 2
A getTextForException() 0 11 1
C getTextBackTrace() 14 34 12
D getPhpVariableAsText() 0 30 10
A displayFile() 0 12 2
B getRelativePath() 0 27 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ExceptionUtils often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ExceptionUtils, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Mouf\Mvc\Splash\Utils;
4
5
class ExceptionUtils
6
{
7
    /**
8
     * Returns the Exception Backtrace as a nice HTML view.
9
     *
10
     * @param unknown_type $backtrace
11
     *
12
     * @return unknown
13
     */
14
    private static function getHTMLBackTrace($backtrace)
15
    {
16
        $str = '';
17
18
        foreach ($backtrace as $step) {
19
            if ($step['function'] != 'getHTMLBackTrace' && $step['function'] != 'handle_error') {
20
                $str .= '<tr><td style="border-bottom: 1px solid #EEEEEE">';
21
                $str .= ((isset($step['class'])) ? htmlspecialchars($step['class'], ENT_NOQUOTES, 'UTF-8') : '').
22
                ((isset($step['type'])) ? htmlspecialchars($step['type'], ENT_NOQUOTES, 'UTF-8') : '').htmlspecialchars($step['function'], ENT_NOQUOTES, 'UTF-8').'(';
23
24 View Code Duplication
                if (is_array($step['args'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
25
                    $drawn = false;
26
                    $params = '';
27
                    foreach ($step['args'] as $param) {
28
                        $params .= self::getPhpVariableAsText($param);
29
                        //$params .= var_export($param, true);
30
                        $params .= ', ';
31
                        $drawn = true;
32
                    }
33
                    $str .= htmlspecialchars($params, ENT_NOQUOTES, 'UTF-8');
34
                    if ($drawn == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
35
                        $str = substr($str, 0, strlen($str) - 2);
36
                    }
37
                }
38
                $str .= ')';
39
                $str .= '</td><td style="border-bottom: 1px solid #EEEEEE">';
40
                $str .= ((isset($step['file'])) ? htmlspecialchars(self::displayFile($step['file']), ENT_NOQUOTES, 'UTF-8') : '');
41
                $str .= '</td><td style="border-bottom: 1px solid #EEEEEE">';
42
                $str .= ((isset($step['line'])) ? $step['line'] : '');
43
                $str .= '</td></tr>';
44
            }
45
        }
46
47
        return $str;
48
    }
49
50
    /**
51
     * Function called to display an exception if it occurs.
52
     * It will make sure to purge anything in the buffer before calling the exception displayer.
53
     *
54
     * @param Exception $exception
55
     */
56
    public static function getHtmlForException(\Exception $exception)
57
    {
58
        //global $sys_error_reporting_mail;
59
        //global $sys_error_messages;
60
        $msg = '';
0 ignored issues
show
Unused Code introduced by
$msg is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
61
62
        $msg = '<table>';
63
64
        $display_errors = ini_get('display_errors');
0 ignored issues
show
Unused Code introduced by
$display_errors is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
65
        $color = '#FF0000';
66
        $type = 'Uncaught '.get_class($exception);
67
        if ($exception->getCode() != null) {
68
            $type .= ' with error code '.$exception->getCode();
69
        }
70
71
        $msg .= "<tr><td colspan='3' style='background-color:$color; color:white; text-align:center'><b>$type</b></td></tr>";
72
73
        $msg .= "<tr><td style='background-color:#AAAAAA; color:white; text-align:center'>Context/Message</td>";
74
        $msg .= "<td style='background-color:#AAAAAA; color:white; text-align:center'>File</td>";
75
        $msg .= "<td style='background-color:#AAAAAA; color:white; text-align:center'>Line</td></tr>";
76
77
        $msg .= "<tr><td style='background-color:#EEEEEE; color:black'><b>".nl2br($exception->getMessage()).'</b></td>';
78
        $msg .= "<td style='background-color:#EEEEEE; color:black'>".self::displayFile($exception->getFile()).'</td>';
79
        $msg .= "<td style='background-color:#EEEEEE; color:black'>".$exception->getLine().'</td></tr>';
80
        $msg .= self::getHTMLBackTrace($exception->getTrace());
0 ignored issues
show
Documentation introduced by
$exception->getTrace() is of type array, but the function expects a object<Mouf\Mvc\Splash\Utils\unknown_type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
81
        $msg .= '</table>';
82
83
        return $msg;
84
    }
85
86
    /**
87
     * Function called to display an exception if it occurs.
88
     * It will make sure to purge anything in the buffer before calling the exception displayer.
89
     *
90
     * @param Exception $exception
91
     */
92
    public static function getTextForException(\Exception $exception)
93
    {
94
        // Now, let's compute the same message, but without the HTML markup for the error log.
95
        $textTrace = 'Message: '.$exception->getMessage()."\n";
96
        $textTrace .= 'File: '.$exception->getFile()."\n";
97
        $textTrace .= 'Line: '.$exception->getLine()."\n";
98
        $textTrace .= "Stacktrace:\n";
99
        $textTrace .= self::getTextBackTrace($exception->getTrace());
0 ignored issues
show
Documentation introduced by
$exception->getTrace() is of type array, but the function expects a object<Mouf\Mvc\Splash\Utils\unknown_type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
100
101
        return $textTrace;
102
    }
103
    /**
104
     * Returns the Exception Backtrace as a text string.
105
     *
106
     * @param unknown_type $backtrace
107
     *
108
     * @return unknown
109
     */
110
    private static function getTextBackTrace($backtrace)
111
    {
112
        $str = '';
113
114
        foreach ($backtrace as $step) {
115
            if ($step['function'] != 'getTextBackTrace' && $step['function'] != 'handle_error') {
116
                if (isset($step['file']) && isset($step['line'])) {
117
                    $str .= 'In '.$step['file'].' at line '.$step['line'].': ';
118
                }
119
                if (isset($step['class']) && isset($step['type']) && isset($step['function'])) {
120
                    $str .= $step['class'].$step['type'].$step['function'].'(';
121
                }
122
123 View Code Duplication
                if (is_array($step['args'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
                    $drawn = false;
125
                    $params = '';
126
                    foreach ($step['args'] as $param) {
127
                        $params .= self::getPhpVariableAsText($param);
128
                        //$params .= var_export($param, true);
129
                        $params .= ', ';
130
                        $drawn = true;
131
                    }
132
                    $str .= $params;
133
                    if ($drawn == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
134
                        $str = substr($str, 0, strlen($str) - 2);
135
                    }
136
                }
137
                $str .= ')';
138
                $str .= "\n";
139
            }
140
        }
141
142
        return $str;
143
    }
144
145
    /**
146
     * Used by the debug function to display a nice view of the parameters.
147
     *
148
     * @param unknown_type $var
149
     *
150
     * @return unknown
151
     */
152
    private static function getPhpVariableAsText($var)
153
    {
154
        if (is_string($var)) {
155
            return('"'.str_replace(array("\x00", "\x0a", "\x0d", "\x1a", "\x09"), array('\0', '\n', '\r', '\Z', '\t'), $var).'"');
156
        } elseif (is_int($var) || is_float($var)) {
157
            return($var);
158
        } elseif (is_bool($var)) {
159
            if ($var) {
160
                return('true');
161
            } else {
162
                return('false');
163
            }
164
        } elseif (is_array($var)) {
165
            $result = 'array( ';
166
            $comma = '';
167
            foreach ($var as $key => $val) {
168
                $result .= $comma.self::getPhpVariableAsText($key).' => '.self::getPhpVariableAsText($val);
0 ignored issues
show
Documentation introduced by
$key is of type integer|string, but the function expects a object<Mouf\Mvc\Splash\Utils\unknown_type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
169
                $comma = ', ';
170
            }
171
            $result .= ' )';
172
173
            return($result);
174
        } elseif (is_object($var)) {
175
            return 'Object '.get_class($var);
176
        } elseif (is_resource($var)) {
177
            return 'Resource '.get_resource_type($var);
178
        }
179
180
        return 'Unknown type variable';
181
    }
182
183
    private static function displayFile($file)
184
    {
185
        $realpath = realpath($file);
186
        if (!$realpath) {
187
            // If the file is a phar::// or something...
188
189
            return $file;
190
        }
191
        $cwd = getcwd().DIRECTORY_SEPARATOR;
192
193
        return self::getRelativePath($cwd, $realpath);
194
    }
195
196
    /**
197
     * Returns a relative path based on 2 absolute paths.
198
     *
199
     * @param string $from
200
     * @param string $to
201
     *
202
     * @return string
203
     */
204
    private static function getRelativePath($from, $to)
205
    {
206
        $from = explode('/', $from);
207
        $to = explode('/', $to);
208
        $relPath = $to;
209
210
        foreach ($from as $depth => $dir) {
211
            // find first non-matching dir
212
            if (isset($to[$depth]) && $dir === $to[$depth]) {
213
                // ignore this directory
214
                array_shift($relPath);
215
            } else {
216
                // get number of remaining dirs to $from
217
                $remaining = count($from) - $depth;
218
                if ($remaining > 1) {
219
                    // add traversals up to first matching dir
220
                    $padLength = (count($relPath) + $remaining - 1) * -1;
221
                    $relPath = array_pad($relPath, $padLength, '..');
222
                    break;
223
                } else {
224
                    $relPath[0] = './'.$relPath[0];
225
                }
226
            }
227
        }
228
229
        return implode('/', $relPath);
230
    }
231
}
232