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
Unused Code
introduced
by
![]() |
|||||
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
|
|||||
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
![]() |
|||||
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 |