ProxyDBExtension::findSource()   F
last analyzed

Complexity

Conditions 24
Paths 5634

Size

Total Lines 111
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 24
eloc 70
c 2
b 0
f 0
nc 5634
nop 0
dl 0
loc 111
rs 0

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 LeKoala\DebugBar\Extension;
4
5
use SqlFormatter;
6
use SilverStripe\ORM\DB;
7
use LeKoala\DebugBar\DebugBar;
8
use SilverStripe\Core\Extension;
9
use SilverStripe\Control\Controller;
10
use TractorCow\ClassProxy\Generators\ProxyGenerator;
11
12
class ProxyDBExtension extends Extension
13
{
14
    const MAX_FIND_SOURCE_LEVEL = 3;
15
16
    /**
17
     * Store queries
18
     *
19
     * @var array
20
     */
21
    protected static $queries = array();
22
23
    /**
24
     * Find source toggle (set by config find_source)
25
     *
26
     * @var boolean
27
     */
28
    protected static $findSource = true;
29
30
    public function updateProxy(ProxyGenerator &$proxy)
31
    {
32
        self::$findSource = DebugBar::config()->get('find_source');
33
34
        // In the closure, $this is the proxied database
35
        $callback = function ($args, $next) {
36
37
            // The first argument is always the sql query
38
            $sql = $args[0];
39
            $parameters = isset($args[2]) ? $args[2] : array();
40
41
            // Sql can be an array
42
            // TODO: verify if it's still the case in SS4
43
            if (is_array($sql)) {
44
                $parameters = $sql[1];
45
                $sql = $sql[0];
46
            }
47
48
            // Inline sql
49
            $sql = DB::inline_parameters($sql, $parameters);
50
51
            // Get time and memory for the request
52
            $startTime = microtime(true);
53
            $startMemory = memory_get_usage(true);
54
55
            // Execute all middleware
56
            $handle = $next(...$args);
57
58
            // Get time and memory after the request
59
            $endTime = microtime(true);
60
            $endMemory = memory_get_usage(true);
61
62
            // Show query on screen
63
            if (DebugBar::getShowQueries()) {
64
                $formattedSql = SqlFormatter::format($sql);
65
                $rows = $handle->numRecords();
66
67
                echo '<pre>The following query took <b>' . round($endTime - $startTime, 4) . '</b>s an returned <b>' . $rows . "</b> row(s) \n";
68
                echo 'Triggered by: <i>' . self::findSource() . '</i></pre>';
69
                echo $formattedSql;
70
71
                // Preview results
72
                $results = iterator_to_array($handle);
73
                if ($rows > 0) {
74
                    if ($rows == 1) {
75
                        dump($results[0]);
76
                    } else {
77
                        $linearValues = count($results[0]);
78
                        if ($linearValues) {
79
                            dump(implode(
80
                                ',',
81
                                array_map(
82
                                    function ($item) {
83
                                        return $item[key($item)];
84
                                    },
85
                                    $results
86
                                )
87
                            ));
88
                        } else {
89
                            if ($rows < 20) {
90
                                dump($results);
91
                            } else {
92
                                dump("Too many results to display");
93
                            }
94
                        }
95
                    }
96
                }
97
                echo '<hr/>';
98
99
                $handle->rewind(); // Rewind the results
100
            }
101
102
            // Sometimes, ugly spaces are there
103
            $sql = preg_replace('/[[:blank:]]+/', ' ', trim($sql));
104
105
            // Sometimes, the select statement can be very long and unreadable
106
            $shortsql = $sql;
107
            $matches = null;
108
            preg_match_all('/SELECT(.+?) FROM/is', $sql, $matches);
109
            $select = empty($matches[1]) ? null : trim($matches[1][0]);
110
            if (strlen($select) > 100) {
0 ignored issues
show
Bug introduced by Thomas
It seems like $select can also be of type null; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
            if (strlen(/** @scrutinizer ignore-type */ $select) > 100) {
Loading history...
111
                $shortsql = str_replace($select, '"ClickToShowFields"', $sql);
112
            } else {
113
                $select = null;
114
            }
115
116
            self::$queries[] = array(
117
                'short_query' => $shortsql,
118
                'select' => $select,
119
                'query' => $sql,
120
                'start_time' => $startTime,
121
                'end_time' => $endTime,
122
                'duration' => $endTime - $startTime,
123
                'memory' => $endMemory - $startMemory,
124
                'rows' => $handle ? $handle->numRecords() : null,
125
                'success' => $handle ? true : false,
126
                'database' => $this->getSelectedDatabase(),
0 ignored issues
show
Bug introduced by Thomas
The method getSelectedDatabase() does not exist on LeKoala\DebugBar\Extension\ProxyDBExtension. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
                'database' => $this->/** @scrutinizer ignore-call */ getSelectedDatabase(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
127
                'source' => self::$findSource ? self::findSource() : null
128
            );
129
130
            return $handle;
131
        };
132
133
        // Attach to benchmarkQuery to fire on both query and preparedQuery
134
        $proxy = $proxy->addMethod('benchmarkQuery', $callback);
135
    }
136
137
    /**
138
     * Reset queries array
139
     *
140
     * Helpful for long running process and avoid accumulating queries
141
     *
142
     * @return array
143
     */
144
    public static function resetQueries()
145
    {
146
        self::$queries = [];
147
    }
148
149
    /**
150
     * @return array
151
     */
152
    public static function getQueries()
153
    {
154
        return self::$queries;
155
    }
156
157
    protected static function findSource()
158
    {
159
        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
160
161
        // Not relevant to determine source
162
        $internalClasses = array(
163
            '',
164
            get_called_class(),
165
            // DebugBar
166
            DebugBar::class,
167
            \LeKoala\DebugBar\Middleware\DebugBarMiddleware::class,
168
            // Proxy
169
            ProxyDBExtension::class,
170
            \TractorCow\ClassProxy\Proxied\ProxiedBehaviour::class,
171
            // Orm
172
            \SilverStripe\ORM\Connect\Database::class,
173
            \SilverStripe\ORM\Connect\DBSchemaManager::class,
174
            \SilverStripe\ORM\Connect\MySQLDatabase::class,
175
            \SilverStripe\ORM\Connect\MySQLSchemaManager::class,
176
            \SilverStripe\ORM\DataObjectSchema::class,
177
            \SilverStripe\ORM\DB::class,
178
            \SilverStripe\ORM\Queries\SQLExpression::class,
179
            \SilverStripe\ORM\DataList::class,
180
            \SilverStripe\ORM\DataObject::class,
181
            \SilverStripe\ORM\DataQuery::class,
182
            \SilverStripe\ORM\Queries\SQLSelect::class,
183
            \SilverStripe\ORM\Map::class,
184
            \SilverStripe\ORM\ListDecorator::class,
185
            // Core
186
            \SilverStripe\Control\Director::class,
187
        );
188
189
        $viewerClasses = array(
190
            \SilverStripe\View\SSViewer_DataPresenter::class,
191
            \SilverStripe\View\SSViewer_Scope::class,
192
            \SilverStripe\View\SSViewer::class,
193
            \LeKoala\DebugBar\Proxy\SSViewerProxy::class,
194
            \SilverStripe\View\ViewableData::class
195
        );
196
197
        $sources = array();
198
        foreach ($traces as $trace) {
199
            $file = isset($trace['file']) ? pathinfo($trace['file'], PATHINFO_FILENAME) : null;
200
            $class = isset($trace['class']) ? $trace['class'] : null;
201
            $line = isset($trace['line']) ? $trace['line'] : null;
202
            $function = isset($trace['function']) ? $trace['function'] : null;
203
            $type = isset($trace['type']) ? $trace['type'] : '::';
204
205
            /* @var $object SSViewer */
206
            $object = isset($trace['object']) ? $trace['object'] : null;
207
208
            if (in_array($class, $internalClasses)) {
209
                continue;
210
            }
211
212
            // Viewer classes need special handling
213
            if (in_array($class, $viewerClasses)) {
214
                if ($function == 'includeGeneratedTemplate') {
215
                    $templates = $object->templates();
216
217
                    $template = null;
218
                    if (isset($templates['main'])) {
219
                        $template = basename($templates['main']);
220
                    } else {
221
                        $keys = array_keys($templates);
222
                        $key = reset($keys);
223
                        if (isset($templates[$key])) {
224
                            $template = $key . ':' . basename($templates[$key]);
225
                        }
226
                    }
227
                    if (!empty($template)) {
228
                        $sources[] = $template;
229
                    }
230
                }
231
                continue;
232
            }
233
234
            $name = $class;
235
            if ($class && !DebugBar::config()->get('show_namespaces')) {
236
                $nameArray = explode("\\", $class);
237
                $name = array_pop($nameArray);
238
239
                // Maybe we are inside a trait?
240
                if ($file && $file != $name) {
241
                    $name .= '(' . $file . ')';
0 ignored issues
show
Bug introduced by Thomas
Are you sure $file of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

241
                    $name .= '(' . /** @scrutinizer ignore-type */ $file . ')';
Loading history...
242
                }
243
            }
244
            if ($function) {
245
                $name .= $type . $function;
246
            }
247
            if ($line) {
248
                // Line number could apply to a trait
249
                $name .= ':' . $line;
250
            }
251
252
            $sources[] = $name;
253
254
            if (count($sources) > self::MAX_FIND_SOURCE_LEVEL) {
255
                break;
256
            }
257
258
            // We reached a Controller, exit loop
259
            if ($object && $object instanceof Controller) {
260
                break;
261
            }
262
        }
263
264
        if (empty($sources)) {
265
            return 'Undefined source';
266
        }
267
        return implode(' > ', $sources);
268
    }
269
}
270