class-thelib-array.php
5 years ago
class-thelib-core.php
5 years ago
class-thelib-debug.php
5 years ago
class-thelib-html.php
5 years ago
class-thelib-net.php
5 years ago
class-thelib-session.php
5 years ago
class-thelib-ui.php
5 years ago
class-thelib-updates.php
5 years ago
class-thelib.php
5 years ago
class-thelib-debug.php
964 lines
| 1 | <?php |
| 2 | /** |
| 3 | * The Debug component. |
| 4 | * Access via function `lib3()->debug`. |
| 5 | * |
| 6 | * @since 1.1.4 |
| 7 | */ |
| 8 | class TheLib_Debug extends TheLib { |
| 9 | |
| 10 | /** |
| 11 | * If set to true or false it will override the WP_DEBUG value |
| 12 | * If set to null the WP_DEBUG and WDEV_DEBUG values are used. |
| 13 | * |
| 14 | * @since 1.1.4 |
| 15 | * @internal |
| 16 | * @var bool |
| 17 | */ |
| 18 | protected $enabled = null; |
| 19 | |
| 20 | /** |
| 21 | * If set to true each debug output will contain a stack-trace. |
| 22 | * Otherwise only the variable will be dumped. |
| 23 | * |
| 24 | * @since 1.1.4 |
| 25 | * @internal |
| 26 | * @var bool |
| 27 | */ |
| 28 | protected $stacktrace = true; |
| 29 | |
| 30 | /** |
| 31 | * Toggles the plain-text / HTML output of the debug. |
| 32 | * All Ajax requests will ignore this flag and use plain-text format. |
| 33 | * |
| 34 | * @since 1.1.4 |
| 35 | * @internal |
| 36 | * @var bool |
| 37 | */ |
| 38 | protected $plain_text = false; |
| 39 | |
| 40 | /** |
| 41 | * Constructor. |
| 42 | * Setup action hooks for debugger. |
| 43 | * |
| 44 | * @since 2.0.0 |
| 45 | * @internal |
| 46 | */ |
| 47 | public function __construct() { |
| 48 | remove_all_actions( 'wdev_debug_log' ); |
| 49 | remove_all_actions( 'wdev_debug_log_trace' ); |
| 50 | remove_all_actions( 'wdev_debug_dump' ); |
| 51 | remove_all_actions( 'wdev_debug_trace' ); |
| 52 | |
| 53 | add_action( |
| 54 | 'wdev_debug_log', |
| 55 | array( $this, 'log' ), |
| 56 | 10, 99 |
| 57 | ); |
| 58 | |
| 59 | add_action( |
| 60 | 'wdev_debug_log_trace', |
| 61 | array( $this, 'log_trace' ) |
| 62 | ); |
| 63 | |
| 64 | add_action( |
| 65 | 'wdev_debug_dump', |
| 66 | array( $this, 'dump' ), |
| 67 | 10, 99 |
| 68 | ); |
| 69 | |
| 70 | add_action( |
| 71 | 'wdev_debug_trace', |
| 72 | array( $this, 'trace' ) |
| 73 | ); |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * Resets all debug-output flags. |
| 78 | * |
| 79 | * @since 1.1.4 |
| 80 | * @api |
| 81 | */ |
| 82 | public function reset() { |
| 83 | $this->enabled = null; |
| 84 | $this->stacktrace = true; |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * Force-Enable debugging. |
| 89 | * |
| 90 | * @since 1.1.4 |
| 91 | * @api |
| 92 | */ |
| 93 | public function enable() { |
| 94 | $this->enabled = true; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Force-Disable debugging. |
| 99 | * |
| 100 | * @since 1.1.4 |
| 101 | * @api |
| 102 | */ |
| 103 | public function disable() { |
| 104 | $this->enabled = false; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Returns the debugging status. False means no debug output is made. |
| 109 | * |
| 110 | * @since 2.0.0 |
| 111 | * @api |
| 112 | * |
| 113 | * @return bool |
| 114 | */ |
| 115 | public function is_enabled() { |
| 116 | $enabled = $this->enabled; |
| 117 | $is_ajax = false; |
| 118 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { $is_ajax = true; } |
| 119 | if ( defined( 'DOING_CRON' ) && DOING_CRON ) { $is_ajax = true; } |
| 120 | |
| 121 | if ( null === $enabled ) { |
| 122 | if ( $is_ajax ) { |
| 123 | $enabled = WDEV_AJAX_DEBUG; |
| 124 | } else { |
| 125 | $enabled = WDEV_DEBUG; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | return $enabled; |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * Enable stack-trace. |
| 134 | * |
| 135 | * @since 1.1.4 |
| 136 | * @api |
| 137 | */ |
| 138 | public function stacktrace_on() { |
| 139 | $this->stacktrace = true; |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Disable stack-trace. |
| 144 | * |
| 145 | * @since 1.1.4 |
| 146 | * @api |
| 147 | */ |
| 148 | public function stacktrace_off() { |
| 149 | $this->stacktrace = false; |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Do not format debug output. |
| 154 | * |
| 155 | * @since 1.1.4 |
| 156 | * @api |
| 157 | */ |
| 158 | public function format_text() { |
| 159 | $this->plain_text = true; |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Use HTML to format debug output. |
| 164 | * |
| 165 | * @since 1.1.4 |
| 166 | * @api |
| 167 | */ |
| 168 | public function format_html() { |
| 169 | $this->plain_text = false; |
| 170 | } |
| 171 | |
| 172 | /** |
| 173 | * Determines if the debug output should be made in plain text. |
| 174 | * |
| 175 | * @since 2.0.0 |
| 176 | * @api |
| 177 | * |
| 178 | * @return bool |
| 179 | */ |
| 180 | public function is_plain_text() { |
| 181 | $plain_text = $this->plain_text; |
| 182 | |
| 183 | $is_ajax = false; |
| 184 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { $is_ajax = true; } |
| 185 | if ( defined( 'DOING_CRON' ) && DOING_CRON ) { $is_ajax = true; } |
| 186 | if ( $is_ajax ) { $plain_text = true; } |
| 187 | |
| 188 | return $plain_text; |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Write debug information to error log file. |
| 193 | * |
| 194 | * @since 2.0.0 |
| 195 | * @api |
| 196 | * |
| 197 | * @param mixed <dynamic> Each param will be dumped |
| 198 | */ |
| 199 | public function log( $first_arg ) { |
| 200 | if ( $this->is_enabled() ) { |
| 201 | $plain_text = $this->plain_text; |
| 202 | $this->format_text(); |
| 203 | $log_file = WP_CONTENT_DIR . '/lib3.log'; |
| 204 | $time = date( "Y-m-d\tH:i:s\t" ); |
| 205 | |
| 206 | foreach ( func_get_args() as $param ) { |
| 207 | if ( is_scalar( $param ) ) { |
| 208 | $dump = $param; |
| 209 | } else { |
| 210 | $dump = var_export( $param, true ); |
| 211 | } |
| 212 | error_log( $time . $dump . "\n", 3, $log_file ); |
| 213 | } |
| 214 | |
| 215 | $this->plain_text = $plain_text; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Write stacktrace information to error log file. |
| 221 | * |
| 222 | * @since 2.0.0 |
| 223 | * @api |
| 224 | */ |
| 225 | public function log_trace() { |
| 226 | if ( $this->is_enabled() ) { |
| 227 | $plain_text = $this->plain_text; |
| 228 | $this->format_text(); |
| 229 | $log_file = WP_CONTENT_DIR . '/lib3.log'; |
| 230 | |
| 231 | // Display the backtrace. |
| 232 | $trace = $this->trace( false ); |
| 233 | error_log( $trace, 3, $log_file ); |
| 234 | |
| 235 | $this->plain_text = $plain_text; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Adds a log-message to the HTTP response header. |
| 241 | * This is very useful to debug Ajax requests or redirects. |
| 242 | * |
| 243 | * @since 2.0.3 |
| 244 | * @param string $message The debug message |
| 245 | */ |
| 246 | public function header( $message ) { |
| 247 | static $Number = 0; |
| 248 | if ( ! $this->is_enabled() ) { return; } |
| 249 | |
| 250 | $Number += 1; |
| 251 | if ( headers_sent() ) { |
| 252 | // HTTP Headers already sent, so add the response as HTML comment. |
| 253 | $message = str_replace( '-->', '--/>', $message ); |
| 254 | printf( "<!-- Debug-Note[%s]: %s -->\n", $Number, $message ); |
| 255 | } else { |
| 256 | // No output was sent yet so add the message to the HTTP headers. |
| 257 | $message = str_replace( array( "\n", "\r" ), ' ', $message ); |
| 258 | header( "X-Debug-Note[$Number]: $message", false ); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Displays a debug message at the current position on the page. |
| 264 | * |
| 265 | * @since 1.0.14 |
| 266 | * @api |
| 267 | * |
| 268 | * @param mixed <dynamic> Each param will be dumped. |
| 269 | */ |
| 270 | public function dump( $first_arg ) { |
| 271 | if ( ! $this->is_enabled() ) { return; } |
| 272 | $plain_text = $this->is_plain_text(); |
| 273 | |
| 274 | $this->add_scripts(); |
| 275 | |
| 276 | if ( ! $plain_text ) { |
| 277 | $block_id = 'wdev-debug-' . md5( rand() ); |
| 278 | $block_label = ''; |
| 279 | if ( is_scalar( $first_arg ) && ! empty( $first_arg ) ) { |
| 280 | $block_label = ': ' . (string) $first_arg; |
| 281 | } |
| 282 | ?> |
| 283 | <div class="wdev-debug"> |
| 284 | <span class="wdev-debug-label" onclick="toggleBlock('<?php echo esc_attr( $block_id ); ?>')"> |
| 285 | DEBUG<?php echo esc_html( $block_label ); ?> |
| 286 | </span> |
| 287 | <div class="<?php echo esc_attr( $block_id ); ?>"> |
| 288 | <table cellspacing="0" cellpadding="0" width="100%" border="0" class="wdev-dump"> |
| 289 | <?php |
| 290 | foreach ( func_get_args() as $param ) { |
| 291 | $this->_dump_var( $param ); |
| 292 | } |
| 293 | ?> |
| 294 | </table> |
| 295 | <?php |
| 296 | } else { |
| 297 | foreach ( func_get_args() as $param ) { |
| 298 | $dump = var_export( $param, true ); |
| 299 | echo "\r\n" . $dump; |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | // Display the backtrace. |
| 304 | if ( $this->stacktrace ) { |
| 305 | $this->trace(); |
| 306 | } |
| 307 | |
| 308 | if ( ! $plain_text ) { |
| 309 | echo '</div>'; |
| 310 | echo '<div class="wdev-debug-clear"></div>'; |
| 311 | echo '</div>'; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Output a stack-trace. |
| 317 | * |
| 318 | * @since 2.0.0 |
| 319 | * @api |
| 320 | * |
| 321 | * @param bool $output Optional. If false then the trace will be returned |
| 322 | * instead of echo'ed. Default: true (echo) |
| 323 | * @return string Returns the stack-trace contents. |
| 324 | */ |
| 325 | public function trace( $output = true ) { |
| 326 | if ( ! $this->is_enabled() ) { return; } |
| 327 | $plain_text = $this->is_plain_text(); |
| 328 | |
| 329 | $this->add_scripts(); |
| 330 | $trace_str = ''; |
| 331 | |
| 332 | if ( ! $plain_text ) { |
| 333 | $block_id = 'wdev-debug-' . md5( rand() ); |
| 334 | $trace_str .= sprintf( |
| 335 | '<span class="wdev-trace-toggle" onclick="toggleBlock(\'%1$s-trace\')"> |
| 336 | <b>Back-Trace</b> |
| 337 | </span> |
| 338 | <div class="%1$s-trace" style="display:none"> |
| 339 | <table class="wdev-trace" width="100%%" cellspacing="0" cellpadding="3" border="1"> |
| 340 | ', |
| 341 | esc_attr( $block_id ) |
| 342 | ); |
| 343 | } |
| 344 | |
| 345 | $trace = debug_backtrace(); |
| 346 | $trace_num = count( $trace ); |
| 347 | $line = 0; |
| 348 | |
| 349 | for ( $i = 0; $i < $trace_num; $i += 1 ) { |
| 350 | $item = $trace[$i]; |
| 351 | $line_item = $item; |
| 352 | $j = $i; |
| 353 | |
| 354 | while ( empty( $line_item['line'] ) && $j < $trace_num ) { |
| 355 | $line_item = $trace[$j]; |
| 356 | $j += 1; |
| 357 | } |
| 358 | |
| 359 | self::$core->array->equip( $line_item, 'file', 'line', 'class', 'type', 'function' ); |
| 360 | self::$core->array->equip( $item, 'args', 'file', 'line', 'class', 'type', 'function' ); |
| 361 | if ( 0 === strpos( $item['class'], 'TheLib_' ) ) { continue; } |
| 362 | |
| 363 | $line += 1; |
| 364 | $args = ''; |
| 365 | $arg_num = ''; |
| 366 | $dummy = array(); |
| 367 | |
| 368 | if ( $i > 0 && is_array( $item['args'] ) && count( $item['args'] ) ) { |
| 369 | foreach ( $item['args'] as $arg ) { |
| 370 | if ( is_scalar( $arg ) ) { |
| 371 | if ( is_bool( $arg ) ) { |
| 372 | $dummy[] = ( $arg ? 'true' : 'false' ); |
| 373 | } elseif ( is_string( $arg ) ) { |
| 374 | $dummy[] = '"' . $arg . '"'; |
| 375 | } else { |
| 376 | $dummy[] = $arg; |
| 377 | } |
| 378 | } elseif ( is_array( $arg ) ) { |
| 379 | $dummy[] = '<i>[Array]</i>'; |
| 380 | } elseif ( is_object( $arg ) ) { |
| 381 | $dummy[] = '<i>[' . get_class( $arg ) . ']</i>'; |
| 382 | } elseif ( is_null( $arg ) ) { |
| 383 | $dummy[] = '<i>NULL</i>'; |
| 384 | } else { |
| 385 | $dummy[] = '<i>[???]</i>'; |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | $args = implode( '</font></span><span class="trc-param"><font>', $dummy ); |
| 390 | $args = '<span class="trc-param"><font>' . $args . '</font></span>'; |
| 391 | } |
| 392 | |
| 393 | if ( $plain_text ) { |
| 394 | $file = $line_item['file']; |
| 395 | if ( strlen( $file ) > 80 ) { |
| 396 | $file = '...' . substr( $line_item['file'], -77 ); |
| 397 | } else { |
| 398 | $file = str_pad( $file, 80, ' ', STR_PAD_RIGHT ); |
| 399 | } |
| 400 | |
| 401 | $trace_str .= sprintf( |
| 402 | "\r\n %s. \t %s \t by %s", |
| 403 | str_pad( $line, 2, ' ', STR_PAD_LEFT ), |
| 404 | $file . ': ' . str_pad( $line_item['line'], 5, ' ', STR_PAD_LEFT ), |
| 405 | $item['class'] . $item['type'] . $item['function'] . '(' . strip_tags( $args ) . ')' |
| 406 | ); |
| 407 | } else { |
| 408 | $trace_str .= sprintf( |
| 409 | "<tr onclick='_m(this)'><td class='trc-num'>%s</td><td class='trc-loc'>%s</td><td class='trc-arg'>%s</td></tr>\r\n", |
| 410 | $line, |
| 411 | $line_item['file'] . ': ' . $line_item['line'], |
| 412 | $item['class'] . $item['type'] . $item['function'] . '(' . $args . ')' |
| 413 | ); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | if ( $plain_text ) { |
| 418 | $trace_str .= "\r\n-----\r\n"; |
| 419 | } else { |
| 420 | $trace_str .= '</table>'; |
| 421 | $trace_str .= '</div>'; |
| 422 | } |
| 423 | |
| 424 | if ( $output ) { |
| 425 | echo '' . $trace_str; |
| 426 | } |
| 427 | |
| 428 | return $trace_str; |
| 429 | } |
| 430 | |
| 431 | /** |
| 432 | * Outputs an advanced var dump. |
| 433 | * |
| 434 | * @since 1.1.0 |
| 435 | * @internal |
| 436 | * |
| 437 | * @param any $input The variable/object/value to dump. |
| 438 | * @param int $default_depth Deeper items will be collapsed |
| 439 | * @param int $level Do not change this value! |
| 440 | */ |
| 441 | protected function _dump_var( $data, $item_key = null, $default_depth = 3, $level = array( null ), $args = array() ) { |
| 442 | if ( ! is_string( $data ) && is_callable( $data ) ) { |
| 443 | $type = 'Callable'; |
| 444 | } else { |
| 445 | $type = ucfirst( gettype( $data ) ); |
| 446 | } |
| 447 | |
| 448 | if ( empty( $level ) ) { $level = array( null ); } |
| 449 | $args['containers'] = self::$core->array->get( $args['containers'] ); |
| 450 | $args['collapsed'] = self::$core->array->get( $args['collapsed'] ); |
| 451 | |
| 452 | $type_data = null; |
| 453 | $type_length = null; |
| 454 | $full_dump = false; |
| 455 | |
| 456 | switch ( $type ) { |
| 457 | case 'String': |
| 458 | $type_length = strlen( $data ); |
| 459 | $type_data = '"' . htmlentities( $data ) . '"'; |
| 460 | break; |
| 461 | |
| 462 | case 'Double': |
| 463 | case 'Float': |
| 464 | $type = 'Float'; |
| 465 | $type_length = strlen( $data ); |
| 466 | $type_data = htmlentities( $data ); |
| 467 | break; |
| 468 | |
| 469 | case 'Integer': |
| 470 | $type_length = strlen( $data ); |
| 471 | $type_data = htmlentities( $data ); |
| 472 | break; |
| 473 | |
| 474 | case 'Boolean': |
| 475 | $type_length = strlen( $data ); |
| 476 | $type_data = $data ? 'TRUE' : 'FALSE'; |
| 477 | break; |
| 478 | |
| 479 | case 'NULL': |
| 480 | $type_length = 0; |
| 481 | $type_data = 'NULL'; |
| 482 | break; |
| 483 | |
| 484 | case 'Array': |
| 485 | $type_length = count( $data ); |
| 486 | break; |
| 487 | |
| 488 | case 'Object': |
| 489 | $full_dump = true; |
| 490 | break; |
| 491 | } |
| 492 | |
| 493 | $type_label = $type . ( $type_length !== null ? '(' . $type_length . ')' : '' ); |
| 494 | |
| 495 | if ( in_array( $type, array( 'Object', 'Array' ) ) ) { |
| 496 | $populated = false; |
| 497 | |
| 498 | $dump_data = (array) $data; |
| 499 | ksort( $dump_data ); |
| 500 | |
| 501 | if ( 'Object' == $type ) { |
| 502 | $type_label .= ' [' . get_class( $data ) . ']'; |
| 503 | } |
| 504 | |
| 505 | $keys = array_keys( $dump_data ); |
| 506 | $last_key = end( $keys ); |
| 507 | reset( $dump_data ); |
| 508 | |
| 509 | foreach ( $dump_data as $key => $value ) { |
| 510 | if ( ! $populated ) { |
| 511 | $populated = true; |
| 512 | |
| 513 | $id = substr( md5( rand() . ':' . $key . ':' . count( $level ) ), 0, 8 ); |
| 514 | $args['containers'][] = $id; |
| 515 | $collapse = count( $args['containers'] ) >= $default_depth; |
| 516 | if ( $collapse ) { |
| 517 | $args['collapsed'][] = $id; |
| 518 | } |
| 519 | |
| 520 | $title_args = $args; |
| 521 | $title_args['toggle'] = $id; |
| 522 | |
| 523 | $this->_dump_line( |
| 524 | $item_key, |
| 525 | $type_label, |
| 526 | '', |
| 527 | $level, |
| 528 | $title_args |
| 529 | ); |
| 530 | unset( $args['protected'] ); |
| 531 | unset( $args['private'] ); |
| 532 | } |
| 533 | |
| 534 | // Tree right before the item-name |
| 535 | $new_level = $level; |
| 536 | |
| 537 | if ( $last_key == $key ) { |
| 538 | $new_level[] = false; |
| 539 | $args['lastkey'] = true; |
| 540 | } else { |
| 541 | $new_level[] = true; |
| 542 | $args['lastkey'] = false; |
| 543 | } |
| 544 | |
| 545 | $encode_key = json_encode( $key ); |
| 546 | $matches = null; |
| 547 | if ( 1 === strpos( $encode_key, '\\u0000*\\u0000' ) ) { |
| 548 | $args['protected'] = true; |
| 549 | $key = substr( $key, 3 ); |
| 550 | } elseif ( 1 === preg_match( '/\\\\u0000(\w+)\\\\u0000/i', $encode_key, $matches ) ) { |
| 551 | $args['private'] = true; |
| 552 | $key = substr( $key, 2 + strlen( $matches[1] ) ); |
| 553 | } |
| 554 | |
| 555 | $this->_dump_var( |
| 556 | $value, |
| 557 | $key, |
| 558 | $default_depth, |
| 559 | $new_level, |
| 560 | $args |
| 561 | ); |
| 562 | |
| 563 | unset( $args['protected'] ); |
| 564 | unset( $args['private'] ); |
| 565 | } // end of array/object loop. |
| 566 | |
| 567 | if ( ! $populated ) { |
| 568 | $this->_dump_line( |
| 569 | $item_key, |
| 570 | $type_label, |
| 571 | '', |
| 572 | $level, |
| 573 | $args |
| 574 | ); |
| 575 | } |
| 576 | } else { |
| 577 | $this->_dump_line( |
| 578 | $item_key, |
| 579 | $type_label, |
| 580 | $type_data, |
| 581 | $level, |
| 582 | $args |
| 583 | ); |
| 584 | } |
| 585 | } |
| 586 | |
| 587 | /** |
| 588 | * Outputs a single line of the dump_var output. |
| 589 | * |
| 590 | * @since 1.1.4 |
| 591 | * @internal |
| 592 | */ |
| 593 | protected function _dump_line( $key, $type, $value, $level, $args = array() ) { |
| 594 | $type_color = '#999'; |
| 595 | $type_key = strtolower( $type ); |
| 596 | if ( strlen( $type_key ) > 4 ) { $type_key = substr( $type_key, 0, 4 ); } |
| 597 | |
| 598 | $custom_type_colors = array( |
| 599 | 'stri' => 'green', |
| 600 | 'doub' => '#0099c5', |
| 601 | 'floa' => '#0099c5', |
| 602 | 'inte' => 'red', |
| 603 | 'bool' => '#92008d', |
| 604 | 'null' => '#AAA', |
| 605 | ); |
| 606 | |
| 607 | if ( isset( $custom_type_colors[ $type_key ] ) ) { |
| 608 | $type_color = $custom_type_colors[ $type_key ]; |
| 609 | } |
| 610 | |
| 611 | $collapse = array_intersect( $args['containers'], $args['collapsed'] ); |
| 612 | $args['do_collapse'] = is_array( $collapse ) && count( $collapse ) > 0; |
| 613 | if ( ! empty( $args['toggle'] ) ) { |
| 614 | $args['containers'] = array_diff( $args['containers'], array( $args['toggle'] ) ); |
| 615 | $args['collapsed'] = array_diff( $args['collapsed'], array( $args['toggle'] ) ); |
| 616 | |
| 617 | $collapse_this = array_intersect( $args['containers'], $args['collapsed'] ); |
| 618 | $args['do_collapse_next'] = $args['do_collapse']; |
| 619 | $args['do_collapse'] = is_array( $collapse_this ) && count( $collapse_this ) > 0; |
| 620 | } |
| 621 | |
| 622 | $row_class = ''; |
| 623 | $row_attr = ''; |
| 624 | if ( ! empty( $args['containers'] ) ) { |
| 625 | $row_class = implode( ' ', $args['containers'] ); |
| 626 | } |
| 627 | if ( ! empty( $args['do_collapse'] ) ) { |
| 628 | $row_attr = 'style="display:none;"'; |
| 629 | } |
| 630 | echo '<tr class="' . $row_class . '"' . $row_attr . '><td>'; |
| 631 | |
| 632 | // Property-key, if set. |
| 633 | if ( $key === null ) { |
| 634 | // Full Tree-level. |
| 635 | echo '<span class="dev-tree">'; |
| 636 | for ( $i = 0; $i < count( $level ); $i += 1 ) { |
| 637 | if ( null === $level[$i] ) { continue; } |
| 638 | if ( $level[$i] ) { echo ' │ '; } |
| 639 | else { echo ' '; } |
| 640 | } |
| 641 | echo '</span>'; |
| 642 | } else { |
| 643 | echo '<span class="dev-tree">'; |
| 644 | // Tree-level without last level. |
| 645 | for ( $i = 0; $i < count( $level ) - 1; $i += 1 ) { |
| 646 | if ( null === $level[$i] ) { continue; } |
| 647 | if ( $level[$i] ) { echo ' │ '; } |
| 648 | else { echo ' '; } |
| 649 | } |
| 650 | |
| 651 | if ( empty( $args['lastkey'] ) ) { |
| 652 | echo ' ├─'; |
| 653 | } else { |
| 654 | echo ' └─'; |
| 655 | } |
| 656 | echo '</span>'; |
| 657 | |
| 658 | $key_style = ''; |
| 659 | if ( ! empty( $args['protected'] ) ) { |
| 660 | $key_style .= 'color:#900;'; |
| 661 | $prefix = ''; |
| 662 | } elseif ( ! empty( $args['private'] ) ) { |
| 663 | $key_style .= 'color:#C00;font-style:italic;'; |
| 664 | $prefix = 'PRIVATE '; |
| 665 | } else { |
| 666 | $key_style .= 'color:#000;'; |
| 667 | $prefix = ''; |
| 668 | } |
| 669 | |
| 670 | $valid_ids = array( 'ID', 'id' ); |
| 671 | $is_id = in_array( (string) $key, $valid_ids ); |
| 672 | if ( $is_id ) { |
| 673 | $key_style .= 'background:#FDA;'; |
| 674 | } |
| 675 | |
| 676 | echo '<span class="dev-item dev-item-key" style="' . $key_style . '">[ ' . $prefix . $key . ' ]</span>'; |
| 677 | echo '<span class="dev-item"> => </span>'; |
| 678 | } |
| 679 | |
| 680 | // Data-Type. |
| 681 | if ( ! empty( $args['toggle'] ) ) { |
| 682 | echo '<a href="javascript:toggleDisplay(\''. $args['toggle'] . '\',\'' . trim( $row_class . ' ' . $args['toggle'] ) . '\');" class="dev-item dev-toggle-item">'; |
| 683 | echo '<span style="color:#666666">' . $type . '</span> '; |
| 684 | echo '</a>'; |
| 685 | } else { |
| 686 | echo '<span class="dev-item" style="color:#666666">' . $type . ' </span>'; |
| 687 | } |
| 688 | |
| 689 | if ( ! empty( $args['toggle'] ) ) { |
| 690 | $collapsed = ! empty( $args['do_collapse_next'] ); |
| 691 | $toggle_style = 'display: ' . ( $collapsed ? 'inline' : 'none' ); |
| 692 | echo '<span id="plus' . $args['toggle'] . '" class="plus dev-item" style="' . $toggle_style . '"> ⤵</span>'; |
| 693 | } |
| 694 | |
| 695 | // Value. |
| 696 | if ( $value !== null ) { |
| 697 | $value_style = ''; |
| 698 | if ( isset( $args['highlight'] ) ) { |
| 699 | $value_style = $args['highlight']; |
| 700 | } |
| 701 | echo '<span class="dev-item" style="color:' . $type_color . ';' . $value_style . '">' . $value . '</span>'; |
| 702 | } |
| 703 | |
| 704 | echo '</td></tr>'; |
| 705 | echo "\r\n"; |
| 706 | } |
| 707 | |
| 708 | /** |
| 709 | * Outputs the CSS and JS scripts required to display the debug dump/trace. |
| 710 | * |
| 711 | * @since 2.0.0 |
| 712 | * @internal |
| 713 | */ |
| 714 | protected function add_scripts() { |
| 715 | if ( $this->is_plain_text() ) { return; } |
| 716 | if ( defined( '__DEBUG_SCRIPT' ) ) { return; } |
| 717 | define( '__DEBUG_SCRIPT', true ); |
| 718 | |
| 719 | if ( ! headers_sent() ) { |
| 720 | header( 'Content-type: text/html; charset=utf-8' ); |
| 721 | } |
| 722 | |
| 723 | ?> |
| 724 | <style> |
| 725 | .wdev-debug { |
| 726 | clear: both; |
| 727 | border: 1px solid #C00; |
| 728 | background: rgba(255, 200, 200, 1); |
| 729 | padding: 10px; |
| 730 | margin: 10px; |
| 731 | position: relative; |
| 732 | z-index: 99999; |
| 733 | box-shadow: 0 1px 5px rgba(0,0,0,0.3); |
| 734 | font-size: 12px; |
| 735 | font-family: sans-serif; |
| 736 | font-weight: 200; |
| 737 | line-height: 1; |
| 738 | } |
| 739 | .wdev-debug .dev-tree { |
| 740 | color: #000; |
| 741 | opacity: .2; |
| 742 | font-family: monospace; |
| 743 | font-size: 19px; |
| 744 | line-height: 16px; |
| 745 | float: left; |
| 746 | } |
| 747 | .wdev-debug .dev-item { |
| 748 | float: left; |
| 749 | line-height: 16px; |
| 750 | white-space: pre; |
| 751 | } |
| 752 | .wdev-debug .dev-toggle-item { |
| 753 | text-decoration: none; |
| 754 | background: rgba(255,255,255,0.2); |
| 755 | display: inline-block; |
| 756 | } |
| 757 | .wdev-debug .wdev-dump { |
| 758 | margin: 0; |
| 759 | border-collapse: collapse; |
| 760 | padding: 0; |
| 761 | border: 0; |
| 762 | } |
| 763 | .wdev-debug .wdev-trace-toggle { |
| 764 | display: block; |
| 765 | margin: 10px 0 0 0; |
| 766 | } |
| 767 | .wdev-debug .wdev-dump td { |
| 768 | font-size: 12px; |
| 769 | line-height: 1; |
| 770 | font-family: sans-serif; |
| 771 | font-weight: 200; |
| 772 | background: transparent; |
| 773 | cursor: default; |
| 774 | padding: 0; |
| 775 | border: 0; |
| 776 | word-break: normal!important; |
| 777 | } |
| 778 | .wdev-debug .wdev-dump tr:hover td { |
| 779 | background-color: #FFF; |
| 780 | background-color: rgba(255,255,255,0.3); |
| 781 | } |
| 782 | .wdev-debug-clear { |
| 783 | clear: both; |
| 784 | display: table; |
| 785 | padding: 0; |
| 786 | margin: 0; |
| 787 | } |
| 788 | .wdev-debug-label { |
| 789 | font-size: 11px; |
| 790 | float: right; |
| 791 | margin: -10px; |
| 792 | color: #FFF; |
| 793 | background-color: #D88; |
| 794 | padding: 2px 8px; |
| 795 | cursor: pointer; |
| 796 | max-width: 50%; |
| 797 | white-space: nowrap; |
| 798 | overflow: hidden; |
| 799 | text-overflow: ellipsis; |
| 800 | } |
| 801 | .wdev-debug-label:hover { |
| 802 | background-color: #E66; |
| 803 | } |
| 804 | .wdev-debug pre { |
| 805 | font-size: 12px !important; |
| 806 | margin: 1px 0 !important; |
| 807 | background: rgba(255, 200, 200, 0.8); |
| 808 | } |
| 809 | .wdev-trace td { |
| 810 | padding: 1px 2px !important; |
| 811 | font-size: 12px; |
| 812 | vertical-align: top; |
| 813 | word-break: normal!important; |
| 814 | } |
| 815 | .wdev-trace { |
| 816 | margin: 4px 0 0 0; |
| 817 | background: #EBB; |
| 818 | border-collapse: collapse; |
| 819 | } |
| 820 | .wdev-trace tr.mark td { |
| 821 | background: #EC9; |
| 822 | } |
| 823 | .wdev-trace tr td { |
| 824 | cursor: default; |
| 825 | } |
| 826 | .wdev-trace-toggle { |
| 827 | cursor: pointer; |
| 828 | } |
| 829 | .wdev-debug .trc-num { |
| 830 | width: 40px; |
| 831 | } |
| 832 | .wdev-debug .trc-loc { |
| 833 | width: 60%; |
| 834 | } |
| 835 | .wdev-debug .trc-arg { |
| 836 | width: 40%; |
| 837 | font-size: 11px; |
| 838 | white-space: nowrap; |
| 839 | } |
| 840 | .wdev-debug .trc-param { |
| 841 | padding: 0 3px; |
| 842 | display: block; |
| 843 | margin: 1px 0 1px 8px; |
| 844 | } |
| 845 | .wdev-debug .trc-param font { |
| 846 | background: rgba( 0,0,0,0.05 ); |
| 847 | } |
| 848 | .wdev-debug .trc-param:after { |
| 849 | content: ','; |
| 850 | } |
| 851 | .wdev-debug .trc-param:last-child:after { |
| 852 | content: ''; |
| 853 | } |
| 854 | </style> |
| 855 | <script> |
| 856 | // Toggle whole block (debug/trace) |
| 857 | function toggleBlock( clsname ) { |
| 858 | var wrap = document.getElementsByClassName( clsname ); |
| 859 | |
| 860 | for ( var i = 0; i < wrap.length; i += 1 ) { |
| 861 | var state = (wrap[i].style.display == 'none' ? 'block' : 'none'); |
| 862 | wrap[i].style.display = state; |
| 863 | } |
| 864 | } |
| 865 | // Mark a table row |
| 866 | function _m( row ) { |
| 867 | row.classList.toggle( 'mark' ); |
| 868 | } |
| 869 | // Toggle a single debug-output-level |
| 870 | function toggleDisplay( clsname, full_class ) { |
| 871 | var elements = document.getElementsByClassName( clsname ), |
| 872 | plus = document.getElementById( "plus" + clsname ), |
| 873 | plus_state = (plus.style.display == 'none' ? 'inline' : 'none'), |
| 874 | el_state = (plus_state == 'none' ? 'table-row' : 'none' ), |
| 875 | sub_id = '', |
| 876 | sub_state = el_state; |
| 877 | |
| 878 | plus.style.display = plus_state; |
| 879 | |
| 880 | for ( var i = 0; i < elements.length; i += 1 ) { |
| 881 | var sub_plus = elements[i].getElementsByClassName( 'plus' ); |
| 882 | |
| 883 | if ( elements[i].className == full_class ) { |
| 884 | if ( sub_plus.length ) { sub_plus[0].style.display = 'inline'; } |
| 885 | elements[i].style.display = el_state; |
| 886 | } else { |
| 887 | if ( sub_plus.length ) { sub_plus[0].style.display = 'inline'; } |
| 888 | elements[i].style.display = 'none'; |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | </script> |
| 893 | <?php |
| 894 | } |
| 895 | |
| 896 | /** |
| 897 | * Returns an HTML element that displays a colored label. By default the |
| 898 | * label is a random/unique MD5 hash. |
| 899 | * This marker is intended for debugging to identify changes in objects |
| 900 | * that are loaded via ajax. |
| 901 | * |
| 902 | * @since 2.0.1 |
| 903 | * @api |
| 904 | * |
| 905 | * @param string $label Optional. The label to display. Default is a |
| 906 | * random MD5 string. |
| 907 | * @param array $styles Optional. Array of CSS styles to apply. |
| 908 | * @return object { |
| 909 | * Marker details |
| 910 | * |
| 911 | * $html |
| 912 | * $hash |
| 913 | * $text |
| 914 | * $color |
| 915 | * } |
| 916 | */ |
| 917 | public function marker_html( $label = null, $styles = array() ) { |
| 918 | $hash = md5( rand( 1000, 9999 ) . time() ); |
| 919 | |
| 920 | if ( null === $label ) { |
| 921 | $label = $hash; |
| 922 | } else { |
| 923 | $hash = md5( $label ); |
| 924 | } |
| 925 | |
| 926 | $color = substr( $hash, 0, 3 ); |
| 927 | $def_styles = array( |
| 928 | 'background' => '#' . $color, |
| 929 | 'color' => '#fff', |
| 930 | 'width' => '280px', |
| 931 | 'font-size' => '12px', |
| 932 | 'text-transform' => 'uppercase', |
| 933 | 'font-family' => 'monospace', |
| 934 | 'text-align' => 'center', |
| 935 | 'margin' => '0 auto 5px', |
| 936 | 'border-radius' => '3px', |
| 937 | 'padding' => '4px', |
| 938 | 'text-shadow' => '0 0 1px #666', |
| 939 | 'box-shadow' => '0 0 1px #000 inset', |
| 940 | ); |
| 941 | $styles = wp_parse_args( |
| 942 | $styles, |
| 943 | $def_styles |
| 944 | ); |
| 945 | |
| 946 | $style = ''; |
| 947 | foreach ( $styles as $key => $val ) { |
| 948 | $style .= $key . ':' . $val . ';'; |
| 949 | } |
| 950 | |
| 951 | $marker = sprintf( |
| 952 | '<div style="%1$s">%2$s</div>', |
| 953 | esc_attr( $style ), |
| 954 | $label |
| 955 | ); |
| 956 | |
| 957 | return (object) array( |
| 958 | 'html' => $marker, |
| 959 | 'hash' => $hash, |
| 960 | 'text' => $label, |
| 961 | 'color' => '#' . $color, |
| 962 | ); |
| 963 | } |
| 964 | } |