Changeset 14725
- Timestamp:
- 12/06/10 23:11:27 (18 months ago)
- Location:
- trunk/plugins/firephp
- Files:
-
- 1 deleted
- 3 edited
-
FirePHP.class.php (modified) (3 diffs)
-
LICENSE (modified) (1 diff)
-
fb.php (modified) (4 diffs)
-
firephp-0.2.1-fx.xpi (deleted)
Legend:
- Unmodified
- Added
- Removed
-
trunk/plugins/firephp/FirePHP.class.php
r8795 r14725 7 7 * Software License Agreement (New BSD License) 8 8 * 9 * Copyright (c) 2006-20 08, Christoph Dorn9 * Copyright (c) 2006-2010, Christoph Dorn 10 10 * All rights reserved. 11 11 * … … 37 37 * ***** END LICENSE BLOCK ***** 38 38 * 39 * @copyright Copyright (C) 2007-2008Christoph Dorn40 * @author Christoph Dorn <christoph@christophdorn.com>41 * @license http://www.opensource.org/licenses/bsd-license.php42 * @package FirePHP39 * @copyright Copyright (C) 2007-2009 Christoph Dorn 40 * @author Christoph Dorn <christoph@christophdorn.com> 41 * @license http://www.opensource.org/licenses/bsd-license.php 42 * @package FirePHPCore 43 43 */ 44 44 45 /** 46 * @see http://code.google.com/p/firephp/issues/detail?id=112 47 */ 48 if (!defined('E_STRICT')) { 49 define('E_STRICT', 2048); 50 } 51 if (!defined('E_RECOVERABLE_ERROR')) { 52 define('E_RECOVERABLE_ERROR', 4096); 53 } 54 if (!defined('E_DEPRECATED')) { 55 define('E_DEPRECATED', 8192); 56 } 57 if (!defined('E_USER_DEPRECATED')) { 58 define('E_USER_DEPRECATED', 16384); 59 } 45 60 46 61 /** … … 51 66 * For more information see: http://www.firephp.org/ 52 67 * 53 * @copyright Copyright (C) 2007-2008Christoph Dorn54 * @author Christoph Dorn <christoph@christophdorn.com>55 * @license http://www.opensource.org/licenses/bsd-license.php56 * @package FirePHP68 * @copyright Copyright (C) 2007-2009 Christoph Dorn 69 * @author Christoph Dorn <christoph@christophdorn.com> 70 * @license http://www.opensource.org/licenses/bsd-license.php 71 * @package FirePHPCore 57 72 */ 58 73 class FirePHP { 74 75 /** 76 * FirePHP version 77 * 78 * @var string 79 */ 80 const VERSION = '0.3'; // @pinf replace '0.3' with '%%package.version%%' 81 82 /** 83 * Firebug LOG level 84 * 85 * Logs a message to firebug console. 86 * 87 * @var string 88 */ 89 const LOG = 'LOG'; 59 90 60 /** 61 * FirePHP version 62 * 63 * @var string 64 */ 65 const VERSION = '0.2.1'; 91 /** 92 * Firebug INFO level 93 * 94 * Logs a message to firebug console and displays an info icon before the message. 95 * 96 * @var string 97 */ 98 const INFO = 'INFO'; 99 100 /** 101 * Firebug WARN level 102 * 103 * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. 104 * 105 * @var string 106 */ 107 const WARN = 'WARN'; 108 109 /** 110 * Firebug ERROR level 111 * 112 * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. 113 * 114 * @var string 115 */ 116 const ERROR = 'ERROR'; 117 118 /** 119 * Dumps a variable to firebug's server panel 120 * 121 * @var string 122 */ 123 const DUMP = 'DUMP'; 124 125 /** 126 * Displays a stack trace in firebug console 127 * 128 * @var string 129 */ 130 const TRACE = 'TRACE'; 131 132 /** 133 * Displays an exception in firebug console 134 * 135 * Increments the firebug error count. 136 * 137 * @var string 138 */ 139 const EXCEPTION = 'EXCEPTION'; 140 141 /** 142 * Displays an table in firebug console 143 * 144 * @var string 145 */ 146 const TABLE = 'TABLE'; 147 148 /** 149 * Starts a group in firebug console 150 * 151 * @var string 152 */ 153 const GROUP_START = 'GROUP_START'; 154 155 /** 156 * Ends a group in firebug console 157 * 158 * @var string 159 */ 160 const GROUP_END = 'GROUP_END'; 161 162 /** 163 * Singleton instance of FirePHP 164 * 165 * @var FirePHP 166 */ 167 protected static $instance = null; 168 169 /** 170 * Flag whether we are logging from within the exception handler 171 * 172 * @var boolean 173 */ 174 protected $inExceptionHandler = false; 175 176 /** 177 * Flag whether to throw PHP errors that have been converted to ErrorExceptions 178 * 179 * @var boolean 180 */ 181 protected $throwErrorExceptions = true; 182 183 /** 184 * Flag whether to convert PHP assertion errors to Exceptions 185 * 186 * @var boolean 187 */ 188 protected $convertAssertionErrorsToExceptions = true; 189 190 /** 191 * Flag whether to throw PHP assertion errors that have been converted to Exceptions 192 * 193 * @var boolean 194 */ 195 protected $throwAssertionExceptions = false; 196 197 /** 198 * Wildfire protocol message index 199 * 200 * @var int 201 */ 202 protected $messageIndex = 1; 203 204 /** 205 * Options for the library 206 * 207 * @var array 208 */ 209 protected $options = array('maxDepth' => 10, 210 'maxObjectDepth' => 5, 211 'maxArrayDepth' => 5, 212 'useNativeJsonEncode' => true, 213 'includeLineNumbers' => true); 214 215 /** 216 * Filters used to exclude object members when encoding 217 * 218 * @var array 219 */ 220 protected $objectFilters = array( 221 'firephp' => array('objectStack', 'instance', 'json_objectStack'), 222 'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack') 223 ); 224 225 /** 226 * A stack of objects used to detect recursion during object encoding 227 * 228 * @var object 229 */ 230 protected $objectStack = array(); 231 232 /** 233 * Flag to enable/disable logging 234 * 235 * @var boolean 236 */ 237 protected $enabled = true; 238 239 /** 240 * The insight console to log to if applicable 241 * 242 * @var object 243 */ 244 protected $logToInsightConsole = null; 245 246 /** 247 * When the object gets serialized only include specific object members. 248 * 249 * @return array 250 */ 251 public function __sleep() 252 { 253 return array('options','objectFilters','enabled'); 254 } 255 256 /** 257 * Gets singleton instance of FirePHP 258 * 259 * @param boolean $AutoCreate 260 * @return FirePHP 261 */ 262 public static function getInstance($AutoCreate = false) 263 { 264 if ($AutoCreate===true && !self::$instance) { 265 self::init(); 266 } 267 return self::$instance; 268 } 269 270 /** 271 * Creates FirePHP object and stores it for singleton access 272 * 273 * @return FirePHP 274 */ 275 public static function init() 276 { 277 return self::setInstance(new self()); 278 } 279 280 /** 281 * Set the instance of the FirePHP singleton 282 * 283 * @param FirePHP $instance The FirePHP object instance 284 * @return FirePHP 285 */ 286 public static function setInstance($instance) 287 { 288 return self::$instance = $instance; 289 } 290 291 /** 292 * Set an Insight console to direct all logging calls to 293 * 294 * @param object $console The console object to log to 295 * @return void 296 */ 297 public function setLogToInsightConsole($console) 298 { 299 if(is_string($console)) { 300 if(get_class($this)!='FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) { 301 throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!'); 302 } 303 $this->logToInsightConsole = $this->to('request')->console($console); 304 } else { 305 $this->logToInsightConsole = $console; 306 } 307 } 308 309 /** 310 * Enable and disable logging to Firebug 311 * 312 * @param boolean $Enabled TRUE to enable, FALSE to disable 313 * @return void 314 */ 315 public function setEnabled($Enabled) 316 { 317 $this->enabled = $Enabled; 318 } 319 320 /** 321 * Check if logging is enabled 322 * 323 * @return boolean TRUE if enabled 324 */ 325 public function getEnabled() 326 { 327 return $this->enabled; 328 } 329 330 /** 331 * Specify a filter to be used when encoding an object 332 * 333 * Filters are used to exclude object members. 334 * 335 * @param string $Class The class name of the object 336 * @param array $Filter An array of members to exclude 337 * @return void 338 */ 339 public function setObjectFilter($Class, $Filter) 340 { 341 $this->objectFilters[strtolower($Class)] = $Filter; 342 } 66 343 67 /** 68 * Firebug LOG level 69 * 70 * Logs a message to firebug console. 71 * 72 * @var string 73 */ 74 const LOG = 'LOG'; 344 /** 345 * Set some options for the library 346 * 347 * Options: 348 * - maxDepth: The maximum depth to traverse (default: 10) 349 * - maxObjectDepth: The maximum depth to traverse objects (default: 5) 350 * - maxArrayDepth: The maximum depth to traverse arrays (default: 5) 351 * - useNativeJsonEncode: If true will use json_encode() (default: true) 352 * - includeLineNumbers: If true will include line numbers and filenames (default: true) 353 * 354 * @param array $Options The options to be set 355 * @return void 356 */ 357 public function setOptions($Options) 358 { 359 $this->options = array_merge($this->options,$Options); 360 } 361 362 /** 363 * Get options from the library 364 * 365 * @return array The currently set options 366 */ 367 public function getOptions() 368 { 369 return $this->options; 370 } 371 372 /** 373 * Set an option for the library 374 * 375 * @param string $Name 376 * @param mixed $Value 377 * @throws Exception 378 * @return void 379 */ 380 public function setOption($Name, $Value) 381 { 382 if (!isset($this->options[$Name])) { 383 throw $this->newException('Unknown option: ' . $Name); 384 } 385 $this->options[$Name] = $Value; 386 } 387 388 /** 389 * Get an option from the library 390 * 391 * @param string $Name 392 * @throws Exception 393 * @return mixed 394 */ 395 public function getOption($Name) 396 { 397 if (!isset($this->options[$Name])) { 398 throw $this->newException('Unknown option: ' . $Name); 399 } 400 return $this->options[$Name]; 401 } 402 403 /** 404 * Register FirePHP as your error handler 405 * 406 * Will throw exceptions for each php error. 407 * 408 * @return mixed Returns a string containing the previously defined error handler (if any) 409 */ 410 public function registerErrorHandler($throwErrorExceptions = false) 411 { 412 //NOTE: The following errors will not be caught by this error handler: 413 // E_ERROR, E_PARSE, E_CORE_ERROR, 414 // E_CORE_WARNING, E_COMPILE_ERROR, 415 // E_COMPILE_WARNING, E_STRICT 416 417 $this->throwErrorExceptions = $throwErrorExceptions; 418 419 return set_error_handler(array($this,'errorHandler')); 420 } 421 422 /** 423 * FirePHP's error handler 424 * 425 * Throws exception for each php error that will occur. 426 * 427 * @param int $errno 428 * @param string $errstr 429 * @param string $errfile 430 * @param int $errline 431 * @param array $errcontext 432 */ 433 public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) 434 { 435 // Don't throw exception if error reporting is switched off 436 if (error_reporting() == 0) { 437 return; 438 } 439 // Only throw exceptions for errors we are asking for 440 if (error_reporting() & $errno) { 441 442 $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline); 443 if ($this->throwErrorExceptions) { 444 throw $exception; 445 } else { 446 $this->fb($exception); 447 } 448 } 449 } 75 450 76 /** 77 * Firebug INFO level 78 * 79 * Logs a message to firebug console and displays an info icon before the message. 80 * 81 * @var string 82 */ 83 const INFO = 'INFO'; 451 /** 452 * Register FirePHP as your exception handler 453 * 454 * @return mixed Returns the name of the previously defined exception handler, 455 * or NULL on error. 456 * If no previous handler was defined, NULL is also returned. 457 */ 458 public function registerExceptionHandler() 459 { 460 return set_exception_handler(array($this,'exceptionHandler')); 461 } 84 462 85 /** 86 * Firebug WARN level 87 * 88 * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. 89 * 90 * @var string 91 */ 92 const WARN = 'WARN'; 463 /** 464 * FirePHP's exception handler 465 * 466 * Logs all exceptions to your firebug console and then stops the script. 467 * 468 * @param Exception $Exception 469 * @throws Exception 470 */ 471 function exceptionHandler($Exception) 472 { 473 474 $this->inExceptionHandler = true; 475 476 header('HTTP/1.1 500 Internal Server Error'); 477 478 try { 479 $this->fb($Exception); 480 } catch (Exception $e) { 481 echo 'We had an exception: ' . $e; 482 } 483 $this->inExceptionHandler = false; 484 } 93 485 94 /** 95 * Firebug ERROR level 96 * 97 * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. 98 * 99 * @var string 100 */ 101 const ERROR = 'ERROR'; 486 /** 487 * Register FirePHP driver as your assert callback 488 * 489 * @param boolean $convertAssertionErrorsToExceptions 490 * @param boolean $throwAssertionExceptions 491 * @return mixed Returns the original setting or FALSE on errors 492 */ 493 public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false) 494 { 495 $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions; 496 $this->throwAssertionExceptions = $throwAssertionExceptions; 497 498 if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) { 499 throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!'); 500 } 501 502 return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler')); 503 } 102 504 103 /** 104 * Dumps a variable to firebug's server panel 105 * 106 * @var string 107 */ 108 const DUMP = 'DUMP'; 505 /** 506 * FirePHP's assertion handler 507 * 508 * Logs all assertions to your firebug console and then stops the script. 509 * 510 * @param string $file File source of assertion 511 * @param int $line Line source of assertion 512 * @param mixed $code Assertion code 513 */ 514 public function assertionHandler($file, $line, $code) 515 { 516 if ($this->convertAssertionErrorsToExceptions) { 517 518 $exception = new ErrorException('Assertion Failed - Code[ '.$code.' ]', 0, null, $file, $line); 519 520 if ($this->throwAssertionExceptions) { 521 throw $exception; 522 } else { 523 $this->fb($exception); 524 } 525 526 } else { 527 $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File'=>$file,'Line'=>$line)); 528 } 529 } 109 530 110 /** 111 * Displays a stack trace in firebug console 112 * 113 * @var string 114 */ 115 const TRACE = 'TRACE'; 531 /** 532 * Start a group for following messages. 533 * 534 * Options: 535 * Collapsed: [true|false] 536 * Color: [#RRGGBB|ColorName] 537 * 538 * @param string $Name 539 * @param array $Options OPTIONAL Instructions on how to log the group 540 * @return true 541 * @throws Exception 542 */ 543 public function group($Name, $Options = null) 544 { 545 546 if (!$Name) { 547 throw $this->newException('You must specify a label for the group!'); 548 } 549 550 if ($Options) { 551 if (!is_array($Options)) { 552 throw $this->newException('Options must be defined as an array!'); 553 } 554 if (array_key_exists('Collapsed', $Options)) { 555 $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false'; 556 } 557 } 558 559 return $this->fb(null, $Name, FirePHP::GROUP_START, $Options); 560 } 116 561 117 /** 118 * Displays an exception in firebug console 119 * 120 * Increments the firebug error count. 121 * 122 * @var string 123 */ 124 const EXCEPTION = 'EXCEPTION'; 562 /** 563 * Ends a group you have started before 564 * 565 * @return true 566 * @throws Exception 567 */ 568 public function groupEnd() 569 { 570 return $this->fb(null, null, FirePHP::GROUP_END); 571 } 572 573 /** 574 * Log object with label to firebug console 575 * 576 * @see FirePHP::LOG 577 * @param mixes $Object 578 * @param string $Label 579 * @return true 580 * @throws Exception 581 */ 582 public function log($Object, $Label = null, $Options = array()) 583 { 584 return $this->fb($Object, $Label, FirePHP::LOG, $Options); 585 } 586 587 /** 588 * Log object with label to firebug console 589 * 590 * @see FirePHP::INFO 591 * @param mixes $Object 592 * @param string $Label 593 * @return true 594 * @throws Exception 595 */ 596 public function info($Object, $Label = null, $Options = array()) 597 { 598 return $this->fb($Object, $Label, FirePHP::INFO, $Options); 599 } 600 601 /** 602 * Log object with label to firebug console 603 * 604 * @see FirePHP::WARN 605 * @param mixes $Object 606 * @param string $Label 607 * @return true 608 * @throws Exception 609 */ 610 public function warn($Object, $Label = null, $Options = array()) 611 { 612 return $this->fb($Object, $Label, FirePHP::WARN, $Options); 613 } 614 615 /** 616 * Log object with label to firebug console 617 * 618 * @see FirePHP::ERROR 619 * @param mixes $Object 620 * @param string $Label 621 * @return true 622 * @throws Exception 623 */ 624 public function error($Object, $Label = null, $Options = array()) 625 { 626 return $this->fb($Object, $Label, FirePHP::ERROR, $Options); 627 } 628 629 /** 630 * Dumps key and variable to firebug server panel 631 * 632 * @see FirePHP::DUMP 633 * @param string $Key 634 * @param mixed $Variable 635 * @return true 636 * @throws Exception 637 */ 638 public function dump($Key, $Variable, $Options = array()) 639 { 640 if (!is_string($Key)) { 641 throw $this->newException('Key passed to dump() is not a string'); 642 } 643 if (strlen($Key)>100) { 644 throw $this->newException('Key passed to dump() is longer than 100 characters'); 645 } 646 if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $Key, $m)) { 647 throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]'); 648 } 649 return $this->fb($Variable, $Key, FirePHP::DUMP, $Options); 650 } 125 651 126 /** 127 * Displays an table in firebug console 128 * 129 * @var string 130 */ 131 const TABLE = 'TABLE'; 652 /** 653 * Log a trace in the firebug console 654 * 655 * @see FirePHP::TRACE 656 * @param string $Label 657 * @return true 658 * @throws Exception 659 */ 660 public function trace($Label) 661 { 662 return $this->fb($Label, FirePHP::TRACE); 663 } 664 665 /** 666 * Log a table in the firebug console 667 * 668 * @see FirePHP::TABLE 669 * @param string $Label 670 * @param string $Table 671 * @return true 672 * @throws Exception 673 */ 674 public function table($Label, $Table, $Options = array()) 675 { 676 return $this->fb($Table, $Label, FirePHP::TABLE, $Options); 677 } 678 679 /** 680 * Insight API wrapper 681 * 682 * @see Insight_Helper::to() 683 */ 684 public static function to() 685 { 686 $instance = self::getInstance(); 687 if (!method_exists($instance, "_to")) { 688 throw new Exception("FirePHP::to() implementation not loaded"); 689 } 690 $args = func_get_args(); 691 return call_user_func_array(array($instance, '_to'), $args); 692 } 693 694 /** 695 * Insight API wrapper 696 * 697 * @see Insight_Helper::plugin() 698 */ 699 public static function plugin() 700 { 701 $instance = self::getInstance(); 702 if (!method_exists($instance, "_plugin")) { 703 throw new Exception("FirePHP::plugin() implementation not loaded"); 704 } 705 $args = func_get_args(); 706 return call_user_func_array(array($instance, '_plugin'), $args); 707 } 708 709 /** 710 * Check if FirePHP is installed on client 711 * 712 * @return boolean 713 */ 714 public function detectClientExtension() 715 { 716 // Check if FirePHP is installed on client via User-Agent header 717 if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si',$this->getUserAgent(),$m) && 718 version_compare($m[1][0],'0.0.6','>=')) { 719 return true; 720 } else 721 // Check if FirePHP is installed on client via X-FirePHP-Version header 722 if (@preg_match_all('/^([\.\d]*)$/si',$this->getRequestHeader("X-FirePHP-Version"),$m) && 723 version_compare($m[1][0],'0.0.6','>=')) { 724 return true; 725 } 726 return false; 727 } 728 729 /** 730 * Log varible to Firebug 731 * 732 * @see http://www.firephp.org/Wiki/Reference/Fb 733 * @param mixed $Object The variable to be logged 734 * @return true Return TRUE if message was added to headers, FALSE otherwise 735 * @throws Exception 736 */ 737 public function fb($Object) 738 { 739 if($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) { 740 if(!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message 741 $this->_logUpgradeClientMessage(); 742 } 743 } 744 745 static $insightGroupStack = array(); 746 747 if (!$this->getEnabled()) { 748 return false; 749 } 750 751 if ($this->headersSent($filename, $linenum)) { 752 // If we are logging from within the exception handler we cannot throw another exception 753 if ($this->inExceptionHandler) { 754 // Simply echo the error out to the page 755 echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>'.$filename.'</b> on line <b>'.$linenum.'</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>'; 756 } else { 757 throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); 758 } 759 } 760 761 $Type = null; 762 $Label = null; 763 $Options = array(); 764 765 if (func_num_args()==1) { 766 } else 767 if (func_num_args()==2) { 768 switch(func_get_arg(1)) { 769 case self::LOG: 770 case self::INFO: 771 case self::WARN: 772 case self::ERROR: 773 case self::DUMP: 774 case self::TRACE: 775 case self::EXCEPTION: 776 case self::TABLE: 777 case self::GROUP_START: 778 case self::GROUP_END: 779 $Type = func_get_arg(1); 780 break; 781 default: 782 $Label = func_get_arg(1); 783 break; 784 } 785 } else 786 if (func_num_args()==3) { 787 $Type = func_get_arg(2); 788 $Label = func_get_arg(1); 789 } else 790 if (func_num_args()==4) { 791 $Type = func_get_arg(2); 792 $Label = func_get_arg(1); 793 $Options = func_get_arg(3); 794 } else { 795 throw $this->newException('Wrong number of arguments to fb() function!'); 796 } 797 798 if($this->logToInsightConsole!==null && (get_class($this)=='FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) { 799 $msg = $this->logToInsightConsole; 800 if ($Object instanceof Exception) { 801 $Type = self::EXCEPTION; 802 } 803 if($Label && $Type!=self::TABLE && $Type!=self::GROUP_START) { 804 $msg = $msg->label($Label); 805 } 806 switch($Type) { 807 case self::DUMP: 808 case self::LOG: 809 return $msg->log($Object); 810 case self::INFO: 811 return $msg->info($Object); 812 case self::WARN: 813 return $msg->warn($Object); 814 case self::ERROR: 815 return $msg->error($Object); 816 case self::TRACE: 817 return $msg->trace($Object); 818 case self::EXCEPTION: 819 return $this->plugin('engine')->handleException($Object, $msg); 820 case self::TABLE: 821 if (isset($Object[0]) && !is_string($Object[0]) && $Label) { 822 $Object = array($Label, $Object); 823 } 824 return $msg->table($Object[0], array_slice($Object[1],1), $Object[1][0]); 825 case self::GROUP_START: 826 $insightGroupStack[] = $msg->group(md5($Label))->open(); 827 return $msg->log($Label); 828 case self::GROUP_END: 829 if(count($insightGroupStack)==0) { 830 throw new Error('Too many groupEnd() as opposed to group() calls!'); 831 } 832 $group = array_pop($insightGroupStack); 833 return $group->close(); 834 default: 835 return $msg->log($Object); 836 } 837 } 838 839 if (!$this->detectClientExtension()) { 840 return false; 841 } 842 843 $meta = array(); 844 $skipFinalObjectEncode = false; 845 846 if ($Object instanceof Exception) { 847 848 $meta['file'] = $this->_escapeTraceFile($Object->getFile()); 849 $meta['line'] = $Object->getLine(); 850 851 $trace = $Object->getTrace(); 852 if ($Object instanceof ErrorException 853 && isset($trace[0]['function']) 854 && $trace[0]['function']=='errorHandler' 855 && isset($trace[0]['class']) 856 && $trace[0]['class']=='FirePHP') { 857 858 $severity = false; 859 switch($Object->getSeverity()) { 860 case E_WARNING: $severity = 'E_WARNING'; break; 861 case E_NOTICE: $severity = 'E_NOTICE'; break; 862 case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; 863 case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; 864 case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; 865 case E_STRICT: $severity = 'E_STRICT'; break; 866 case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; 867 case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; 868 case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; 869 } 870 871 $Object = array('Class'=>get_class($Object), 872 'Message'=>$severity.': '.$Object->getMessage(), 873 'File'=>$this->_escapeTraceFile($Object->getFile()), 874 'Line'=>$Object->getLine(), 875 'Type'=>'trigger', 876 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); 877 $skipFinalObjectEncode = true; 878 } else { 879 $Object = array('Class'=>get_class($Object), 880 'Message'=>$Object->getMessage(), 881 'File'=>$this->_escapeTraceFile($Object->getFile()), 882 'Line'=>$Object->getLine(), 883 'Type'=>'throw', 884 'Trace'=>$this->_escapeTrace($trace)); 885 $skipFinalObjectEncode = true; 886 } 887 $Type = self::EXCEPTION; 888 889 } else 890 if ($Type==self::TRACE) { 891 892 $trace = debug_backtrace(); 893 if (!$trace) return false; 894 for( $i=0 ; $i<sizeof($trace) ; $i++ ) { 895 896 if (isset($trace[$i]['class']) 897 && isset($trace[$i]['file']) 898 && ($trace[$i]['class']=='FirePHP' 899 || $trace[$i]['class']=='FB') 900 && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' 901 || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { 902 /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ 903 } else 904 if (isset($trace[$i]['class']) 905 && isset($trace[$i+1]['file']) 906 && $trace[$i]['class']=='FirePHP' 907 && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { 908 /* Skip fb() */ 909 } else 910 if ($trace[$i]['function']=='fb' 911 || $trace[$i]['function']=='trace' 912 || $trace[$i]['function']=='send') { 913 914 $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', 915 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', 916 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', 917 'Message'=>$trace[$i]['args'][0], 918 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', 919 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', 920 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', 921 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); 922 923 $skipFinalObjectEncode = true; 924 $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; 925 $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; 926 break; 927 } 928 } 929 930 } else 931 if ($Type==self::TABLE) { 932 933 if (isset($Object[0]) && is_string($Object[0])) { 934 $Object[1] = $this->encodeTable($Object[1]); 935 } else { 936 $Object = $this->encodeTable($Object); 937 } 938 939 $skipFinalObjectEncode = true; 940 941 } else 942 if ($Type==self::GROUP_START) { 943 944 if (!$Label) { 945 throw $this->newException('You must specify a label for the group!'); 946 } 947 948 } else { 949 if ($Type===null) { 950 $Type = self::LOG; 951 } 952 } 953 954 if ($this->options['includeLineNumbers']) { 955 if (!isset($meta['file']) || !isset($meta['line'])) { 956 957 $trace = debug_backtrace(); 958 for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) { 959 960 if (isset($trace[$i]['class']) 961 && isset($trace[$i]['file']) 962 && ($trace[$i]['class']=='FirePHP' 963 || $trace[$i]['class']=='FB') 964 && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' 965 || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { 966 /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ 967 } else 968 if (isset($trace[$i]['class']) 969 && isset($trace[$i+1]['file']) 970 && $trace[$i]['class']=='FirePHP' 971 && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { 972 /* Skip fb() */ 973 } else 974 if (isset($trace[$i]['file']) 975 && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { 976 /* Skip FB::fb() */ 977 } else { 978 $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; 979 $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; 980 break; 981 } 982 } 983 } 984 } else { 985 unset($meta['file']); 986 unset($meta['line']); 987 } 988 989 $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); 990 $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); 991 992 $structure_index = 1; 993 if ($Type==self::DUMP) { 994 $structure_index = 2; 995 $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); 996 } else { 997 $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); 998 } 999 1000 if ($Type==self::DUMP) { 1001 $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; 1002 } else { 1003 $msg_meta = $Options; 1004 $msg_meta['Type'] = $Type; 1005 if ($Label!==null) { 1006 $msg_meta['Label'] = $Label; 1007 } 1008 if (isset($meta['file']) && !isset($msg_meta['File'])) { 1009 $msg_meta['File'] = $meta['file']; 1010 } 1011 if (isset($meta['line']) && !isset($msg_meta['Line'])) { 1012 $msg_meta['Line'] = $meta['line']; 1013 } 1014 $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; 1015 } 1016 1017 $parts = explode("\n",chunk_split($msg, 5000, "\n")); 1018 1019 for( $i=0 ; $i<count($parts) ; $i++) { 1020 1021 $part = $parts[$i]; 1022 if ($part) { 1023 1024 if (count($parts)>2) { 1025 // Message needs to be split into multiple parts 1026 $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, 1027 (($i==0)?strlen($msg):'') 1028 . '|' . $part . '|' 1029 . (($i<count($parts)-2)?'\\':'')); 1030 } else { 1031 $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, 1032 strlen($part) . '|' . $part . '|'); 1033 } 1034 1035 $this->messageIndex++; 1036 1037 if ($this->messageIndex > 99999) { 1038 throw $this->newException('Maximum number (99,999) of messages reached!'); 1039 } 1040 } 1041 } 1042 1043 $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); 1044 1045 return true; 1046 } 132 1047 133 /** 134 * Starts a group in firebug console 135 * 136 * @var string 137 */ 138 const GROUP_START = 'GROUP_START'; 1048 /** 1049 * Standardizes path for windows systems. 1050 * 1051 * @param string $Path 1052 * @return string 1053 */ 1054 protected function _standardizePath($Path) 1055 { 1056 return preg_replace('/\\\\+/','/',$Path); 1057 } 139 1058 140 /** 141 * Ends a group in firebug console 142 * 143 * @var string 144 */ 145 const GROUP_END = 'GROUP_END'; 1059 /** 1060 * Escape trace path for windows systems 1061 * 1062 * @param array $Trace 1063 * @return array 1064 */ 1065 protected function _escapeTrace($Trace) 1066 { 1067 if (!$Trace) return $Trace; 1068 for( $i=0 ; $i<sizeof($Trace) ; $i++ ) { 1069 if (isset($Trace[$i]['file'])) { 1070 $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']); 1071 } 1072 if (isset($Trace[$i]['args'])) { 1073 $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); 1074 } 1075 } 1076 return $Trace; 1077 } 146 1078 147 /** 148 * Singleton instance of FirePHP 149 * 150 * @var FirePHP 151 */ 152 protected static $instance = null; 1079 /** 1080 * Escape file information of trace for windows systems 1081 * 1082 * @param string $File 1083 * @return string 1084 */ 1085 protected function _escapeTraceFile($File) 1086 { 1087 /* Check if we have a windows filepath */ 1088 if (strpos($File,'\\')) { 1089 /* First strip down to single \ */ 1090 1091 $file = preg_replace('/\\\\+/','\\',$File); 1092 1093 return $file; 1094 } 1095 return $File; 1096 } 1097 1098 /** 1099 * Check if headers have already been sent 1100 * 1101 * @param string $Filename 1102 * @param integer $Linenum 1103 */ 1104 protected function headersSent(&$Filename, &$Linenum) 1105 { 1106 return headers_sent($Filename, $Linenum); 1107 } 1108 1109 /** 1110 * Send header 1111 * 1112 * @param string $Name 1113 * @param string $Value 1114 */ 1115 protected function setHeader($Name, $Value) 1116 { 1117 return header($Name.': '.$Value); 1118 } 1119 1120 /** 1121 * Get user agent 1122 * 1123 * @return string|false 1124 */ 1125 protected function getUserAgent() 1126 { 1127 if (!isset($_SERVER['HTTP_USER_AGENT'])) return false; 1128 return $_SERVER['HTTP_USER_AGENT']; 1129 } 1130 1131 /** 1132 * Get all request headers 1133 * 1134 * @return array 1135 */ 1136 public static function getAllRequestHeaders() { 1137 static $_cached_headers = false; 1138 if($_cached_headers!==false) { 1139 return $_cached_headers; 1140 } 1141 $headers = array(); 1142 if(function_exists('getallheaders')) { 1143 foreach( getallheaders() as $name => $value ) { 1144 $headers[strtolower($name)] = $value; 1145 } 1146 } else { 1147 foreach($_SERVER as $name => $value) { 1148 if(substr($name, 0, 5) == 'HTTP_') { 1149 $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value; 1150 } 1151 } 1152 } 1153 return $_cached_headers = $headers; 1154 } 1155 1156 /** 1157 * Get a request header 1158 * 1159 * @return string|false 1160 */ 1161 protected function getRequestHeader($Name) 1162 { 1163 $headers = self::getAllRequestHeaders(); 1164 if (isset($headers[strtolower($Name)])) { 1165 return $headers[strtolower($Name)]; 1166 } 1167 return false; 1168 } 1169 1170 /** 1171 * Returns a new exception 1172 * 1173 * @param string $Message 1174 * @return Exception 1175 */ 1176 protected function newException($Message) 1177 { 1178 return new Exception($Message); 1179 } 153 1180 154 /** 155 * Wildfire protocol message index 156 * 157 * @var int 158 */ 159 protected $messageIndex = 1; 160 161 /** 162 * Options for the library 163 * 164 * @var array 165 */ 166 protected $options = array(); 167 168 /** 169 * Filters used to exclude object members when encoding 170 * 171 * @var array 172 */ 173 protected $objectFilters = array(); 174 175 /** 176 * A stack of objects used to detect recursion during object encoding 177 * 178 * @var object 179 */ 180 protected $objectStack = array(); 181 182 /** 183 * Flag to enable/disable logging 184 * 185 * @var boolean 186 */ 187 protected $enabled = true; 188 189 /** 190 * The object constructor 191 */ 192 function __construct() { 193 $this->options['maxObjectDepth'] = 10; 194 $this->options['maxArrayDepth'] = 20; 195 $this->options['useNativeJsonEncode'] = true; 196 $this->options['includeLineNumbers'] = true; 197 } 198 199 /** 200 * When the object gets serialized only include specific object members. 201 * 202 * @return array 203 */ 204 public function __sleep() { 205 return array('options','objectFilters','enabled'); 206 } 207 208 /** 209 * Gets singleton instance of FirePHP 210 * 211 * @param boolean $AutoCreate 212 * @return FirePHP 213 */ 214 public static function getInstance($AutoCreate=false) { 215 if($AutoCreate===true && !self::$instance) { 216 self::init(); 217 } 218 return self::$instance; 219 } 220 221 /** 222 * Creates FirePHP object and stores it for singleton access 223 * 224 * @return FirePHP 225 */ 226 public static function init() { 227 return self::$instance = new self(); 228 } 229 230 /** 231 * Enable and disable logging to Firebug 232 * 233 * @param boolean $Enabled TRUE to enable, FALSE to disable 234 * @return void 235 */ 236 public function setEnabled($Enabled) { 237 $this->enabled = $Enabled; 238 } 239 240 /** 241 * Check if logging is enabled 242 * 243 * @return boolean TRUE if enabled 244 */ 245 public function getEnabled() { 246 return $this->enabled; 247 } 248 249 /** 250 * Specify a filter to be used when encoding an object 251 * 252 * Filters are used to exclude object members. 253 * 254 * @param string $Class The class name of the object 255 * @param array $Filter An array or members to exclude 256 * @return void 257 */ 258 public function setObjectFilter($Class, $Filter) { 259 $this->objectFilters[$Class] = $Filter; 260 } 261 262 /** 263 * Set some options for the library 264 * 265 * Options: 266 * - maxObjectDepth: The maximum depth to traverse objects (default: 10) 267 * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) 268 * - useNativeJsonEncode: If true will use json_encode() (default: true) 269 * - includeLineNumbers: If true will include line numbers and filenames (default: true) 270 * 271 * @param array $Options The options to be set 272 * @return void 273 */ 274 public function setOptions($Options) { 275 $this->options = array_merge($this->options,$Options); 276 } 277 278 /** 279 * Register FirePHP as your error handler 280 * 281 * Will throw exceptions for each php error. 282 */ 283 public function registerErrorHandler() 284 { 285 //NOTE: The following errors will not be caught by this error handler: 286 // E_ERROR, E_PARSE, E_CORE_ERROR, 287 // E_CORE_WARNING, E_COMPILE_ERROR, 288 // E_COMPILE_WARNING, E_STRICT 289 290 set_error_handler(array($this,'errorHandler')); 291 } 292 293 /** 294 * FirePHP's error handler 295 * 296 * Throws exception for each php error that will occur. 297 * 298 * @param int $errno 299 * @param string $errstr 300 * @param string $errfile 301 * @param int $errline 302 * @param array $errcontext 303 */ 304 public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) 305 { 306 // Don't throw exception if error reporting is switched off 307 if (error_reporting() == 0) { 308 return; 309 } 310 // Only throw exceptions for errors we are asking for 311 if (error_reporting() & $errno) { 312 throw new ErrorException($errstr, 0, $errno, $errfile, $errline); 313 } 314 } 315 316 /** 317 * Register FirePHP as your exception handler 318 */ 319 public function registerExceptionHandler() 320 { 321 set_exception_handler(array($this,'exceptionHandler')); 322 } 323 324 /** 325 * FirePHP's exception handler 326 * 327 * Logs all exceptions to your firebug console and then stops the script. 328 * 329 * @param Exception $Exception 330 * @throws Exception 331 */ 332 function exceptionHandler($Exception) { 333 $this->fb($Exception); 334 } 335 336 /** 337 * Set custom processor url for FirePHP 338 * 339 * @param string $URL 340 */ 341 public function setProcessorUrl($URL) 342 { 343 $this->setHeader('X-FirePHP-ProcessorURL', $URL); 344 } 345 346 /** 347 * Set custom renderer url for FirePHP 348 * 349 * @param string $URL 350 */ 351 public function setRendererUrl($URL) 352 { 353 $this->setHeader('X-FirePHP-RendererURL', $URL); 354 } 355 356 /** 357 * Start a group for following messages 358 * 359 * @param string $Name 360 * @return true 361 * @throws Exception 362 */ 363 public function group($Name) { 364 return $this->fb(null, $Name, FirePHP::GROUP_START); 365 } 366 367 /** 368 * Ends a group you have started before 369 * 370 * @return true 371 * @throws Exception 372 */ 373 public function groupEnd() { 374 return $this->fb(null, null, FirePHP::GROUP_END); 375 } 376 377 /** 378 * Log object with label to firebug console 379 * 380 * @see FirePHP::LOG 381 * @param mixes $Object 382 * @param string $Label 383 * @return true 384 * @throws Exception 385 */ 386 public function log($Object, $Label=null) { 387 return $this->fb($Object, $Label, FirePHP::LOG); 388 } 389 390 /** 391 * Log object with label to firebug console 392 * 393 * @see FirePHP::INFO 394 * @param mixes $Object 395 * @param string $Label 396 * @return true 397 * @throws Exception 398 */ 399 public function info($Object, $Label=null) { 400 return $this->fb($Object, $Label, FirePHP::INFO); 401 } 402 403 /** 404 * Log object with label to firebug console 405 * 406 * @see FirePHP::WARN 407 * @param mixes $Object 408 * @param string $Label 409 * @return true 410 * @throws Exception 411 */ 412 public function warn($Object, $Label=null) { 413 return $this->fb($Object, $Label, FirePHP::WARN); 414 } 415 416 /** 417 * Log object with label to firebug console 418 * 419 * @see FirePHP::ERROR 420 * @param mixes $Object 421 * @param string $Label 422 * @return true 423 * @throws Exception 424 */ 425 public function error($Object, $Label=null) { 426 return $this->fb($Object, $Label, FirePHP::ERROR); 427 } 428 429 /** 430 * Dumps key and variable to firebug server panel 431 * 432 * @see FirePHP::DUMP 433 * @param string $Key 434 * @param mixed $Variable 435 * @return true 436 * @throws Exception 437 */ 438 public function dump($Key, $Variable) { 439 return $this->fb($Variable, $Key, FirePHP::DUMP); 440 } 441 442 /** 443 * Log a trace in the firebug console 444 * 445 * @see FirePHP::TRACE 446 * @param string $Label 447 * @return true 448 * @throws Exception 449 */ 450 public function trace($Label) { 451 return $this->fb($Label, FirePHP::TRACE); 452 } 453 454 /** 455 * Log a table in the firebug console 456 * 457 * @see FirePHP::TABLE 458 * @param string $Label 459 * @param string $Table 460 * @return true 461 * @throws Exception 462 */ 463 public function table($Label, $Table) { 464 return $this->fb($Table, $Label, FirePHP::TABLE); 465 } 466 467 /** 468 * Check if FirePHP is installed on client 469 * 470 * @return boolean 471 */ 472 public function detectClientExtension() { 473 /* Check if FirePHP is installed on client */ 474 if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || 475 !version_compare($m[1][0],'0.0.6','>=')) { 476 return false; 477 } 478 return true; 479 } 480 481 /** 482 * Log varible to Firebug 483 * 484 * @see http://www.firephp.org/Wiki/Reference/Fb 485 * @param mixed $Object The variable to be logged 486 * @return true Return TRUE if message was added to headers, FALSE otherwise 487 * @throws Exception 488 */ 489 public function fb($Object) { 490 491 if(!$this->enabled) { 492 return false; 493 } 494 495 if (headers_sent($filename, $linenum)) { 496 throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); 497 } 498 499 $Type = null; 500 $Label = null; 501 502 if(func_num_args()==1) { 503 } else 504 if(func_num_args()==2) { 505 switch(func_get_arg(1)) { 506 case self::LOG: 507 case self::INFO: 508 case self::WARN: 509 case self::ERROR: 510 case self::DUMP: 511 case self::TRACE: 512 case self::EXCEPTION: 513 case self::TABLE: 514 case self::GROUP_START: 515 case self::GROUP_END: 516 $Type = func_get_arg(1); 517 break; 518 default: 519 $Label = func_get_arg(1); 520 break; 521 } 522 } else 523 if(func_num_args()==3) { 524 $Type = func_get_arg(2); 525 $Label = func_get_arg(1); 526 } else { 527 throw $this->newException('Wrong number of arguments to fb() function!'); 528 } 529 530 531 if(!$this->detectClientExtension()) { 532 return false; 533 } 534 535 $meta = array(); 536 $skipFinalObjectEncode = false; 537 538 if($Object instanceof Exception) { 539 540 $meta['file'] = $this->_escapeTraceFile($Object->getFile()); 541 $meta['line'] = $Object->getLine(); 1181 /** 1182 * Encode an object into a JSON string 1183 * 1184 * Uses PHP's jeson_encode() if available 1185 * 1186 * @param object $Object The object to be encoded 1187 * @return string The JSON string 1188 */ 1189 public function jsonEncode($Object, $skipObjectEncode = false) 1190 { 1191 if (!$skipObjectEncode) { 1192 $Object = $this->encodeObject($Object); 1193 } 1194 1195 if (function_exists('json_encode') 1196 && $this->options['useNativeJsonEncode']!=false) { 1197 1198 return json_encode($Object); 1199 } else { 1200 return $this->json_encode($Object); 1201 } 1202 } 1203 1204 /** 1205 * Encodes a table by encoding each row and column with encodeObject() 1206 * 1207 * @param array $Table The table to be encoded 1208 * @return array 1209 */ 1210 protected function encodeTable($Table) 1211 { 1212 1213 if (!$Table) return $Table; 1214 1215 $new_table = array(); 1216 foreach($Table as $row) { 542 1217 543 $trace = $Object->getTrace(); 544 if($Object instanceof ErrorException 545 && isset($trace[0]['function']) 546 && $trace[0]['function']=='errorHandler' 547 && isset($trace[0]['class']) 548 && $trace[0]['class']=='FirePHP') { 549 550 $severity = false; 551 switch($Object->getSeverity()) { 552 case E_WARNING: $severity = 'E_WARNING'; break; 553 case E_NOTICE: $severity = 'E_NOTICE'; break; 554 case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; 555 case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; 556 case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; 557 case E_STRICT: $severity = 'E_STRICT'; break; 558 case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; 559 case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; 560 case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; 561 } 562 563 $Object = array('Class'=>get_class($Object), 564 'Message'=>$severity.': '.$Object->getMessage(), 565 'File'=>$this->_escapeTraceFile($Object->getFile()), 566 'Line'=>$Object->getLine(), 567 'Type'=>'trigger', 568 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); 569 $skipFinalObjectEncode = true; 570 } else { 571 $Object = array('Class'=>get_class($Object), 572 'Message'=>$Object->getMessage(), 573 'File'=>$this->_escapeTraceFile($Object->getFile()), 574 'Line'=>$Object->getLine(), 575 'Type'=>'throw', 576 'Trace'=>$this->_escapeTrace($trace)); 577 $skipFinalObjectEncode = true; 578 } 579 $Type = self::EXCEPTION; 580 581 } else 582 if($Type==self::TRACE) { 583 584 $trace = debug_backtrace(); 585 if(!$trace) return false; 586 for( $i=0 ; $i<sizeof($trace) ; $i++ ) { 587 588 if(isset($trace[$i]['class']) 589 && isset($trace[$i]['file']) 590 && ($trace[$i]['class']=='FirePHP' 591 || $trace[$i]['class']=='FB') 592 && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' 593 || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { 594 /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ 595 } else 596 if(isset($trace[$i]['class']) 597 && isset($trace[$i+1]['file']) 598 && $trace[$i]['class']=='FirePHP' 599 && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { 600 /* Skip fb() */ 601 } else 602 if($trace[$i]['function']=='fb' 603 || $trace[$i]['function']=='trace' 604 || $trace[$i]['function']=='send') { 605 $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', 606 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', 607 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', 608 'Message'=>$trace[$i]['args'][0], 609 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', 610 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', 611 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', 612 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); 613 614 $skipFinalObjectEncode = true; 615 $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; 616 $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; 617 break; 618 } 619 } 620 621 } else 622 if($Type==self::TABLE) { 623 624 if(isset($Object[0]) && is_string($Object[0])) { 625 $Object[1] = $this->encodeTable($Object[1]); 626 } else { 627 $Object = $this->encodeTable($Object); 628 } 629 630 $skipFinalObjectEncode = true; 631 632 } else { 633 if($Type===null) { 634 $Type = self::LOG; 635 } 636 } 637 638 if($this->options['includeLineNumbers']) { 639 if(!isset($meta['file']) || !isset($meta['line'])) { 640 641 $trace = debug_backtrace(); 642 for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) { 643 644 if(isset($trace[$i]['class']) 645 && isset($trace[$i]['file']) 646 && ($trace[$i]['class']=='FirePHP' 647 || $trace[$i]['class']=='FB') 648 && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' 649 || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { 650 /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ 651 } else 652 if(isset($trace[$i]['class']) 653 && isset($trace[$i+1]['file']) 654 && $trace[$i]['class']=='FirePHP' 655 && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { 656 /* Skip fb() */ 657 } else 658 if(isset($trace[$i]['file']) 659 && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { 660 /* Skip FB::fb() */ 661 } else { 662 $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; 663 $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; 664 break; 665 } 666 } 667 668 } 669 } else { 670 unset($meta['file']); 671 unset($meta['line']); 672 } 673 674 $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); 675 $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); 676 677 $structure_index = 1; 678 if($Type==self::DUMP) { 679 $structure_index = 2; 680 $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); 681 } else { 682 $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); 683 } 684 685 if($Type==self::DUMP) { 686 $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; 687 } else { 688 $msg_meta = array('Type'=>$Type); 689 if($Label!==null) { 690 $msg_meta['Label'] = $Label; 691 } 692 if(isset($meta['file'])) { 693 $msg_meta['File'] = $meta['file']; 694 } 695 if(isset($meta['line'])) { 696 $msg_meta['Line'] = $meta['line']; 697 } 698 $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; 699 } 700 701 $parts = explode("\n",chunk_split($msg, 5000, "\n")); 702 703 for( $i=0 ; $i<count($parts) ; $i++) { 1218 if (is_array($row)) { 1219 $new_row = array(); 1220 1221 foreach($row as $item) { 1222 $new_row[] = $this->encodeObject($item); 1223 } 1224 1225 $new_table[] = $new_row; 1226 } 1227 } 704 1228 705 $part = $parts[$i]; 706 if ($part) { 1229 return $new_table; 1230 } 1231 1232 /** 1233 * Encodes an object including members with 1234 * protected and private visibility 1235 * 1236 * @param Object $Object The object to be encoded 1237 * @param int $Depth The current traversal depth 1238 * @return array All members of the object 1239 */ 1240 protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1, $MaxDepth = 1) 1241 { 1242 if ($MaxDepth > $this->options['maxDepth']) { 1243 return '** Max Depth ('.$this->options['maxDepth'].') **'; 1244 } 1245 1246 $return = array(); 1247 1248 if (is_resource($Object)) { 1249 1250 return '** '.(string)$Object.' **'; 1251 1252 } else 1253 if (is_object($Object)) { 1254 1255 if ($ObjectDepth > $this->options['maxObjectDepth']) { 1256 return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; 1257 } 707 1258 708 if(count($parts)>2) { 709 // Message needs to be split into multiple parts 710 $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, 711 (($i==0)?strlen($msg):'') 712 . '|' . $part . '|' 713 . (($i<count($parts)-2)?'\\':'')); 1259 foreach ($this->objectStack as $refVal) { 1260 if ($refVal === $Object) { 1261 return '** Recursion ('.get_class($Object).') **'; 1262 } 1263 } 1264 array_push($this->objectStack, $Object); 1265 1266 $return['__className'] = $class = get_class($Object); 1267 $class_lower = strtolower($class); 1268 1269 $reflectionClass = new ReflectionClass($class); 1270 $properties = array(); 1271 foreach( $reflectionClass->getProperties() as $property) { 1272 $properties[$property->getName()] = $property; 1273 } 1274 1275 $members = (array)$Object; 1276 1277 foreach( $properties as $plain_name => $property ) { 1278 1279 $name = $raw_name = $plain_name; 1280 if ($property->isStatic()) { 1281 $name = 'static:'.$name; 1282 } 1283 if ($property->isPublic()) { 1284 $name = 'public:'.$name; 1285 } else 1286 if ($property->isPrivate()) { 1287 $name = 'private:'.$name; 1288 $raw_name = "\0".$class."\0".$raw_name; 1289 } else 1290 if ($property->isProtected()) { 1291 $name = 'protected:'.$name; 1292 $raw_name = "\0".'*'."\0".$raw_name; 1293 } 1294 1295 if (!(isset($this->objectFilters[$class_lower]) 1296 && is_array($this->objectFilters[$class_lower]) 1297 && in_array($plain_name,$this->objectFilters[$class_lower]))) { 1298 1299 if (array_key_exists($raw_name,$members) 1300 && !$property->isStatic()) { 1301 1302 $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1, $MaxDepth + 1); 1303 1304 } else { 1305 if (method_exists($property,'setAccessible')) { 1306 $property->setAccessible(true); 1307 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1); 1308 } else 1309 if ($property->isPublic()) { 1310 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1); 1311 } else { 1312 $return[$name] = '** Need PHP 5.3 to get value **'; 1313 } 1314 } 1315 } else { 1316 $return[$name] = '** Excluded by Filter **'; 1317 } 1318 } 1319 1320 // Include all members that are not defined in the class 1321 // but exist in the object 1322 foreach( $members as $raw_name => $value ) { 1323 1324 $name = $raw_name; 1325 1326 if ($name{0} == "\0") { 1327 $parts = explode("\0", $name); 1328 $name = $parts[2]; 1329 } 1330 1331 $plain_name = $name; 1332 1333 if (!isset($properties[$name])) { 1334 $name = 'undeclared:'.$name; 1335 1336 if (!(isset($this->objectFilters[$class_lower]) 1337 && is_array($this->objectFilters[$class_lower]) 1338 && in_array($plain_name,$this->objectFilters[$class_lower]))) { 1339 1340 $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1, $MaxDepth + 1); 1341 } else { 1342 $return[$name] = '** Excluded by Filter **'; 1343 } 1344 } 1345 } 1346 1347 array_pop($this->objectStack); 1348 1349 } elseif (is_array($Object)) { 1350 1351 if ($ArrayDepth > $this->options['maxArrayDepth']) { 1352 return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; 1353 } 1354 1355 foreach ($Object as $key => $val) { 1356 1357 // Encoding the $GLOBALS PHP array causes an infinite loop 1358 // if the recursion is not reset here as it contains 1359 // a reference to itself. This is the only way I have come up 1360 // with to stop infinite recursion in this case. 1361 if ($key=='GLOBALS' 1362 && is_array($val) 1363 && array_key_exists('GLOBALS',$val)) { 1364 $val['GLOBALS'] = '** Recursion (GLOBALS) **'; 1365 } 1366 1367 $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1, $MaxDepth + 1); 1368 } 1369 } else { 1370 if (self::is_utf8($Object)) { 1371 return $Object; 714 1372 } else { 715 $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, 716 strlen($part) . '|' . $part . '|'); 717 } 718 719 $this->messageIndex++; 720 721 if ($this->messageIndex > 99999) { 722 throw new Exception('Maximum number (99,999) of messages reached!'); 723 } 724 } 725 } 726 727 $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); 728 729 return true; 730 } 731 732 /** 733 * Standardizes path for windows systems. 734 * 735 * @param string $Path 736 * @return string 737 */ 738 protected function _standardizePath($Path) { 739 return preg_replace('/\\\\+/','/',$Path); 740 } 741 742 /** 743 * Escape trace path for windows systems 744 * 745 * @param array $Trace 746 * @return array 747 */ 748 protected function _escapeTrace($Trace) { 749 if(!$Trace) return $Trace; 750 for( $i=0 ; $i<sizeof($Trace) ; $i++ ) { 751 if(isset($Trace[$i]['file'])) { 752 $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']); 753 } 754 if(isset($Trace[$i]['args'])) { 755 $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); 756 } 757 } 758 return $Trace; 759 } 760 761 /** 762 * Escape file information of trace for windows systems 763 * 764 * @param string $File 765 * @return string 766 */ 767 protected function _escapeTraceFile($File) { 768 /* Check if we have a windows filepath */ 769 if(strpos($File,'\\')) { 770 /* First strip down to single \ */ 771 772 $file = preg_replace('/\\\\+/','\\',$File); 773 774 return $file; 775 } 776 return $File; 777 } 778 779 /** 780 * Send header 781 * 782 * @param string $Name 783 * @param string_type $Value 784 */ 785 protected function setHeader($Name, $Value) { 786 return header($Name.': '.$Value); 787 } 788 789 /** 790 * Get user agent 791 * 792 * @return string|false 793 */ 794 protected function getUserAgent() { 795 if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; 796 return $_SERVER['HTTP_USER_AGENT']; 797 } 798 799 /** 800 * Returns a new exception 801 * 802 * @param string $Message 803 * @return Exception 804 */ 805 protected function newException($Message) { 806 return new Exception($Message); 807 } 808 809 /** 810 * Encode an object into a JSON string 811 * 812 * Uses PHP's jeson_encode() if available 813 * 814 * @param object $Object The object to be encoded 815 * @return string The JSON string 816 */ 817 public function jsonEncode($Object, $skipObjectEncode=false) 818 { 819 if(!$skipObjectEncode) { 820 $Object = $this->encodeObject($Object); 821 } 822 823 if(function_exists('json_encode') 824 && $this->options['useNativeJsonEncode']!=false) { 825 826 return json_encode($Object); 827 } else { 828 return $this->json_encode($Object); 829 } 830 } 831 832 /** 833 * Encodes a table by encoding each row and column with encodeObject() 834 * 835 * @param array $Table The table to be encoded 836 * @return array 837 */ 838 protected function encodeTable($Table) { 839 if(!$Table) return $Table; 840 for( $i=0 ; $i<count($Table) ; $i++ ) { 841 if(is_array($Table[$i])) { 842 for( $j=0 ; $j<count($Table[$i]) ; $j++ ) { 843 $Table[$i][$j] = $this->encodeObject($Table[$i][$j]); 844 } 845 } 846 } 847 return $Table; 848 } 849 850 /** 851 * Encodes an object including members with 852 * protected and private visibility 853 * 854 * @param Object $Object The object to be encoded 855 * @param int $Depth The current traversal depth 856 * @return array All members of the object 857 */ 858 protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) 859 { 860 $return = array(); 861 862 if (is_resource($Object)) { 863 864 return '** '.(string)$Object.' **'; 865 866 } else 867 if (is_object($Object)) { 868 869 if ($ObjectDepth > $this->options['maxObjectDepth']) { 870 return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; 871 } 872 873 foreach ($this->objectStack as $refVal) { 874 if ($refVal === $Object) { 875 return '** Recursion ('.get_class($Object).') **'; 876 } 877 } 878 array_push($this->objectStack, $Object); 879 880 $return['__className'] = $class = get_class($Object); 881 882 $reflectionClass = new ReflectionClass($class); 883 $properties = array(); 884 foreach( $reflectionClass->getProperties() as $property) { 885 $properties[$property->getName()] = $property; 886 } 887 888 $members = (array)$Object; 889 890 foreach( $properties as $raw_name => $property ) { 891 892 $name = $raw_name; 893 if($property->isStatic()) { 894 $name = 'static:'.$name; 895 } 896 if($property->isPublic()) { 897 $name = 'public:'.$name; 898 } else 899 if($property->isPrivate()) { 900 $name = 'private:'.$name; 901 $raw_name = "\0".$class."\0".$raw_name; 902 } else 903 if($property->isProtected()) { 904 $name = 'protected:'.$name; 905 $raw_name = "\0".'*'."\0".$raw_name; 906 } 907 908 if(!(isset($this->objectFilters[$class]) 909 && is_array($this->objectFilters[$class]) 910 && in_array($raw_name,$this->objectFilters[$class]))) { 911 912 if(array_key_exists($raw_name,$members) 913 && !$property->isStatic()) { 914 915 $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1); 916 917 } else { 918 if(method_exists($property,'setAccessible')) { 919 $property->setAccessible(true); 920 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); 921 } else 922 if($property->isPublic()) { 923 $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); 924 } else { 925 $return[$name] = '** Need PHP 5.3 to get value **'; 926 } 927 } 928 } else { 929 $return[$name] = '** Excluded by Filter **'; 930 } 931 } 932 933 // Include all members that are not defined in the class 934 // but exist in the object 935 foreach( $members as $raw_name => $value ) { 936 937 $name = $raw_name; 938 939 if ($name{0} == "\0") { 940 $parts = explode("\0", $name); 941 $name = $parts[2]; 942 } 943 944 if(!isset($properties[$name])) { 945 $name = 'undeclared:'.$name; 946 947 if(!(isset($this->objectFilters[$class]) 948 && is_array($this->objectFilters[$class]) 949 && in_array($raw_name,$this->objectFilters[$class]))) { 950 951 $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); 952 } else { 953 $return[$name] = '** Excluded by Filter **'; 954 } 955 } 956 } 957 958 array_pop($this->objectStack); 959 960 } elseif (is_array($Object)) { 961 962 if ($ArrayDepth > $this->options['maxArrayDepth']) { 963 return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; 964 } 965 966 foreach ($Object as $key => $val) { 967 968 // Encoding the $GLOBALS PHP array causes an infinite loop 969 // if the recursion is not reset here as it contains 970 // a reference to itself. This is the only way I have come up 971 // with to stop infinite recursion in this case. 972 if($key=='GLOBALS' 973 && is_array($val) 974 && array_key_exists('GLOBALS',$val)) { 975 $val['GLOBALS'] = '** Recursion (GLOBALS) **'; 976 } 977 978 $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); 979 } 980 } else { 981 if(self::is_utf8($Object)) { 982 return $Object; 983 } else { 984 return utf8_encode($Object); 985 } 986 } 987 return $return; 988 } 989 990 /** 991 * Returns true if $string is valid UTF-8 and false otherwise. 992 * 993 * @param mixed $str String to be tested 994 * @return boolean 995 */ 996 protected static function is_utf8($str) { 997 $c=0; $b=0; 998 $bits=0; 999 $len=strlen($str); 1000 for($i=0; $i<$len; $i++){ 1001 $c=ord($str[$i]); 1002 if($c > 128){ 1003 if(($c >= 254)) return false; 1004 elseif($c >= 252) $bits=6; 1005 elseif($c >= 248) $bits=5; 1006 elseif($c >= 240) $bits=4; 1007 elseif($c >= 224) $bits=3; 1008 elseif($c >= 192) $bits=2; 1009 else return false; 1010 if(($i+$bits) > $len) return false; 1011 while($bits > 1){ 1012 $i++; 1013 $b=ord($str[$i]); 1014 if($b < 128 || $b > 191) return false; 1015 $bits--; 1016 } 1017 } 1018 } 1019 return true; 1020 } 1021 1022 /** 1023 * Converts to and from JSON format. 1024 * 1025 * JSON (JavaScript Object Notation) is a lightweight data-interchange 1026 * format. It is easy for humans to read and write. It is easy for machines 1027 * to parse and generate. It is based on a subset of the JavaScript 1028 * Programming Language, Standard ECMA-262 3rd Edition - December 1999. 1029 * This feature can also be found in Python. JSON is a text format that is 1030 * completely language independent but uses conventions that are familiar 1031 * to programmers of the C-family of languages, including C, C++, C#, Java, 1032 * JavaScript, Perl, TCL, and many others. These properties make JSON an 1033 * ideal data-interchange language. 1034 * 1035 * This package provides a simple encoder and decoder for JSON notation. It 1036 * is intended for use with client-side Javascript applications that make 1037 * use of HTTPRequest to perform server communication functions - data can 1038 * be encoded into JSON notation for use in a client-side javascript, or 1039 * decoded from incoming Javascript requests. JSON format is native to 1040 * Javascript, and can be directly eval()'ed with no further parsing 1041 * overhead 1042 * 1043 * All strings should be in ASCII or UTF-8 format! 1044 * 1045 * LICENSE: Redistribution and use in source and binary forms, with or 1046 * without modification, are permitted provided that the following 1047 * conditions are met: Redistributions of source code must retain the 1048 * above copyright notice, this list of conditions and the following 1049 * disclaimer. Redistributions in binary form must reproduce the above 1050 * copyright notice, this list of conditions and the following disclaimer 1051 * in the documentation and/or other materials provided with the 1052 * distribution. 1053 * 1054 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 1055 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1056 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 1057 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1058 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1059 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1060 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1061 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 1062 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 1063 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 1064 * DAMAGE. 1065 * 1066 * @category 1067 * @package Services_JSON 1068 * @author Michal Migurski <mike-json@teczno.com> 1069 * @author Matt Knapp <mdknapp[at]gmail[dot]com> 1070 * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> 1071 * @author Christoph Dorn <christoph@christophdorn.com> 1072 * @copyright 2005 Michal Migurski 1073 * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ 1074 * @license http://www.opensource.org/licenses/bsd-license.php 1075 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 1076 */ 1373 return utf8_encode($Object); 1374 } 1375 } 1376 return $return; 1377 } 1378 1379 /** 1380 * Returns true if $string is valid UTF-8 and false otherwise. 1381 * 1382 * @param mixed $str String to be tested 1383 * @return boolean 1384 */ 1385 protected static function is_utf8($str) 1386 { 1387 if(function_exists('mb_detect_encoding')) { 1388 return (mb_detect_encoding($str) == 'UTF-8'); 1389 } 1390 $c=0; $b=0; 1391 $bits=0; 1392 $len=strlen($str); 1393 for($i=0; $i<$len; $i++){ 1394 $c=ord($str[$i]); 1395 if ($c > 128){ 1396 if (($c >= 254)) return false; 1397 elseif ($c >= 252) $bits=6; 1398 elseif ($c >= 248) $bits=5; 1399 elseif ($c >= 240) $bits=4; 1400 elseif ($c >= 224) $bits=3; 1401 elseif ($c >= 192) $bits=2; 1402 else return false; 1403 if (($i+$bits) > $len) return false; 1404 while($bits > 1){ 1405 $i++; 1406 $b=ord($str[$i]); 1407 if ($b < 128 || $b > 191) return false; 1408 $bits--; 1409 } 1410 } 1411 } 1412 return true; 1413 } 1414 1415 /** 1416 * Converts to and from JSON format. 1417 * 1418 * JSON (JavaScript Object Notation) is a lightweight data-interchange 1419 * format. It is easy for humans to read and write. It is easy for machines 1420 * to parse and generate. It is based on a subset of the JavaScript 1421 * Programming Language, Standard ECMA-262 3rd Edition - December 1999. 1422 * This feature can also be found in Python. JSON is a text format that is 1423 * completely language independent but uses conventions that are familiar 1424 * to programmers of the C-family of languages, including C, C++, C#, Java, 1425 * JavaScript, Perl, TCL, and many others. These properties make JSON an 1426 * ideal data-interchange language. 1427 * 1428 * This package provides a simple encoder and decoder for JSON notation. It 1429 * is intended for use with client-side Javascript applications that make 1430 * use of HTTPRequest to perform server communication functions - data can 1431 * be encoded into JSON notation for use in a client-side javascript, or 1432 * decoded from incoming Javascript requests. JSON format is native to 1433 * Javascript, and can be directly eval()'ed with no further parsing 1434 * overhead 1435 * 1436 * All strings should be in ASCII or UTF-8 format! 1437 * 1438 * LICENSE: Redistribution and use in source and binary forms, with or 1439 * without modification, are permitted provided that the following 1440 * conditions are met: Redistributions of source code must retain the 1441 * above copyright notice, this list of conditions and the following 1442 * disclaimer. Redistributions in binary form must reproduce the above 1443 * copyright notice, this list of conditions and the following disclaimer 1444 * in the documentation and/or other materials provided with the 1445 * distribution. 1446 * 1447 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 1448 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1449 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 1450 * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1451 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1452 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1453 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1454 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 1455 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 1456 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 1457 * DAMAGE. 1458 * 1459 * @category 1460 * @package Services_JSON 1461 * @author Michal Migurski <mike-json@teczno.com> 1462 * @author Matt Knapp <mdknapp[at]gmail[dot]com> 1463 * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> 1464 * @author Christoph Dorn <christoph@christophdorn.com> 1465 * @copyright 2005 Michal Migurski 1466 * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ 1467 * @license http://www.opensource.org/licenses/bsd-license.php 1468 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 1469 */ 1077 1470 1078 1471 1079 /**1080 * Keep a list of objects as we descend into the array so we can detect recursion.1081 */1082 private $json_objectStack = array();1083 1084 1085 /**1086 * convert a string from one UTF-8 char to one UTF-16 char1087 *1088 * Normally should be handled by mb_convert_encoding, but1089 * provides a slower PHP-only method for installations1090 * that lack the multibye string extension.1091 *1092 * @param string $utf8 UTF-8 character1093 * @return string UTF-16 character1094 * @access private1095 */1096 private function json_utf82utf16($utf8)1097 {1098 // oh please oh please oh please oh please oh please1099 if(function_exists('mb_convert_encoding')) {1100 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');1101 }1102 1103 switch(strlen($utf8)) {1104 case 1:1105 // this case should never be reached, because we are in ASCII range1106 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81107 return $utf8;1108 1109 case 2:1110 // return a UTF-16 character from a 2-byte UTF-8 char1111 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81112 return chr(0x07 & (ord($utf8{0}) >> 2))1113 . chr((0xC0 & (ord($utf8{0}) << 6))1472 /** 1473 * Keep a list of objects as we descend into the array so we can detect recursion. 1474 */ 1475 private $json_objectStack = array(); 1476 1477 1478 /** 1479 * convert a string from one UTF-8 char to one UTF-16 char 1480 * 1481 * Normally should be handled by mb_convert_encoding, but 1482 * provides a slower PHP-only method for installations 1483 * that lack the multibye string extension. 1484 * 1485 * @param string $utf8 UTF-8 character 1486 * @return string UTF-16 character 1487 * @access private 1488 */ 1489 private function json_utf82utf16($utf8) 1490 { 1491 // oh please oh please oh please oh please oh please 1492 if (function_exists('mb_convert_encoding')) { 1493 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); 1494 } 1495 1496 switch(strlen($utf8)) { 1497 case 1: 1498 // this case should never be reached, because we are in ASCII range 1499 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1500 return $utf8; 1501 1502 case 2: 1503 // return a UTF-16 character from a 2-byte UTF-8 char 1504 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1505 return chr(0x07 & (ord($utf8{0}) >> 2)) 1506 . chr((0xC0 & (ord($utf8{0}) << 6)) 1114 1507 | (0x3F & ord($utf8{1}))); 1115 1508 1116 case 3:1117 // return a UTF-16 character from a 3-byte UTF-8 char1118 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81119 return chr((0xF0 & (ord($utf8{0}) << 4))1509 case 3: 1510 // return a UTF-16 character from a 3-byte UTF-8 char 1511 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1512 return chr((0xF0 & (ord($utf8{0}) << 4)) 1120 1513 | (0x0F & (ord($utf8{1}) >> 2))) 1121 . chr((0xC0 & (ord($utf8{1}) << 6))1514 . chr((0xC0 & (ord($utf8{1}) << 6)) 1122 1515 | (0x7F & ord($utf8{2}))); 1123 }1124 1125 // ignoring UTF-32 for now, sorry1126 return '';1127 }1128 1129 /**1130 * encodes an arbitrary variable into JSON format1131 *1132 * @param mixed $var any number, boolean, string, array, or object to be encoded.1133 * see argument 1 to Services_JSON() above for array-parsing behavior.1134 * if var is a strng, note that encode() always expects it1135 * to be in ASCII or UTF-8 format!1136 *1137 * @return mixed JSON string representation of input var or an error if a problem occurs1138 * @access public1139 */1140 private function json_encode($var)1141 {1142 1143 if(is_object($var)) {1144 if(in_array($var,$this->json_objectStack)) {1145 return '"** Recursion **"';1146 }1147 }1516 } 1517 1518 // ignoring UTF-32 for now, sorry 1519 return ''; 1520 } 1521 1522 /** 1523 * encodes an arbitrary variable into JSON format 1524 * 1525 * @param mixed $var any number, boolean, string, array, or object to be encoded. 1526 * see argument 1 to Services_JSON() above for array-parsing behavior. 1527 * if var is a strng, note that encode() always expects it 1528 * to be in ASCII or UTF-8 format! 1529 * 1530 * @return mixed JSON string representation of input var or an error if a problem occurs 1531 * @access public 1532 */ 1533 private function json_encode($var) 1534 { 1535 1536 if (is_object($var)) { 1537 if (in_array($var,$this->json_objectStack)) { 1538 return '"** Recursion **"'; 1539 } 1540 } 1148 1541 1149 switch (gettype($var)) {1150 case 'boolean':1151 return $var ? 'true' : 'false';1152 1153 case 'NULL':1154 return 'null';1155 1156 case 'integer':1157 return (int) $var;1158 1159 case 'double':1160 case 'float':1161 return (float) $var;1162 1163 case 'string':1164 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT1165 $ascii = '';1166 $strlen_var = strlen($var);1167 1168 /*1169 * Iterate over every character in the string,1170 * escaping with a slash or encoding to UTF-8 where necessary1171 */1172 for ($c = 0; $c < $strlen_var; ++$c) {1173 1174 $ord_var_c = ord($var{$c});1175 1176 switch (true) {1177 case $ord_var_c == 0x08:1178 $ascii .= '\b';1179 break;1180 case $ord_var_c == 0x09:1181 $ascii .= '\t';1182 break;1183 case $ord_var_c == 0x0A:1184 $ascii .= '\n';1185 break;1186 case $ord_var_c == 0x0C:1187 $ascii .= '\f';1188 break;1189 case $ord_var_c == 0x0D:1190 $ascii .= '\r';1191 break;1192 1193 case $ord_var_c == 0x22:1194 case $ord_var_c == 0x2F:1195 case $ord_var_c == 0x5C:1196 // double quote, slash, slosh1197 $ascii .= '\\'.$var{$c};1198 break;1199 1200 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):1201 // characters U-00000000 - U-0000007F (same as ASCII)1202 $ascii .= $var{$c};1203 break;1204 1205 case (($ord_var_c & 0xE0) == 0xC0):1206 // characters U-00000080 - U-000007FF, mask 110XXXXX1207 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81208 $char = pack('C*', $ord_var_c, ord($var{$c + 1}));1209 $c += 1;1210 $utf16 = $this->json_utf82utf16($char);1211 $ascii .= sprintf('\u%04s', bin2hex($utf16));1212 break;1213 1214 case (($ord_var_c & 0xF0) == 0xE0):1215 // characters U-00000800 - U-0000FFFF, mask 1110XXXX1216 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81217 $char = pack('C*', $ord_var_c,1218 ord($var{$c + 1}),1219 ord($var{$c + 2}));1220 $c += 2;1221 $utf16 = $this->json_utf82utf16($char);1222 $ascii .= sprintf('\u%04s', bin2hex($utf16));1223 break;1224 1225 case (($ord_var_c & 0xF8) == 0xF0):1226 // characters U-00010000 - U-001FFFFF, mask 11110XXX1227 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81228 $char = pack('C*', $ord_var_c,1229 ord($var{$c + 1}),1230 ord($var{$c + 2}),1231 ord($var{$c + 3}));1232 $c += 3;1233 $utf16 = $this->json_utf82utf16($char);1234 $ascii .= sprintf('\u%04s', bin2hex($utf16));1235 break;1236 1237 case (($ord_var_c & 0xFC) == 0xF8):1238 // characters U-00200000 - U-03FFFFFF, mask 111110XX1239 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81240 $char = pack('C*', $ord_var_c,1241 ord($var{$c + 1}),1242 ord($var{$c + 2}),1243 ord($var{$c + 3}),1244 ord($var{$c + 4}));1245 $c += 4;1246 $utf16 = $this->json_utf82utf16($char);1247 $ascii .= sprintf('\u%04s', bin2hex($utf16));1248 break;1249 1250 case (($ord_var_c & 0xFE) == 0xFC):1251 // characters U-04000000 - U-7FFFFFFF, mask 1111110X1252 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-81253 $char = pack('C*', $ord_var_c,1254 ord($var{$c + 1}),1255 ord($var{$c + 2}),1256 ord($var{$c + 3}),1257 ord($var{$c + 4}),1258 ord($var{$c + 5}));1259 $c += 5;1260 $utf16 = $this->json_utf82utf16($char);1261 $ascii .= sprintf('\u%04s', bin2hex($utf16));1262 break;1263 }1264 }1265 1266 return '"'.$ascii.'"';1267 1268 case 'array':1269 /*1270 * As per JSON spec if any array key is not an integer1271 * we must treat the the whole array as an object. We1272 * also try to catch a sparsely populated associative1273 * array with numeric keys here because some JS engines1274 * will create an array with empty indexes up to1275 * max_index which can cause memory issues and because1276 * the keys, which may be relevant, will be remapped1277 * otherwise.1278 *1279 * As per the ECMA and JSON specification an object may1280 * have any string as a property. Unfortunately due to1281 * a hole in the ECMA specification if the key is a1282 * ECMA reserved word or starts with a digit the1283 * parameter is only accessible using ECMAScript's1284 * bracket notation.1285 */1286 1287 // treat as a JSON object1288 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {1542 switch (gettype($var)) { 1543 case 'boolean': 1544 return $var ? 'true' : 'false'; 1545 1546 case 'NULL': 1547 return 'null'; 1548 1549 case 'integer': 1550 return (int) $var; 1551 1552 case 'double': 1553 case 'float': 1554 return (float) $var; 1555 1556 case 'string': 1557 // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 1558 $ascii = ''; 1559 $strlen_var = strlen($var); 1560 1561 /* 1562 * Iterate over every character in the string, 1563 * escaping with a slash or encoding to UTF-8 where necessary 1564 */ 1565 for ($c = 0; $c < $strlen_var; ++$c) { 1566 1567 $ord_var_c = ord($var{$c}); 1568 1569 switch (true) { 1570 case $ord_var_c == 0x08: 1571 $ascii .= '\b'; 1572 break; 1573 case $ord_var_c == 0x09: 1574 $ascii .= '\t'; 1575 break; 1576 case $ord_var_c == 0x0A: 1577 $ascii .= '\n'; 1578 break; 1579 case $ord_var_c == 0x0C: 1580 $ascii .= '\f'; 1581 break; 1582 case $ord_var_c == 0x0D: 1583 $ascii .= '\r'; 1584 break; 1585 1586 case $ord_var_c == 0x22: 1587 case $ord_var_c == 0x2F: 1588 case $ord_var_c == 0x5C: 1589 // double quote, slash, slosh 1590 $ascii .= '\\'.$var{$c}; 1591 break; 1592 1593 case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): 1594 // characters U-00000000 - U-0000007F (same as ASCII) 1595 $ascii .= $var{$c}; 1596 break; 1597 1598 case (($ord_var_c & 0xE0) == 0xC0): 1599 // characters U-00000080 - U-000007FF, mask 110XXXXX 1600 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1601 $char = pack('C*', $ord_var_c, ord($var{$c + 1})); 1602 $c += 1; 1603 $utf16 = $this->json_utf82utf16($char); 1604 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 1605 break; 1606 1607 case (($ord_var_c & 0xF0) == 0xE0): 1608 // characters U-00000800 - U-0000FFFF, mask 1110XXXX 1609 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1610 $char = pack('C*', $ord_var_c, 1611 ord($var{$c + 1}), 1612 ord($var{$c + 2})); 1613 $c += 2; 1614 $utf16 = $this->json_utf82utf16($char); 1615 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 1616 break; 1617 1618 case (($ord_var_c & 0xF8) == 0xF0): 1619 // characters U-00010000 - U-001FFFFF, mask 11110XXX 1620 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1621 $char = pack('C*', $ord_var_c, 1622 ord($var{$c + 1}), 1623 ord($var{$c + 2}), 1624 ord($var{$c + 3})); 1625 $c += 3; 1626 $utf16 = $this->json_utf82utf16($char); 1627 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 1628 break; 1629 1630 case (($ord_var_c & 0xFC) == 0xF8): 1631 // characters U-00200000 - U-03FFFFFF, mask 111110XX 1632 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1633 $char = pack('C*', $ord_var_c, 1634 ord($var{$c + 1}), 1635 ord($var{$c + 2}), 1636 ord($var{$c + 3}), 1637 ord($var{$c + 4})); 1638 $c += 4; 1639 $utf16 = $this->json_utf82utf16($char); 1640 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 1641 break; 1642 1643 case (($ord_var_c & 0xFE) == 0xFC): 1644 // characters U-04000000 - U-7FFFFFFF, mask 1111110X 1645 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 1646 $char = pack('C*', $ord_var_c, 1647 ord($var{$c + 1}), 1648 ord($var{$c + 2}), 1649 ord($var{$c + 3}), 1650 ord($var{$c + 4}), 1651 ord($var{$c + 5})); 1652 $c += 5; 1653 $utf16 = $this->json_utf82utf16($char); 1654 $ascii .= sprintf('\u%04s', bin2hex($utf16)); 1655 break; 1656 } 1657 } 1658 1659 return '"'.$ascii.'"'; 1660 1661 case 'array': 1662 /* 1663 * As per JSON spec if any array key is not an integer 1664 * we must treat the the whole array as an object. We 1665 * also try to catch a sparsely populated associative 1666 * array with numeric keys here because some JS engines 1667 * will create an array with empty indexes up to 1668 * max_index which can cause memory issues and because 1669 * the keys, which may be relevant, will be remapped 1670 * otherwise. 1671 * 1672 * As per the ECMA and JSON specification an object may 1673 * have any string as a property. Unfortunately due to 1674 * a hole in the ECMA specification if the key is a 1675 * ECMA reserved word or starts with a digit the 1676 * parameter is only accessible using ECMAScript's 1677 * bracket notation. 1678 */ 1679 1680 // treat as a JSON object 1681 if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { 1289 1682 1290 $this->json_objectStack[] = $var;1291 1292 $properties = array_map(array($this, 'json_name_value'),1293 array_keys($var),1294 array_values($var));1295 1296 array_pop($this->json_objectStack);1297 1298 foreach($properties as $property) {1299 if($property instanceof Exception) {1300 return $property;1301 }1302 }1303 1304 return '{' . join(',', $properties) . '}';1305 }1306 1307 $this->json_objectStack[] = $var;1308 1309 // treat it like a regular array1310 $elements = array_map(array($this, 'json_encode'), $var);1311 1312 array_pop($this->json_objectStack);1313 1314 foreach($elements as $element) {1315 if($element instanceof Exception) {1316 return $element;1317 }1318 }1319 1320 return '[' . join(',', $elements) . ']';1321 1322 case 'object':1323 $vars = self::encodeObject($var);1324 1325 $this->json_objectStack[] = $var;1326 1327 $properties = array_map(array($this, 'json_name_value'),1328 array_keys($vars),1329 array_values($vars));1330 1331 array_pop($this->json_objectStack);1683 $this->json_objectStack[] = $var; 1684 1685 $properties = array_map(array($this, 'json_name_value'), 1686 array_keys($var), 1687 array_values($var)); 1688 1689 array_pop($this->json_objectStack); 1690 1691 foreach($properties as $property) { 1692 if ($property instanceof Exception) { 1693 return $property; 1694 } 1695 } 1696 1697 return '{' . join(',', $properties) . '}'; 1698 } 1699 1700 $this->json_objectStack[] = $var; 1701 1702 // treat it like a regular array 1703 $elements = array_map(array($this, 'json_encode'), $var); 1704 1705 array_pop($this->json_objectStack); 1706 1707 foreach($elements as $element) { 1708 if ($element instanceof Exception) { 1709 return $element; 1710 } 1711 } 1712 1713 return '[' . join(',', $elements) . ']'; 1714 1715 case 'object': 1716 $vars = self::encodeObject($var); 1717 1718 $this->json_objectStack[] = $var; 1719 1720 $properties = array_map(array($this, 'json_name_value'), 1721 array_keys($vars), 1722 array_values($vars)); 1723 1724 array_pop($this->json_objectStack); 1332 1725 1333 foreach($properties as $property) {1334 if($property instanceof Exception) {1335 return $property;1336 }1337 }1726 foreach($properties as $property) { 1727 if ($property instanceof Exception) { 1728 return $property; 1729 } 1730 } 1338 1731 1339 return '{' . join(',', $properties) . '}'; 1340 1341 default: 1342 return null; 1343 } 1344 } 1345 1346 /** 1347 * array-walking function for use in generating JSON-formatted name-value pairs 1348 * 1349 * @param string $name name of key to use 1350 * @param mixed $value reference to an array element to be encoded 1351 * 1352 * @return string JSON-formatted name-value pair, like '"name":value' 1353 * @access private 1354 */ 1355 private function json_name_value($name, $value) 1356 { 1357 // Encoding the $GLOBALS PHP array causes an infinite loop 1358 // if the recursion is not reset here as it contains 1359 // a reference to itself. This is the only way I have come up 1360 // with to stop infinite recursion in this case. 1361 if($name=='GLOBALS' 1362 && is_array($value) 1363 && array_key_exists('GLOBALS',$value)) { 1364 $value['GLOBALS'] = '** Recursion **'; 1365 } 1366 1367 $encoded_value = $this->json_encode($value); 1368 1369 if($encoded_value instanceof Exception) { 1370 return $encoded_value; 1371 } 1372 1373 return $this->json_encode(strval($name)) . ':' . $encoded_value; 1374 } 1732 return '{' . join(',', $properties) . '}'; 1733 1734 default: 1735 return null; 1736 } 1737 } 1738 1739 /** 1740 * array-walking function for use in generating JSON-formatted name-value pairs 1741 * 1742 * @param string $name name of key to use 1743 * @param mixed $value reference to an array element to be encoded 1744 * 1745 * @return string JSON-formatted name-value pair, like '"name":value' 1746 * @access private 1747 */ 1748 private function json_name_value($name, $value) 1749 { 1750 // Encoding the $GLOBALS PHP array causes an infinite loop 1751 // if the recursion is not reset here as it contains 1752 // a reference to itself. This is the only way I have come up 1753 // with to stop infinite recursion in this case. 1754 if ($name=='GLOBALS' 1755 && is_array($value) 1756 && array_key_exists('GLOBALS',$value)) { 1757 $value['GLOBALS'] = '** Recursion **'; 1758 } 1759 1760 $encoded_value = $this->json_encode($value); 1761 1762 if ($encoded_value instanceof Exception) { 1763 return $encoded_value; 1764 } 1765 1766 return $this->json_encode(strval($name)) . ':' . $encoded_value; 1767 } 1768 1769 /** 1770 * @deprecated 1771 */ 1772 public function setProcessorUrl($URL) 1773 { 1774 trigger_error("The FirePHP::setProcessorUrl() method is no longer supported", E_USER_DEPRECATED); 1775 } 1776 1777 /** 1778 * @deprecated 1779 */ 1780 public function setRendererUrl($URL) 1781 { 1782 trigger_error("The FirePHP::setRendererUrl() method is no longer supported", E_USER_DEPRECATED); 1783 } 1375 1784 } -
trunk/plugins/firephp/LICENSE
r8795 r14725 1 1 Software License Agreement (New BSD License) 2 2 3 Copyright (c) 2006-200 8, Christoph Dorn3 Copyright (c) 2006-2009, Christoph Dorn 4 4 All rights reserved. 5 5 -
trunk/plugins/firephp/fb.php
r13492 r14725 7 7 * Software License Agreement (New BSD License) 8 8 * 9 * Copyright (c) 2006-20 08, Christoph Dorn9 * Copyright (c) 2006-2010, Christoph Dorn 10 10 * All rights reserved. 11 11 * … … 37 37 * ***** END LICENSE BLOCK ***** 38 38 * 39 * @copyright Copyright (C) 2007-200 8Christoph Dorn39 * @copyright Copyright (C) 2007-2009 Christoph Dorn 40 40 * @author Christoph Dorn <christoph@christophdorn.com> 41 41 * @license http://www.opensource.org/licenses/bsd-license.php 42 * @package FirePHP 42 * @package FirePHPCore 43 43 */ 44 44 45 require_once dirname(__FILE__).'/FirePHP.class.php'; 45 if(!class_exists('FirePHP')) { 46 require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'FirePHP.class.php'; 47 } 46 48 47 49 /** … … 57 59 function fb() 58 60 { 59 $mixedRet = false; 60 61 try { 62 63 $instance = FirePHP::getInstance(true); 64 65 $args = func_get_args(); 66 $mixedRet = call_user_func_array(array($instance,'fb'),$args); 67 68 } catch (Exception $e) { 69 //echo 'Caught exception: ', $e->getMessage(), "\n"; 70 } 71 72 return $mixedRet; 61 $instance = FirePHP::getInstance(true); 62 63 $args = func_get_args(); 64 return call_user_func_array(array($instance,'fb'),$args); 73 65 } 74 66 … … 76 68 class FB 77 69 { 78 /** 79 * Enable and disable logging to Firebug 80 * 81 * @see FirePHP->setEnabled() 82 * @param boolean $Enabled TRUE to enable, FALSE to disable 83 * @return void 84 */ 85 public static function setEnabled($Enabled) { 86 $instance = FirePHP::getInstance(true); 87 $instance->setEnabled($Enabled); 88 } 89 90 /** 91 * Check if logging is enabled 92 * 93 * @see FirePHP->getEnabled() 94 * @return boolean TRUE if enabled 95 */ 96 public static function getEnabled() { 97 $instance = FirePHP::getInstance(true); 98 return $instance->getEnabled(); 99 } 100 101 /** 102 * Specify a filter to be used when encoding an object 103 * 104 * Filters are used to exclude object members. 105 * 106 * @see FirePHP->setObjectFilter() 107 * @param string $Class The class name of the object 108 * @param array $Filter An array or members to exclude 109 * @return void 110 */ 111 public static function setObjectFilter($Class, $Filter) { 112 $instance = FirePHP::getInstance(true); 113 $instance->setObjectFilter($Class, $Filter); 114 } 115 116 /** 117 * Set some options for the library 118 * 119 * @see FirePHP->setOptions() 120 * @param array $Options The options to be set 121 * @return void 122 */ 123 public static function setOptions($Options) { 124 $instance = FirePHP::getInstance(true); 125 $instance->setOptions($Options); 126 } 127 128 /** 129 * Log object to firebug 130 * 131 * @see http://www.firephp.org/Wiki/Reference/Fb 132 * @param mixed $Object 133 * @return true 134 * @throws Exception 135 */ 136 public static function send() 137 { 138 $instance = FirePHP::getInstance(true); 139 $args = func_get_args(); 140 return call_user_func_array(array($instance,'fb'),$args); 141 } 142 143 /** 144 * Start a group for following messages 145 * 146 * @param string $Name 147 * @return true 148 * @throws Exception 149 */ 150 public static function group($Name) { 151 return self::send(null, $Name, FirePHP::GROUP_START); 152 } 153 154 /** 155 * Ends a group you have started before 156 * 157 * @return true 158 * @throws Exception 159 */ 160 public static function groupEnd() { 161 return self::send(null, null, FirePHP::GROUP_END); 162 } 163 164 /** 165 * Log object with label to firebug console 166 * 167 * @see FirePHP::LOG 168 * @param mixes $Object 169 * @param string $Label 170 * @return true 171 * @throws Exception 172 */ 173 public static function log($Object, $Label=null) { 174 return self::send($Object, $Label, FirePHP::LOG); 175 } 176 177 /** 178 * Log object with label to firebug console 179 * 180 * @see FirePHP::INFO 181 * @param mixes $Object 182 * @param string $Label 183 * @return true 184 * @throws Exception 185 */ 186 public static function info($Object, $Label=null) { 187 return self::send($Object, $Label, FirePHP::INFO); 188 } 189 190 /** 191 * Log object with label to firebug console 192 * 193 * @see FirePHP::WARN 194 * @param mixes $Object 195 * @param string $Label 196 * @return true 197 * @throws Exception 198 */ 199 public static function warn($Object, $Label=null) { 200 return self::send($Object, $Label, FirePHP::WARN); 201 } 202 203 /** 204 * Log object with label to firebug console 205 * 206 * @see FirePHP::ERROR 207 * @param mixes $Object 208 * @param string $Label 209 * @return true 210 * @throws Exception 211 */ 212 public static function error($Object, $Label=null) { 213 return self::send($Object, $Label, FirePHP::ERROR); 214 } 215 216 /** 217 * Dumps key and variable to firebug server panel 218 * 219 * @see FirePHP::DUMP 220 * @param string $Key 221 * @param mixed $Variable 222 * @return true 223 * @throws Exception 224 */ 225 public static function dump($Key, $Variable) { 226 return self::send($Variable, $Key, FirePHP::DUMP); 227 } 228 229 /** 230 * Log a trace in the firebug console 231 * 232 * @see FirePHP::TRACE 233 * @param string $Label 234 * @return true 235 * @throws Exception 236 */ 237 public static function trace($Label) { 238 return self::send($Label, FirePHP::TRACE); 239 } 240 241 /** 242 * Log a table in the firebug console 243 * 244 * @see FirePHP::TABLE 245 * @param string $Label 246 * @param string $Table 247 * @return true 248 * @throws Exception 249 */ 250 public static function table($Label, $Table) { 251 return self::send($Table, $Label, FirePHP::TABLE); 252 } 70 /** 71 * Enable and disable logging to Firebug 72 * 73 * @see FirePHP->setEnabled() 74 * @param boolean $Enabled TRUE to enable, FALSE to disable 75 * @return void 76 */ 77 public static function setEnabled($Enabled) 78 { 79 $instance = FirePHP::getInstance(true); 80 $instance->setEnabled($Enabled); 81 } 82 83 /** 84 * Check if logging is enabled 85 * 86 * @see FirePHP->getEnabled() 87 * @return boolean TRUE if enabled 88 */ 89 public static function getEnabled() 90 { 91 $instance = FirePHP::getInstance(true); 92 return $instance->getEnabled(); 93 } 94 95 /** 96 * Specify a filter to be used when encoding an object 97 * 98 * Filters are used to exclude object members. 99 * 100 * @see FirePHP->setObjectFilter() 101 * @param string $Class The class name of the object 102 * @param array $Filter An array or members to exclude 103 * @return void 104 */ 105 public static function setObjectFilter($Class, $Filter) 106 { 107 $instance = FirePHP::getInstance(true); 108 $instance->setObjectFilter($Class, $Filter); 109 } 110 111 /** 112 * Set some options for the library 113 * 114 * @see FirePHP->setOptions() 115 * @param array $Options The options to be set 116 * @return void 117 */ 118 public static function setOptions($Options) 119 { 120 $instance = FirePHP::getInstance(true); 121 $instance->setOptions($Options); 122 } 123 124 /** 125 * Get options for the library 126 * 127 * @see FirePHP->getOptions() 128 * @return array The options 129 */ 130 public static function getOptions() 131 { 132 $instance = FirePHP::getInstance(true); 133 return $instance->getOptions(); 134 } 135 136 /** 137 * Log object to firebug 138 * 139 * @see http://www.firephp.org/Wiki/Reference/Fb 140 * @param mixed $Object 141 * @return true 142 * @throws Exception 143 */ 144 public static function send() 145 { 146 $instance = FirePHP::getInstance(true); 147 $args = func_get_args(); 148 return call_user_func_array(array($instance,'fb'),$args); 149 } 150 151 /** 152 * Start a group for following messages 153 * 154 * Options: 155 * Collapsed: [true|false] 156 * Color: [#RRGGBB|ColorName] 157 * 158 * @param string $Name 159 * @param array $Options OPTIONAL Instructions on how to log the group 160 * @return true 161 */ 162 public static function group($Name, $Options=null) 163 { 164 $instance = FirePHP::getInstance(true); 165 return $instance->group($Name, $Options); 166 } 167 168 /** 169 * Ends a group you have started before 170 * 171 * @return true 172 * @throws Exception 173 */ 174 public static function groupEnd() 175 { 176 return self::send(null, null, FirePHP::GROUP_END); 177 } 178 179 /** 180 * Log object with label to firebug console 181 * 182 * @see FirePHP::LOG 183 * @param mixes $Object 184 * @param string $Label 185 * @return true 186 * @throws Exception 187 */ 188 public static function log($Object, $Label=null) 189 { 190 return self::send($Object, $Label, FirePHP::LOG); 191 } 192 193 /** 194 * Log object with label to firebug console 195 * 196 * @see FirePHP::INFO 197 * @param mixes $Object 198 * @param string $Label 199 * @return true 200 * @throws Exception 201 */ 202 public static function info($Object, $Label=null) 203 { 204 return self::send($Object, $Label, FirePHP::INFO); 205 } 206 207 /** 208 * Log object with label to firebug console 209 * 210 * @see FirePHP::WARN 211 * @param mixes $Object 212 * @param string $Label 213 * @return true 214 * @throws Exception 215 */ 216 public static function warn($Object, $Label=null) 217 { 218 return self::send($Object, $Label, FirePHP::WARN); 219 } 220 221 /** 222 * Log object with label to firebug console 223 * 224 * @see FirePHP::ERROR 225 * @param mixes $Object 226 * @param string $Label 227 * @return true 228 * @throws Exception 229 */ 230 public static function error($Object, $Label=null) 231 { 232 return self::send($Object, $Label, FirePHP::ERROR); 233 } 234 235 /** 236 * Dumps key and variable to firebug server panel 237 * 238 * @see FirePHP::DUMP 239 * @param string $Key 240 * @param mixed $Variable 241 * @return true 242 * @throws Exception 243 */ 244 public static function dump($Key, $Variable) 245 { 246 return self::send($Variable, $Key, FirePHP::DUMP); 247 } 248 249 /** 250 * Log a trace in the firebug console 251 * 252 * @see FirePHP::TRACE 253 * @param string $Label 254 * @return true 255 * @throws Exception 256 */ 257 public static function trace($Label) 258 { 259 return self::send($Label, FirePHP::TRACE); 260 } 261 262 /** 263 * Log a table in the firebug console 264 * 265 * @see FirePHP::TABLE 266 * @param string $Label 267 * @param string $Table 268 * @return true 269 * @throws Exception 270 */ 271 public static function table($Label, $Table) 272 { 273 return self::send($Table, $Label, FirePHP::TABLE); 274 } 253 275 254 276 } 255 256
Note: See TracChangeset
for help on using the changeset viewer.