Completed
Push — master ( 53fb95...674a62 )
by recca
31:52 queued 27:07
created

Helper::hightlight()   C

Complexity

Conditions 16
Paths 1

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 16.0476

Importance

Changes 0
Metric Value
cc 16
nc 1
nop 3
dl 0
loc 62
rs 5.5666
c 0
b 0
f 0
ccs 33
cts 35
cp 0.9429
crap 16.0476

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
namespace Recca0120\LaravelTracy\Panels;
4
5
use PDO;
6
use DateTime;
7
8
class Helper
9
{
10
    /**
11
     * KEYWORDS1.
12
     *
13
     * @var string
14
     */
15
    const KEYWORDS1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
16
17
    /**
18
     * KEYWORDS2.
19
     *
20
     * @var string
21
     */
22
    const KEYWORDS2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|[RI]?LIKE|REGEXP|TRUE|FALSE';
23
24
    /**
25
     * Returns syntax highlighted SQL command.
26
     *
27
     *
28
     * @param string $sql
29
     * @param array $bindings
30
     * @param \PDO $pdo
31
     * @return string
32
     */
33 3
    public static function hightlight($sql, array $bindings = [], PDO $pdo = null)
34
    {
35
        // insert new lines
36 3
        $sql = " $sql ";
37 3
        $sql = preg_replace('#(?<=[\\s,(])('.static::KEYWORDS1.')(?=[\\s,)])#i', "\n\$1", $sql);
38
39
        // reduce spaces
40 3
        $sql = preg_replace('#[ \t]{2,}#', ' ', $sql);
41
42
        // syntax highlight
43 3
        $sql = htmlspecialchars($sql, ENT_IGNORE, 'UTF-8');
44
        $sql = preg_replace_callback('#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])('.static::KEYWORDS1.')(?=[\\s,)])|(?<=[\\s,(=])('.static::KEYWORDS2.')(?=[\\s,)=])#is', function ($matches) {
45 3
            if (! empty($matches[1])) { // comment
46 1
                return '<em style="color:gray">'.$matches[1].'</em>';
47 3
            } elseif (! empty($matches[2])) { // error
48 1
                return '<strong style="color:red">'.$matches[2].'</strong>';
49 3
            } elseif (! empty($matches[3])) { // most important keywords
50 3
                return '<strong style="color:blue; text-transform: uppercase;">'.$matches[3].'</strong>';
51 1
            } elseif (! empty($matches[4])) { // other keywords
52 1
                return '<strong style="color:green">'.$matches[4].'</strong>';
53
            }
54 3
        }, $sql);
55
56
        $bindings = array_map(function ($binding) use ($pdo) {
57 3
            if (is_array($binding) === true) {
58
                $binding = implode(', ', array_map(function ($value) {
59 1
                    return is_string($value) === true ? htmlspecialchars('\''.$value.'\'', ENT_NOQUOTES, 'UTF-8') : $value;
60 1
                }, $binding));
61
62 1
                return htmlspecialchars('('.$binding.')', ENT_NOQUOTES, 'UTF-8');
63
            }
64
65 3
            if (is_string($binding) === true && (preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $binding) || preg_last_error())) {
66
                return '<i title="Length '.strlen($binding).' bytes">&lt;binary&gt;</i>';
67
            }
68
69 3
            if (is_string($binding) === true) {
70 3
                $text = htmlspecialchars($pdo ? $pdo->quote($binding) : '\''.$binding.'\'', ENT_NOQUOTES, 'UTF-8');
71
72 3
                return '<span title="Length '.strlen($text).' characters">'.$text.'</span>';
73
            }
74
75 1
            if (is_resource($binding) === true) {
76 1
                $type = get_resource_type($binding);
77 1
                if ($type === 'stream') {
78 1
                    $info = stream_get_meta_data($binding);
79
                }
80
81 1
                return '<i'.(isset($info['uri']) ? ' title="'.htmlspecialchars($info['uri'], ENT_NOQUOTES, 'UTF-8').'"' : null)
82 1
                    .'>&lt;'.htmlspecialchars($type, ENT_NOQUOTES, 'UTF-8').' resource&gt;</i>';
83
            }
84
85 1
            if ($binding instanceof DateTime) {
86 1
                return htmlspecialchars('\''.$binding->format('Y-m-d H:i:s').'\'', ENT_NOQUOTES, 'UTF-8');
87
            }
88
89
            return htmlspecialchars($binding, ENT_NOQUOTES, 'UTF-8');
90 3
        }, $bindings);
91 3
        $sql = str_replace(['%', '?'], ['%%', '%s'], $sql);
92
93 3
        return '<div><code>'.nl2br(trim(vsprintf($sql, $bindings))).'</code></div>';
94
    }
95
96
    /**
97
     * perform quer analysis hint.
98
     *
99
     * @param string $sql
100
     * @param string $version
101
     * @param float $driver
102
     * @return array
103
     */
104 2
    public static function performQueryAnalysis($sql, $version = null, $driver = null)
105
    {
106 2
        $hints = [];
107 2
        if (preg_match('/^\\s*SELECT\\s*`?[a-zA-Z0-9]*`?\\.?\\*/i', $sql)) {
108 2
            $hints[] = 'Use <code>SELECT *</code> only if you need all columns from table';
109
        }
110 2
        if (preg_match('/ORDER BY RAND()/i', $sql)) {
111 1
            $hints[] = '<code>ORDER BY RAND()</code> is slow, try to avoid if you can.
112
                You can <a href="http://stackoverflow.com/questions/2663710/how-does-mysqls-order-by-rand-work">read this</a>
113
                or <a href="http://stackoverflow.com/questions/1244555/how-can-i-optimize-mysqls-order-by-rand-function">this</a>';
114
        }
115 2
        if (strpos($sql, '!=') !== false) {
116 1
            $hints[] = 'The <code>!=</code> operator is not standard. Use the <code>&lt;&gt;</code> operator to test for inequality instead.';
117
        }
118 2
        if (stripos($sql, 'WHERE') === false) {
119 1
            $hints[] = 'The <code>SELECT</code> statement has no <code>WHERE</code> clause and could examine many more rows than intended';
120
        }
121 2
        if (preg_match('/LIMIT\\s/i', $sql) && stripos($sql, 'ORDER BY') === false) {
122 1
            $hints[] = '<code>LIMIT</code> without <code>ORDER BY</code> causes non-deterministic results, depending on the query execution plan';
123
        }
124 2
        if (preg_match('/LIKE\\s[\'"](%.*?)[\'"]/i', $sql, $matches)) {
125 1
            $hints[] = 'An argument has a leading wildcard character: <code>'.$matches[1].'</code>.
126
                The predicate with this argument is not sargable and cannot use an index if one exists.';
127
        }
128 2
        if ($version < 5.5 && $driver === 'mysql') {
129 2
            if (preg_match('/\\sIN\\s*\\(\\s*SELECT/i', $sql)) {
130 1
                $hints[] = '<code>IN()</code> and <code>NOT IN()</code> subqueries are poorly optimized in that MySQL version : '.$version.
131 1
                    '. MySQL executes the subquery as a dependent subquery for each row in the outer query';
132
            }
133
        }
134
135 2
        return $hints;
136
    }
137
138
    /**
139
     * explain sql.
140
     *
141
     * @param PDO $pdo
142
     * @param string $sql
143
     * @param array $bindings
144
     * @return array
145
     */
146 1
    public static function explain(PDO $pdo, $sql, $bindings = [])
147
    {
148 1
        $explains = [];
149 1
        if (preg_match('#\s*\(?\s*SELECT\s#iA', $sql)) {
150 1
            $statement = $pdo->prepare('EXPLAIN '.$sql);
151 1
            $statement->execute($bindings);
152 1
            $explains = $statement->fetchAll(PDO::FETCH_CLASS);
153
        }
154
155 1
        return $explains;
156
    }
157
}
158