Completed
Pull Request — master (#47)
by Robbie
01:29
created

DebugBar::clearDebugBar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace LeKoala\DebugBar;
4
5
use DebugBar\Bridge\MonologCollector;
6
use DebugBar\DebugBar as BaseDebugBar;
7
use DebugBar\DataCollector\MemoryCollector;
8
use DebugBar\DataCollector\PDO\PDOCollector;
9
use DebugBar\DataCollector\PDO\TraceablePDO;
10
use DebugBar\DataCollector\PhpInfoCollector;
11
use DebugBar\Storage\FileStorage;
12
use Exception;
13
use LeKoala\DebugBar\Collector\DatabaseCollector;
14
use LeKoala\DebugBar\Collector\SilverStripeCollector;
15
use LeKoala\DebugBar\Collector\TimeDataCollector;
16
use LeKoala\DebugBar\Extension\ControllerExtension;
17
use LeKoala\DebugBar\LogWriter;
18
use LeKoala\DebugBar\Proxy\DatabaseProxy;
19
use Psr\Log\LoggerInterface;
20
use ReflectionObject;
21
use SilverStripe\Admin\LeftAndMain;
22
use SilverStripe\Control\Controller as BaseController;
23
use SilverStripe\Control\Director;
24
use SilverStripe\Core\Config\Configurable;
25
use SilverStripe\Core\Injector\Injectable;
26
use SilverStripe\Core\Injector\Injector;
27
use SilverStripe\ORM\Connect\PDOConnector;
28
use SilverStripe\ORM\DB;
29
use SilverStripe\View\Requirements;
30
31
/**
32
 * A simple helper
33
 */
34
class DebugBar
35
{
36
    use Configurable;
37
    use Injectable;
38
39
    /**
40
     * @var DebugBar\DebugBar
41
     */
42
    protected static $debugbar = null;
43
44
    /**
45
     * @var bool
46
     */
47
    public static $bufferingEnabled = false;
48
49
    /**
50
     * @var DebugBar\JavascriptRenderer
51
     */
52
    protected static $renderer = null;
53
54
    /**
55
     * @var bool
56
     */
57
    protected static $showQueries = false;
58
59
    /**
60
     * Get the Debug Bar instance
61
     * @return \DebugBar\StandardDebugBar
62
     * @throws Exception
63
     * @global array $databaseConfig
64
     */
65
    public static function getDebugBar()
66
    {
67
        if (self::$debugbar !== null) {
68
            return self::$debugbar;
69
        }
70
71
        if (!Director::isDev() || self::isDisabled() || self::vendorNotInstalled() ||
72
            self::notLocalIp() || Director::is_cli() || self::isDevUrl() ||
73
            (self::isAdminUrl() && !self::config()->enabled_in_admin)
74
        ) {
75
            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...
76
            return;
77
        }
78
79
        self::initDebugBar();
80
81
        if (!self::$debugbar) {
82
            throw new Exception("Failed to initialize the DebugBar");
83
        }
84
85
        return self::$debugbar;
86
    }
87
88
    /**
89
     * Init the debugbar instance
90
     *
91
     * @global array $databaseConfig
92
     * @return DebugBar\StandardDebugBar
93
     */
94
    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...
95
    {
96
        // Prevent multiple inits
97
        if (self::$debugbar) {
98
            return self::$debugbar;
99
        }
100
101
        // Add the controller extension programmaticaly because it might not be added properly through yml
102
        Controller::add_extension(ControllerExtension::class);
103
104
        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...
105
106
        if (isset($_REQUEST['showqueries'])) {
107
            self::setShowQueries(true);
108
            echo "The queries above have been run before we started DebugBar";
109
            echo '<hr>';
110
            unset($_REQUEST['showqueries']);
111
        }
112
113
        $debugbar->addCollector(new PhpInfoCollector);
114
        $debugbar->addCollector(new TimeDataCollector);
115
        $debugbar->addCollector(new MemoryCollector);
116
117
        if (!DB::get_conn()) {
118
            global $databaseConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
119
            if ($databaseConfig) {
120
                DB::connect($databaseConfig);
121
            }
122
        }
123
124
        $connector = DB::get_connector();
125
        if (!self::config()->force_proxy && $connector instanceof PDOConnector) {
126
            // Use a little bit of magic to replace the pdo instance
127
            $refObject = new ReflectionObject($connector);
128
            $refProperty = $refObject->getProperty('pdoConnection');
129
            $refProperty->setAccessible(true);
130
            $traceablePdo = new TraceablePDO($refProperty->getValue($connector));
131
            $refProperty->setValue($connector, $traceablePdo);
132
133
            $debugbar->addCollector(new PDOCollector($traceablePdo));
134
        } else {
135
            DB::set_conn($db = new DatabaseProxy(DB::get_conn()));
136
            $db->setShowQueries(self::getShowQueries());
137
            $debugbar->addCollector(new DatabaseCollector);
138
        }
139
140
        // Add message collector last so other collectors can send messages to the console using it
141
        $logger = Injector::inst()->get(LoggerInterface::class);
142
        $debugbar->addCollector(new MonologCollector($logger));
143
144
        // Add some SilverStripe specific infos
145
        $debugbar->addCollector(new SilverStripeCollector);
146
147
        if (self::config()->enable_storage) {
148
            $debugbar->setStorage(new FileStorage(TEMP_FOLDER . '/debugbar'));
149
        }
150
151
        // Since we buffer everything, why not enable all dev options ?
152
        if (self::config()->auto_debug) {
153
            $_REQUEST['debug'] = true;
154
            $_REQUEST['debug_request'] = true;
155
        }
156
157
        if (isset($_REQUEST['debug']) || isset($_REQUEST['debug_request'])) {
158
            self::$bufferingEnabled = true;
159
            ob_start(); // We buffer everything until we have called an action
160
        }
161
162
        return $debugbar;
163
    }
164
165
    public static function clearDebugBar()
166
    {
167
        self::$debugbar = null;
168
    }
169
170
    public static function getShowQueries()
171
    {
172
        return self::$showQueries;
173
    }
174
175
    public static function setShowQueries($showQueries)
176
    {
177
        self::$showQueries = $showQueries;
178
    }
179
180
    public static function includeRequirements()
181
    {
182
        $debugbar = self::getDebugBar();
183
184
        if (!$debugbar) {
185
            return;
186
        }
187
188
        // Already called
189
        if (self::$renderer) {
190
            return;
191
        }
192
193
        $renderer = $debugbar->getJavascriptRenderer();
194
195
        // We don't need the true path since we are going to use Requirements API that appends the BASE_PATH
196
        $renderer->setBasePath(DEBUGBAR_DIR . '/assets');
197
        $renderer->setBaseUrl(DEBUGBAR_DIR . '/assets');
198
199
        $includeJquery = self::config()->include_jquery;
200
        // In CMS, jQuery is already included
201
        if (self::isAdminController()) {
202
            $includeJquery = false;
203
        }
204
        // If jQuery is already included, set to false
205
        $js = Requirements::backend()->getJavascript();
206
        foreach ($js as $url) {
207
            $name = basename($url);
208
            if ($name == 'jquery.js' || $name == 'jquery.min.js') {
209
                $includeJquery = false;
210
                break;
211
            }
212
        }
213
214
        if ($includeJquery) {
215
            $renderer->setEnableJqueryNoConflict(true);
216
        } else {
217
            $renderer->disableVendor('jquery');
218
            $renderer->setEnableJqueryNoConflict(false);
219
        }
220
221
        if (DebugBar::config()->enable_storage) {
222
            $renderer->setOpenHandlerUrl('__debugbar');
223
        }
224
225
        foreach ($renderer->getAssets('css') as $cssFile) {
226
            Requirements::css(ltrim($cssFile, '/'));
227
        }
228
229
        foreach ($renderer->getAssets('js') as $jsFile) {
230
            Requirements::javascript(ltrim($jsFile, '/'));
231
        }
232
233
        self::$renderer = $renderer;
234
    }
235
236
    public static function renderDebugBar()
237
    {
238
        if (!self::$renderer) {
239
            return;
240
        }
241
242
        // Requirements may have been cleared (CMS iframes...) or not set (Security...)
243
        $js = Requirements::backend()->getJavascript();
244
        if (!in_array('debugbar/assets/debugbar.js', $js)) {
245
            return;
246
        }
247
        $initialize = true;
248
        if (Director::is_ajax()) {
249
            $initialize = false;
250
        }
251
252
        $script = self::$renderer->render($initialize);
253
        return $script;
254
    }
255
256
    /**
257
     * Determine why DebugBar is disabled
258
     *
259
     * @return string
260
     */
261
    public static function whyDisabled()
262
    {
263
        if (!Director::isDev()) {
264
            return 'Not in dev mode';
265
        }
266
        if (self::isDisabled()) {
267
            return 'Disabled by a constant or configuration';
268
        }
269
        if (self::vendorNotInstalled()) {
270
            return 'DebugBar is not installed in vendors';
271
        }
272
        if (self::notLocalIp()) {
273
            return 'Not a local ip';
274
        }
275
        if (Director::is_cli()) {
276
            return 'In CLI mode';
277
        }
278
        if (self::isDevUrl()) {
279
            return 'Dev tools';
280
        }
281
        if (self::isAdminUrl() && !self::config()->enabled_in_admin) {
282
            return 'In admin';
283
        }
284
        return "I don't know why";
285
    }
286
287
    public static function vendorNotInstalled()
288
    {
289
        return !class_exists('DebugBar\\StandardDebugBar');
290
    }
291
292
    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...
293
    {
294
        if (!self::config()->check_local_ip) {
295
            return false;
296
        }
297
        if (isset($_SERVER['REMOTE_ADDR'])) {
298
            return !in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1', '1'));
299
        }
300
        return false;
301
    }
302
303
    public static function isDisabled()
304
    {
305
        if ((defined('DEBUGBAR_DISABLE') && DEBUGBAR_DISABLE) || static::config()->disabled) {
306
            return true;
307
        }
308
        return false;
309
    }
310
311
    public static function isDevUrl()
312
    {
313
        return strpos(self::getRequestUrl(), '/dev/') === 0;
314
    }
315
316
    public static function isAdminUrl()
317
    {
318
        return strpos(self::getRequestUrl(), '/admin/') === 0;
319
    }
320
321
    public static function isAdminController()
322
    {
323
        if (Controller::curr()) {
324
            return Controller::curr() instanceof LeftAndMain;
325
        }
326
        return self::isAdminUrl();
327
    }
328
329
    /**
330
     * Avoid triggering data collection for open handler
331
     *
332
     * @return boolean
333
     */
334
    public static function isDebugBarRequest()
335
    {
336
        if ($url = self::getRequestUrl()) {
337
            return strpos($url, '/__debugbar') === 0;
338
        }
339
        return true;
340
    }
341
342
    /**
343
     * Get request url
344
     *
345
     * @return string
346
     */
347
    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...
348
    {
349
        if (isset($_REQUEST['url'])) {
350
            return $_REQUEST['url'];
351
        }
352
        if (isset($_SERVER['REQUEST_URI'])) {
353
            return $_SERVER['REQUEST_URI'];
354
        }
355
        return '';
356
    }
357
358
    /**
359
     * Helper to make code cleaner
360
     *
361
     * @param callable $callback
362
     */
363
    public static function withDebugBar($callback)
364
    {
365
        if (self::getDebugBar() && !self::isDebugBarRequest()) {
366
            $callback(self::getDebugBar());
367
        }
368
    }
369
}
370