PageRenderTime 58ms CodeModel.GetById 16ms app.highlight 24ms RepoModel.GetById 7ms app.codeStats 0ms

/mkatiano.assets/mkatiano.min213/lib/FirePHP.php

https://bitbucket.org/kioko/mkatiano_v1
PHP | 1370 lines | 748 code | 148 blank | 474 comment | 142 complexity | f619b5a77fee4b21e4397e98d858fbf4 MD5 | raw 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-2008, 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-2008 Christoph Dorn
  40 * @author      Christoph Dorn <christoph@christophdorn.com>
  41 * @license     http://www.opensource.org/licenses/bsd-license.php
  42 * @package     FirePHP
  43 */
  44 
  45 
  46/**
  47 * Sends the given data to the FirePHP Firefox Extension.
  48 * The data can be displayed in the Firebug Console or in the
  49 * "Server" request tab.
  50 * 
  51 * For more information see: http://www.firephp.org/
  52 * 
  53 * @copyright   Copyright (C) 2007-2008 Christoph Dorn
  54 * @author      Christoph Dorn <christoph@christophdorn.com>
  55 * @license     http://www.opensource.org/licenses/bsd-license.php
  56 * @package     FirePHP
  57 */
  58class FirePHP {
  59  
  60  /**
  61   * FirePHP version
  62   *
  63   * @var string
  64   */
  65  const VERSION = '0.2.0';
  66  
  67  /**
  68   * Firebug LOG level
  69   *
  70   * Logs a message to firebug console.
  71   * 
  72   * @var string
  73   */
  74  const LOG = 'LOG';
  75  
  76  /**
  77   * Firebug INFO level
  78   *
  79   * Logs a message to firebug console and displays an info icon before the message.
  80   * 
  81   * @var string
  82   */
  83  const INFO = 'INFO';
  84  
  85  /**
  86   * Firebug WARN level
  87   *
  88   * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
  89   * 
  90   * @var string
  91   */
  92  const WARN = 'WARN';
  93  
  94  /**
  95   * Firebug ERROR level
  96   *
  97   * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
  98   * 
  99   * @var string
 100   */
 101  const ERROR = 'ERROR';
 102  
 103  /**
 104   * Dumps a variable to firebug's server panel
 105   *
 106   * @var string
 107   */
 108  const DUMP = 'DUMP';
 109  
 110  /**
 111   * Displays a stack trace in firebug console
 112   *
 113   * @var string
 114   */
 115  const TRACE = 'TRACE';
 116  
 117  /**
 118   * Displays an exception in firebug console
 119   * 
 120   * Increments the firebug error count.
 121   *
 122   * @var string
 123   */
 124  const EXCEPTION = 'EXCEPTION';
 125  
 126  /**
 127   * Displays an table in firebug console
 128   *
 129   * @var string
 130   */
 131  const TABLE = 'TABLE';
 132  
 133  /**
 134   * Starts a group in firebug console
 135   * 
 136   * @var string
 137   */
 138  const GROUP_START = 'GROUP_START';
 139  
 140  /**
 141   * Ends a group in firebug console
 142   * 
 143   * @var string
 144   */
 145  const GROUP_END = 'GROUP_END';
 146  
 147  /**
 148   * Singleton instance of FirePHP
 149   *
 150   * @var FirePHP
 151   */
 152  protected static $instance = null;
 153  
 154  /**
 155   * Wildfire protocol message index
 156   *
 157   * @var int
 158   */
 159  protected $messageIndex = 1;
 160    
 161  /**
 162   * Options for the library
 163   * 
 164   * @var array
 165   */
 166  protected $options = array();
 167  
 168  /**
 169   * Filters used to exclude object members when encoding
 170   * 
 171   * @var array
 172   */
 173  protected $objectFilters = array();
 174  
 175  /**
 176   * A stack of objects used to detect recursion during object encoding
 177   * 
 178   * @var object
 179   */
 180  protected $objectStack = array();
 181  
 182  /**
 183   * Flag to enable/disable logging
 184   * 
 185   * @var boolean
 186   */
 187  protected $enabled = true;
 188  
 189  /**
 190   * The object constructor
 191   */
 192  function __construct() {
 193    $this->options['maxObjectDepth'] = 10;
 194    $this->options['maxArrayDepth'] = 20;
 195    $this->options['useNativeJsonEncode'] = true;
 196    $this->options['includeLineNumbers'] = true;
 197  }
 198    
 199  /**
 200   * When the object gets serialized only include specific object members.
 201   * 
 202   * @return array
 203   */  
 204  public function __sleep() {
 205    return array('options','objectFilters','enabled');
 206  }
 207    
 208  /**
 209   * Gets singleton instance of FirePHP
 210   *
 211   * @param boolean $AutoCreate
 212   * @return FirePHP
 213   */
 214  public static function getInstance($AutoCreate=false) {
 215    if($AutoCreate===true && !self::$instance) {
 216      self::init();
 217    }
 218    return self::$instance;
 219  }
 220   
 221  /**
 222   * Creates FirePHP object and stores it for singleton access
 223   *
 224   * @return FirePHP
 225   */
 226  public static function init() {
 227    return self::$instance = new self();
 228  }
 229  
 230  /**
 231   * Enable and disable logging to Firebug
 232   * 
 233   * @param boolean $Enabled TRUE to enable, FALSE to disable
 234   * @return void
 235   */
 236  public function setEnabled($Enabled) {
 237    $this->enabled = $Enabled;
 238  }
 239  
 240  /**
 241   * Check if logging is enabled
 242   * 
 243   * @return boolean TRUE if enabled
 244   */
 245  public function getEnabled() {
 246    return $this->enabled;
 247  }
 248  
 249  /**
 250   * Specify a filter to be used when encoding an object
 251   * 
 252   * Filters are used to exclude object members.
 253   * 
 254   * @param string $Class The class name of the object
 255   * @param array $Filter An array or members to exclude
 256   * @return void
 257   */
 258  public function setObjectFilter($Class, $Filter) {
 259    $this->objectFilters[$Class] = $Filter;
 260  }
 261  
 262  /**
 263   * Set some options for the library
 264   * 
 265   * Options:
 266   *  - maxObjectDepth: The maximum depth to traverse objects (default: 10)
 267   *  - maxArrayDepth: The maximum depth to traverse arrays (default: 20)
 268   *  - useNativeJsonEncode: If true will use json_encode() (default: true)
 269   *  - includeLineNumbers: If true will include line numbers and filenames (default: true)
 270   * 
 271   * @param array $Options The options to be set
 272   * @return void
 273   */
 274  public function setOptions($Options) {
 275    $this->options = array_merge($this->options,$Options);
 276  }
 277  
 278  /**
 279   * Register FirePHP as your error handler
 280   * 
 281   * Will throw exceptions for each php error.
 282   */
 283  public function registerErrorHandler()
 284  {
 285    //NOTE: The following errors will not be caught by this error handler:
 286    //      E_ERROR, E_PARSE, E_CORE_ERROR,
 287    //      E_CORE_WARNING, E_COMPILE_ERROR,
 288    //      E_COMPILE_WARNING, E_STRICT
 289    
 290    set_error_handler(array($this,'errorHandler'));     
 291  }
 292
 293  /**
 294   * FirePHP's error handler
 295   * 
 296   * Throws exception for each php error that will occur.
 297   *
 298   * @param int $errno
 299   * @param string $errstr
 300   * @param string $errfile
 301   * @param int $errline
 302   * @param array $errcontext
 303   */
 304  public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
 305  {
 306    // Don't throw exception if error reporting is switched off
 307    if (error_reporting() == 0) {
 308      return;
 309    }
 310    // Only throw exceptions for errors we are asking for
 311    if (error_reporting() & $errno) {
 312      throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
 313    }
 314  }
 315  
 316  /**
 317   * Register FirePHP as your exception handler
 318   */
 319  public function registerExceptionHandler()
 320  {
 321    set_exception_handler(array($this,'exceptionHandler'));     
 322  }
 323  
 324  /**
 325   * FirePHP's exception handler
 326   * 
 327   * Logs all exceptions to your firebug console and then stops the script.
 328   *
 329   * @param Exception $Exception
 330   * @throws Exception
 331   */
 332  function exceptionHandler($Exception) {
 333    $this->fb($Exception);
 334  }
 335  
 336  /**
 337   * Set custom processor url for FirePHP
 338   *
 339   * @param string $URL
 340   */    
 341  public function setProcessorUrl($URL)
 342  {
 343    $this->setHeader('X-FirePHP-ProcessorURL', $URL);
 344  }
 345
 346  /**
 347   * Set custom renderer url for FirePHP
 348   *
 349   * @param string $URL
 350   */
 351  public function setRendererUrl($URL)
 352  {
 353    $this->setHeader('X-FirePHP-RendererURL', $URL);
 354  }
 355  
 356  /**
 357   * Start a group for following messages
 358   *
 359   * @param string $Name
 360   * @return true
 361   * @throws Exception
 362   */
 363  public function group($Name) {
 364    return $this->fb(null, $Name, FirePHP::GROUP_START);
 365  }
 366  
 367  /**
 368   * Ends a group you have started before
 369   *
 370   * @return true
 371   * @throws Exception
 372   */
 373  public function groupEnd() {
 374    return $this->fb(null, null, FirePHP::GROUP_END);
 375  }
 376
 377  /**
 378   * Log object with label to firebug console
 379   *
 380   * @see FirePHP::LOG
 381   * @param mixes $Object
 382   * @param string $Label
 383   * @return true
 384   * @throws Exception
 385   */
 386  public function log($Object, $Label=null) {
 387    return $this->fb($Object, $Label, FirePHP::LOG);
 388  } 
 389
 390  /**
 391   * Log object with label to firebug console
 392   *
 393   * @see FirePHP::INFO
 394   * @param mixes $Object
 395   * @param string $Label
 396   * @return true
 397   * @throws Exception
 398   */
 399  public function info($Object, $Label=null) {
 400    return $this->fb($Object, $Label, FirePHP::INFO);
 401  } 
 402
 403  /**
 404   * Log object with label to firebug console
 405   *
 406   * @see FirePHP::WARN
 407   * @param mixes $Object
 408   * @param string $Label
 409   * @return true
 410   * @throws Exception
 411   */
 412  public function warn($Object, $Label=null) {
 413    return $this->fb($Object, $Label, FirePHP::WARN);
 414  } 
 415
 416  /**
 417   * Log object with label to firebug console
 418   *
 419   * @see FirePHP::ERROR
 420   * @param mixes $Object
 421   * @param string $Label
 422   * @return true
 423   * @throws Exception
 424   */
 425  public function error($Object, $Label=null) {
 426    return $this->fb($Object, $Label, FirePHP::ERROR);
 427  } 
 428
 429  /**
 430   * Dumps key and variable to firebug server panel
 431   *
 432   * @see FirePHP::DUMP
 433   * @param string $Key
 434   * @param mixed $Variable
 435   * @return true
 436   * @throws Exception
 437   */
 438  public function dump($Key, $Variable) {
 439    return $this->fb($Variable, $Key, FirePHP::DUMP);
 440  }
 441  
 442  /**
 443   * Log a trace in the firebug console
 444   *
 445   * @see FirePHP::TRACE
 446   * @param string $Label
 447   * @return true
 448   * @throws Exception
 449   */
 450  public function trace($Label) {
 451    return $this->fb($Label, FirePHP::TRACE);
 452  } 
 453
 454  /**
 455   * Log a table in the firebug console
 456   *
 457   * @see FirePHP::TABLE
 458   * @param string $Label
 459   * @param string $Table
 460   * @return true
 461   * @throws Exception
 462   */
 463  public function table($Label, $Table) {
 464    return $this->fb($Table, $Label, FirePHP::TABLE);
 465  }
 466  
 467  /**
 468   * Check if FirePHP is installed on client
 469   *
 470   * @return boolean
 471   */
 472  public function detectClientExtension() {
 473    /* Check if FirePHP is installed on client */
 474    if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) ||
 475       !version_compare($m[1][0],'0.0.6','>=')) {
 476      return false;
 477    }
 478    return true;    
 479  }
 480 
 481  /**
 482   * Log varible to Firebug
 483   * 
 484   * @see http://www.firephp.org/Wiki/Reference/Fb
 485   * @param mixed $Object The variable to be logged
 486   * @return true Return TRUE if message was added to headers, FALSE otherwise
 487   * @throws Exception
 488   */
 489  public function fb($Object) {
 490  
 491    if(!$this->enabled) {
 492      return false;
 493    }
 494  
 495    if (headers_sent($filename, $linenum)) {
 496        throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
 497    }
 498  
 499    $Type = null;
 500    $Label = null;
 501  
 502    if(func_num_args()==1) {
 503    } else
 504    if(func_num_args()==2) {
 505      switch(func_get_arg(1)) {
 506        case self::LOG:
 507        case self::INFO:
 508        case self::WARN:
 509        case self::ERROR:
 510        case self::DUMP:
 511        case self::TRACE:
 512        case self::EXCEPTION:
 513        case self::TABLE:
 514        case self::GROUP_START:
 515        case self::GROUP_END:
 516          $Type = func_get_arg(1);
 517          break;
 518        default:
 519          $Label = func_get_arg(1);
 520          break;
 521      }
 522    } else
 523    if(func_num_args()==3) {
 524      $Type = func_get_arg(2);
 525      $Label = func_get_arg(1);
 526    } else {
 527      throw $this->newException('Wrong number of arguments to fb() function!');
 528    }
 529  
 530  
 531    if(!$this->detectClientExtension()) {
 532      return false;
 533    }
 534  
 535    $meta = array();
 536    $skipFinalObjectEncode = false;
 537  
 538    if($Object instanceof Exception) {
 539
 540      $meta['file'] = $this->_escapeTraceFile($Object->getFile());
 541      $meta['line'] = $Object->getLine();
 542      
 543      $trace = $Object->getTrace();
 544      if($Object instanceof ErrorException
 545         && isset($trace[0]['function'])
 546         && $trace[0]['function']=='errorHandler'
 547         && isset($trace[0]['class'])
 548         && $trace[0]['class']=='FirePHP') {
 549           
 550        $severity = false;
 551        switch($Object->getSeverity()) {
 552          case E_WARNING: $severity = 'E_WARNING'; break;
 553          case E_NOTICE: $severity = 'E_NOTICE'; break;
 554          case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
 555          case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
 556          case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
 557          case E_STRICT: $severity = 'E_STRICT'; break;
 558          case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
 559          case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
 560          case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
 561        }
 562           
 563        $Object = array('Class'=>get_class($Object),
 564                        'Message'=>$severity.': '.$Object->getMessage(),
 565                        'File'=>$this->_escapeTraceFile($Object->getFile()),
 566                        'Line'=>$Object->getLine(),
 567                        'Type'=>'trigger',
 568                        'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
 569        $skipFinalObjectEncode = true;
 570      } else {
 571        $Object = array('Class'=>get_class($Object),
 572                        'Message'=>$Object->getMessage(),
 573                        'File'=>$this->_escapeTraceFile($Object->getFile()),
 574                        'Line'=>$Object->getLine(),
 575                        'Type'=>'throw',
 576                        'Trace'=>$this->_escapeTrace($trace));
 577        $skipFinalObjectEncode = true;
 578      }
 579      $Type = self::EXCEPTION;
 580      
 581    } else
 582    if($Type==self::TRACE) {
 583      
 584      $trace = debug_backtrace();
 585      if(!$trace) return false;
 586      for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
 587
 588        if(isset($trace[$i]['class'])
 589           && isset($trace[$i]['file'])
 590           && ($trace[$i]['class']=='FirePHP'
 591               || $trace[$i]['class']=='FB')
 592           && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
 593               || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
 594          /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
 595        } else
 596        if(isset($trace[$i]['class'])
 597           && isset($trace[$i+1]['file'])
 598           && $trace[$i]['class']=='FirePHP'
 599           && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
 600          /* Skip fb() */
 601        } else
 602        if($trace[$i]['function']=='fb'
 603           || $trace[$i]['function']=='trace'
 604           || $trace[$i]['function']=='send') {
 605          $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
 606                          'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
 607                          'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
 608                          'Message'=>$trace[$i]['args'][0],
 609                          'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
 610                          'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
 611                          'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
 612                          'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
 613
 614          $skipFinalObjectEncode = true;
 615          $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
 616          $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
 617          break;
 618        }
 619      }
 620
 621    } else
 622    if($Type==self::TABLE) {
 623      
 624      if(isset($Object[0]) && is_string($Object[0])) {
 625        $Object[1] = $this->encodeTable($Object[1]);
 626      } else {
 627        $Object = $this->encodeTable($Object);
 628      }
 629
 630      $skipFinalObjectEncode = true;
 631      
 632    } else {
 633      if($Type===null) {
 634        $Type = self::LOG;
 635      }
 636    }
 637    
 638    if($this->options['includeLineNumbers']) {
 639      if(!isset($meta['file']) || !isset($meta['line'])) {
 640
 641        $trace = debug_backtrace();
 642        for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
 643  
 644          if(isset($trace[$i]['class'])
 645             && isset($trace[$i]['file'])
 646             && ($trace[$i]['class']=='FirePHP'
 647                 || $trace[$i]['class']=='FB')
 648             && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
 649                 || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
 650            /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
 651          } else
 652          if(isset($trace[$i]['class'])
 653             && isset($trace[$i+1]['file'])
 654             && $trace[$i]['class']=='FirePHP'
 655             && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
 656            /* Skip fb() */
 657          } else
 658          if(isset($trace[$i]['file'])
 659             && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
 660            /* Skip FB::fb() */
 661          } else {
 662            $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
 663            $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
 664            break;
 665          }
 666        }      
 667      
 668      }
 669    } else {
 670      unset($meta['file']);
 671      unset($meta['line']);
 672    }
 673
 674  	$this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
 675  	$this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
 676 
 677    $structure_index = 1;
 678    if($Type==self::DUMP) {
 679      $structure_index = 2;
 680    	$this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
 681    } else {
 682    	$this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
 683    }
 684  
 685    if($Type==self::DUMP) {
 686    	$msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
 687    } else {
 688      $msg_meta = array('Type'=>$Type);
 689      if($Label!==null) {
 690        $msg_meta['Label'] = $Label;
 691      }
 692      if(isset($meta['file'])) {
 693        $msg_meta['File'] = $meta['file'];
 694      }
 695      if(isset($meta['line'])) {
 696        $msg_meta['Line'] = $meta['line'];
 697      }
 698    	$msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
 699    }
 700    
 701    $parts = explode("\n",chunk_split($msg, 5000, "\n"));
 702
 703    for( $i=0 ; $i<count($parts) ; $i++) {
 704        
 705        $part = $parts[$i];
 706        if ($part) {
 707            
 708            if(count($parts)>2) {
 709              // Message needs to be split into multiple parts
 710              $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
 711                               (($i==0)?strlen($msg):'')
 712                               . '|' . $part . '|'
 713                               . (($i<count($parts)-2)?'\\':''));
 714            } else {
 715              $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
 716                               strlen($part) . '|' . $part . '|');
 717            }
 718            
 719            $this->messageIndex++;
 720            
 721            if ($this->messageIndex > 99999) {
 722                throw new Exception('Maximum number (99,999) of messages reached!');             
 723            }
 724        }
 725    }
 726
 727  	$this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
 728
 729    return true;
 730  }
 731  
 732  /**
 733   * Standardizes path for windows systems.
 734   *
 735   * @param string $Path
 736   * @return string
 737   */
 738  protected function _standardizePath($Path) {
 739    return preg_replace('/\\\\+/','/',$Path);    
 740  }
 741  
 742  /**
 743   * Escape trace path for windows systems
 744   *
 745   * @param array $Trace
 746   * @return array
 747   */
 748  protected function _escapeTrace($Trace) {
 749    if(!$Trace) return $Trace;
 750    for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
 751      if(isset($Trace[$i]['file'])) {
 752        $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
 753      }
 754      if(isset($Trace[$i]['args'])) {
 755        $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
 756      }
 757    }
 758    return $Trace;    
 759  }
 760  
 761  /**
 762   * Escape file information of trace for windows systems
 763   *
 764   * @param string $File
 765   * @return string
 766   */
 767  protected function _escapeTraceFile($File) {
 768    /* Check if we have a windows filepath */
 769    if(strpos($File,'\\')) {
 770      /* First strip down to single \ */
 771      
 772      $file = preg_replace('/\\\\+/','\\',$File);
 773      
 774      return $file;
 775    }
 776    return $File;
 777  }
 778
 779  /**
 780   * Send header
 781   *
 782   * @param string $Name
 783   * @param string_type $Value
 784   */
 785  protected function setHeader($Name, $Value) {
 786    return header($Name.': '.$Value);
 787  }
 788
 789  /**
 790   * Get user agent
 791   *
 792   * @return string|false
 793   */
 794  protected function getUserAgent() {
 795    if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
 796    return $_SERVER['HTTP_USER_AGENT'];
 797  }
 798
 799  /**
 800   * Returns a new exception
 801   *
 802   * @param string $Message
 803   * @return Exception
 804   */
 805  protected function newException($Message) {
 806    return new Exception($Message);
 807  }
 808  
 809  /**
 810   * Encode an object into a JSON string
 811   * 
 812   * Uses PHP's jeson_encode() if available
 813   * 
 814   * @param object $Object The object to be encoded
 815   * @return string The JSON string
 816   */
 817  protected function jsonEncode($Object, $skipObjectEncode=false)
 818  {
 819    if(!$skipObjectEncode) {
 820      $Object = $this->encodeObject($Object);
 821    }
 822    
 823    if(function_exists('json_encode')
 824       && $this->options['useNativeJsonEncode']!=false) {
 825
 826      return json_encode($Object);
 827    } else {
 828      return $this->json_encode($Object);
 829    }
 830  }
 831  
 832  /**
 833   * Encodes a table by encoding each row and column with encodeObject()
 834   * 
 835   * @param array $Table The table to be encoded
 836   * @return array
 837   */  
 838  protected function encodeTable($Table) {
 839    if(!$Table) return $Table;
 840    for( $i=0 ; $i<count($Table) ; $i++ ) {
 841      if(is_array($Table[$i])) {
 842        for( $j=0 ; $j<count($Table[$i]) ; $j++ ) {
 843          $Table[$i][$j] = $this->encodeObject($Table[$i][$j]);
 844        }
 845      }
 846    }
 847    return $Table;
 848  }
 849  
 850  /**
 851   * Encodes an object including members with
 852   * protected and private visibility
 853   * 
 854   * @param Object $Object The object to be encoded
 855   * @param int $Depth The current traversal depth
 856   * @return array All members of the object
 857   */
 858  protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
 859  {
 860    $return = array();
 861    
 862    if (is_object($Object)) {
 863
 864        if ($ObjectDepth > $this->options['maxObjectDepth']) {
 865          return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
 866        }
 867        
 868        foreach ($this->objectStack as $refVal) {
 869            if ($refVal === $Object) {
 870                return '** Recursion ('.get_class($Object).') **';
 871            }
 872        }
 873        array_push($this->objectStack, $Object);
 874                
 875        $return['__className'] = $class = get_class($Object);
 876
 877        $reflectionClass = new ReflectionClass($class);  
 878        $properties = array();
 879        foreach( $reflectionClass->getProperties() as $property) {
 880          $properties[$property->getName()] = $property;
 881        }
 882            
 883        $members = (array)$Object;
 884            
 885        foreach( $properties as $raw_name => $property ) {
 886          
 887          $name = $raw_name;
 888          if($property->isStatic()) {
 889            $name = 'static:'.$name;
 890          }
 891          if($property->isPublic()) {
 892            $name = 'public:'.$name;
 893          } else
 894          if($property->isPrivate()) {
 895            $name = 'private:'.$name;
 896            $raw_name = "\0".$class."\0".$raw_name;
 897          } else
 898          if($property->isProtected()) {
 899            $name = 'protected:'.$name;
 900            $raw_name = "\0".'*'."\0".$raw_name;
 901          }
 902          
 903          if(!(isset($this->objectFilters[$class])
 904               && is_array($this->objectFilters[$class])
 905               && in_array($raw_name,$this->objectFilters[$class]))) {
 906
 907            if(array_key_exists($raw_name,$members)
 908               && !$property->isStatic()) {
 909              
 910              $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1);      
 911            
 912            } else {
 913              if(method_exists($property,'setAccessible')) {
 914                $property->setAccessible(true);
 915                $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
 916              } else
 917              if($property->isPublic()) {
 918                $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1);
 919              } else {
 920                $return[$name] = '** Need PHP 5.3 to get value **';
 921              }
 922            }
 923          } else {
 924            $return[$name] = '** Excluded by Filter **';
 925          }
 926        }
 927        
 928        // Include all members that are not defined in the class
 929        // but exist in the object
 930        foreach( $members as $raw_name => $value ) {
 931          
 932          $name = $raw_name;
 933          
 934          if ($name{0} == "\0") {
 935            $parts = explode("\0", $name);
 936            $name = $parts[2];
 937          }
 938          
 939          if(!isset($properties[$name])) {
 940            $name = 'undeclared:'.$name;
 941              
 942            if(!(isset($this->objectFilters[$class])
 943                 && is_array($this->objectFilters[$class])
 944                 && in_array($raw_name,$this->objectFilters[$class]))) {
 945              
 946              $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
 947            } else {
 948              $return[$name] = '** Excluded by Filter **';
 949            }
 950          }
 951        }
 952        
 953        array_pop($this->objectStack);
 954        
 955    } elseif (is_array($Object)) {
 956
 957        if ($ArrayDepth > $this->options['maxArrayDepth']) {
 958          return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
 959        }
 960      
 961        foreach ($Object as $key => $val) {
 962          
 963          // Encoding the $GLOBALS PHP array causes an infinite loop
 964          // if the recursion is not reset here as it contains
 965          // a reference to itself. This is the only way I have come up
 966          // with to stop infinite recursion in this case.
 967          if($key=='GLOBALS'
 968             && is_array($val)
 969             && array_key_exists('GLOBALS',$val)) {
 970            $val['GLOBALS'] = '** Recursion (GLOBALS) **';
 971          }
 972          
 973          $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
 974        }
 975    } else {
 976      if(self::is_utf8($Object)) {
 977        return $Object;
 978      } else {
 979        return utf8_encode($Object);
 980      }
 981    }
 982    return $return;
 983  }
 984
 985  /**
 986   * Returns true if $string is valid UTF-8 and false otherwise.
 987   *
 988   * @param mixed $str String to be tested
 989   * @return boolean
 990   */
 991  protected static function is_utf8($str) {
 992    $c=0; $b=0;
 993    $bits=0;
 994    $len=strlen($str);
 995    for($i=0; $i<$len; $i++){
 996        $c=ord($str[$i]);
 997        if($c > 128){
 998            if(($c >= 254)) return false;
 999            elseif($c >= 252) $bits=6;
1000            elseif($c >= 248) $bits=5;
1001            elseif($c >= 240) $bits=4;
1002            elseif($c >= 224) $bits=3;
1003            elseif($c >= 192) $bits=2;
1004            else return false;
1005            if(($i+$bits) > $len) return false;
1006            while($bits > 1){
1007                $i++;
1008                $b=ord($str[$i]);
1009                if($b < 128 || $b > 191) return false;
1010                $bits--;
1011            }
1012        }
1013    }
1014    return true;
1015  } 
1016
1017  /**
1018   * Converts to and from JSON format.
1019   *
1020   * JSON (JavaScript Object Notation) is a lightweight data-interchange
1021   * format. It is easy for humans to read and write. It is easy for machines
1022   * to parse and generate. It is based on a subset of the JavaScript
1023   * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1024   * This feature can also be found in  Python. JSON is a text format that is
1025   * completely language independent but uses conventions that are familiar
1026   * to programmers of the C-family of languages, including C, C++, C#, Java,
1027   * JavaScript, Perl, TCL, and many others. These properties make JSON an
1028   * ideal data-interchange language.
1029   *
1030   * This package provides a simple encoder and decoder for JSON notation. It
1031   * is intended for use with client-side Javascript applications that make
1032   * use of HTTPRequest to perform server communication functions - data can
1033   * be encoded into JSON notation for use in a client-side javascript, or
1034   * decoded from incoming Javascript requests. JSON format is native to
1035   * Javascript, and can be directly eval()'ed with no further parsing
1036   * overhead
1037   *
1038   * All strings should be in ASCII or UTF-8 format!
1039   *
1040   * LICENSE: Redistribution and use in source and binary forms, with or
1041   * without modification, are permitted provided that the following
1042   * conditions are met: Redistributions of source code must retain the
1043   * above copyright notice, this list of conditions and the following
1044   * disclaimer. Redistributions in binary form must reproduce the above
1045   * copyright notice, this list of conditions and the following disclaimer
1046   * in the documentation and/or other materials provided with the
1047   * distribution.
1048   *
1049   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1050   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1051   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1052   * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1053   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1054   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1055   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1056   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1057   * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1058   * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1059   * DAMAGE.
1060   *
1061   * @category
1062   * @package     Services_JSON
1063   * @author      Michal Migurski <mike-json@teczno.com>
1064   * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
1065   * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1066   * @author      Christoph Dorn <christoph@christophdorn.com>
1067   * @copyright   2005 Michal Migurski
1068   * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1069   * @license     http://www.opensource.org/licenses/bsd-license.php
1070   * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1071   */
1072   
1073     
1074  /**
1075   * Keep a list of objects as we descend into the array so we can detect recursion.
1076   */
1077  private $json_objectStack = array();
1078
1079
1080 /**
1081  * convert a string from one UTF-8 char to one UTF-16 char
1082  *
1083  * Normally should be handled by mb_convert_encoding, but
1084  * provides a slower PHP-only method for installations
1085  * that lack the multibye string extension.
1086  *
1087  * @param    string  $utf8   UTF-8 character
1088  * @return   string  UTF-16 character
1089  * @access   private
1090  */
1091  private function json_utf82utf16($utf8)
1092  {
1093      // oh please oh please oh please oh please oh please
1094      if(function_exists('mb_convert_encoding')) {
1095          return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1096      }
1097
1098      switch(strlen($utf8)) {
1099          case 1:
1100              // this case should never be reached, because we are in ASCII range
1101              // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1102              return $utf8;
1103
1104          case 2:
1105              // return a UTF-16 character from a 2-byte UTF-8 char
1106              // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1107              return chr(0x07 & (ord($utf8{0}) >> 2))
1108                   . chr((0xC0 & (ord($utf8{0}) << 6))
1109                       | (0x3F & ord($utf8{1})));
1110
1111          case 3:
1112              // return a UTF-16 character from a 3-byte UTF-8 char
1113              // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1114              return chr((0xF0 & (ord($utf8{0}) << 4))
1115                       | (0x0F & (ord($utf8{1}) >> 2)))
1116                   . chr((0xC0 & (ord($utf8{1}) << 6))
1117                       | (0x7F & ord($utf8{2})));
1118      }
1119
1120      // ignoring UTF-32 for now, sorry
1121      return '';
1122  }
1123
1124 /**
1125  * encodes an arbitrary variable into JSON format
1126  *
1127  * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
1128  *                           see argument 1 to Services_JSON() above for array-parsing behavior.
1129  *                           if var is a strng, note that encode() always expects it
1130  *                           to be in ASCII or UTF-8 format!
1131  *
1132  * @return   mixed   JSON string representation of input var or an error if a problem occurs
1133  * @access   public
1134  */
1135  private function json_encode($var)
1136  {
1137    
1138    if(is_object($var)) {
1139      if(in_array($var,$this->json_objectStack)) {
1140        return '"** Recursion **"';
1141      }
1142    }
1143          
1144      switch (gettype($var)) {
1145          case 'boolean':
1146              return $var ? 'true' : 'false';
1147
1148          case 'NULL':
1149              return 'null';
1150
1151          case 'integer':
1152              return (int) $var;
1153
1154          case 'double':
1155          case 'float':
1156              return (float) $var;
1157
1158          case 'string':
1159              // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1160              $ascii = '';
1161              $strlen_var = strlen($var);
1162
1163             /*
1164              * Iterate over every character in the string,
1165              * escaping with a slash or encoding to UTF-8 where necessary
1166              */
1167              for ($c = 0; $c < $strlen_var; ++$c) {
1168
1169                  $ord_var_c = ord($var{$c});
1170
1171                  switch (true) {
1172                      case $ord_var_c == 0x08:
1173                          $ascii .= '\b';
1174                          break;
1175                      case $ord_var_c == 0x09:
1176                          $ascii .= '\t';
1177                          break;
1178                      case $ord_var_c == 0x0A:
1179                          $ascii .= '\n';
1180                          break;
1181                      case $ord_var_c == 0x0C:
1182                          $ascii .= '\f';
1183                          break;
1184                      case $ord_var_c == 0x0D:
1185                          $ascii .= '\r';
1186                          break;
1187
1188                      case $ord_var_c == 0x22:
1189                      case $ord_var_c == 0x2F:
1190                      case $ord_var_c == 0x5C:
1191                          // double quote, slash, slosh
1192                          $ascii .= '\\'.$var{$c};
1193                          break;
1194
1195                      case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1196                          // characters U-00000000 - U-0000007F (same as ASCII)
1197                          $ascii .= $var{$c};
1198                          break;
1199
1200                      case (($ord_var_c & 0xE0) == 0xC0):
1201                          // characters U-00000080 - U-000007FF, mask 110XXXXX
1202                          // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1203                          $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1204                          $c += 1;
1205                          $utf16 = $this->json_utf82utf16($char);
1206                          $ascii .= sprintf('\u%04s', bin2hex($utf16));
1207                          break;
1208
1209                      case (($ord_var_c & 0xF0) == 0xE0):
1210                          // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1211                          // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1212                          $char = pack('C*', $ord_var_c,
1213                                       ord($var{$c + 1}),
1214                                       ord($var{$c + 2}));
1215                          $c += 2;
1216                          $utf16 = $this->json_utf82utf16($char);
1217                          $ascii .= sprintf('\u%04s', bin2hex($utf16));
1218                          break;
1219
1220                      case (($ord_var_c & 0xF8) == 0xF0):
1221                          // characters U-00010000 - U-001FFFFF, mask 11110XXX
1222                          // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1223                          $char = pack('C*', $ord_var_c,
1224                                       ord($var{$c + 1}),
1225                                       ord($var{$c + 2}),
1226                                       ord($var{$c + 3}));
1227                          $c += 3;
1228                          $utf16 = $this->json_utf82utf16($char);
1229                          $ascii .= sprintf('\u%04s', bin2hex($utf16));
1230                          break;
1231
1232                      case (($ord_var_c & 0xFC) == 0xF8):
1233                          // characters U-00200000 - U-03FFFFFF, mask 111110XX
1234                          // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1235                          $char = pack('C*', $ord_var_c,
1236                                       ord($var{$c + 1}),
1237                                       ord($var{$c + 2}),
1238                                       ord($var{$c + 3}),
1239                                       ord($var{$c + 4}));
1240                          $c += 4;
1241                          $utf16 = $this->json_utf82utf16($char);
1242                          $ascii .= sprintf('\u%04s', bin2hex($utf16));
1243                          break;
1244
1245                      case (($ord_var_c & 0xFE) == 0xFC):
1246                          // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1247                          // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1248                          $char = pack('C*', $ord_var_c,
1249                                       ord($var{$c + 1}),
1250                                       ord($var{$c + 2}),
1251                                       ord($var{$c + 3}),
1252                                       ord($var{$c + 4}),
1253                                       ord($var{$c + 5}));
1254                          $c += 5;
1255                          $utf16 = $this->json_utf82utf16($char);
1256                          $ascii .= sprintf('\u%04s', bin2hex($utf16));
1257                          break;
1258                  }
1259              }
1260
1261              return '"'.$ascii.'"';
1262
1263          case 'array':
1264             /*
1265              * As per JSON spec if any array key is not an integer
1266              * we must treat the the whole array as an object. We
1267              * also try to catch a sparsely populated associative
1268              * array with numeric keys here because some JS engines
1269              * will create an array with empty indexes up to
1270              * max_index which can cause memory issues and because
1271              * the keys, which may be relevant, will be remapped
1272              * otherwise.
1273              *
1274              * As per the ECMA and JSON specification an object may
1275              * have any string as a property. Unfortunately due to
1276              * a hole in the ECMA specification if the key is a
1277              * ECMA reserved word or starts with a digit the
1278              * parameter is only accessible using ECMAScript's
1279              * bracket notation.
1280              */
1281
1282              // treat as a JSON object
1283              if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1284                  
1285                  $this->json_objectStack[] = $var;
1286
1287                  $properties = array_map(array($this, 'json_name_value'),
1288                                          array_keys($var),
1289                                          array_values($var));
1290
1291                  array_pop($this->json_objectStack);
1292
1293                  foreach($properties as $property) {
1294                      if($property instanceof Exception) {
1295                          return $property;
1296                      }
1297                  }
1298
1299                  return '{' . join(',', $properties) . '}';
1300              }
1301
1302              $this->json_objectStack[] = $var;
1303
1304              // treat it like a regular array
1305              $elements = array_map(array($this, 'json_encode'), $var);
1306
1307              array_pop($this->json_objectStack);
1308
1309              foreach($elements as $element) {
1310                  if($element instanceof Exception) {
1311                      return $element;
1312                  }
1313              }
1314
1315              return '[' . join(',', $elements) . ']';
1316
1317          case 'object':
1318              $vars = self::encodeObject($var);
1319
1320              $this->json_objectStack[] = $var;
1321
1322              $properties = array_map(array($this, 'json_name_value'),
1323                                      array_keys($vars),
1324                                      array_values($vars));
1325
1326              array_pop($this->json_objectStack);
1327              
1328              foreach($properties as $property) {
1329                  if($property instanceof Exception) {
1330                      return $property;
1331                  }
1332              }
1333                     
1334              return '{' . join(',', $properties) . '}';
1335
1336          default:
1337              return null;
1338      }
1339  }
1340
1341 /**
1342  * array-walking function for use in generating JSON-formatted name-value pairs
1343  *
1344  * @param    string  $name   name of key to use
1345  * @param    mixed   $value  reference to an array element to be encoded
1346  *
1347  * @return   string  JSON-formatted name-value pair, like '"name":value'
1348  * @access   private
1349  */
1350  private function json_name_value($name, $value)
1351  {
1352      // Encoding the $GLOBALS PHP array causes an infinite loop
1353      // if the recursion is not reset here as it contains
1354      // a reference to itself. This is the only way I have come up
1355      // with to stop infinite recursion in this case.
1356      if($name=='GLOBALS'
1357         && is_array($value)
1358         && array_key_exists('GLOBALS',$value)) {
1359        $value['GLOBALS'] = '** Recursion **';
1360      }
1361    
1362      $encoded_value = $this->json_encode($value);
1363
1364      if($encoded_value instanceof Exception) {
1365          return $encoded_value;
1366      }
1367
1368      return $this->json_encode(strval($name)) . ':' . $encoded_value;
1369  }
1370}