[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * This file is part of the DebugBar package. 4 * 5 * (c) 2013 Maxime Bouroumeau-Fuseau 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11 namespace DebugBar; 12 13 use DebugBar\DataCollector\AssetProvider; 14 use DebugBar\DataCollector\Renderable; 15 16 /** 17 * Renders the debug bar using the client side javascript implementation 18 * 19 * Generates all the needed initialization code of controls 20 */ 21 class JavascriptRenderer 22 { 23 const INITIALIZE_CONSTRUCTOR = 2; 24 25 const INITIALIZE_CONTROLS = 4; 26 27 const REPLACEABLE_TAG = "{--DEBUGBAR_OB_START_REPLACE_ME--}"; 28 29 const RELATIVE_PATH = 'path'; 30 31 const RELATIVE_URL = 'url'; 32 33 protected $debugBar; 34 35 protected $baseUrl; 36 37 protected $basePath; 38 39 protected $cssVendors = array( 40 'fontawesome' => 'vendor/font-awesome/css/font-awesome.min.css', 41 'highlightjs' => 'vendor/highlightjs/styles/github.css' 42 ); 43 44 protected $jsVendors = array( 45 'jquery' => 'vendor/jquery/dist/jquery.min.js', 46 'highlightjs' => 'vendor/highlightjs/highlight.pack.js' 47 ); 48 49 protected $includeVendors = true; 50 51 protected $cssFiles = array('debugbar.css', 'widgets.css', 'openhandler.css'); 52 53 protected $jsFiles = array('debugbar.js', 'widgets.js', 'openhandler.js'); 54 55 protected $additionalAssets = array(); 56 57 protected $javascriptClass = 'PhpDebugBar.DebugBar'; 58 59 protected $variableName = 'phpdebugbar'; 60 61 protected $enableJqueryNoConflict = true; 62 63 protected $useRequireJs = false; 64 65 protected $initialization; 66 67 protected $controls = array(); 68 69 protected $ignoredCollectors = array(); 70 71 protected $ajaxHandlerClass = 'PhpDebugBar.AjaxHandler'; 72 73 protected $ajaxHandlerBindToFetch = false; 74 75 protected $ajaxHandlerBindToJquery = true; 76 77 protected $ajaxHandlerBindToXHR = false; 78 79 protected $ajaxHandlerAutoShow = true; 80 81 protected $openHandlerClass = 'PhpDebugBar.OpenHandler'; 82 83 protected $openHandlerUrl; 84 85 protected $cspNonce; 86 87 /** 88 * @param \DebugBar\DebugBar $debugBar 89 * @param string $baseUrl 90 * @param string $basePath 91 */ 92 public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = null) 93 { 94 $this->debugBar = $debugBar; 95 96 if ($baseUrl === null) { 97 $baseUrl = '/vendor/maximebf/debugbar/src/DebugBar/Resources'; 98 } 99 $this->baseUrl = $baseUrl; 100 101 if ($basePath === null) { 102 $basePath = __DIR__ . DIRECTORY_SEPARATOR . 'Resources'; 103 } 104 $this->basePath = $basePath; 105 106 // bitwise operations cannot be done in class definition :( 107 $this->initialization = self::INITIALIZE_CONSTRUCTOR | self::INITIALIZE_CONTROLS; 108 } 109 110 /** 111 * Sets options from an array 112 * 113 * Options: 114 * - base_path 115 * - base_url 116 * - include_vendors 117 * - javascript_class 118 * - variable_name 119 * - initialization 120 * - enable_jquery_noconflict 121 * - controls 122 * - disable_controls 123 * - ignore_collectors 124 * - ajax_handler_classname 125 * - ajax_handler_bind_to_jquery 126 * - ajax_handler_auto_show 127 * - open_handler_classname 128 * - open_handler_url 129 * 130 * @param array $options [description] 131 */ 132 public function setOptions(array $options) 133 { 134 if (array_key_exists('base_path', $options)) { 135 $this->setBasePath($options['base_path']); 136 } 137 if (array_key_exists('base_url', $options)) { 138 $this->setBaseUrl($options['base_url']); 139 } 140 if (array_key_exists('include_vendors', $options)) { 141 $this->setIncludeVendors($options['include_vendors']); 142 } 143 if (array_key_exists('javascript_class', $options)) { 144 $this->setJavascriptClass($options['javascript_class']); 145 } 146 if (array_key_exists('variable_name', $options)) { 147 $this->setVariableName($options['variable_name']); 148 } 149 if (array_key_exists('initialization', $options)) { 150 $this->setInitialization($options['initialization']); 151 } 152 if (array_key_exists('enable_jquery_noconflict', $options)) { 153 $this->setEnableJqueryNoConflict($options['enable_jquery_noconflict']); 154 } 155 if (array_key_exists('use_requirejs', $options)) { 156 $this->setUseRequireJs($options['use_requirejs']); 157 } 158 if (array_key_exists('controls', $options)) { 159 foreach ($options['controls'] as $name => $control) { 160 $this->addControl($name, $control); 161 } 162 } 163 if (array_key_exists('disable_controls', $options)) { 164 foreach ((array) $options['disable_controls'] as $name) { 165 $this->disableControl($name); 166 } 167 } 168 if (array_key_exists('ignore_collectors', $options)) { 169 foreach ((array) $options['ignore_collectors'] as $name) { 170 $this->ignoreCollector($name); 171 } 172 } 173 if (array_key_exists('ajax_handler_classname', $options)) { 174 $this->setAjaxHandlerClass($options['ajax_handler_classname']); 175 } 176 if (array_key_exists('ajax_handler_bind_to_jquery', $options)) { 177 $this->setBindAjaxHandlerToJquery($options['ajax_handler_bind_to_jquery']); 178 } 179 if (array_key_exists('ajax_handler_auto_show', $options)) { 180 $this->setAjaxHandlerAutoShow($options['ajax_handler_auto_show']); 181 } 182 if (array_key_exists('open_handler_classname', $options)) { 183 $this->setOpenHandlerClass($options['open_handler_classname']); 184 } 185 if (array_key_exists('open_handler_url', $options)) { 186 $this->setOpenHandlerUrl($options['open_handler_url']); 187 } 188 if (array_key_exists('csp_nonce', $options)) { 189 $this->setCspNonce($options['csp_nonce']); 190 } 191 } 192 193 /** 194 * Sets the path which assets are relative to 195 * 196 * @param string $path 197 */ 198 public function setBasePath($path) 199 { 200 $this->basePath = $path; 201 return $this; 202 } 203 204 /** 205 * Returns the path which assets are relative to 206 * 207 * @return string 208 */ 209 public function getBasePath() 210 { 211 return $this->basePath; 212 } 213 214 /** 215 * Sets the base URL from which assets will be served 216 * 217 * @param string $url 218 */ 219 public function setBaseUrl($url) 220 { 221 $this->baseUrl = $url; 222 return $this; 223 } 224 225 /** 226 * Returns the base URL from which assets will be served 227 * 228 * @return string 229 */ 230 public function getBaseUrl() 231 { 232 return $this->baseUrl; 233 } 234 235 /** 236 * Whether to include vendor assets 237 * 238 * You can only include js or css vendors using 239 * setIncludeVendors('css') or setIncludeVendors('js') 240 * 241 * @param boolean $enabled 242 */ 243 public function setIncludeVendors($enabled = true) 244 { 245 if (is_string($enabled)) { 246 $enabled = array($enabled); 247 } 248 $this->includeVendors = $enabled; 249 250 if (!$enabled || (is_array($enabled) && !in_array('js', $enabled))) { 251 // no need to call jQuery.noConflict() if we do not include our own version 252 $this->enableJqueryNoConflict = false; 253 } 254 255 return $this; 256 } 257 258 /** 259 * Checks if vendors assets are included 260 * 261 * @return boolean 262 */ 263 public function areVendorsIncluded() 264 { 265 return $this->includeVendors !== false; 266 } 267 268 /** 269 * Disable a specific vendor's assets. 270 * 271 * @param string $name "jquery", "fontawesome", "highlightjs" 272 * 273 * @return void 274 */ 275 public function disableVendor($name) 276 { 277 if (array_key_exists($name, $this->cssVendors)) { 278 unset($this->cssVendors[$name]); 279 } 280 if (array_key_exists($name, $this->jsVendors)) { 281 unset($this->jsVendors[$name]); 282 } 283 } 284 285 /** 286 * Sets the javascript class name 287 * 288 * @param string $className 289 */ 290 public function setJavascriptClass($className) 291 { 292 $this->javascriptClass = $className; 293 return $this; 294 } 295 296 /** 297 * Returns the javascript class name 298 * 299 * @return string 300 */ 301 public function getJavascriptClass() 302 { 303 return $this->javascriptClass; 304 } 305 306 /** 307 * Sets the variable name of the class instance 308 * 309 * @param string $name 310 */ 311 public function setVariableName($name) 312 { 313 $this->variableName = $name; 314 return $this; 315 } 316 317 /** 318 * Returns the variable name of the class instance 319 * 320 * @return string 321 */ 322 public function getVariableName() 323 { 324 return $this->variableName; 325 } 326 327 /** 328 * Sets what should be initialized 329 * 330 * - INITIALIZE_CONSTRUCTOR: only initializes the instance 331 * - INITIALIZE_CONTROLS: initializes the controls and data mapping 332 * - INITIALIZE_CONSTRUCTOR | INITIALIZE_CONTROLS: initialize everything (default) 333 * 334 * @param integer $init 335 */ 336 public function setInitialization($init) 337 { 338 $this->initialization = $init; 339 return $this; 340 } 341 342 /** 343 * Returns what should be initialized 344 * 345 * @return integer 346 */ 347 public function getInitialization() 348 { 349 return $this->initialization; 350 } 351 352 /** 353 * Sets whether to call jQuery.noConflict() 354 * 355 * @param boolean $enabled 356 */ 357 public function setEnableJqueryNoConflict($enabled = true) 358 { 359 $this->enableJqueryNoConflict = $enabled; 360 return $this; 361 } 362 363 /** 364 * Checks if jQuery.noConflict() will be called 365 * 366 * @return boolean 367 */ 368 public function isJqueryNoConflictEnabled() 369 { 370 return $this->enableJqueryNoConflict; 371 } 372 373 /** 374 * Sets whether to use RequireJS or not 375 * 376 * @param boolean $enabled 377 * @return $this 378 */ 379 public function setUseRequireJs($enabled = true) 380 { 381 $this->useRequireJs = $enabled; 382 return $this; 383 } 384 385 /** 386 * Checks if RequireJS is used 387 * 388 * @return boolean 389 */ 390 public function isRequireJsUsed() 391 { 392 return $this->useRequireJs; 393 } 394 395 /** 396 * Adds a control to initialize 397 * 398 * Possible options: 399 * - icon: icon name 400 * - tooltip: string 401 * - widget: widget class name 402 * - title: tab title 403 * - map: a property name from the data to map the control to 404 * - default: a js string, default value of the data map 405 * 406 * "icon" or "widget" are at least needed 407 * 408 * @param string $name 409 * @param array $options 410 */ 411 public function addControl($name, array $options) 412 { 413 if (count(array_intersect(array_keys($options), array('icon', 'widget', 'tab', 'indicator'))) === 0) { 414 throw new DebugBarException("Not enough options for control '$name'"); 415 } 416 $this->controls[$name] = $options; 417 return $this; 418 } 419 420 /** 421 * Disables a control 422 * 423 * @param string $name 424 */ 425 public function disableControl($name) 426 { 427 $this->controls[$name] = null; 428 return $this; 429 } 430 431 /** 432 * Returns the list of controls 433 * 434 * This does not include controls provided by collectors 435 * 436 * @return array 437 */ 438 public function getControls() 439 { 440 return $this->controls; 441 } 442 443 /** 444 * Ignores widgets provided by a collector 445 * 446 * @param string $name 447 */ 448 public function ignoreCollector($name) 449 { 450 $this->ignoredCollectors[] = $name; 451 return $this; 452 } 453 454 /** 455 * Returns the list of ignored collectors 456 * 457 * @return array 458 */ 459 public function getIgnoredCollectors() 460 { 461 return $this->ignoredCollectors; 462 } 463 464 /** 465 * Sets the class name of the ajax handler 466 * 467 * Set to false to disable 468 * 469 * @param string $className 470 */ 471 public function setAjaxHandlerClass($className) 472 { 473 $this->ajaxHandlerClass = $className; 474 return $this; 475 } 476 477 /** 478 * Returns the class name of the ajax handler 479 * 480 * @return string 481 */ 482 public function getAjaxHandlerClass() 483 { 484 return $this->ajaxHandlerClass; 485 } 486 487 /** 488 * Sets whether to call bindToFetch() on the ajax handler 489 * 490 * @param boolean $bind 491 */ 492 public function setBindAjaxHandlerToFetch($bind = true) 493 { 494 $this->ajaxHandlerBindToFetch = $bind; 495 return $this; 496 } 497 498 /** 499 * Checks whether bindToFetch() will be called on the ajax handler 500 * 501 * @return boolean 502 */ 503 public function isAjaxHandlerBoundToFetch() 504 { 505 return $this->ajaxHandlerBindToFetch; 506 } 507 508 /** 509 * Sets whether to call bindToJquery() on the ajax handler 510 * 511 * @param boolean $bind 512 */ 513 public function setBindAjaxHandlerToJquery($bind = true) 514 { 515 $this->ajaxHandlerBindToJquery = $bind; 516 return $this; 517 } 518 519 /** 520 * Checks whether bindToJquery() will be called on the ajax handler 521 * 522 * @return boolean 523 */ 524 public function isAjaxHandlerBoundToJquery() 525 { 526 return $this->ajaxHandlerBindToJquery; 527 } 528 529 /** 530 * Sets whether to call bindToXHR() on the ajax handler 531 * 532 * @param boolean $bind 533 */ 534 public function setBindAjaxHandlerToXHR($bind = true) 535 { 536 $this->ajaxHandlerBindToXHR = $bind; 537 return $this; 538 } 539 540 /** 541 * Checks whether bindToXHR() will be called on the ajax handler 542 * 543 * @return boolean 544 */ 545 public function isAjaxHandlerBoundToXHR() 546 { 547 return $this->ajaxHandlerBindToXHR; 548 } 549 550 /** 551 * Sets whether new ajax debug data will be immediately shown. Setting to false could be useful 552 * if there are a lot of tracking events cluttering things. 553 * 554 * @param boolean $autoShow 555 */ 556 public function setAjaxHandlerAutoShow($autoShow = true) 557 { 558 $this->ajaxHandlerAutoShow = $autoShow; 559 return $this; 560 } 561 562 /** 563 * Checks whether the ajax handler will immediately show new ajax requests. 564 * 565 * @return boolean 566 */ 567 public function isAjaxHandlerAutoShow() 568 { 569 return $this->ajaxHandlerAutoShow; 570 } 571 572 /** 573 * Sets the class name of the js open handler 574 * 575 * @param string $className 576 */ 577 public function setOpenHandlerClass($className) 578 { 579 $this->openHandlerClass = $className; 580 return $this; 581 } 582 583 /** 584 * Returns the class name of the js open handler 585 * 586 * @return string 587 */ 588 public function getOpenHandlerClass() 589 { 590 return $this->openHandlerClass; 591 } 592 593 /** 594 * Sets the url of the open handler 595 * 596 * @param string $url 597 */ 598 public function setOpenHandlerUrl($url) 599 { 600 $this->openHandlerUrl = $url; 601 return $this; 602 } 603 604 /** 605 * Returns the url for the open handler 606 * 607 * @return string 608 */ 609 public function getOpenHandlerUrl() 610 { 611 return $this->openHandlerUrl; 612 } 613 614 /** 615 * Sets the CSP Nonce (or remove it by setting to null) 616 * 617 * @param string|null $nonce 618 * @return $this 619 */ 620 public function setCspNonce($nonce = null) 621 { 622 $this->cspNonce = $nonce; 623 return $this; 624 } 625 626 /** 627 * Get the CSP Nonce 628 * 629 * @return string|null 630 */ 631 public function getCspNonce() 632 { 633 return $this->cspNonce; 634 } 635 636 /** 637 * Add assets stored in files to render in the head 638 * 639 * @param array $cssFiles An array of filenames 640 * @param array $jsFiles An array of filenames 641 * @param string $basePath Base path of those files 642 * @param string $baseUrl Base url of those files 643 * @return $this 644 */ 645 public function addAssets($cssFiles, $jsFiles, $basePath = null, $baseUrl = null) 646 { 647 $this->additionalAssets[] = array( 648 'base_path' => $basePath, 649 'base_url' => $baseUrl, 650 'css' => (array) $cssFiles, 651 'js' => (array) $jsFiles 652 ); 653 return $this; 654 } 655 656 /** 657 * Add inline assets to render inline in the head. Ideally, you should store static assets in 658 * files that you add with the addAssets function. However, adding inline assets is useful when 659 * integrating with 3rd-party libraries that require static assets that are only available in an 660 * inline format. 661 * 662 * The inline content arrays require special string array keys: they are used to deduplicate 663 * content. This is particularly useful if multiple instances of the same asset end up being 664 * added. Inline assets from all collectors are merged together into the same array, so these 665 * content IDs effectively deduplicate the inline assets. 666 * 667 * @param array $inlineCss An array map of content ID to inline CSS content (not including <style> tag) 668 * @param array $inlineJs An array map of content ID to inline JS content (not including <script> tag) 669 * @param array $inlineHead An array map of content ID to arbitrary inline HTML content (typically 670 * <style>/<script> tags); it must be embedded within the <head> element 671 * @return $this 672 */ 673 public function addInlineAssets($inlineCss, $inlineJs, $inlineHead) 674 { 675 $this->additionalAssets[] = array( 676 'inline_css' => (array) $inlineCss, 677 'inline_js' => (array) $inlineJs, 678 'inline_head' => (array) $inlineHead 679 ); 680 return $this; 681 } 682 683 /** 684 * Returns the list of asset files 685 * 686 * @param string $type 'css', 'js', 'inline_css', 'inline_js', 'inline_head', or null for all 687 * @param string $relativeTo The type of path to which filenames must be relative (path, url or null) 688 * @return array 689 */ 690 public function getAssets($type = null, $relativeTo = self::RELATIVE_PATH) 691 { 692 $cssFiles = $this->cssFiles; 693 $jsFiles = $this->jsFiles; 694 $inlineCss = array(); 695 $inlineJs = array(); 696 $inlineHead = array(); 697 698 if ($this->includeVendors !== false) { 699 if ($this->includeVendors === true || in_array('css', $this->includeVendors)) { 700 $cssFiles = array_merge($this->cssVendors, $cssFiles); 701 } 702 if ($this->includeVendors === true || in_array('js', $this->includeVendors)) { 703 $jsFiles = array_merge($this->jsVendors, $jsFiles); 704 } 705 } 706 707 if ($relativeTo) { 708 $root = $this->getRelativeRoot($relativeTo, $this->basePath, $this->baseUrl); 709 $cssFiles = $this->makeUriRelativeTo($cssFiles, $root); 710 $jsFiles = $this->makeUriRelativeTo($jsFiles, $root); 711 } 712 713 $additionalAssets = $this->additionalAssets; 714 // finds assets provided by collectors 715 foreach ($this->debugBar->getCollectors() as $collector) { 716 if (($collector instanceof AssetProvider) && !in_array($collector->getName(), $this->ignoredCollectors)) { 717 $additionalAssets[] = $collector->getAssets(); 718 } 719 } 720 721 foreach ($additionalAssets as $assets) { 722 $basePath = isset($assets['base_path']) ? $assets['base_path'] : ''; 723 $baseUrl = isset($assets['base_url']) ? $assets['base_url'] : ''; 724 $root = $this->getRelativeRoot($relativeTo, 725 $this->makeUriRelativeTo($basePath, $this->basePath), 726 $this->makeUriRelativeTo($baseUrl, $this->baseUrl)); 727 if (isset($assets['css'])) { 728 $cssFiles = array_merge($cssFiles, $this->makeUriRelativeTo((array) $assets['css'], $root)); 729 } 730 if (isset($assets['js'])) { 731 $jsFiles = array_merge($jsFiles, $this->makeUriRelativeTo((array) $assets['js'], $root)); 732 } 733 734 if (isset($assets['inline_css'])) { 735 $inlineCss = array_merge($inlineCss, (array) $assets['inline_css']); 736 } 737 if (isset($assets['inline_js'])) { 738 $inlineJs = array_merge($inlineJs, (array) $assets['inline_js']); 739 } 740 if (isset($assets['inline_head'])) { 741 $inlineHead = array_merge($inlineHead, (array) $assets['inline_head']); 742 } 743 } 744 745 // Deduplicate files 746 $cssFiles = array_unique($cssFiles); 747 $jsFiles = array_unique($jsFiles); 748 749 return $this->filterAssetArray(array($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead), $type ?? ''); 750 } 751 752 /** 753 * Returns the correct base according to the type 754 * 755 * @param string $relativeTo 756 * @param string $basePath 757 * @param string $baseUrl 758 * @return string 759 */ 760 protected function getRelativeRoot($relativeTo, $basePath, $baseUrl) 761 { 762 if ($relativeTo === self::RELATIVE_PATH) { 763 return $basePath; 764 } 765 if ($relativeTo === self::RELATIVE_URL) { 766 return $baseUrl; 767 } 768 return null; 769 } 770 771 /** 772 * Makes a URI relative to another 773 * 774 * @param string|array $uri 775 * @param string $root 776 * @return string 777 */ 778 protected function makeUriRelativeTo($uri, $root) 779 { 780 if (!$root) { 781 return $uri; 782 } 783 784 if (is_array($uri)) { 785 $uris = array(); 786 foreach ($uri as $u) { 787 $uris[] = $this->makeUriRelativeTo($u, $root); 788 } 789 return $uris; 790 } 791 792 $uri = $uri ?? ''; 793 794 if (substr($uri, 0, 1) === '/' || preg_match('/^([a-zA-Z]+:\/\/|[a-zA-Z]:\/|[a-zA-Z]:\\\)/', $uri)) { 795 return $uri; 796 } 797 return rtrim($root, '/') . "/$uri"; 798 } 799 800 /** 801 * Filters a tuple of (css, js, inline_css, inline_js, inline_head) assets according to $type 802 * 803 * @param array $array 804 * @param string $type 'css', 'js', 'inline_css', 'inline_js', 'inline_head', or null for all 805 * @return array 806 */ 807 protected function filterAssetArray($array, $type = '') 808 { 809 $types = array('css', 'js', 'inline_css', 'inline_js', 'inline_head'); 810 $typeIndex = array_search(strtolower($type ?? ''), $types); 811 return $typeIndex !== false ? $array[$typeIndex] : $array; 812 } 813 814 /** 815 * Returns an array where all items are Assetic AssetCollection: 816 * - The first one contains the CSS files 817 * - The second one contains the JS files 818 * - The third one contains arbitrary inline HTML (typically composed of <script>/<style> 819 * elements); it must be embedded within the <head> element 820 * 821 * @param string $type Optionally return only 'css', 'js', or 'inline_head' collection 822 * @return array|\Assetic\Asset\AssetCollection 823 */ 824 public function getAsseticCollection($type = null) 825 { 826 $types = array('css', 'js', 'inline_head'); 827 $typeIndex = array_search(strtolower($type), $types); 828 829 list($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead) = $this->getAssets(); 830 $collections = array( 831 $this->createAsseticCollection($cssFiles, $inlineCss), 832 $this->createAsseticCollection($jsFiles, $inlineJs), 833 $this->createAsseticCollection(null, $inlineHead) 834 ); 835 return $typeIndex !== false ? $collections[$typeIndex] : $collections; 836 } 837 838 /** 839 * Create an Assetic AssetCollection with the given content. 840 * Filenames will be converted to absolute path using 841 * the base path. 842 * 843 * @param array|null $files Array of asset filenames. 844 * @param array|null $content Array of inline asset content. 845 * @return \Assetic\Asset\AssetCollection 846 */ 847 protected function createAsseticCollection($files = null, $content = null) 848 { 849 $assets = array(); 850 if ($files) { 851 foreach ($files as $file) { 852 $assets[] = new \Assetic\Asset\FileAsset($file); 853 } 854 } 855 if ($content) { 856 foreach ($content as $item) { 857 $assets[] = new \Assetic\Asset\StringAsset($item); 858 } 859 } 860 return new \Assetic\Asset\AssetCollection($assets); 861 } 862 863 /** 864 * Write all CSS assets to standard output or in a file 865 * 866 * @param string $targetFilename 867 */ 868 public function dumpCssAssets($targetFilename = null) 869 { 870 $this->dumpAssets($this->getAssets('css'), $this->getAssets('inline_css'), $targetFilename); 871 } 872 873 /** 874 * Write all JS assets to standard output or in a file 875 * 876 * @param string $targetFilename 877 */ 878 public function dumpJsAssets($targetFilename = null) 879 { 880 $this->dumpAssets($this->getAssets('js'), $this->getAssets('inline_js'), $targetFilename, $this->useRequireJs); 881 } 882 883 /** 884 * Write all inline HTML header assets to standard output or in a file (only returns assets not 885 * already returned by dumpCssAssets or dumpJsAssets) 886 * 887 * @param string $targetFilename 888 */ 889 public function dumpHeadAssets($targetFilename = null) 890 { 891 $this->dumpAssets(null, $this->getAssets('inline_head'), $targetFilename); 892 } 893 894 /** 895 * Write assets to standard output or in a file 896 * 897 * @param array|null $files Filenames containing assets 898 * @param array|null $content Inline content to dump 899 * @param string $targetFilename 900 * @param bool $useRequireJs 901 */ 902 protected function dumpAssets($files = null, $content = null, $targetFilename = null, $useRequireJs = false) 903 { 904 $dumpedContent = ''; 905 if ($files) { 906 foreach ($files as $file) { 907 $dumpedContent .= file_get_contents($file) . "\n"; 908 } 909 } 910 if ($content) { 911 foreach ($content as $item) { 912 $dumpedContent .= $item . "\n"; 913 } 914 } 915 if ($useRequireJs) { 916 $dumpedContent = "define('debugbar', ['jquery'], function($){\r\n" . $dumpedContent . "\r\n return PhpDebugBar; \r\n});"; 917 } 918 if ($targetFilename !== null) { 919 file_put_contents($targetFilename, $dumpedContent); 920 } else { 921 echo $dumpedContent; 922 } 923 } 924 925 /** 926 * Renders the html to include needed assets 927 * 928 * Only useful if Assetic is not used 929 * 930 * @return string 931 */ 932 public function renderHead() 933 { 934 list($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead) = $this->getAssets(null, self::RELATIVE_URL); 935 $html = ''; 936 937 $nonce = $this->getNonceAttribute(); 938 939 foreach ($cssFiles as $file) { 940 $html .= sprintf('<link rel="stylesheet" type="text/css" href="%s">' . "\n", $file); 941 } 942 943 foreach ($inlineCss as $content) { 944 $html .= sprintf('<style type="text/css">%s</style>' . "\n", $content); 945 } 946 947 foreach ($jsFiles as $file) { 948 $html .= sprintf('<script type="text/javascript" src="%s"></script>' . "\n", $file); 949 } 950 951 foreach ($inlineJs as $content) { 952 $html .= sprintf('<script type="text/javascript"%s>%s</script>' . "\n", $nonce, $content); 953 } 954 955 foreach ($inlineHead as $content) { 956 $html .= $content . "\n"; 957 } 958 959 if ($this->enableJqueryNoConflict && !$this->useRequireJs) { 960 $html .= '<script type="text/javascript"' . $nonce . '>jQuery.noConflict(true);</script>' . "\n"; 961 } 962 963 return $html; 964 } 965 966 /** 967 * Register shutdown to display the debug bar 968 * 969 * @param boolean $here Set position of HTML. True if is to current position or false for end file 970 * @param boolean $initialize Whether to render the de bug bar initialization code 971 * @param bool $renderStackedData 972 * @param bool $head 973 * @return string Return "{--DEBUGBAR_OB_START_REPLACE_ME--}" or return an empty string if $here == false 974 */ 975 public function renderOnShutdown($here = true, $initialize = true, $renderStackedData = true, $head = false) 976 { 977 register_shutdown_function(array($this, "replaceTagInBuffer"), $here, $initialize, $renderStackedData, $head); 978 979 if (ob_get_level() === 0) { 980 ob_start(); 981 } 982 983 return ($here) ? self::REPLACEABLE_TAG : ""; 984 } 985 986 /** 987 * Same as renderOnShutdown() with $head = true 988 * 989 * @param boolean $here 990 * @param boolean $initialize 991 * @param boolean $renderStackedData 992 * @return string 993 */ 994 public function renderOnShutdownWithHead($here = true, $initialize = true, $renderStackedData = true) 995 { 996 return $this->renderOnShutdown($here, $initialize, $renderStackedData, true); 997 } 998 999 /** 1000 * Is callback function for register_shutdown_function(...) 1001 * 1002 * @param boolean $here Set position of HTML. True if is to current position or false for end file 1003 * @param boolean $initialize Whether to render the de bug bar initialization code 1004 * @param bool $renderStackedData 1005 * @param bool $head 1006 */ 1007 public function replaceTagInBuffer($here = true, $initialize = true, $renderStackedData = true, $head = false) 1008 { 1009 $render = ($head ? $this->renderHead() : "") 1010 . $this->render($initialize, $renderStackedData); 1011 1012 $current = ($here && ob_get_level() > 0) ? ob_get_clean() : self::REPLACEABLE_TAG; 1013 1014 echo str_replace(self::REPLACEABLE_TAG, $render, $current, $count); 1015 1016 if ($count === 0) { 1017 echo $render; 1018 } 1019 } 1020 1021 /** 1022 * Returns the code needed to display the debug bar 1023 * 1024 * AJAX request should not render the initialization code. 1025 * 1026 * @param boolean $initialize Whether or not to render the debug bar initialization code 1027 * @param boolean $renderStackedData Whether or not to render the stacked data 1028 * @return string 1029 */ 1030 public function render($initialize = true, $renderStackedData = true) 1031 { 1032 $js = ''; 1033 1034 if ($initialize) { 1035 $js = $this->getJsInitializationCode(); 1036 } 1037 1038 if ($renderStackedData && $this->debugBar->hasStackedData()) { 1039 foreach ($this->debugBar->getStackedData() as $id => $data) { 1040 $js .= $this->getAddDatasetCode($id, $data, '(stacked)'); 1041 } 1042 } 1043 1044 $suffix = !$initialize ? '(ajax)' : null; 1045 $js .= $this->getAddDatasetCode($this->debugBar->getCurrentRequestId(), $this->debugBar->getData(), $suffix); 1046 1047 $nonce = $this->getNonceAttribute(); 1048 1049 if ($this->useRequireJs){ 1050 return "<script type=\"text/javascript\"{$nonce}>\nrequire(['debugbar'], function(PhpDebugBar){ $js });\n</script>\n"; 1051 } else { 1052 return "<script type=\"text/javascript\"{$nonce}>\n$js\n</script>\n"; 1053 } 1054 1055 } 1056 1057 /** 1058 * Returns the js code needed to initialize the debug bar 1059 * 1060 * @return string 1061 */ 1062 protected function getJsInitializationCode() 1063 { 1064 $js = ''; 1065 1066 if (($this->initialization & self::INITIALIZE_CONSTRUCTOR) === self::INITIALIZE_CONSTRUCTOR) { 1067 $js .= sprintf("var %s = new %s();\n", $this->variableName, $this->javascriptClass); 1068 } 1069 1070 if (($this->initialization & self::INITIALIZE_CONTROLS) === self::INITIALIZE_CONTROLS) { 1071 $js .= $this->getJsControlsDefinitionCode($this->variableName); 1072 } 1073 1074 if ($this->ajaxHandlerClass) { 1075 $js .= sprintf("%s.ajaxHandler = new %s(%s, undefined, %s);\n", 1076 $this->variableName, 1077 $this->ajaxHandlerClass, 1078 $this->variableName, 1079 $this->ajaxHandlerAutoShow ? 'true' : 'false' 1080 ); 1081 if ($this->ajaxHandlerBindToFetch) { 1082 $js .= sprintf("%s.ajaxHandler.bindToFetch();\n", $this->variableName); 1083 } 1084 if ($this->ajaxHandlerBindToXHR) { 1085 $js .= sprintf("%s.ajaxHandler.bindToXHR();\n", $this->variableName); 1086 } elseif ($this->ajaxHandlerBindToJquery) { 1087 $js .= sprintf("if (jQuery) %s.ajaxHandler.bindToJquery(jQuery);\n", $this->variableName); 1088 } 1089 } 1090 1091 if ($this->openHandlerUrl !== null) { 1092 $js .= sprintf("%s.setOpenHandler(new %s(%s));\n", $this->variableName, 1093 $this->openHandlerClass, 1094 json_encode(array("url" => $this->openHandlerUrl))); 1095 } 1096 1097 return $js; 1098 } 1099 1100 /** 1101 * Returns the js code needed to initialized the controls and data mapping of the debug bar 1102 * 1103 * Controls can be defined by collectors themselves or using {@see addControl()} 1104 * 1105 * @param string $varname Debug bar's variable name 1106 * @return string 1107 */ 1108 protected function getJsControlsDefinitionCode($varname) 1109 { 1110 $js = ''; 1111 $dataMap = array(); 1112 $excludedOptions = array('indicator', 'tab', 'map', 'default', 'widget', 'position'); 1113 1114 // finds controls provided by collectors 1115 $widgets = array(); 1116 foreach ($this->debugBar->getCollectors() as $collector) { 1117 if (($collector instanceof Renderable) && !in_array($collector->getName(), $this->ignoredCollectors)) { 1118 if ($w = $collector->getWidgets()) { 1119 $widgets = array_merge($widgets, $w); 1120 } 1121 } 1122 } 1123 $controls = array_merge($widgets, $this->controls); 1124 1125 foreach (array_filter($controls) as $name => $options) { 1126 $opts = array_diff_key($options, array_flip($excludedOptions)); 1127 1128 if (isset($options['tab']) || isset($options['widget'])) { 1129 if (!isset($opts['title'])) { 1130 $opts['title'] = ucfirst(str_replace('_', ' ', $name)); 1131 } 1132 $js .= sprintf("%s.addTab(\"%s\", new %s({%s%s}));\n", 1133 $varname, 1134 $name, 1135 isset($options['tab']) ? $options['tab'] : 'PhpDebugBar.DebugBar.Tab', 1136 substr(json_encode($opts, JSON_FORCE_OBJECT), 1, -1), 1137 isset($options['widget']) ? sprintf('%s"widget": new %s()', count($opts) ? ', ' : '', $options['widget']) : '' 1138 ); 1139 } elseif (isset($options['indicator']) || isset($options['icon'])) { 1140 $js .= sprintf("%s.addIndicator(\"%s\", new %s(%s), \"%s\");\n", 1141 $varname, 1142 $name, 1143 isset($options['indicator']) ? $options['indicator'] : 'PhpDebugBar.DebugBar.Indicator', 1144 json_encode($opts, JSON_FORCE_OBJECT), 1145 isset($options['position']) ? $options['position'] : 'right' 1146 ); 1147 } 1148 1149 if (isset($options['map']) && isset($options['default'])) { 1150 $dataMap[$name] = array($options['map'], $options['default']); 1151 } 1152 } 1153 1154 // creates the data mapping object 1155 $mapJson = array(); 1156 foreach ($dataMap as $name => $values) { 1157 $mapJson[] = sprintf('"%s": ["%s", %s]', $name, $values[0], $values[1]); 1158 } 1159 $js .= sprintf("%s.setDataMap({\n%s\n});\n", $varname, implode(",\n", $mapJson)); 1160 1161 // activate state restoration 1162 $js .= sprintf("%s.restoreState();\n", $varname); 1163 1164 return $js; 1165 } 1166 1167 /** 1168 * Returns the js code needed to add a dataset 1169 * 1170 * @param string $requestId 1171 * @param array $data 1172 * @param mixed $suffix 1173 * @return string 1174 */ 1175 protected function getAddDatasetCode($requestId, $data, $suffix = null) 1176 { 1177 $js = sprintf("%s.addDataSet(%s, \"%s\"%s);\n", 1178 $this->variableName, 1179 json_encode($data), 1180 $requestId, 1181 $suffix ? ", " . json_encode($suffix) : '' 1182 ); 1183 return $js; 1184 } 1185 1186 /** 1187 * If a nonce it set, create the correct attribute 1188 * @return string 1189 */ 1190 protected function getNonceAttribute() 1191 { 1192 if ($nonce = $this->getCspNonce()) { 1193 return ' nonce="' . $nonce .'"'; 1194 } 1195 1196 return ''; 1197 } 1198 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |