Completed
Push — master ( ac629c...442bc4 )
by Robbie
9s
created

DebugBar::whyDisabled()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
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 SilverStripe\ORM\Connect\PDOConnector;
21
use SilverStripe\Core\Injector\Injectable;
22
use SilverStripe\Core\Config\Configurable;
23
use SilverStripe\Admin\AdminRootController;
24
use LeKoala\DebugBar\Messages\LogFormatter;
25
use SilverStripe\Control\Email\SwiftMailer;
26
use DebugBar\DataCollector\MemoryCollector;
27
use SilverStripe\Core\Manifest\ModuleLoader;
28
use DebugBar\DataCollector\PDO\PDOCollector;
29
use DebugBar\DataCollector\PDO\TraceablePDO;
30
use DebugBar\DataCollector\PhpInfoCollector;
31
use DebugBar\DataCollector\MessagesCollector;
32
use SilverStripe\Core\Manifest\ModuleResource;
33
use LeKoala\DebugBar\Proxy\ConfigManifestProxy;
34
use LeKoala\DebugBar\Collector\ConfigCollector;
35
use LeKoala\DebugBar\Collector\DatabaseCollector;
36
use LeKoala\DebugBar\Collector\TimeDataCollector;
37
use DebugBar\Bridge\SwiftMailer\SwiftLogCollector;
38
use DebugBar\Bridge\SwiftMailer\SwiftMailCollector;
39
use LeKoala\DebugBar\Collector\PartialCacheCollector;
40
use LeKoala\DebugBar\Collector\SilverStripeCollector;
41
use SilverStripe\Config\Collections\CachedConfigCollection;
42
43
/**
44
 * A simple helper
45
 */
46
class DebugBar
47
{
48
    use Configurable;
49
    use Injectable;
50
51
    /**
52
     * @var DebugBar\DebugBar
0 ignored issues
show
Bug introduced by
The type LeKoala\DebugBar\DebugBar\DebugBar was not found. Did you mean DebugBar\DebugBar? If so, make sure to prefix the type with \.
Loading history...
53
     */
54
    protected static $debugbar;
55
56
    /**
57
     * @var bool
58
     */
59
    public static $bufferingEnabled = false;
60
61
    /**
62
     * @var DebugBar\JavascriptRenderer
0 ignored issues
show
Bug introduced by
The type LeKoala\DebugBar\DebugBar\JavascriptRenderer was not found. Did you mean DebugBar\JavascriptRenderer? If so, make sure to prefix the type with \.
Loading history...
63
     */
64
    protected static $renderer;
65
66
    /**
67
     * @var bool
68
     */
69
    protected static $showQueries = false;
70
71
    /**
72
     * @var HTTPRequest
73
     */
74
    protected static $request;
75
76
    /**
77
     * Get the Debug Bar instance
78
     * @return \DebugBar\StandardDebugBar
79
     * @throws Exception
80
     * @global array $databaseConfig
81
     */
82
    public static function getDebugBar()
83
    {
84
        if (self::$debugbar !== null) {
85
            return self::$debugbar;
86
        }
87
88
        $reasons = self::disabledCriteria();
89
        if (!empty($reasons)) {
90
            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 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...
91
            return;
92
        }
93
94
        self::initDebugBar();
95
96
        if (!self::$debugbar) {
97
            throw new Exception("Failed to initialize the DebugBar");
98
        }
99
100
        return self::$debugbar;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::debugbar returns the type void which is incompatible with the documented return type DebugBar\StandardDebugBar.
Loading history...
101
    }
102
103
    /**
104
     * Init the debugbar instance
105
     *
106
     * @global array $databaseConfig
107
     * @return DebugBar\DebugBar|null
108
     */
109
    public static function initDebugBar()
110
    {
111
        // Prevent multiple inits
112
        if (self::$debugbar) {
113
            return self::$debugbar;
114
        }
115
116
        self::$debugbar = $debugbar = new BaseDebugBar;
0 ignored issues
show
Documentation Bug introduced by
It seems like $debugbar = new DebugBar\DebugBar() of type DebugBar\DebugBar is incompatible with the declared type 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...
117
118
        if (isset($_REQUEST['showqueries']) && Director::isDev()) {
119
            self::setShowQueries(true);
120
            unset($_REQUEST['showqueries']);
121
        }
122
123
        $debugbar->addCollector(new PhpInfoCollector);
124
        $debugbar->addCollector(new TimeDataCollector);
125
        $debugbar->addCollector(new MemoryCollector);
126
127
        // Add config proxy replacing the core config manifest
128
        $configManifest = false;
129
130
        if (self::config()->config_collector) {
131
            /** @var SilverStripe\Core\Config\ConfigLoader $configLoader */
132
            $configLoader = Injector::inst()->get(Kernel::class)->getConfigLoader();
133
            // Let's safely access the manifest value without popping things
134
            $manifests = self::getProtectedValue($configLoader, 'manifests');
135
            foreach ($manifests as $manifest) {
136
                if ($manifest instanceof CachedConfigCollection) {
137
                    $configManifest = $manifest;
138
                    break;
139
                }
140
            }
141
            // We can display a CachedConfigCollection
142
            if ($configManifest) {
143
                $configProxy = new ConfigManifestProxy($configManifest);
144
                $configLoader->pushManifest($configProxy);
145
            }
146
        }
147
148
        $connector = DB::get_connector();
149
        if (!self::config()->get('force_proxy') && $connector instanceof PDOConnector) {
150
            $traceablePdo = new TraceablePDO(self::getProtectedValue($connector, 'pdoConnection'));
151
            $refProperty->setValue($connector, $traceablePdo);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $refProperty seems to be never defined.
Loading history...
152
153
            $debugbar->addCollector(new PDOCollector($traceablePdo));
154
        } else {
155
            $debugbar->addCollector(new DatabaseCollector);
156
        }
157
158
        // Add message collector last so other collectors can send messages to the console using it
159
        $debugbar->addCollector(new MessagesCollector());
160
161
        // Aggregate monolog into messages
162
        $logger = Injector::inst()->get(LoggerInterface::class);
163
        if ($logger instanceof Logger) {
164
            $logCollector = new MonologCollector($logger);
165
            $logCollector->setFormatter(new LogFormatter);
166
            $debugbar['messages']->aggregate($logCollector);
0 ignored issues
show
Bug introduced by
The method aggregate() does not exist on DebugBar\DataCollector\DataCollectorInterface. It seems like you code against a sub-type of DebugBar\DataCollector\DataCollectorInterface such as DebugBar\DataCollector\MessagesCollector. ( Ignorable by Annotation )

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

166
            $debugbar['messages']->/** @scrutinizer ignore-call */ 
167
                                   aggregate($logCollector);
Loading history...
167
        }
168
169
        // Add some SilverStripe specific infos
170
        $debugbar->addCollector(new SilverStripeCollector);
171
172
        if (self::config()->get('enable_storage')) {
173
            $debugbar->setStorage(new FileStorage(TEMP_FOLDER . '/debugbar'));
174
        }
175
176
        if ($configManifest) {
177
            // Add the config collector
178
            $debugbar->addCollector(new ConfigCollector);
179
        }
180
181
        // Partial cache
182
        if (self::config()->partial_cache_collector) {
183
            $debugbar->addCollector(new PartialCacheCollector);
184
        }
185
186
        // Email logging
187
        if (self::config()->email_collector) {
188
            $mailer = Injector::inst()->get(Mailer::class);
189
            if ($mailer instanceof SwiftMailer) {
190
                $swiftInst = $mailer->getSwiftMailer();
191
                $debugbar['messages']->aggregate(new SwiftLogCollector($swiftInst));
192
                $debugbar->addCollector(new SwiftMailCollector($swiftInst));
193
            }
194
        }
195
196
        // Since we buffer everything, why not enable all dev options ?
197
        if (self::config()->get('auto_debug')) {
198
            $_REQUEST['debug'] = true;
199
            $_REQUEST['debug_request'] = true;
200
        }
201
202
        if (isset($_REQUEST['debug']) || isset($_REQUEST['debug_request'])) {
203
            self::$bufferingEnabled = true;
204
            ob_start(); // We buffer everything until we have called an action
205
        }
206
207
        return $debugbar;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $debugbar returns the type DebugBar\DebugBar which is incompatible with the documented return type null|LeKoala\DebugBar\DebugBar\DebugBar.
Loading history...
208
    }
209
210
    /**
211
     * Access a protected property when the api does not allow access
212
     *
213
     * @param object $object
214
     * @param string $property
215
     * @return mixed
216
     */
217
    protected static function getProtectedValue($object, $property)
218
    {
219
        $refObject = new ReflectionObject($object);
220
        $refProperty = $refObject->getProperty($property);
221
        $refProperty->setAccessible(true);
222
        return $refProperty->getValue($object);
223
    }
224
225
    /**
226
     * Clear the current instance of DebugBar
227
     *
228
     * @return void
229
     */
230
    public static function clearDebugBar()
231
    {
232
        self::$debugbar = null;
233
    }
234
235
    /**
236
     * @return boolean
237
     */
238
    public static function getShowQueries()
239
    {
240
        return self::$showQueries;
241
    }
242
243
    /**
244
     * Override default showQueries mode
245
     *
246
     * @param boolean $showQueries
247
     * @return void
248
     */
249
    public static function setShowQueries($showQueries)
250
    {
251
        self::$showQueries = $showQueries;
252
    }
253
254
    /**
255
     * Helper to access this module resources
256
     *
257
     * @param string $path
258
     * @return ModuleResource
259
     */
260
    public static function moduleResource($path)
261
    {
262
        return ModuleLoader::getModule('lekoala/silverstripe-debugbar')->getResource($path);
263
    }
264
265
    public static function includeRequirements()
266
    {
267
        $debugbar = self::getDebugBar();
268
269
        if (!$debugbar) {
270
            return;
271
        }
272
273
        // Already called
274
        if (self::$renderer) {
275
            return;
276
        }
277
278
        $renderer = $debugbar->getJavascriptRenderer();
279
280
        // We don't need the true path since we are going to use Requirements API that appends the BASE_PATH
281
        $assetsResource = self::moduleResource('assets');
282
        $renderer->setBasePath($assetsResource->getRelativePath());
283
        $renderer->setBaseUrl(Director::makeRelative($assetsResource->getURL()));
284
285
        $includeJquery = self::config()->get('include_jquery');
286
        // In CMS, jQuery is already included
287
        if (self::isAdminController()) {
288
            $includeJquery = false;
289
        }
290
        // If jQuery is already included, set to false
291
        $js = Requirements::backend()->getJavascript();
292
        foreach ($js as $url => $args) {
293
            $name = basename($url);
294
            if ($name == 'jquery.js' || $name == 'jquery.min.js') {
295
                $includeJquery = false;
296
                break;
297
            }
298
        }
299
300
        if ($includeJquery) {
301
            $renderer->setEnableJqueryNoConflict(true);
302
        } else {
303
            $renderer->disableVendor('jquery');
304
            $renderer->setEnableJqueryNoConflict(false);
305
        }
306
307
        if (DebugBar::config()->get('enable_storage')) {
308
            $renderer->setOpenHandlerUrl('__debugbar');
309
        }
310
311
        foreach ($renderer->getAssets('css') as $cssFile) {
312
            Requirements::css(Director::makeRelative(ltrim($cssFile, '/')));
313
        }
314
315
        foreach ($renderer->getAssets('js') as $jsFile) {
316
            Requirements::javascript(Director::makeRelative(ltrim($jsFile, '/')));
317
        }
318
319
        self::$renderer = $renderer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $renderer of type DebugBar\JavascriptRenderer is incompatible with the declared type LeKoala\DebugBar\DebugBar\JavascriptRenderer of property $renderer.

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...
320
    }
321
322
    public static function renderDebugBar()
323
    {
324
        if (!self::$renderer) {
325
            return;
326
        }
327
328
        // Requirements may have been cleared (CMS iframes...) or not set (Security...)
329
        $js = Requirements::backend()->getJavascript();
330
        $debugBarResource = self::moduleResource('assets/debugbar.js');
331
        $path = $debugBarResource->getRelativePath();
332
333
        // Url in getJavascript has a / slash, so fix if necessary
334
        $path = str_replace("assets\\debugbar.js", "assets/debugbar.js", $path);
335
        if (!array_key_exists($path, $js)) {
336
            return;
337
        }
338
        $initialize = true;
339
        if (Director::is_ajax()) {
340
            $initialize = false;
341
        }
342
343
        $script = self::$renderer->render($initialize);
344
        return $script;
345
    }
346
347
    /**
348
     * Get all criteria why the DebugBar could be disabled
349
     *
350
     * @return array
351
     */
352
    public static function disabledCriteria() {
353
        $reasons = array();
354
        if (!Director::isDev()) {
355
            $reasons[] = 'Not in dev mode';
356
        }
357
        if (self::isDisabled()) {
358
            $reasons[] = 'Disabled by a constant or configuration';
359
        }
360
        if (self::vendorNotInstalled()) {
361
            $reasons[] = 'DebugBar is not installed in vendors';
362
        }
363
        if (self::notLocalIp()) {
364
            $reasons[] = 'Not a local ip';
365
        }
366
        if (Director::is_cli()) {
367
            $reasons[] = 'In CLI mode';
368
        }
369
        if (self::isDevUrl()) {
370
            $reasons[] = 'Dev tools';
371
        }
372
        if (self::isAdminUrl() && !self::config()->get('enabled_in_admin')) {
373
            $reasons[] = 'In admin';
374
        }
375
        if(isset($_GET['CMSPreview'])) {
376
            $reasons[] = 'CMS Preview';
377
        }
378
        return $reasons;
379
    }
380
381
    /**
382
     * Determine why DebugBar is disabled
383
     *
384
     * Deprecated in favor of disabledCriteria
385
     *
386
     * @return string
387
     */
388
    public static function whyDisabled()
389
    {
390
        $reasons = self::disabledCriteria();
391
        if(!empty($reasons)) {
392
            return $reasons[0];
393
        }
394
        return "I don't know why";
395
    }
396
397
    public static function vendorNotInstalled()
398
    {
399
        return !class_exists('DebugBar\\StandardDebugBar');
400
    }
401
402
    public static function notLocalIp()
403
    {
404
        if (!self::config()->get('check_local_ip')) {
405
            return false;
406
        }
407
        if (isset($_SERVER['REMOTE_ADDR'])) {
408
            return !in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1', '1'));
409
        }
410
        return false;
411
    }
412
413
    public static function isDisabled()
414
    {
415
        if (Environment::getEnv('DEBUGBAR_DISABLE') || static::config()->get('disabled')) {
416
            return true;
417
        }
418
        return false;
419
    }
420
421
    public static function isDevUrl()
422
    {
423
        return strpos(self::getRequestUrl(), '/dev/') === 0;
424
    }
425
426
    public static function isAdminUrl()
427
    {
428
        $baseUrl = rtrim(BASE_URL, '/');
429
        $adminUrl = AdminRootController::config()->get('url_base');
430
431
        return strpos(self::getRequestUrl(), $baseUrl . '/' . $adminUrl . '/') === 0;
432
    }
433
434
    public static function isAdminController()
435
    {
436
        if (Controller::curr()) {
437
            return Controller::curr() instanceof LeftAndMain;
438
        }
439
        return self::isAdminUrl();
440
    }
441
442
    /**
443
     * Avoid triggering data collection for open handler
444
     *
445
     * @return boolean
446
     */
447
    public static function isDebugBarRequest()
448
    {
449
        if ($url = self::getRequestUrl()) {
450
            return strpos($url, '/__debugbar') === 0;
451
        }
452
        return true;
453
    }
454
455
    /**
456
     * Get request url
457
     *
458
     * @return string
459
     */
460
    public static function getRequestUrl()
461
    {
462
        if (isset($_REQUEST['url'])) {
463
            return $_REQUEST['url'];
464
        }
465
        if (isset($_SERVER['REQUEST_URI'])) {
466
            return $_SERVER['REQUEST_URI'];
467
        }
468
        return '';
469
    }
470
471
    /**
472
     * Helper to make code cleaner
473
     *
474
     * @param callable $callback
475
     */
476
    public static function withDebugBar($callback)
477
    {
478
        if (self::getDebugBar() && !self::isDebugBarRequest()) {
479
            $callback(self::getDebugBar());
480
        }
481
    }
482
483
    /**
484
     * Set the current request. Is provided by the DebugBarMiddleware.
485
     *
486
     * @param HTTPRequest $request
487
     */
488
    public static function setRequest(HTTPRequest $request)
489
    {
490
        self::$request = $request;
491
    }
492
493
    /**
494
     * Get the current request
495
     *
496
     * @return HTTPRequest
497
     */
498
    public static function getRequest()
499
    {
500
        if (self::$request) {
501
            return self::$request;
502
        }
503
        // Fall back to trying from the global state
504
        if (Controller::has_curr()) {
505
            return Controller::curr()->getRequest();
506
        }
507
    }
508
}
509