Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
 */
54
define('FirePHP_VERSION', '0.3');
55
 
56
/**
57
 * Firebug LOG level
58
 *
59
 * Logs a message to firebug console
60
 *
61
 * @var string
62
 */
63
define('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
 */
72
define('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
 */
81
define('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
 */
90
define('FirePHP_ERROR', 'ERROR');
91
 
92
/**
93
 * Dumps a variable to firebug's server panel
94
 *
95
 * @var string
96
 */
97
define('FirePHP_DUMP', 'DUMP');
98
 
99
/**
100
 * Displays a stack trace in firebug console
101
 *
102
 * @var string
103
 */
104
define('FirePHP_TRACE', 'TRACE');
105
 
106
/**
107
 * Displays a table in firebug console
108
 *
109
 * @var string
110
 */
111
define('FirePHP_TABLE', 'TABLE');
112
 
113
/**
114
 * Starts a group in firebug console
115
 *
116
 * @var string
117
 */
118
define('FirePHP_GROUP_START', 'GROUP_START');
119
 
120
/**
121
 * Ends a group in firebug console
122
 *
123
 * @var string
124
 */
125
define('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
 */
140
class 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