Issues (5)

src/Vardump.php (3 issues)

1
<?php
2
3
namespace Sunnysideup\Vardump;
4
5
use SilverStripe\Control\Director;
6
use SilverStripe\Core\Environment;
7
use SilverStripe\Dev\Debug;
8
use SilverStripe\ORM\ArrayList;
9
use SilverStripe\ORM\DataList;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\ORM\DB;
12
use SilverStripe\ORM\FieldType\DBHTMLText;
13
use SilverStripe\ORM\PaginatedList;
14
use SilverStripe\Security\Permission;
15
use SilverStripe\View\ArrayData;
16
17
class Vardump
18
{
19
    /**
20
     * @var array
21
     *            List of words to be replaced
22
     */
23
    private const SQL_PHRASES = [
24
        'SELECT',
25
        'FROM',
26
        'WHERE',
27
        'HAVING',
28
        'GROUP',
29
        'ORDER BY',
30
        'INNER JOIN',
31
        'LEFT JOIN',
32
    ];
33
34
    protected static $singleton;
35
36
    public static function formatted_variable($data = null): string
37
    {
38
        $html = '';
39
        $html .= '<div style="max-width: calc(100% - 20px); width:fit-content; margin: 20px;">';
40
        $html .= self::inst()->mixedToUl($data);
41
42
        return $html . '</div>';
43
    }
44
45
    public static function now($data = null, ?string $method = '', ?string $className = '')
46
    {
47
        echo '<div style="max-width: calc(100% - 20px); width:fit-content; margin: 20px auto;">';
48
        echo self::inst()->vardumpMe($data, $method, $className)->RAW();
49
        echo '</div>';
50
    }
51
52
    public static function inst()
53
    {
54
        if (null === self::$singleton) {
55
            self::$singleton = new self();
56
        }
57
58
        return self::$singleton;
59
    }
60
61
    public function isSafe(): bool
62
    {
63
        return (Permission::check('ADMIN') && (Director::isDev() || Environment::getEnv('SS_VARDUMP_DEBUG_ALLOWED')));
64
    }
65
66
    public function vardumpMeRaw($data, ?string $method = '', ?string $className = '')
67
    {
68
        return $this->vardumpMe($data, $method, $className)->raw();
69
    }
70
71
    /**
72
     * @param mixed  $data
73
     * @param string $method
74
     * @param string $className
75
     *
76
     * @return null|DBHTMLText
77
     */
78
    public function vardumpMe($data, ?string $method = '', ?string $className = '')
79
    {
80
        $obj = null;
81
        if (Vardump::inst()->isSafe()) {
82
            $html = Vardump::inst()->mixedToUl($data) . $this->addMethodInformation($method, $className);
83
            /** @var DBHTMLText $obj */
84
            $obj = DBHTMLText::create_field('HTMLText', $html);
85
        } elseif (Director::isDev()) {
86
            /** @var DBHTMLText $obj */
87
            $obj = DBHTMLText::create_field('HTMLText', 'Error: please login');
88
        }
89
90
        return $obj;
91
    }
92
93
    public function mixedToUl($mixed): string
94
    {
95
        if ($this->isSafe()) {
96
            if (false === $mixed) {
97
                return '<span style="color: grey">[NO]</span>';
98
            }
99
100
            if (true === $mixed) {
101
                return '<span style="color: grey">[YES]</span>';
102
            }
103
104
            if (null === $mixed) {
105
                return '<span style="color: grey">[NULL]</span>';
106
            }
107
108
            if (0 === $mixed) {
109
                return '<span style="color: green">[ZERO]</span>';
110
            }
111
112
            if (1 === $mixed) {
113
                return '<span style="color: green">[ONE]</span>';
114
            }
115
116
            if (is_int($mixed)) {
117
                return '<span style="color: green">' . $mixed . '</span>';
118
            }
119
120
            if (is_float($mixed)) {
121
                return '<span style="color: green">' . $mixed . '</span>';
122
            }
123
124
            if ('' === $mixed) {
125
                return '<span style="color: grey">[EMPTY STRING]</span>';
126
            }
127
128
            if (is_array($mixed) && [] === $mixed) {
129
                return '<span style="color: grey">[EMPTY ARRAY]</span>';
130
            }
131
132
            if (is_object($mixed)) {
133
                if ($mixed instanceof ArrayData) {
134
                    return $this->mixedToUl($mixed->toMap());
135
                }
136
137
                if ($mixed instanceof ArrayList) {
138
                    return $this->mixedToUl($mixed->toArray());
139
                }
140
141
                if ($mixed instanceof DataList || $mixed instanceof PaginatedList) {
142
                    $parameters = null;
143
                    $sql = $mixed->sql($parameters);
144
                    $sql = DB::inline_parameters($sql, $parameters);
145
                    $sql = str_replace('"', '`', $sql);
146
147
                    return
148
                        $this->mixedToUl($sql) . '<hr />' .
149
                        $this->mixedToUl($mixed->map('ID', 'Title')->toArray());
150
                }
151
152
                if ($mixed instanceof DataObject) {
153
                    return $mixed->i18n_singular_name() . ': ' . $mixed->getTitle() .
154
                        ' (' . $mixed->ClassName . ', ' . $mixed->ID . ')';
155
                }
156
157
                return '<span style="color: red">' . substr(Debug::text($mixed), 0, 500) . '</span>';
158
            }
159
160
            if (is_array($mixed)) {
161
                $html = '';
162
                $isAssoc = $this->isAssoc($mixed);
163
                $count = count($mixed);
164
                $isLarge = false;
165
                if ($count > 1) {
166
                    $html .= '' . count($mixed) . ' entries ... ';
167
                    $isLarge = count($mixed) > 20;
168
                }
169
170
                $after = '';
171
                $style = '';
172
                $keyString = '';
173
                if ($isLarge) {
174
                    $style = 'display: inline;';
175
                    $after = ', ';
176
                }
177
178
                $itemHTML = '<ol>';
0 ignored issues
show
The assignment to $itemHTML is dead and can be removed.
Loading history...
179
                $count = 0;
180
                $itemHTML = '';
181
                $flatArray = true;
182
                foreach ($mixed as $key => $item) {
183
                    if (is_array($item) || is_object($item)) {
184
                        $flatArray = false;
185
                    }
186
187
                    ++$count;
188
                    if ($isAssoc) {
189
                        $keyString = '<strong>' . $key . '</strong>: ';
190
                    }
191
192
                    if ($count > 20) {
193
                        $data = '.';
0 ignored issues
show
The assignment to $data is dead and can be removed.
Loading history...
194
                        $keyString = '';
195
                    }
196
197
                    if (!$flatArray) {
198
                        $mixed[$key] = $this->mixedToUl($item);
199
                    }
200
201
                    $itemHTML .= '<li style="' . $style . '">' . $keyString . $mixed[$key] . $after . '</li>';
202
                }
203
204
                if ($flatArray) {
205
                    $itemHTML = ArrayToTable::convert($mixed, 10, 100);
206
                } else {
207
                    $itemHTML .= '</ol>';
208
                }
209
210
                return $html . $itemHTML;
211
            }
212
213
            if (is_string($mixed)) {
214
                $isSql = '';
215
                if ($this->isSql($mixed)) {
216
                    $mixed = $this->stringToSqlExplainer($isSql . $mixed);
217
                }
218
219
                return '<span style="color: green">' . substr((string) $mixed, 0, 10000) . '</span>';
220
            }
221
222
            return '<span style="color: red">' . substr(Debug::text($mixed), 0, 500) . '</span>';
223
        }
224
225
        return '<span style="color: red">ERROR: please turn on SS_VARDUMP_DEBUG_ALLOWED to see data.</span>';
226
    }
227
228
    protected function isAssoc(array $arr)
229
    {
230
        if ([] === $arr) {
231
            return false;
232
        }
233
234
        return array_keys($arr) !== range(0, count($arr) - 1);
235
    }
236
237
    protected function addMethodInformation($method, $className)
238
    {
239
        $callers = debug_backtrace();
240
        foreach ($callers as $call) {
241
            if ($call['class'] !== static::class) {
242
                break;
243
            }
244
        }
245
246
        if (!$method) {
247
            $method = $call['function'] ?? 'unknown_method';
248
        }
249
250
        if (!$className) {
251
            $className = $call['class'] ?? 'unknown_class';
252
        }
253
254
        // foreach($call as $key => $value) {
255
        //     echo $key;
256
        // }
257
        $args = $call['args'] ?? '';
258
259
        return '
260
            <div style="color: blue; font-size: 12px; margin-top: 0.7rem;">
261
                ⇒' . $className . '::<strong>' . $method . '(' . print_r($args, 1) . ')</strong>
0 ignored issues
show
Are you sure print_r($args, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

261
                ⇒' . $className . '::<strong>' . $method . '(' . /** @scrutinizer ignore-type */ print_r($args, 1) . ')</strong>
Loading history...
262
            </div>
263
            <hr style="margin-bottom: 2rem;"/>
264
        ';
265
    }
266
267
    protected function stringToSqlExplainer($string): string
268
    {
269
        $string = ' ' . $string . ' ';
270
        $output = preg_replace('#\s+#', ' ', $string);
271
        foreach (self::SQL_PHRASES as $phrase) {
272
            $output = str_replace(
273
                $phrase,
274
                '<br /><br />' . $phrase . ' ',
275
                $output
276
            );
277
        }
278
279
        return $output;
280
    }
281
282
    protected function isSql(string $string): bool
283
    {
284
        $sqlCount = 0;
285
        foreach (self::SQL_PHRASES as $phrase) {
286
            if (false !== stripos($string, $phrase)) {
287
                ++$sqlCount;
288
            }
289
        }
290
291
        return $sqlCount > 2;
292
    }
293
}
294