Completed
Push — master ( 4f9c8e...9e0130 )
by Thomas
01:26
created

DebugBarDatabaseNewProxy::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
1
<?php
2
3
/**
4
 * A proxy database to log queries (compatible with 3.2+)
5
 *
6
 * @author Koala
7
 */
8
class DebugBarDatabaseNewProxy extends SS_Database
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9
{
10
    /** @var MySQLDatabase */
11
    protected $realConn;
12
    protected $findSource;
13
14
    /** @var array */
15
    protected $queries;
16
    protected $connector;
17
    protected $schemaManager;
18
    protected $queryBuilder;
19
    protected $showQueries = false;
20
21
    /**
22
     * @param MySQLDatabase|array $realConn
23
     */
24
    public function __construct($realConn)
25
    {
26
        // Some service hooks pass $databaseConfig to constructor
27
        if (is_array($realConn)) {
28
            $realConn = DB::connect($realConn);
29
        }
30
        $this->realConn      = $realConn;
31
        $this->connector     = $realConn->getConnector();
32
        $this->schemaManager = $realConn->getSchemaManager();
33
        $this->queryBuilder  = $realConn->getQueryBuilder();
34
        $this->queries       = array();
35
        $this->findSource    = DebugBar::config()->find_source;
36
    }
37
38
    public function getShowQueries()
39
    {
40
        return $this->showQueries;
41
    }
42
43
    public function setShowQueries($showQueries)
44
    {
45
        $this->showQueries = $showQueries;
46
        return $this;
47
    }
48
49
    /**
50
     * Get the current connector
51
     *
52
     * @return DBConnector
53
     */
54
    public function getConnector()
55
    {
56
        return $this->realConn->getConnector();
57
    }
58
59
    /**
60
     * Injector injection point for connector dependency
61
     *
62
     * @param DBConnector $connector
63
     */
64
    public function setConnector(DBConnector $connector)
65
    {
66
        parent::setConnector($connector);
67
        $this->realConn->setConnector($connector);
68
    }
69
70
    /**
71
     * Returns the current schema manager
72
     *
73
     * @return DBSchemaManager
74
     */
75
    public function getSchemaManager()
76
    {
77
        return $this->realConn->getSchemaManager();
78
    }
79
80
    /**
81
     * Injector injection point for schema manager
82
     *
83
     * @param DBSchemaManager $schemaManager
84
     */
85
    public function setSchemaManager(DBSchemaManager $schemaManager)
86
    {
87
        parent::setSchemaManager($schemaManager);
88
        $this->realConn->setSchemaManager($schemaManager);
89
    }
90
91
    /**
92
     * Returns the current query builder
93
     *
94
     * @return DBQueryBuilder
95
     */
96
    public function getQueryBuilder()
97
    {
98
        return $this->realConn->getQueryBuilder();
99
    }
100
101
    /**
102
     * Injector injection point for schema manager
103
     *
104
     * @param DBQueryBuilder $queryBuilder
105
     */
106
    public function setQueryBuilder(DBQueryBuilder $queryBuilder)
107
    {
108
        parent::setQueryBuilder($queryBuilder);
109
        $this->realConn->setQueryBuilder($queryBuilder);
110
    }
111
112
    /**
113
     * Determines if the query should be previewed, and thus interrupted silently.
114
     * If so, this function also displays the query via the debuging system.
115
     * Subclasess should respect the results of this call for each query, and not
116
     * execute any queries that generate a true response.
117
     *
118
     * @param string $sql The query to be executed
119
     * @return boolean Flag indicating that the query was previewed
120
     */
121
    protected function previewWrite($sql)
0 ignored issues
show
Coding Style introduced by
previewWrite uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
122
    {
123
        // Only preview if previewWrite is set, we are in dev mode, and
124
        // the query is mutable
125
        if (isset($_REQUEST['previewwrite']) && Director::isDev() && $this->connector->isQueryMutable($sql)
126
        ) {
127
            // output preview message
128
            Debug::message("Will execute: $sql");
129
            return true;
130
        } else {
131
            return false;
132
        }
133
    }
134
135
    /**
136
     * Allows the display and benchmarking of queries as they are being run
137
     *
138
     * @param string $sql Query to run, and single parameter to callback
139
     * @param callable $callback Callback to execute code
140
     * @param array $parameters
141
     * @return mixed Result of query
142
     */
143
    protected function benchmarkQuery($sql, $callback, $parameters = array())
144
    {
145
        $starttime   = microtime(true);
146
        $startmemory = memory_get_usage(true);
147
148
        if (is_array($sql)) {
149
            $parameters = $sql[1];
150
            $sql        = $sql[0];
151
        }
152
153
        if ($this->showQueries && Director::isDev()) {
154
            $starttime = microtime(true);
155
            $result    = $callback($sql);
156
            $endtime   = round(microtime(true) - $starttime, 4);
157
158
            $formattedSql = JdornSqlFormatter::format($sql);
159
            $rows         = $result->numRecords();
160
            echo '<pre>The following query took <b>'.$endtime.'</b>s an returned <b>'.$rows."</b> row(s) \n";
161
            echo 'Triggered by: <i>'.$this->findSource().'</i></pre>';
162
            echo $formattedSql;
163
164
            $results = iterator_to_array($result);
165
            if ($rows > 0) {
166
                if ($rows == 1) {
167
                    dump($results[0]);
168
                } else {
169
                    $linearValues = count($results[0]);
170
                    if ($linearValues) {
171
                        dump(implode(
172
                            ',',
173
                            array_map(
174
                                function ($item) {
175
                                    return $item[key($item)];
176
                                },
177
                                $results
178
                            )
179
                        ));
180
                    } else {
181
                        if ($rows < 20) {
182
                            dump($results);
183
                        } else {
184
                            dump("Too many results to display");
185
                        }
186
                    }
187
                }
188
            }
189
            echo '<hr/>';
190
191
            $handle = $result;
192
            $handle->rewind(); // Rewind the results
193
        } else {
194
            /* @var $handle PDOQuery */
195
            $handle = $callback($sql);
196
        }
197
        $endtime   = microtime(true);
198
        $endmemory = memory_get_usage(true);
199
200
        $rawsql = $sql;
201
202
        // Prepared query are not so readable
203
        if (!empty($parameters)) {
204
            foreach ($parameters as $param) {
205
                $pos = strpos($sql, '?');
206
                if ($pos !== false) {
207
                    $param = '"'.$param.'"';
208
                    $sql   = substr_replace($sql, $param, $pos, 1);
209
                }
210
            }
211
        }
212
213
214
        // Sometimes, ugly spaces are there
215
        $sql = preg_replace('/[[:blank:]]+/', ' ', trim($sql));
216
217
        $shortsql = $sql;
218
219
        // Sometimes, the select statement can be very long and unreadable
220
        $matches = null;
221
        preg_match_all('/SELECT(.+?) FROM/is', $sql, $matches);
222
        $select  = empty($matches[1]) ? null : trim($matches[1][0]);
223
        if (strlen($select) > 100) {
224
            $shortsql = str_replace($select, '"ClickToShowFields"', $sql);
225
        } else {
226
            $select = null;
227
        }
228
229
        $this->queries[] = array(
230
            'raw_query' => $rawsql,
231
            'short_query' => $shortsql,
232
            'select' => $select,
233
            'query' => $sql,
234
            'start_time' => $starttime,
235
            'end_time' => $endtime,
236
            'duration' => $endtime - $starttime,
237
            'memory' => $endmemory - $startmemory,
238
            'rows' => $handle ? $handle->numRecords() : null,
239
            'success' => $handle ? true : false,
240
            'database' => $this->getSelectedDatabase(),
241
            'source' => $this->findSource ? $this->findSource() : null
242
        );
243
        return $handle;
244
    }
245
246
    protected function findSource()
247
    {
248
        $traces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
249
250
        // Not relevant to determine source
251
        $internalClasses = array(
252
            'DB', 'SQLExpression', 'DataList', 'DataObject',
253
            'DataQuery', 'SQLSelect', 'SQLQuery', 'SS_Map', 'SS_ListDecorator', 'Object'
254
        );
255
256
        $viewerClasses = array(
257
            'SSViewer_DataPresenter', 'SSViewer_Scope', 'SSViewer',
258
            'ViewableData'
259
        );
260
261
        $sources = array();
262
        foreach ($traces as $trace) {
263
            $class    = isset($trace['class']) ? $trace['class'] : null;
264
            $line     = isset($trace['line']) ? $trace['line'] : null;
265
            $function = isset($trace['function']) ? $trace['function'] : null;
266
            $type     = isset($trace['type']) ? $trace['type'] : '::';
267
268
            /* @var $object SSViewer */
269
            $object = isset($trace['object']) ? $trace['object'] : null;
270
271
            if (!$class) {
272
                continue;
273
            }
274
            if ($function && $function == '{closure}') {
275
                continue;
276
            }
277
            if (strpos($class, 'DebugBar') === 0) {
278
                continue;
279
            }
280
            if (in_array($class, $internalClasses)) {
281
                continue;
282
            }
283
            if (in_array($class, $viewerClasses)) {
284
                if ($function == 'includeGeneratedTemplate') {
285
                    $templates = $object->templates();
286
287
                    $template = null;
288
                    if (isset($templates['main'])) {
289
                        $template = basename($templates['main']);
290
                    } else {
291
                        $keys = array_keys($templates);
292
                        $key  = reset($keys);
293
                        if (isset($templates[$key])) {
294
                            $template = $key.':'.basename($templates[$key]);
295
                        }
296
                    }
297
                    if (!empty($template)) {
298
                        $sources[] = $template;
299
                    }
300
                }
301
                continue;
302
            }
303
304
            $name = $class;
305
            if ($function) {
306
                $name .= $type.$function;
307
            }
308
            if ($line) {
309
                $name .= ':'.$line;
310
            }
311
312
            $sources[] = $name;
313
314
            if (count($sources) > 3) {
315
                break;
316
            }
317
318
319
            // We reached a Controller, exit loop
320
            if ($object && $object instanceof Controller) {
321
                break;
322
            }
323
        }
324
325
        if (empty($sources)) {
326
            return 'Undefined source';
327
        }
328
        return implode(' > ', $sources);
329
    }
330
331
    /**
332
     * Execute the given SQL query.
333
     *
334
     * @param string $sql The SQL query to execute
335
     * @param int $errorLevel The level of error reporting to enable for the query
336
     * @return SS_Query
337
     */
338
    public function query($sql, $errorLevel = E_USER_ERROR)
339
    {
340
        // Check if we should only preview this query
341
        if ($this->previewWrite($sql)) {
342
            return;
343
        }
344
345
        if (!$this->connector) {
346
            $self = $this;
347
            return $this->benchmarkQuery(
348
                $sql,
349
                function ($sql) use ($self, $errorLevel) {
350
                    return $self->oldQuery($sql, $errorLevel);
351
                }
352
            );
353
        }
354
355
        // Benchmark query
356
        $connector = $this->connector;
357
        return $this->benchmarkQuery(
358
            $sql,
359
            function ($sql) use ($connector, $errorLevel) {
360
                return $connector->query($sql, $errorLevel);
361
            }
362
        );
363
    }
364
365
    public function oldQuery($sql, $errorLevel = E_USER_ERROR)
366
    {
367
        return $this->realConn->query($sql, $errorLevel);
368
    }
369
370
    /**
371
     * Execute the given SQL parameterised query with the specified arguments
372
     *
373
     * @param string $sql The SQL query to execute. The ? character will denote parameters.
374
     * @param array $parameters An ordered list of arguments.
375
     * @param int $errorLevel The level of error reporting to enable for the query
376
     * @return SS_Query
377
     */
378
    public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR)
379
    {
380
        // Check if we should only preview this query
381
        if ($this->previewWrite($sql)) {
382
            return;
383
        }
384
385
        // Benchmark query
386
        $connector = $this->connector;
387
        return $this->benchmarkQuery(
388
            array($sql, $parameters),
0 ignored issues
show
Documentation introduced by
array($sql, $parameters) is of type array<integer,string|arr...:"string","1":"array"}>, but the function expects a string.

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...
389
            function ($sql) use ($connector, $parameters, $errorLevel) {
390
                return $connector->preparedQuery($sql, $parameters, $errorLevel);
391
            }
392
        );
393
    }
394
395
    /**
396
     * @return array
397
     */
398
    public function getQueries()
399
    {
400
        return $this->queries;
401
    }
402
403
    /**
404
     * @param string $name
405
     * @param array $arguments
406
     * @return mixed
407
     */
408
    public function __call($name, $arguments)
409
    {
410
        return call_user_func_array(array($this->realConn, $name), $arguments);
411
    }
412
413
    public function addslashes($val)
414
    {
415
        return call_user_func_array(
416
            array($this->realConn, __FUNCTION__),
417
            func_get_args()
418
        );
419
    }
420
421 View Code Duplication
    public function alterTable(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
422
        $table,
423
        $newFields = null,
424
        $newIndexes = null,
425
        $alteredFields = null,
426
        $alteredIndexes = null,
427
        $alteredOptions = null,
428
        $advancedOptions = null
429
    ) {
430
        return call_user_func_array(
431
            array($this->realConn, __FUNCTION__),
432
            func_get_args()
433
        );
434
    }
435
436 View Code Duplication
    public function comparisonClause(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
437
        $field,
438
        $value,
439
        $exact = false,
440
        $negate = false,
441
        $caseSensitive = false,
442
        $parameterised = false
443
    ) {
444
        return call_user_func_array(
445
            array($this->realConn, __FUNCTION__),
446
            func_get_args()
447
        );
448
    }
449
450
    public function createDatabase()
451
    {
452
        return call_user_func_array(
453
            array($this->realConn, __FUNCTION__),
454
            func_get_args()
455
        );
456
    }
457
458
    public function createField($table, $field, $spec)
459
    {
460
        return call_user_func_array(
461
            array($this->realConn, __FUNCTION__),
462
            func_get_args()
463
        );
464
    }
465
466 View Code Duplication
    public function createTable(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
467
        $table,
468
        $fields = null,
469
        $indexes = null,
470
        $options = null,
471
        $advancedOptions = null
472
    ) {
473
        return call_user_func_array(
474
            array($this->realConn, __FUNCTION__),
475
            func_get_args()
476
        );
477
    }
478
479
    public function datetimeDifferenceClause($date1, $date2)
480
    {
481
        return call_user_func_array(
482
            array($this->realConn, __FUNCTION__),
483
            func_get_args()
484
        );
485
    }
486
487
    public function datetimeIntervalClause($date, $interval)
488
    {
489
        return call_user_func_array(
490
            array($this->realConn, __FUNCTION__),
491
            func_get_args()
492
        );
493
    }
494
495
    public function enumValuesForField($tableName, $fieldName)
496
    {
497
        return call_user_func_array(
498
            array($this->realConn, __FUNCTION__),
499
            func_get_args()
500
        );
501
    }
502
503
    public function fieldList($table)
504
    {
505
        return call_user_func_array(
506
            array($this->realConn, __FUNCTION__),
507
            func_get_args()
508
        );
509
    }
510
511
    public function formattedDatetimeClause($date, $format)
512
    {
513
        return call_user_func_array(
514
            array($this->realConn, __FUNCTION__),
515
            func_get_args()
516
        );
517
    }
518
519
    public function getConnect($parameters)
520
    {
521
        return call_user_func_array(
522
            array($this->realConn, __FUNCTION__),
523
            func_get_args()
524
        );
525
    }
526
527
    public function getGeneratedID($table)
528
    {
529
        return call_user_func_array(
530
            array($this->realConn, __FUNCTION__),
531
            func_get_args()
532
        );
533
    }
534
535
    public function hasTable($tableName)
536
    {
537
        return call_user_func_array(
538
            array($this->realConn, __FUNCTION__),
539
            func_get_args()
540
        );
541
    }
542
543
    public function isActive()
544
    {
545
        return call_user_func_array(
546
            array($this->realConn, __FUNCTION__),
547
            func_get_args()
548
        );
549
    }
550
551
    public function renameField($tableName, $oldName, $newName)
552
    {
553
        return call_user_func_array(
554
            array($this->realConn, __FUNCTION__),
555
            func_get_args()
556
        );
557
    }
558
559
    public function renameTable($oldTableName, $newTableName)
560
    {
561
        return call_user_func_array(
562
            array($this->realConn, __FUNCTION__),
563
            func_get_args()
564
        );
565
    }
566
567
    public function supportsTimezoneOverride()
568
    {
569
        return call_user_func_array(
570
            array($this->realConn, __FUNCTION__),
571
            func_get_args()
572
        );
573
    }
574
575
    public function supportsTransactions()
576
    {
577
        return call_user_func_array(
578
            array($this->realConn, __FUNCTION__),
579
            func_get_args()
580
        );
581
    }
582
583
    public function tableList()
584
    {
585
        return call_user_func_array(
586
            array($this->realConn, __FUNCTION__),
587
            func_get_args()
588
        );
589
    }
590
591
    public function transactionEnd($chain = false)
592
    {
593
        return call_user_func_array(
594
            array($this->realConn, __FUNCTION__),
595
            func_get_args()
596
        );
597
    }
598
599
    public function transactionRollback($savepoint = false)
600
    {
601
        return call_user_func_array(
602
            array($this->realConn, __FUNCTION__),
603
            func_get_args()
604
        );
605
    }
606
607
    public function transactionSavepoint($savepoint)
608
    {
609
        return call_user_func_array(
610
            array($this->realConn, __FUNCTION__),
611
            func_get_args()
612
        );
613
    }
614
615
    public function transactionStart($transaction_mode = false, $session_characteristics = false)
616
    {
617
        return call_user_func_array(
618
            array($this->realConn, __FUNCTION__),
619
            func_get_args()
620
        );
621
    }
622
623
    public function clearTable($table)
624
    {
625
        return call_user_func_array(
626
            array($this->realConn, __FUNCTION__),
627
            func_get_args()
628
        );
629
    }
630
631
    public function getDatabaseServer()
632
    {
633
        return call_user_func_array(
634
            array($this->realConn, __FUNCTION__),
635
            func_get_args()
636
        );
637
    }
638
639
    public function now()
640
    {
641
        return call_user_func_array(
642
            array($this->realConn, __FUNCTION__),
643
            func_get_args()
644
        );
645
    }
646
647
    public function random()
648
    {
649
        return call_user_func_array(
650
            array($this->realConn, __FUNCTION__),
651
            func_get_args()
652
        );
653
    }
654
655
    public function searchEngine(
656
        $classesToSearch,
657
        $keywords,
658
        $start,
659
        $pageLength,
660
        $sortBy = "Relevance DESC",
661
        $extraFilter = "",
662
        $booleanSearch = false,
663
        $alternativeFileFilter = "",
664
        $invertedMatch = false
665
    ) {
666
        return call_user_func_array(
667
            array($this->realConn, __FUNCTION__),
668
            func_get_args()
669
        );
670
    }
671
672
    public function supportsCollations()
673
    {
674
        return call_user_func_array(
675
            array($this->realConn, __FUNCTION__),
676
            func_get_args()
677
        );
678
    }
679
}
680