PageRenderTime 63ms CodeModel.GetById 17ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/FirePHPCore/FirePHP.class.php

http://github.com/madiedinro/FirePHPBundle
PHP | 1784 lines | 1029 code | 187 blank | 568 comment | 194 complexity | 48f5cd9953b22b2520dad0052bb63dfe MD5 | raw file

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

   1<?php
   2/**
   3 * *** BEGIN LICENSE BLOCK *****
   4 *  
   5 * This file is part of FirePHP (http://www.firephp.org/).
   6 * 
   7 * Software License Agreement (New BSD License)
   8 * 
   9 * Copyright (c) 2006-2010, Christoph Dorn
  10 * All rights reserved.
  11 * 
  12 * Redistribution and use in source and binary forms, with or without modification,
  13 * are permitted provided that the following conditions are met:
  14 * 
  15 *     * Redistributions of source code must retain the above copyright notice,
  16 *       this list of conditions and the following disclaimer.
  17 * 
  18 *     * Redistributions in binary form must reproduce the above copyright notice,
  19 *       this list of conditions and the following disclaimer in the documentation
  20 *       and/or other materials provided with the distribution.
  21 * 
  22 *     * Neither the name of Christoph Dorn nor the names of its
  23 *       contributors may be used to endorse or promote products derived from this
  24 *       software without specific prior written permission.
  25 * 
  26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  29 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  30 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  33 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36 * 
  37 * ***** END LICENSE BLOCK *****
  38 * 
  39 * @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 */
  44
  45/**
  46 * @see http://code.google.com/p/firephp/issues/detail?id=112
  47 */
  48if (!defined('E_STRICT')) {
  49    define('E_STRICT', 2048);
  50}
  51if (!defined('E_RECOVERABLE_ERROR')) {
  52    define('E_RECOVERABLE_ERROR', 4096);
  53}
  54if (!defined('E_DEPRECATED')) {
  55    define('E_DEPRECATED', 8192);
  56}
  57if (!defined('E_USER_DEPRECATED')) {
  58    define('E_USER_DEPRECATED', 16384);
  59} 
  60 
  61/**
  62 * Sends the given data to the FirePHP Firefox Extension.
  63 * The data can be displayed in the Firebug Console or in the
  64 * "Server" request tab.
  65 * 
  66 * For more information see: http://www.firephp.org/
  67 * 
  68 * @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
  72 */
  73class 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';
  90  
  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    }
 343  
 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    }
 450  
 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    }
 462  
 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    }
 485  
 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    }
 504  
 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    }
 530  
 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    }
 561  
 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    }
 651  
 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    }
1047  
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    }
1058  
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    }
1078  
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    }
1180  
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) {
1217      
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        }
1228        
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            }
1258            
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;
1372            } else {
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     */
1470   
1471     
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

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