Completed
Push — master ( c61fb9...d072ef )
by Thomas
01:45
created

DebugBar::getDebugBar()   B

Complexity

Conditions 11
Paths 4

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
rs 7.1162
cc 11
eloc 11
nc 4
nop 0

How to fix   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
namespace LeKoala\DebugBar;
3
4
use Exception;
5
use Monolog\Logger;
6
use ReflectionObject;
7
use SilverStripe\ORM\DB;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Core\Kernel;
10
use DebugBar\Storage\FileStorage;
11
use SilverStripe\Core\Environment;
12
use SilverStripe\Control\Director;
13
use SilverStripe\View\Requirements;
14
use SilverStripe\Admin\LeftAndMain;
15
use SilverStripe\Control\HTTPRequest;
16
use DebugBar\Bridge\MonologCollector;
17
use SilverStripe\Control\Email\Mailer;
18
use DebugBar\DebugBar as BaseDebugBar;
19
use SilverStripe\Core\Injector\Injector;
20
use LeKoala\DebugBar\Proxy\DatabaseProxy;
21
use SilverStripe\ORM\Connect\PDOConnector;
22
use SilverStripe\Core\Injector\Injectable;
23
use SilverStripe\Core\Config\Configurable;
24
use DebugBar\DataCollector\MemoryCollector;
25
use SilverStripe\Admin\AdminRootController;
26
use LeKoala\DebugBar\Messages\LogFormatter;
27
use DebugBar\DataCollector\PDO\PDOCollector;
28
use DebugBar\DataCollector\PDO\TraceablePDO;
29
use DebugBar\DataCollector\PhpInfoCollector;
30
use LeKoala\DebugBar\Collector\ConfigCollector;
31
use LeKoala\DebugBar\Proxy\ConfigManifestProxy;
32
use LeKoala\DebugBar\Collector\TimeDataCollector;
33
use LeKoala\DebugBar\Collector\DatabaseCollector;
34
use DebugBar\Bridge\SwiftMailer\SwiftLogCollector;
35
use DebugBar\Bridge\SwiftMailer\SwiftMailCollector;
36
use LeKoala\DebugBar\Collector\PartialCacheCollector;
37
use LeKoala\DebugBar\Collector\SilverStripeCollector;
38
use SilverStripe\Config\Collections\CachedConfigCollection;
39
use SilverStripe\Control\Email\SwiftMailer;
40
use DebugBar\DataCollector\MessagesCollector;
41
42
/**
43
 * A simple helper
44
 */
45
class DebugBar
46
{
47
    use Configurable;
48
    use Injectable;
49
50
    /**
51
     * @var DebugBar\DebugBar
52
     */
53
    protected static $debugbar;
54
55
    /**
56
     * @var bool
57
     */
58
    public static $bufferingEnabled = false;
59
60
    /**
61
     * @var DebugBar\JavascriptRenderer
62
     */
63
    protected static $renderer;
64
65
    /**
66
     * @var bool
67
     */
68
    protected static $showQueries = false;
69
70
    /**
71
     * @var HTTPRequest
72
     */
73
    protected static $request;
74
75
    /**
76
     * Get the Debug Bar instance
77
     * @return \DebugBar\StandardDebugBar
78
     * @throws Exception
79
     * @global array $databaseConfig
80
     */
81
    public static function getDebugBar()
82
    {
83
        if (self::$debugbar !== null) {
84
            return self::$debugbar;
85
        }
86
87
        if (!Director::isDev() || self::isDisabled() || self::vendorNotInstalled() ||
88
            self::notLocalIp() || Director::is_cli() || self::isDevUrl() || (self::isAdminUrl() && !self::config()->get('enabled_in_admin'))) {
89
            self::$debugbar = false; // no need to check again
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object<LeKoala\DebugBar\DebugBar\DebugBar> of property $debugbar.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
90
            return;
91
        }
92
93
        self::initDebugBar();
94
95
        if (!self::$debugbar) {
96
            throw new Exception("Failed to initialize the DebugBar");
97
        }
98
99
        return self::$debugbar;
100
    }
101
102
    /**
103
     * Init the debugbar instance
104
     *
105
     * @global array $databaseConfig
106
     * @return DebugBar\StandardDebugBar
107
     */
108
    public static function initDebugBar()
0 ignored issues
show
Coding Style introduced by
initDebugBar 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...
109
    {
110
        // Prevent multiple inits
111
        if (self::$debugbar) {
112
            return self::$debugbar;
113
        }
114
115
        self::$debugbar = $debugbar = new BaseDebugBar;
0 ignored issues
show
Documentation Bug introduced by
It seems like $debugbar = new \DebugBar\DebugBar() of type object<DebugBar\DebugBar> is incompatible with the declared type object<LeKoala\DebugBar\DebugBar\DebugBar> of property $debugbar.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
116
117
        if (isset($_REQUEST['showqueries'])) {
118
            self::setShowQueries(true);
119
            echo "The queries above have been run before we started DebugBar";
120
            echo '<hr>';
121
            unset($_REQUEST['showqueries']);
122
        }
123
124
        $debugbar->addCollector(new PhpInfoCollector);
125
        $debugbar->addCollector(new TimeDataCollector);
126
        $debugbar->addCollector(new MemoryCollector);
127
128
        // Add config proxy replacing the core config manifest
129
        /** @var SilverStripe\Core\Config\ConfigLoader $configLoader */
130
        $configLoader = Injector::inst()->get(Kernel::class)->getConfigLoader();
131
        $configManifest = false;
132
        while ($configLoader->hasManifest()) {
133
            $eachManifest = $configLoader->popManifest();
134
            if ($eachManifest instanceof CachedConfigCollection) {
135
                $configManifest = $eachManifest;
136
                break;
137
            }
138
        }
139
        if ($configManifest) {
140
            $configProxy = new ConfigManifestProxy($configManifest);
141
            $configLoader->pushManifest($configProxy);
142
        }
143
144
        $connector = DB::get_connector();
145
        if (!self::config()->get('force_proxy') && $connector instanceof PDOConnector) {
146
            // Use a little bit of magic to replace the pdo instance
147
            $refObject = new ReflectionObject($connector);
148
            $refProperty = $refObject->getProperty('pdoConnection');
149
            $refProperty->setAccessible(true);
150
            $traceablePdo = new TraceablePDO($refProperty->getValue($connector));
151
            $refProperty->setValue($connector, $traceablePdo);
152
153
            $debugbar->addCollector(new PDOCollector($traceablePdo));
154
        } else {
155
            DB::set_conn($db = new DatabaseProxy(DB::get_conn()));
156
            $db->setShowQueries(self::getShowQueries());
157
            $debugbar->addCollector(new DatabaseCollector);
158
        }
159
160
        // Add message collector last so other collectors can send messages to the console using it
161
        $debugbar->addCollector(new MessagesCollector());
162
163
        // Aggregate monolog into messages
164
        $logger = Injector::inst()->get(LoggerInterface::class);
165
        if ($logger instanceof Logger) {
166
            $logCollector = new MonologCollector($logger);
167
            $logCollector->setFormatter(new LogFormatter);
168
            $debugbar['messages']->aggregate($logCollector);
169
        }
170
171
        // Add some SilverStripe specific infos
172
        $debugbar->addCollector(new SilverStripeCollector);
173
174
        if (self::config()->get('enable_storage')) {
175
            $debugbar->setStorage(new FileStorage(TEMP_FOLDER . '/debugbar'));
176
        }
177
178
        if ($configManifest) {
179
            // Add the config collector
180
            $debugbar->addCollector(new ConfigCollector);
181
        }
182
        $debugbar->addCollector(new PartialCacheCollector);
183
184
        // Email logging
185
        $mailer = Injector::inst()->get(Mailer::class);
186
        if ($mailer instanceof SwiftMailer) {
187
            $swiftInst = $mailer->getSwiftMailer();
188
            $debugbar['messages']->aggregate(new SwiftLogCollector($swiftInst));
189
            $debugbar->addCollector(new SwiftMailCollector($swiftInst));
190
        }
191
192
        // Since we buffer everything, why not enable all dev options ?
193
        if (self::config()->get('auto_debug')) {
194
            $_REQUEST['debug'] = true;
195
            $_REQUEST['debug_request'] = true;
196
        }
197
198
        if (isset($_REQUEST['debug']) || isset($_REQUEST['debug_request'])) {
199
            self::$bufferingEnabled = true;
200
            ob_start(); // We buffer everything until we have called an action
201
        }
202
203
        return $debugbar;
204
    }
205
206
    public static function clearDebugBar()
207
    {
208
        self::$debugbar = null;
209
    }
210
211
    public static function getShowQueries()
212
    {
213
        return self::$showQueries;
214
    }
215
216
    public static function setShowQueries($showQueries)
217
    {
218
        self::$showQueries = $showQueries;
219
    }
220
221
    public static function includeRequirements()
222
    {
223
        $debugbar = self::getDebugBar();
224
225
        if (!$debugbar) {
226
            return;
227
        }
228
229
        // Already called
230
        if (self::$renderer) {
231
            return;
232
        }
233
234
        $renderer = $debugbar->getJavascriptRenderer();
235
236
        // We don't need the true path since we are going to use Requirements API that appends the BASE_PATH
237
        $renderer->setBasePath(DEBUGBAR_DIR . '/assets');
238
        $renderer->setBaseUrl(DEBUGBAR_DIR . '/assets');
239
240
        $includeJquery = self::config()->get('include_jquery');
241
        // In CMS, jQuery is already included
242
        if (self::isAdminController()) {
243
            $includeJquery = false;
244
        }
245
        // If jQuery is already included, set to false
246
        $js = Requirements::backend()->getJavascript();
247
        foreach ($js as $url => $args) {
248
            $name = basename($url);
249
            if ($name == 'jquery.js' || $name == 'jquery.min.js') {
250
                $includeJquery = false;
251
                break;
252
            }
253
        }
254
255
        if ($includeJquery) {
256
            $renderer->setEnableJqueryNoConflict(true);
257
        } else {
258
            $renderer->disableVendor('jquery');
259
            $renderer->setEnableJqueryNoConflict(false);
260
        }
261
262
        if (DebugBar::config()->get('enable_storage')) {
263
            $renderer->setOpenHandlerUrl('__debugbar');
264
        }
265
266
        foreach ($renderer->getAssets('css') as $cssFile) {
267
            Requirements::css(ltrim($cssFile, '/'));
268
        }
269
270
        foreach ($renderer->getAssets('js') as $jsFile) {
271
            Requirements::javascript(ltrim($jsFile, '/'));
272
        }
273
274
        self::$renderer = $renderer;
275
    }
276
277
    public static function renderDebugBar()
278
    {
279
        if (!self::$renderer) {
280
            return;
281
        }
282
283
        // Requirements may have been cleared (CMS iframes...) or not set (Security...)
284
        $js = Requirements::backend()->getJavascript();
285
        if (!array_key_exists('debugbar/assets/debugbar.js', $js)) {
286
            return;
287
        }
288
        $initialize = true;
289
        if (Director::is_ajax()) {
290
            $initialize = false;
291
        }
292
293
        $script = self::$renderer->render($initialize);
294
        return $script;
295
    }
296
297
    /**
298
     * Determine why DebugBar is disabled
299
     *
300
     * @return string
301
     */
302
    public static function whyDisabled()
303
    {
304
        if (!Director::isDev()) {
305
            return 'Not in dev mode';
306
        }
307
        if (self::isDisabled()) {
308
            return 'Disabled by a constant or configuration';
309
        }
310
        if (self::vendorNotInstalled()) {
311
            return 'DebugBar is not installed in vendors';
312
        }
313
        if (self::notLocalIp()) {
314
            return 'Not a local ip';
315
        }
316
        if (Director::is_cli()) {
317
            return 'In CLI mode';
318
        }
319
        if (self::isDevUrl()) {
320
            return 'Dev tools';
321
        }
322
        if (self::isAdminUrl() && !self::config()->get('enabled_in_admin')) {
323
            return 'In admin';
324
        }
325
        return "I don't know why";
326
    }
327
328
    public static function vendorNotInstalled()
329
    {
330
        return !class_exists('DebugBar\\StandardDebugBar');
331
    }
332
333
    public static function notLocalIp()
0 ignored issues
show
Coding Style introduced by
notLocalIp uses the super-global variable $_SERVER 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...
334
    {
335
        if (!self::config()->get('check_local_ip')) {
336
            return false;
337
        }
338
        if (isset($_SERVER['REMOTE_ADDR'])) {
339
            return !in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1', '1'));
340
        }
341
        return false;
342
    }
343
344
    public static function isDisabled()
345
    {
346
        if (Environment::getEnv('DEBUGBAR_DISABLE') || static::config()->get('disabled')) {
0 ignored issues
show
Bug introduced by
The method getEnv() does not seem to exist on object<SilverStripe\Core\Environment>.

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...
347
            return true;
348
        }
349
        return false;
350
    }
351
352
    public static function isDevUrl()
353
    {
354
        return strpos(self::getRequestUrl(), '/dev/') === 0;
355
    }
356
357
    public static function isAdminUrl()
358
    {
359
        $baseUrl = rtrim(BASE_URL, '/');
360
        $adminUrl = AdminRootController::config()->get('url_base');
361
362
        return strpos(self::getRequestUrl(), $baseUrl . '/' . $adminUrl . '/') === 0;
363
    }
364
365
    public static function isAdminController()
366
    {
367
        if (Controller::curr()) {
368
            return Controller::curr() instanceof LeftAndMain;
369
        }
370
        return self::isAdminUrl();
371
    }
372
373
    /**
374
     * Avoid triggering data collection for open handler
375
     *
376
     * @return boolean
377
     */
378
    public static function isDebugBarRequest()
379
    {
380
        if ($url = self::getRequestUrl()) {
381
            return strpos($url, '/__debugbar') === 0;
382
        }
383
        return true;
384
    }
385
386
    /**
387
     * Get request url
388
     *
389
     * @return string
390
     */
391
    public static function getRequestUrl()
0 ignored issues
show
Coding Style introduced by
getRequestUrl 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...
Coding Style introduced by
getRequestUrl uses the super-global variable $_SERVER 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...
392
    {
393
        if (isset($_REQUEST['url'])) {
394
            return $_REQUEST['url'];
395
        }
396
        if (isset($_SERVER['REQUEST_URI'])) {
397
            return $_SERVER['REQUEST_URI'];
398
        }
399
        return '';
400
    }
401
402
    /**
403
     * Helper to make code cleaner
404
     *
405
     * @param callable $callback
406
     */
407
    public static function withDebugBar($callback)
408
    {
409
        if (self::getDebugBar() && !self::isDebugBarRequest()) {
410
            $callback(self::getDebugBar());
411
        }
412
    }
413
414
    /**
415
     * Set the current request. Is provided by the DebugBarMiddleware.
416
     *
417
     * @param HTTPRequest $request
418
     */
419
    public static function setRequest(HTTPRequest $request)
420
    {
421
        self::$request = $request;
422
    }
423
424
    /**
425
     * Get the current request
426
     *
427
     * @return HTTPRequest
428
     */
429
    public static function getRequest()
430
    {
431
        if (self::$request) {
432
            return self::$request;
433
        }
434
        // Fall back to trying from the global state
435
        if (Controller::has_curr()) {
436
            return Controller::curr()->getRequest();
437
        }
438
    }
439
}
440