PageRenderTime 121ms CodeModel.GetById 53ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 0ms

/library/php/firePHP/FirePHPCore/FirePHP.class.php

https://code.google.com/p/haquery/
PHP | 1828 lines | 1071 code | 194 blank | 563 comment | 266 complexity | 97b017bd7bd413b0d7f72b2fff0f73b2 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2// Authors:
   3// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
   4// - qbbr, Sokolov Innokenty <sokolov.innokenty@gmail.com>, Copyright 2011, New BSD License
   5// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
   6
   7/**
   8 * *** BEGIN LICENSE BLOCK *****
   9 * 
  10 * [MIT License](http://www.opensource.org/licenses/mit-license.php)
  11 * 
  12 * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
  13 * 
  14 * Permission is hereby granted, free of charge, to any person obtaining a copy
  15 * of this software and associated documentation files (the "Software"), to deal
  16 * in the Software without restriction, including without limitation the rights
  17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18 * copies of the Software, and to permit persons to whom the Software is
  19 * furnished to do so, subject to the following conditions:
  20 *
  21 * The above copyright notice and this permission notice shall be included in
  22 * all copies or substantial portions of the Software.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  30 * THE SOFTWARE.
  31 * 
  32 * ***** END LICENSE BLOCK *****
  33 * 
  34 * @copyright       Copyright (C) 2007+ Christoph Dorn
  35 * @author          Christoph Dorn <christoph@christophdorn.com>
  36 * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
  37 * @package         FirePHPCore
  38 */
  39
  40/**
  41 * @see http://code.google.com/p/firephp/issues/detail?id=112
  42 */
  43if (!defined('E_STRICT')) {
  44    define('E_STRICT', 2048);
  45}
  46if (!defined('E_RECOVERABLE_ERROR')) {
  47    define('E_RECOVERABLE_ERROR', 4096);
  48}
  49if (!defined('E_DEPRECATED')) {
  50    define('E_DEPRECATED', 8192);
  51}
  52if (!defined('E_USER_DEPRECATED')) {
  53    define('E_USER_DEPRECATED', 16384);
  54} 
  55 
  56/**
  57 * Sends the given data to the FirePHP Firefox Extension.
  58 * The data can be displayed in the Firebug Console or in the
  59 * "Server" request tab.
  60 * 
  61 * For more information see: http://www.firephp.org/
  62 * 
  63 * @copyright       Copyright (C) 2007+ Christoph Dorn
  64 * @author          Christoph Dorn <christoph@christophdorn.com>
  65 * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
  66 * @package         FirePHPCore
  67 */
  68class FirePHP {
  69
  70    /**
  71     * FirePHP version
  72     *
  73     * @var string
  74     */
  75    const VERSION = '0.3';    // @pinf replace '0.3' with '%%VERSION%%'
  76
  77    /**
  78     * Firebug LOG level
  79     *
  80     * Logs a message to firebug console.
  81     * 
  82     * @var string
  83     */
  84    const LOG = 'LOG';
  85  
  86    /**
  87     * Firebug INFO level
  88     *
  89     * Logs a message to firebug console and displays an info icon before the message.
  90     * 
  91     * @var string
  92     */
  93    const INFO = 'INFO';
  94    
  95    /**
  96     * Firebug WARN level
  97     *
  98     * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
  99     * 
 100     * @var string
 101     */
 102    const WARN = 'WARN';
 103    
 104    /**
 105     * Firebug ERROR level
 106     *
 107     * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
 108     * 
 109     * @var string
 110     */
 111    const ERROR = 'ERROR';
 112    
 113    /**
 114     * Dumps a variable to firebug's server panel
 115     *
 116     * @var string
 117     */
 118    const DUMP = 'DUMP';
 119    
 120    /**
 121     * Displays a stack trace in firebug console
 122     *
 123     * @var string
 124     */
 125    const TRACE = 'TRACE';
 126    
 127    /**
 128     * Displays an exception in firebug console
 129     * 
 130     * Increments the firebug error count.
 131     *
 132     * @var string
 133     */
 134    const EXCEPTION = 'EXCEPTION';
 135    
 136    /**
 137     * Displays an table in firebug console
 138     *
 139     * @var string
 140     */
 141    const TABLE = 'TABLE';
 142    
 143    /**
 144     * Starts a group in firebug console
 145     * 
 146     * @var string
 147     */
 148    const GROUP_START = 'GROUP_START';
 149    
 150    /**
 151     * Ends a group in firebug console
 152     * 
 153     * @var string
 154     */
 155    const GROUP_END = 'GROUP_END';
 156    
 157    /**
 158     * Singleton instance of FirePHP
 159     *
 160     * @var FirePHP
 161     */
 162    protected static $instance = null;
 163    
 164    /**
 165     * Flag whether we are logging from within the exception handler
 166     * 
 167     * @var boolean
 168     */
 169    protected $inExceptionHandler = false;
 170    
 171    /**
 172     * Flag whether to throw PHP errors that have been converted to ErrorExceptions
 173     * 
 174     * @var boolean
 175     */
 176    protected $throwErrorExceptions = true;
 177    
 178    /**
 179     * Flag whether to convert PHP assertion errors to Exceptions
 180     * 
 181     * @var boolean
 182     */
 183    protected $convertAssertionErrorsToExceptions = true;
 184    
 185    /**
 186     * Flag whether to throw PHP assertion errors that have been converted to Exceptions
 187     * 
 188     * @var boolean
 189     */
 190    protected $throwAssertionExceptions = false;
 191
 192    /**
 193     * Wildfire protocol message index
 194     *
 195     * @var integer
 196     */
 197    protected $messageIndex = 1;
 198    
 199    /**
 200     * Options for the library
 201     * 
 202     * @var array
 203     */
 204    protected $options = array('maxDepth' => 10,
 205                               'maxObjectDepth' => 5,
 206                               'maxArrayDepth' => 5,
 207                               'useNativeJsonEncode' => true,
 208                               'includeLineNumbers' => true);
 209
 210    /**
 211     * Filters used to exclude object members when encoding
 212     * 
 213     * @var array
 214     */
 215    protected $objectFilters = array(
 216        'firephp' => array('objectStack', 'instance', 'json_objectStack'),
 217        'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
 218    );
 219
 220    /**
 221     * A stack of objects used to detect recursion during object encoding
 222     * 
 223     * @var object
 224     */
 225    protected $objectStack = array();
 226
 227    /**
 228     * Flag to enable/disable logging
 229     * 
 230     * @var boolean
 231     */
 232    protected $enabled = true;
 233
 234    /**
 235     * The insight console to log to if applicable
 236     * 
 237     * @var object
 238     */
 239    protected $logToInsightConsole = null;
 240
 241    /**
 242     * When the object gets serialized only include specific object members.
 243     * 
 244     * @return array
 245     */  
 246    public function __sleep()
 247    {
 248        return array('options', 'objectFilters', 'enabled');
 249    }
 250    
 251    /**
 252     * Gets singleton instance of FirePHP
 253     *
 254     * @param boolean $autoCreate
 255     * @return FirePHP
 256     */
 257    public static function getInstance($autoCreate = false)
 258    {
 259        if ($autoCreate === true && !self::$instance) {
 260            self::init();
 261        }
 262        return self::$instance;
 263    }
 264    
 265    /**
 266     * Creates FirePHP object and stores it for singleton access
 267     *
 268     * @return FirePHP
 269     */
 270    public static function init()
 271    {
 272        return self::setInstance(new self());
 273    }
 274
 275    /**
 276     * Set the instance of the FirePHP singleton
 277     * 
 278     * @param FirePHP $instance The FirePHP object instance
 279     * @return FirePHP
 280     */
 281    public static function setInstance($instance)
 282    {
 283        return self::$instance = $instance;
 284    }
 285
 286    /**
 287     * Set an Insight console to direct all logging calls to
 288     * 
 289     * @param object $console The console object to log to
 290     * @return void
 291     */
 292    public function setLogToInsightConsole($console)
 293    {
 294        if (is_string($console)) {
 295            if (get_class($this) != 'FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
 296                throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
 297            }
 298            $this->logToInsightConsole = $this->to('request')->console($console);
 299        } else {
 300            $this->logToInsightConsole = $console;
 301        }
 302    }
 303
 304    /**
 305     * Enable and disable logging to Firebug
 306     * 
 307     * @param boolean $enabled TRUE to enable, FALSE to disable
 308     * @return void
 309     */
 310    public function setEnabled($enabled)
 311    {
 312       $this->enabled = $enabled;
 313    }
 314    
 315    /**
 316     * Check if logging is enabled
 317     * 
 318     * @return boolean TRUE if enabled
 319     */
 320    public function getEnabled()
 321    {
 322        return $this->enabled;
 323    }
 324    
 325    /**
 326     * Specify a filter to be used when encoding an object
 327     * 
 328     * Filters are used to exclude object members.
 329     * 
 330     * @param string $class The class name of the object
 331     * @param array $filter An array of members to exclude
 332     * @return void
 333     */
 334    public function setObjectFilter($class, $filter)
 335    {
 336        $this->objectFilters[strtolower($class)] = $filter;
 337    }
 338  
 339    /**
 340     * Set some options for the library
 341     * 
 342     * Options:
 343     *  - maxDepth: The maximum depth to traverse (default: 10)
 344     *  - maxObjectDepth: The maximum depth to traverse objects (default: 5)
 345     *  - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
 346     *  - useNativeJsonEncode: If true will use json_encode() (default: true)
 347     *  - includeLineNumbers: If true will include line numbers and filenames (default: true)
 348     * 
 349     * @param array $options The options to be set
 350     * @return void
 351     */
 352    public function setOptions($options)
 353    {
 354        $this->options = array_merge($this->options, $options);
 355    }
 356
 357    /**
 358     * Get options from the library
 359     *
 360     * @return array The currently set options
 361     */
 362    public function getOptions()
 363    {
 364        return $this->options;
 365    }
 366
 367    /**
 368     * Set an option for the library
 369     * 
 370     * @param string $name
 371     * @param mixed $value
 372     * @return void
 373     * @throws Exception
 374     */  
 375    public function setOption($name, $value)
 376    {
 377        if (!isset($this->options[$name])) {
 378            throw $this->newException('Unknown option: ' . $name);
 379        }
 380        $this->options[$name] = $value;
 381    }
 382
 383    /**
 384     * Get an option from the library
 385     *
 386     * @param string $name
 387     * @return mixed
 388     * @throws Exception
 389     */
 390    public function getOption($name)
 391    {
 392        if (!isset($this->options[$name])) {
 393            throw $this->newException('Unknown option: ' . $name);
 394        }
 395        return $this->options[$name];
 396    }
 397
 398    /**
 399     * Register FirePHP as your error handler
 400     * 
 401     * Will throw exceptions for each php error.
 402     * 
 403     * @return mixed Returns a string containing the previously defined error handler (if any)
 404     */
 405    public function registerErrorHandler($throwErrorExceptions = false)
 406    {
 407        //NOTE: The following errors will not be caught by this error handler:
 408        //      E_ERROR, E_PARSE, E_CORE_ERROR,
 409        //      E_CORE_WARNING, E_COMPILE_ERROR,
 410        //      E_COMPILE_WARNING, E_STRICT
 411    
 412        $this->throwErrorExceptions = $throwErrorExceptions;
 413    
 414        return set_error_handler(array($this, 'errorHandler'));
 415    }
 416
 417    /**
 418     * FirePHP's error handler
 419     * 
 420     * Throws exception for each php error that will occur.
 421     *
 422     * @param integer $errno
 423     * @param string $errstr
 424     * @param string $errfile
 425     * @param integer $errline
 426     * @param array $errcontext
 427     */
 428    public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
 429    {
 430        // Don't throw exception if error reporting is switched off
 431        if (error_reporting() == 0) {
 432            return;
 433        }
 434        // Only throw exceptions for errors we are asking for
 435        if (error_reporting() & $errno) {
 436
 437            $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
 438            if ($this->throwErrorExceptions) {
 439                throw $exception;
 440            } else {
 441                $this->fb($exception);
 442            }
 443        }
 444    }
 445  
 446    /**
 447     * Register FirePHP as your exception handler
 448     * 
 449     * @return mixed Returns the name of the previously defined exception handler,
 450     *               or NULL on error.
 451     *               If no previous handler was defined, NULL is also returned.
 452     */
 453    public function registerExceptionHandler()
 454    {
 455        return set_exception_handler(array($this, 'exceptionHandler'));
 456    }
 457  
 458    /**
 459     * FirePHP's exception handler
 460     * 
 461     * Logs all exceptions to your firebug console and then stops the script.
 462     *
 463     * @param Exception $exception
 464     * @throws Exception
 465     */
 466    function exceptionHandler($exception)
 467    {
 468        $this->inExceptionHandler = true;
 469    
 470        header('HTTP/1.1 500 Internal Server Error');
 471    
 472        try {
 473            $this->fb($exception);
 474        } catch (Exception $e) {
 475            echo 'We had an exception: ' . $e;
 476        }
 477        
 478        $this->inExceptionHandler = false;
 479    }
 480  
 481    /**
 482     * Register FirePHP driver as your assert callback
 483     * 
 484     * @param boolean $convertAssertionErrorsToExceptions
 485     * @param boolean $throwAssertionExceptions
 486     * @return mixed Returns the original setting or FALSE on errors
 487     */
 488    public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
 489    {
 490        $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
 491        $this->throwAssertionExceptions = $throwAssertionExceptions;
 492        
 493        if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
 494            throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
 495        }
 496        
 497        return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
 498    }
 499  
 500    /**
 501     * FirePHP's assertion handler
 502     *
 503     * Logs all assertions to your firebug console and then stops the script.
 504     *
 505     * @param string $file File source of assertion
 506     * @param integer $line Line source of assertion
 507     * @param mixed $code Assertion code
 508     */
 509    public function assertionHandler($file, $line, $code)
 510    {
 511        if ($this->convertAssertionErrorsToExceptions) {
 512          
 513          $exception = new ErrorException('Assertion Failed - Code[ ' . $code . ' ]', 0, null, $file, $line);
 514    
 515          if ($this->throwAssertionExceptions) {
 516              throw $exception;
 517          } else {
 518              $this->fb($exception);
 519          }
 520        
 521        } else {
 522            $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File' => $file, 'Line' => $line));
 523        }
 524    }
 525  
 526    /**
 527     * Start a group for following messages.
 528     * 
 529     * Options:
 530     *   Collapsed: [true|false]
 531     *   Color:     [#RRGGBB|ColorName]
 532     *
 533     * @param string $name
 534     * @param array $options OPTIONAL Instructions on how to log the group
 535     * @return true
 536     * @throws Exception
 537     */
 538    public function group($name, $options = null)
 539    {
 540    
 541        if (!$name) {
 542            throw $this->newException('You must specify a label for the group!');
 543        }
 544
 545        if ($options) {
 546            if (!is_array($options)) {
 547                throw $this->newException('Options must be defined as an array!');
 548            }
 549            if (array_key_exists('Collapsed', $options)) {
 550                $options['Collapsed'] = ($options['Collapsed']) ? 'true' : 'false';
 551            }
 552        }
 553
 554        return $this->fb(null, $name, FirePHP::GROUP_START, $options);
 555    }
 556  
 557    /**
 558     * Ends a group you have started before
 559     *
 560     * @return true
 561     * @throws Exception
 562     */
 563    public function groupEnd()
 564    {
 565        return $this->fb(null, null, FirePHP::GROUP_END);
 566    }
 567
 568    /**
 569     * Log object with label to firebug console
 570     *
 571     * @see FirePHP::LOG
 572     * @param mixes $object
 573     * @param string $label
 574     * @return true
 575     * @throws Exception
 576     */
 577    public function log($object, $label = null, $options = array())
 578    {
 579        return $this->fb($object, $label, FirePHP::LOG, $options);
 580    } 
 581
 582    /**
 583     * Log object with label to firebug console
 584     *
 585     * @see FirePHP::INFO
 586     * @param mixes $object
 587     * @param string $label
 588     * @return true
 589     * @throws Exception
 590     */
 591    public function info($object, $label = null, $options = array())
 592    {
 593        return $this->fb($object, $label, FirePHP::INFO, $options);
 594    } 
 595
 596    /**
 597     * Log object with label to firebug console
 598     *
 599     * @see FirePHP::WARN
 600     * @param mixes $object
 601     * @param string $label
 602     * @return true
 603     * @throws Exception
 604     */
 605    public function warn($object, $label = null, $options = array())
 606    {
 607        return $this->fb($object, $label, FirePHP::WARN, $options);
 608    } 
 609
 610    /**
 611     * Log object with label to firebug console
 612     *
 613     * @see FirePHP::ERROR
 614     * @param mixes $object
 615     * @param string $label
 616     * @return true
 617     * @throws Exception
 618     */
 619    public function error($object, $label = null, $options = array())
 620    {
 621        return $this->fb($object, $label, FirePHP::ERROR, $options);
 622    } 
 623
 624    /**
 625     * Dumps key and variable to firebug server panel
 626     *
 627     * @see FirePHP::DUMP
 628     * @param string $key
 629     * @param mixed $variable
 630     * @return true
 631     * @throws Exception
 632     */
 633    public function dump($key, $variable, $options = array())
 634    {
 635        if (!is_string($key)) {
 636            throw $this->newException('Key passed to dump() is not a string');
 637        }
 638        if (strlen($key) > 100) {
 639            throw $this->newException('Key passed to dump() is longer than 100 characters');
 640        }
 641        if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $key, $m)) {
 642            throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
 643        }
 644        return $this->fb($variable, $key, FirePHP::DUMP, $options);
 645    }
 646  
 647    /**
 648     * Log a trace in the firebug console
 649     *
 650     * @see FirePHP::TRACE
 651     * @param string $label
 652     * @return true
 653     * @throws Exception
 654     */
 655    public function trace($label)
 656    {
 657        return $this->fb($label, FirePHP::TRACE);
 658    } 
 659
 660    /**
 661     * Log a table in the firebug console
 662     *
 663     * @see FirePHP::TABLE
 664     * @param string $label
 665     * @param string $table
 666     * @return true
 667     * @throws Exception
 668     */
 669    public function table($label, $table, $options = array())
 670    {
 671        return $this->fb($table, $label, FirePHP::TABLE, $options);
 672    }
 673
 674    /**
 675     * Insight API wrapper
 676     * 
 677     * @see Insight_Helper::to()
 678     */
 679    public static function to()
 680    {
 681        $instance = self::getInstance();
 682        if (!method_exists($instance, '_to')) {
 683            throw new Exception('FirePHP::to() implementation not loaded');
 684        }
 685        $args = func_get_args();
 686        return call_user_func_array(array($instance, '_to'), $args);
 687    }
 688
 689    /**
 690     * Insight API wrapper
 691     * 
 692     * @see Insight_Helper::plugin()
 693     */
 694    public static function plugin()
 695    {
 696        $instance = self::getInstance();
 697        if (!method_exists($instance, '_plugin')) {
 698            throw new Exception('FirePHP::plugin() implementation not loaded');
 699        }
 700        $args = func_get_args();
 701        return call_user_func_array(array($instance, '_plugin'), $args);
 702    }
 703
 704    /**
 705     * Check if FirePHP is installed on client
 706     *
 707     * @return boolean
 708     */
 709    public function detectClientExtension()
 710    {
 711        // Check if FirePHP is installed on client via User-Agent header
 712        if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
 713           version_compare($m[1][0], '0.0.6', '>=')) {
 714            return true;
 715        } else
 716        // Check if FirePHP is installed on client via X-FirePHP-Version header
 717        if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader('X-FirePHP-Version'), $m) &&
 718           version_compare($m[1][0], '0.0.6', '>=')) {
 719            return true;
 720        }
 721        return false;
 722    }
 723 
 724    /**
 725     * Log varible to Firebug
 726     * 
 727     * @see http://www.firephp.org/Wiki/Reference/Fb
 728     * @param mixed $object The variable to be logged
 729     * @return boolean Return TRUE if message was added to headers, FALSE otherwise
 730     * @throws Exception
 731     */
 732    public function fb($object)
 733    {
 734        if ($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
 735            if (!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
 736                $this->_logUpgradeClientMessage();
 737            }
 738        }
 739
 740        static $insightGroupStack = array();
 741
 742        if (!$this->getEnabled()) {
 743            return false;
 744        }
 745
 746        if ($this->headersSent($filename, $linenum)) {
 747            // If we are logging from within the exception handler we cannot throw another exception
 748            if ($this->inExceptionHandler) {
 749                // Simply echo the error out to the page
 750                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>';
 751            } else {
 752                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.');
 753            }
 754        }
 755      
 756        $type = null;
 757        $label = null;
 758        $options = array();
 759      
 760        if (func_num_args() == 1) {
 761        } else if (func_num_args() == 2) {
 762            switch (func_get_arg(1)) {
 763                case self::LOG:
 764                case self::INFO:
 765                case self::WARN:
 766                case self::ERROR:
 767                case self::DUMP:
 768                case self::TRACE:
 769                case self::EXCEPTION:
 770                case self::TABLE:
 771                case self::GROUP_START:
 772                case self::GROUP_END:
 773                    $type = func_get_arg(1);
 774                    break;
 775                default:
 776                    $label = func_get_arg(1);
 777                    break;
 778            }
 779        } else if (func_num_args() == 3) {
 780            $type = func_get_arg(2);
 781            $label = func_get_arg(1);
 782        } else if (func_num_args() == 4) {
 783            $type = func_get_arg(2);
 784            $label = func_get_arg(1);
 785            $options = func_get_arg(3);
 786        } else {
 787            throw $this->newException('Wrong number of arguments to fb() function!');
 788        }
 789
 790        if ($this->logToInsightConsole !== null && (get_class($this) == 'FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
 791            $trace = debug_backtrace();
 792            if (!$trace) return false;
 793            for ($i = 0; $i < sizeof($trace); $i++) {
 794                if (isset($trace[$i]['class'])) {
 795                    if ($trace[$i]['class'] == 'FirePHP' || $trace[$i]['class'] == 'FB') {
 796                        continue;
 797                    }
 798                }
 799                if (isset($trace[$i]['file'])) {
 800                    $path = $this->_standardizePath($trace[$i]['file']);
 801                    if (substr($path, -18, 18) == 'FirePHPCore/fb.php' || substr($path, -29, 29) == 'FirePHPCore/FirePHP.class.php') {
 802                        continue;
 803                    }
 804                }
 805                if (isset($trace[$i]['function']) && $trace[$i]['function'] == 'fb' &&
 806                        isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
 807                    continue;
 808                }
 809                if (isset($trace[$i]['class']) && $trace[$i]['class'] == 'FB' &&
 810                        isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
 811                    continue;
 812                }
 813                break;
 814            }
 815            // adjust trace offset
 816            $msg = $this->logToInsightConsole->option('encoder.trace.offsetAdjustment', $i);
 817
 818            if ($object instanceof Exception) {
 819                $type = self::EXCEPTION;
 820            }
 821            if ($label && $type != self::TABLE && $type != self::GROUP_START) {
 822                $msg = $msg->label($label);
 823            }
 824            switch ($type) {
 825                case self::DUMP:
 826                case self::LOG:
 827                    return $msg->log($object);
 828                case self::INFO:
 829                    return $msg->info($object);
 830                case self::WARN:
 831                    return $msg->warn($object);
 832                case self::ERROR:
 833                    return $msg->error($object);
 834                case self::TRACE:
 835                    return $msg->trace($object);
 836                case self::EXCEPTION:
 837                    return $this->plugin('error')->handleException($object, $msg);
 838                case self::TABLE:
 839                    if (isset($object[0]) && !is_string($object[0]) && $label) {
 840                        $object = array($label, $object);
 841                    }
 842                    return $msg->table($object[0], array_slice($object[1], 1), $object[1][0]);
 843                case self::GROUP_START:
 844                    $insightGroupStack[] = $msg->group(md5($label))->open();
 845                    return $msg->log($label);
 846                case self::GROUP_END:
 847                    if (count($insightGroupStack) == 0) {
 848                        throw new Error('Too many groupEnd() as opposed to group() calls!');
 849                    }
 850                    $group = array_pop($insightGroupStack);
 851                    return $group->close();
 852                default:
 853                    return $msg->log($object);
 854            }
 855        }
 856
 857        if (!$this->detectClientExtension()) {
 858            return false;
 859        }
 860
 861        $meta = array();
 862        $skipFinalObjectEncode = false;
 863      
 864        if ($object instanceof Exception) {
 865    
 866            $meta['file'] = $this->_escapeTraceFile($object->getFile());
 867            $meta['line'] = $object->getLine();
 868          
 869            $trace = $object->getTrace();
 870            if ($object instanceof ErrorException
 871               && isset($trace[0]['function'])
 872               && $trace[0]['function'] == 'errorHandler'
 873               && isset($trace[0]['class'])
 874               && $trace[0]['class'] == 'FirePHP') {
 875               
 876                $severity = false;
 877                switch ($object->getSeverity()) {
 878                    case E_WARNING:
 879                        $severity = 'E_WARNING';
 880                        break;
 881
 882                    case E_NOTICE:
 883                        $severity = 'E_NOTICE';
 884                        break;
 885
 886                    case E_USER_ERROR:
 887                        $severity = 'E_USER_ERROR';
 888                        break;
 889
 890                    case E_USER_WARNING:
 891                        $severity = 'E_USER_WARNING';
 892                        break;
 893
 894                    case E_USER_NOTICE:
 895                        $severity = 'E_USER_NOTICE';
 896                        break;
 897
 898                    case E_STRICT:
 899                        $severity = 'E_STRICT';
 900                        break;
 901
 902                    case E_RECOVERABLE_ERROR:
 903                        $severity = 'E_RECOVERABLE_ERROR';
 904                        break;
 905
 906                    case E_DEPRECATED:
 907                        $severity = 'E_DEPRECATED';
 908                        break;
 909
 910                    case E_USER_DEPRECATED:
 911                        $severity = 'E_USER_DEPRECATED';
 912                        break;
 913                }
 914                   
 915                $object = array('Class' => get_class($object),
 916                                'Message' => $severity . ': ' . $object->getMessage(),
 917                                'File' => $this->_escapeTraceFile($object->getFile()),
 918                                'Line' => $object->getLine(),
 919                                'Type' => 'trigger',
 920                                'Trace' => $this->_escapeTrace(array_splice($trace, 2)));
 921                $skipFinalObjectEncode = true;
 922            } else {
 923                $object = array('Class' => get_class($object),
 924                                'Message' => $object->getMessage(),
 925                                'File' => $this->_escapeTraceFile($object->getFile()),
 926                                'Line' => $object->getLine(),
 927                                'Type' => 'throw',
 928                                'Trace' => $this->_escapeTrace($trace));
 929                $skipFinalObjectEncode = true;
 930            }
 931            $type = self::EXCEPTION;
 932          
 933        } else if ($type == self::TRACE) {
 934          
 935            $trace = debug_backtrace();
 936            if (!$trace) return false;
 937            for ($i = 0; $i < sizeof($trace); $i++) {
 938    
 939                if (isset($trace[$i]['class'])
 940                   && isset($trace[$i]['file'])
 941                   && ($trace[$i]['class'] == 'FirePHP'
 942                       || $trace[$i]['class'] == 'FB')
 943                   && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
 944                       || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')) {
 945                    /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
 946                } else
 947                if (isset($trace[$i]['class'])
 948                   && isset($trace[$i+1]['file'])
 949                   && $trace[$i]['class'] == 'FirePHP'
 950                   && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
 951                    /* Skip fb() */
 952                } else
 953                if ($trace[$i]['function'] == 'fb'
 954                   || $trace[$i]['function'] == 'trace'
 955                   || $trace[$i]['function'] == 'send') {
 956
 957                    $object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
 958                                    'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
 959                                    'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
 960                                    'Message' => $trace[$i]['args'][0],
 961                                    'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
 962                                    'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
 963                                    'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
 964                                    'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
 965        
 966                    $skipFinalObjectEncode = true;
 967                    $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
 968                    $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
 969                    break;
 970                }
 971            }
 972    
 973        } else
 974        if ($type == self::TABLE) {
 975          
 976            if (isset($object[0]) && is_string($object[0])) {
 977                $object[1] = $this->encodeTable($object[1]);
 978            } else {
 979                $object = $this->encodeTable($object);
 980            }
 981    
 982            $skipFinalObjectEncode = true;
 983          
 984        } else if ($type == self::GROUP_START) {
 985          
 986            if (!$label) {
 987                throw $this->newException('You must specify a label for the group!');
 988            }
 989          
 990        } else {
 991            if ($type === null) {
 992                $type = self::LOG;
 993            }
 994        }
 995        
 996        if ($this->options['includeLineNumbers']) {
 997            if (!isset($meta['file']) || !isset($meta['line'])) {
 998    
 999                $trace = debug_backtrace();
1000                for ($i = 0; $trace && $i < sizeof($trace); $i++) {
1001          
1002                    if (isset($trace[$i]['class'])
1003                       && isset($trace[$i]['file'])
1004                       && ($trace[$i]['class'] == 'FirePHP'
1005                           || $trace[$i]['class'] == 'FB')
1006                       && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
1007                           || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')) {
1008                        /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
1009                    } else
1010                    if (isset($trace[$i]['class'])
1011                       && isset($trace[$i + 1]['file'])
1012                       && $trace[$i]['class'] == 'FirePHP'
1013                       && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
1014                        /* Skip fb() */
1015                    } else
1016                    if (isset($trace[$i]['file'])
1017                       && substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php') {
1018                        /* Skip FB::fb() */
1019                    } else {
1020                        $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
1021                        $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
1022                        break;
1023                    }
1024                }      
1025            }
1026        } else {
1027            unset($meta['file']);
1028            unset($meta['line']);
1029        }
1030
1031        $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
1032        $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . self::VERSION);
1033     
1034        $structureIndex = 1;
1035        if ($type == self::DUMP) {
1036            $structureIndex = 2;
1037            $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
1038        } else {
1039            $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
1040        }
1041      
1042        if ($type == self::DUMP) {
1043            $msg = '{"' . $label . '":' . $this->jsonEncode($object, $skipFinalObjectEncode) . '}';
1044        } else {
1045            $msgMeta = $options;
1046            $msgMeta['Type'] = $type;
1047            if ($label !== null) {
1048                $msgMeta['Label'] = $label;
1049            }
1050            if (isset($meta['file']) && !isset($msgMeta['File'])) {
1051                $msgMeta['File'] = $meta['file'];
1052            }
1053            if (isset($meta['line']) && !isset($msgMeta['Line'])) {
1054                $msgMeta['Line'] = $meta['line'];
1055            }
1056            $msg = '[' . $this->jsonEncode($msgMeta) . ',' . $this->jsonEncode($object, $skipFinalObjectEncode) . ']';
1057        }
1058        
1059        $parts = explode("\n", chunk_split($msg, 5000, "\n"));
1060
1061        for ($i = 0; $i < count($parts); $i++) {
1062            
1063            $part = $parts[$i];
1064            if ($part) {
1065                
1066                if (count($parts) > 2) {
1067                    // Message needs to be split into multiple parts
1068                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1069                                     (($i == 0) ? strlen($msg) : '')
1070                                     . '|' . $part . '|'
1071                                     . (($i < count($parts) - 2) ? '\\' : ''));
1072                } else {
1073                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1074                                     strlen($part) . '|' . $part . '|');
1075                }
1076                
1077                $this->messageIndex++;
1078                
1079                if ($this->messageIndex > 99999) {
1080                    throw $this->newException('Maximum number (99,999) of messages reached!');             
1081                }
1082            }
1083        }
1084    
1085        $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
1086    
1087        return true;
1088    }
1089  
1090    /**
1091     * Standardizes path for windows systems.
1092     *
1093     * @param string $path
1094     * @return string
1095     */
1096    protected function _standardizePath($path)
1097    {
1098        return preg_replace('/\\\\+/', '/', $path);
1099    }
1100  
1101    /**
1102     * Escape trace path for windows systems
1103     *
1104     * @param array $trace
1105     * @return array
1106     */
1107    protected function _escapeTrace($trace)
1108    {
1109        if (!$trace) return $trace;
1110        for ($i = 0; $i < sizeof($trace); $i++) {
1111            if (isset($trace[$i]['file'])) {
1112                $trace[$i]['file'] = $this->_escapeTraceFile($trace[$i]['file']);
1113            }
1114            if (isset($trace[$i]['args'])) {
1115                $trace[$i]['args'] = $this->encodeObject($trace[$i]['args']);
1116            }
1117        }
1118        return $trace;    
1119    }
1120  
1121    /**
1122     * Escape file information of trace for windows systems
1123     *
1124     * @param string $file
1125     * @return string
1126     */
1127    protected function _escapeTraceFile($file)
1128    {
1129        /* Check if we have a windows filepath */
1130        if (strpos($file, '\\')) {
1131            /* First strip down to single \ */
1132
1133            $file = preg_replace('/\\\\+/', '\\', $file);
1134
1135            return $file;
1136        }
1137        return $file;
1138    }
1139
1140    /**
1141     * Check if headers have already been sent
1142     *
1143     * @param string $filename
1144     * @param integer $linenum
1145     */
1146    protected function headersSent(&$filename, &$linenum)
1147    {
1148        return headers_sent($filename, $linenum);
1149    }
1150
1151    /**
1152     * Send header
1153     *
1154     * @param string $name
1155     * @param string $value
1156     */
1157    protected function setHeader($name, $value)
1158    {
1159        return header($name . ': ' . $value);
1160    }
1161
1162    /**
1163     * Get user agent
1164     *
1165     * @return string|false
1166     */
1167    protected function getUserAgent()
1168    {
1169        if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
1170        return $_SERVER['HTTP_USER_AGENT'];
1171    }
1172
1173    /**
1174     * Get all request headers
1175     * 
1176     * @return array
1177     */
1178    public static function getAllRequestHeaders()
1179    {
1180        static $_cachedHeaders = false;
1181        if ($_cachedHeaders !== false) {
1182            return $_cachedHeaders;
1183        }
1184        $headers = array();
1185        if (function_exists('getallheaders')) {
1186            foreach (getallheaders() as $name => $value) {
1187                $headers[strtolower($name)] = $value;
1188            }
1189        } else {
1190            foreach ($_SERVER as $name => $value) {
1191                if (substr($name, 0, 5) == 'HTTP_') {
1192                    $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
1193                }
1194            }
1195        }
1196        return $_cachedHeaders = $headers;
1197    }
1198
1199    /**
1200     * Get a request header
1201     *
1202     * @return string|false
1203     */
1204    protected function getRequestHeader($name)
1205    {
1206        $headers = self::getAllRequestHeaders();
1207        if (isset($headers[strtolower($name)])) {
1208            return $headers[strtolower($name)];
1209        }
1210        return false;
1211    }
1212
1213    /**
1214     * Returns a new exception
1215     *
1216     * @param string $message
1217     * @return Exception
1218     */
1219    protected function newException($message)
1220    {
1221        return new Exception($message);
1222    }
1223  
1224    /**
1225     * Encode an object into a JSON string
1226     * 
1227     * Uses PHP's jeson_encode() if available
1228     * 
1229     * @param object $object The object to be encoded
1230     * @param boolean $skipObjectEncode
1231     * @return string The JSON string
1232     */
1233    public function jsonEncode($object, $skipObjectEncode = false)
1234    {
1235        if (!$skipObjectEncode) {
1236            $object = $this->encodeObject($object);
1237        }
1238        
1239        if (function_exists('json_encode')
1240           && $this->options['useNativeJsonEncode'] != false) {
1241    
1242            return json_encode($object);
1243        } else {
1244            return $this->json_encode($object);
1245        }
1246    }
1247
1248    /**
1249     * Encodes a table by encoding each row and column with encodeObject()
1250     * 
1251     * @param array $table The table to be encoded
1252     * @return array
1253     */  
1254    protected function encodeTable($table)
1255    {
1256        if (!$table) return $table;
1257        
1258        $newTable = array();
1259        foreach ($table as $row) {
1260
1261            if (is_array($row)) {
1262                $newRow = array();
1263
1264                foreach ($row as $item) {
1265                    $newRow[] = $this->encodeObject($item);
1266                }
1267
1268                $newTable[] = $newRow;
1269            }
1270        }
1271
1272        return $newTable;
1273    }
1274
1275    /**
1276     * Encodes an object including members with
1277     * protected and private visibility
1278     * 
1279     * @param object $object The object to be encoded
1280     * @param integer $Depth The current traversal depth
1281     * @return array All members of the object
1282     */
1283    protected function encodeObject($object, $objectDepth = 1, $arrayDepth = 1, $maxDepth = 1)
1284    {
1285        if ($maxDepth > $this->options['maxDepth']) {
1286            return '** Max Depth (' . $this->options['maxDepth'] . ') **';
1287        }
1288
1289        $return = array();
1290    
1291        if (is_resource($object)) {
1292    
1293            return '** ' . (string) $object . ' **';
1294    
1295        } else if (is_object($object)) {
1296    
1297            if ($objectDepth > $this->options['maxObjectDepth']) {
1298                return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
1299            }
1300            
1301            foreach ($this->objectStack as $refVal) {
1302                if ($refVal === $object) {
1303                    return '** Recursion (' . get_class($object) . ') **';
1304                }
1305            }
1306            array_push($this->objectStack, $object);
1307                    
1308            $return['__className'] = $class = get_class($object);
1309            $classLower = strtolower($class);
1310
1311            $reflectionClass = new ReflectionClass($class);
1312            $properties = array();
1313            foreach ($reflectionClass->getProperties() as $property) {
1314                $properties[$property->getName()] = $property;
1315            }
1316                
1317            $members = (array)$object;
1318    
1319            foreach ($properties as $plainName => $property) {
1320    
1321                $name = $rawName = $plainName;
1322                if ($property->isStatic()) {
1323                    $name = 'static:' . $name;
1324                }
1325                if ($property->isPublic()) {
1326                    $name = 'public:' . $name;
1327                } else if ($property->isPrivate()) {
1328                    $name = 'private:' . $name;
1329                    $rawName = "\0" . $class . "\0" . $rawName;
1330                } else if ($property->isProtected()) {
1331                    $name = 'protected:' . $name;
1332                    $rawName = "\0" . '*' . "\0" . $rawName;
1333                }
1334    
1335                if (!(isset($this->objectFilters[$classLower])
1336                     && is_array($this->objectFilters[$classLower])
1337                     && in_array($plainName, $this->objectFilters[$classLower]))) {
1338    
1339                    if (array_key_exists($rawName, $members) && !$property->isStatic()) {
1340                        $return[$name] = $this->encodeObject($members[$rawName], $objectDepth + 1, 1, $maxDepth + 1);
1341                    } else {
1342                        if (method_exists($property, 'setAccessible')) {
1343                            $property->setAccessible(true);
1344                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1345                        } else
1346                        if ($property->isPublic()) {
1347                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1348                        } else {
1349                            $return[$name] = '** Need PHP 5.3 to get value **';
1350                        }
1351                    }
1352                } else {
1353                    $return[$name] = '** Excluded by Filter **';
1354                }
1355            }
1356            
1357            // Include all members that are not defined in the class
1358            // but exist in the object
1359            foreach ($members as $rawName => $value) {
1360    
1361                $name = $rawName;
1362
1363                if ($name{0} == "\0") {
1364                    $parts = explode("\0", $name);
1365                    $name = $parts[2];
1366                }
1367
1368                $plainName = $name;
1369    
1370                if (!isset($properties[$name])) {
1371                    $name = 'undeclared:' . $name;
1372    
1373                    if (!(isset($this->objectFilters[$classLower])
1374                         && is_array($this->objectFilters[$classLower])
1375                         && in_array($plainName, $this->objectFilters[$classLower]))) {
1376    
1377                        $return[$name] = $this->encodeObject($value, $objectDepth + 1, 1, $maxDepth + 1);
1378                    } else {
1379                        $return[$name] = '** Excluded by Filter **';
1380                    }
1381                }
1382            }
1383            
1384            array_pop($this->objectStack);
1385            
1386        } elseif (is_array($object)) {
1387    
1388            if ($arrayDepth > $this->options['maxArrayDepth']) {
1389                return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
1390            }
1391          
1392            foreach ($object as $key => $val) {                
1393
1394                // Encoding the $GLOBALS PHP array causes an infinite loop
1395                // if the recursion is not reset here as it contains
1396                // a reference to itself. This is the only way I have come up
1397                // with to stop infinite recursion in this case.
1398                if ($key == 'GLOBALS'
1399                   && is_array($val)
1400                   && array_key_exists('GLOBALS', $val)) {
1401                    $val['GLOBALS'] = '** Recursion (GLOBALS) **';
1402                }
1403
1404                if (!$this->is_utf8($key)) {
1405                    $key = utf8_encode($key);
1406                }
1407
1408                $return[$key] = $this->encodeObject($val, 1, $arrayDepth + 1, $maxDepth + 1);
1409            }
1410        } else {
1411            if ($this->is_utf8($object)) {
1412                return $object;
1413            } else {
1414                return utf8_encode($object);
1415            }
1416        }
1417        return $return;
1418    }
1419
1420    /**
1421     * Returns true if $string is valid UTF-8 and false otherwise.
1422     *
1423     * @param mixed $str String to be tested
1424     * @return boolean
1425     */
1426    protected function is_utf8($str)
1427    {
1428        if (function_exists('mb_detect_encoding')) {
1429            return (
1430                mb_detect_encoding($str, 'UTF-8', true) == 'UTF-8' &&
1431                ($str === null || $this->jsonEncode($str, true) !== 'null')
1432            );
1433        }
1434        $c = 0;
1435        $b = 0;
1436        $bits = 0;
1437        $len = strlen($str);
1438        for ($i = 0; $i < $len; $i++) {
1439            $c = ord($str[$i]);
1440            if ($c > 128) {
1441                if (($c >= 254)) return false;
1442                elseif ($c >= 252) $bits = 6;
1443                elseif ($c >= 248) $bits = 5;
1444                elseif ($c >= 240) $bits = 4;
1445                elseif ($c >= 224) $bits = 3;
1446                elseif ($c >= 192) $bits = 2;
1447                else return false;
1448                if (($i + $bits) > $len) return false;
1449                while($bits > 1) {
1450                    $i++;
1451                    $b = ord($str[$i]);
1452                    if ($b < 128 || $b > 191) return false;
1453                    $bits--;
1454                }
1455            }
1456        }
1457        return ($str === null || $this->jsonEncode($str, true) !== 'null');
1458    } 
1459
1460    /**
1461     * Converts to and from JSON format.
1462     *
1463     * JSON (JavaScript Object Notation) is a lightweight data-interchange
1464     * format. It is easy for humans to read and write. It is easy for machines
1465     * to parse and generate. It is based on a subset of the JavaScript
1466     * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1467     * This feature can also be found in  Python. JSON is a text format that is
1468     * completely language independent but uses conventions that are familiar
1469     * to programmers of the C-family of languages, including C, C++, C#, Java,
1470     * JavaScript, Perl, TCL, and many others. These properties make JSON an
1471     * ideal data-interchange language.
1472     *
1473     * This package provides a simple encoder and decoder for JSON notation. It
1474     * is intended for use with client-side Javascript applications that make
1475     * use of HTTPRequest to perform server communication functions - data can
1476     * be encoded into JSON notation for use in a client-side javascript, or
1477     * decoded from incoming Javascript requests. JSON format is native to
1478     * Javascript, and can be directly eval()'ed with no further parsing
1479     * overhead
1480     *
1481     * All strings should be in ASCII or UTF-8 format!
1482     *
1483     * LICENSE: Redistribution and use in source and binary forms, with or
1484     * without modification, are permitted provided that the following
1485     * conditions are met: Redistributions of source code must retain the
1486     * above copyright notice, this list of conditions and the following
1487     * disclaimer. Redistributions in binary form must reproduce the above
1488     * copyright notice, this list of conditions and the following disclaimer
1489     * in the documentation and/or other materials provided with the
1490     * distribution.
1491     *
1492     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED…

Large files files are truncated, but you can click here to view the full file