Passed
Pull Request — 4 (#8209)
by Ingo
09:07
created

Debug::supportsHTML()   B

Complexity

Conditions 8
Paths 11

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 11
nop 1
dl 0
loc 32
rs 8.1635
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use SilverStripe\Control\Director;
6
use SilverStripe\Control\HTTPRequest;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\ORM\DB;
9
use SilverStripe\Security\Permission;
10
use SilverStripe\Security\Security;
11
12
/**
13
 * Supports debugging and core error handling.
14
 *
15
 * Attaches custom methods to the default error handling hooks
16
 * in PHP. Currently, two levels of error are supported:
17
 *
18
 * - Notice
19
 * - Warning
20
 * - Error
21
 *
22
 * Uncaught exceptions are currently passed to the debug
23
 * reporter as standard PHP errors.
24
 *
25
 * Errors handled by this class are passed along to {@link SS_Log}.
26
 * For configuration information, see the {@link SS_Log}
27
 * class documentation.
28
 *
29
 * @todo add support for user defined config: Debug::die_on_notice(true | false)
30
 * @todo better way of figuring out the error context to display in highlighted source
31
 */
32
class Debug
33
{
34
35
    /**
36
     * Show the contents of val in a debug-friendly way.
37
     * Debug::show() is intended to be equivalent to dprintr()
38
     * Does not work on live mode.
39
     *
40
     * @param mixed $val
41
     * @param bool $showHeader
42
     * @param HTTPRequest|null $request
43
     */
44
    public static function show($val, $showHeader = true, HTTPRequest $request = null)
45
    {
46
        // Don't show on live
47
        if (Director::isLive()) {
48
            return;
49
        }
50
51
        echo static::create_debug_view($request)
52
            ->debugVariable($val, static::caller(), $showHeader);
53
    }
54
55
    /**
56
     * Returns the caller for a specific method
57
     *
58
     * @return array
59
     */
60
    public static function caller()
61
    {
62
        $bt = debug_backtrace();
63
        $caller = isset($bt[2]) ? $bt[2] : array();
64
        $caller['line'] = $bt[1]['line'];
65
        $caller['file'] = $bt[1]['file'];
66
        if (!isset($caller['class'])) {
67
            $caller['class'] = '';
68
        }
69
        if (!isset($caller['type'])) {
70
            $caller['type'] = '';
71
        }
72
        if (!isset($caller['function'])) {
73
            $caller['function'] = '';
74
        }
75
        return $caller;
76
    }
77
78
    /**
79
     * Close out the show dumper.
80
     * Does not work on live mode
81
     *
82
     * @param mixed $val
83
     * @param bool $showHeader
84
     * @param HTTPRequest $request
85
     */
86
    public static function endshow($val, $showHeader = true, HTTPRequest $request = null)
87
    {
88
        // Don't show on live
89
        if (Director::isLive()) {
90
            return;
91
        }
92
93
        echo static::create_debug_view($request)
94
            ->debugVariable($val, static::caller(), $showHeader);
95
96
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
97
    }
98
99
    /**
100
     * Quick dump of a variable.
101
     * Note: This method will output in live!
102
     *
103
     * @param mixed $val
104
     * @param HTTPRequest $request Current request to influence output format
105
     */
106
    public static function dump($val, HTTPRequest $request = null)
107
    {
108
        echo self::create_debug_view($request)
109
            ->renderVariable($val, self::caller());
110
    }
111
112
    /**
113
     * Get debug text for this object
114
     *
115
     * @param mixed $val
116
     * @param HTTPRequest $request
117
     * @return string
118
     */
119
    public static function text($val, HTTPRequest $request = null)
120
    {
121
        return static::create_debug_view($request)
122
            ->debugVariableText($val);
123
    }
124
125
    /**
126
     * Show a debugging message.
127
     * Does not work on live mode
128
     *
129
     * @param string $message
130
     * @param bool $showHeader
131
     * @param HTTPRequest|null $request
132
     */
133
    public static function message($message, $showHeader = true, HTTPRequest $request = null)
134
    {
135
        // Don't show on live
136
        if (Director::isLive()) {
137
            return;
138
        }
139
140
        echo static::create_debug_view($request)
141
            ->renderMessage($message, static::caller(), $showHeader);
142
    }
143
144
    /**
145
     * Create an instance of an appropriate DebugView object.
146
     *
147
     * @param HTTPRequest $request Optional request to target this view for
148
     * @return DebugView
149
     */
150
    public static function create_debug_view(HTTPRequest $request = null)
151
    {
152
        $service = static::supportsHTML($request)
153
            ? DebugView::class
154
            : CliDebugView::class;
155
        return Injector::inst()->get($service);
156
    }
157
158
    /**
159
     * Determine if the given request supports html output
160
     *
161
     * @param HTTPRequest $request
162
     * @return bool
163
     */
164
    protected static function supportsHTML(HTTPRequest $request = null)
165
    {
166
        // No HTML output in CLI
167
        if (Director::is_cli()) {
168
            return false;
169
        }
170
171
        // Get current request if registered
172
        if (!$request && Injector::inst()->has(HTTPRequest::class)) {
173
            $request = Injector::inst()->get(HTTPRequest::class);
174
        }
175
        if (!$request) {
176
            return false;
177
        }
178
        // Request must include text/html
179
        $accepted = $request->getAcceptMimetypes(false);
180
181
        // Explicit opt in
182
        if (in_array('text/html', $accepted)) {
183
            return true;
184
        };
185
186
        // Implicit opt-out
187
        if (in_array('application/json', $accepted)) {
188
            return false;
189
        }
190
191
        // Fallback to wildcard comparison
192
        if (in_array('*/*', $accepted)) {
193
            return true;
194
        }
195
        return false;
196
    }
197
198
    /**
199
     * Check if the user has permissions to run URL debug tools,
200
     * else redirect them to log in.
201
     */
202
    public static function require_developer_login()
203
    {
204
        // Don't require login for dev mode
205
        if (Director::isDev()) {
206
            return;
207
        }
208
209
        if (isset($_SESSION['loggedInAs'])) {
210
            // We have to do some raw SQL here, because this method is called in Object::defineMethods().
211
            // This means we have to be careful about what objects we create, as we don't want Object::defineMethods()
212
            // being called again.
213
            // This basically calls Permission::checkMember($_SESSION['loggedInAs'], 'ADMIN');
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
214
215
            // @TODO - Rewrite safely using DataList::filter
216
            $memberID = $_SESSION['loggedInAs'];
217
            $permission = DB::prepared_query(
218
                '
219
				SELECT "ID" FROM "Permission"
220
				INNER JOIN "Group_Members" ON "Permission"."GroupID" = "Group_Members"."GroupID"
221
				WHERE "Permission"."Code" = ?
222
				AND "Permission"."Type" = ?
223
				AND "Group_Members"."MemberID" = ?',
224
                array(
225
                    'ADMIN', // Code
226
                    Permission::GRANT_PERMISSION, // Type
227
                    $memberID // MemberID
228
                )
229
            )->value();
230
231
            if ($permission) {
232
                return;
233
            }
234
        }
235
236
        // This basically does the same as
237
        // Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.")
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
238
        // We have to do this because of how early this method is called in execution.
239
        $_SESSION['SilverStripe\\Security\\Security']['Message']['message']
240
            = "You need to login with developer access to make use of debugging tools.";
241
        $_SESSION['SilverStripe\\Security\\Security']['Message']['type'] =  'warning';
242
        $_SESSION['BackURL'] = $_SERVER['REQUEST_URI'];
243
        header($_SERVER['SERVER_PROTOCOL'] . " 302 Found");
244
        header("Location: " . Director::baseURL() . Security::login_url());
245
        die();
246
    }
247
}
248