PageRenderTime 130ms CodeModel.GetById 79ms app.highlight 42ms RepoModel.GetById 1ms app.codeStats 0ms

/web/selektory/selector/inc/FirePHPCore/FirePHP.class.php4

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